blob: 10c46101123cc02601bb0d669d77b7f8da35e3d6 [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
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070019#include <memory>
20#include <string>
21
Adam Lesinskice5e56e2016-10-21 17:56:45 -070022#include "android-base/macros.h"
23
24#include "util/Util.h"
25#include "xml/XmlDom.h"
26
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070027namespace aapt {
28namespace proguard {
29
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070030class BaseVisitor : public xml::Visitor {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070031 public:
Adam Lesinski6b372992017-08-09 10:54:23 -070032 using xml::Visitor::Visit;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070033
Adam Lesinski6b372992017-08-09 10:54:23 -070034 BaseVisitor(const Source& source, KeepSet* keep_set) : source_(source), keep_set_(keep_set) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070035 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070036
Adam Lesinski6b372992017-08-09 10:54:23 -070037 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070038 if (!node->namespace_uri.empty()) {
39 Maybe<xml::ExtractedPackage> maybe_package =
40 xml::ExtractPackageFromNamespace(node->namespace_uri);
41 if (maybe_package) {
42 // This is a custom view, let's figure out the class name from this.
43 std::string package = maybe_package.value().package + "." + node->name;
44 if (util::IsJavaClassName(package)) {
45 AddClass(node->line_number, package);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070046 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070047 }
48 } else if (util::IsJavaClassName(node->name)) {
49 AddClass(node->line_number, node->name);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070050 }
51
Adam Lesinskice5e56e2016-10-21 17:56:45 -070052 for (const auto& child : node->children) {
53 child->Accept(this);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070054 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070055 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070056
Adam Lesinskice5e56e2016-10-21 17:56:45 -070057 protected:
58 void AddClass(size_t line_number, const std::string& class_name) {
59 keep_set_->AddClass(Source(source_.path, line_number), class_name);
60 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070061
Adam Lesinskice5e56e2016-10-21 17:56:45 -070062 void AddMethod(size_t line_number, const std::string& method_name) {
63 keep_set_->AddMethod(Source(source_.path, line_number), method_name);
64 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070065
Adam Lesinskice5e56e2016-10-21 17:56:45 -070066 private:
67 DISALLOW_COPY_AND_ASSIGN(BaseVisitor);
68
69 Source source_;
70 KeepSet* keep_set_;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070071};
72
Adam Lesinskice5e56e2016-10-21 17:56:45 -070073class LayoutVisitor : public BaseVisitor {
74 public:
Adam Lesinski6b372992017-08-09 10:54:23 -070075 LayoutVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
76 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070077
Adam Lesinski6b372992017-08-09 10:54:23 -070078 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070079 bool check_class = false;
80 bool check_name = false;
81 if (node->namespace_uri.empty()) {
Adam Lesinskif762df22017-06-26 16:39:03 -070082 if (node->name == "view") {
83 check_class = true;
84 } else if (node->name == "fragment") {
85 check_class = check_name = true;
86 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070087 } else if (node->namespace_uri == xml::kSchemaAndroid) {
88 check_name = node->name == "fragment";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070089 }
90
Adam Lesinskice5e56e2016-10-21 17:56:45 -070091 for (const auto& attr : node->attributes) {
92 if (check_class && attr.namespace_uri.empty() && attr.name == "class" &&
93 util::IsJavaClassName(attr.value)) {
94 AddClass(node->line_number, attr.value);
95 } else if (check_name && attr.namespace_uri == xml::kSchemaAndroid &&
96 attr.name == "name" && util::IsJavaClassName(attr.value)) {
97 AddClass(node->line_number, attr.value);
98 } else if (attr.namespace_uri == xml::kSchemaAndroid &&
99 attr.name == "onClick") {
100 AddMethod(node->line_number, attr.value);
101 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700102 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700103
104 BaseVisitor::Visit(node);
105 }
106
107 private:
108 DISALLOW_COPY_AND_ASSIGN(LayoutVisitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700109};
110
Adam Lesinskif762df22017-06-26 16:39:03 -0700111class MenuVisitor : public BaseVisitor {
112 public:
113 MenuVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
114 }
115
Adam Lesinski6b372992017-08-09 10:54:23 -0700116 void Visit(xml::Element* node) override {
Adam Lesinskif762df22017-06-26 16:39:03 -0700117 if (node->namespace_uri.empty() && node->name == "item") {
118 for (const auto& attr : node->attributes) {
119 if (attr.namespace_uri == xml::kSchemaAndroid) {
120 if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
121 util::IsJavaClassName(attr.value)) {
122 AddClass(node->line_number, attr.value);
123 } else if (attr.name == "onClick") {
124 AddMethod(node->line_number, attr.value);
125 }
126 }
127 }
128 }
129
130 BaseVisitor::Visit(node);
131 }
132
133 private:
134 DISALLOW_COPY_AND_ASSIGN(MenuVisitor);
135};
136
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700137class XmlResourceVisitor : public BaseVisitor {
138 public:
Adam Lesinski6b372992017-08-09 10:54:23 -0700139 XmlResourceVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
140 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700141
Adam Lesinski6b372992017-08-09 10:54:23 -0700142 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700143 bool check_fragment = false;
144 if (node->namespace_uri.empty()) {
145 check_fragment =
146 node->name == "PreferenceScreen" || node->name == "header";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700147 }
148
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700149 if (check_fragment) {
150 xml::Attribute* attr =
151 node->FindAttribute(xml::kSchemaAndroid, "fragment");
152 if (attr && util::IsJavaClassName(attr->value)) {
153 AddClass(node->line_number, attr->value);
154 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700155 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700156
157 BaseVisitor::Visit(node);
158 }
159
160 private:
161 DISALLOW_COPY_AND_ASSIGN(XmlResourceVisitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700162};
163
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700164class TransitionVisitor : public BaseVisitor {
165 public:
Adam Lesinski6b372992017-08-09 10:54:23 -0700166 TransitionVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
167 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700168
Adam Lesinski6b372992017-08-09 10:54:23 -0700169 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700170 bool check_class =
Adam Lesinski6b372992017-08-09 10:54:23 -0700171 node->namespace_uri.empty() && (node->name == "transition" || node->name == "pathMotion");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700172 if (check_class) {
173 xml::Attribute* attr = node->FindAttribute({}, "class");
174 if (attr && util::IsJavaClassName(attr->value)) {
175 AddClass(node->line_number, attr->value);
176 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700177 }
178
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700179 BaseVisitor::Visit(node);
180 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700181
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700182 private:
183 DISALLOW_COPY_AND_ASSIGN(TransitionVisitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700184};
185
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700186class ManifestVisitor : public BaseVisitor {
187 public:
188 ManifestVisitor(const Source& source, KeepSet* keep_set, bool main_dex_only)
189 : BaseVisitor(source, keep_set), main_dex_only_(main_dex_only) {}
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700190
Adam Lesinski6b372992017-08-09 10:54:23 -0700191 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700192 if (node->namespace_uri.empty()) {
193 bool get_name = false;
194 if (node->name == "manifest") {
195 xml::Attribute* attr = node->FindAttribute({}, "package");
196 if (attr) {
197 package_ = attr->value;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700198 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700199 } else if (node->name == "application") {
200 get_name = true;
Adam Lesinski6b372992017-08-09 10:54:23 -0700201 xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700202 if (attr) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700203 Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700204 if (result) {
205 AddClass(node->line_number, result.value());
206 }
207 }
208 if (main_dex_only_) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700209 xml::Attribute* default_process = node->FindAttribute(xml::kSchemaAndroid, "process");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700210 if (default_process) {
211 default_process_ = default_process->value;
212 }
213 }
214 } else if (node->name == "activity" || node->name == "service" ||
215 node->name == "receiver" || node->name == "provider") {
216 get_name = true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700217
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700218 if (main_dex_only_) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700219 xml::Attribute* component_process = node->FindAttribute(xml::kSchemaAndroid, "process");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700220
221 const std::string& process =
222 component_process ? component_process->value : default_process_;
223 get_name = !process.empty() && process[0] != ':';
224 }
225 } else if (node->name == "instrumentation") {
226 get_name = true;
227 }
228
229 if (get_name) {
230 xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "name");
231 get_name = attr != nullptr;
232
233 if (get_name) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700234 Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700235 if (result) {
236 AddClass(node->line_number, result.value());
237 }
238 }
239 }
240 }
241 BaseVisitor::Visit(node);
242 }
243
244 private:
245 DISALLOW_COPY_AND_ASSIGN(ManifestVisitor);
246
247 std::string package_;
248 const bool main_dex_only_;
249 std::string default_process_;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700250};
251
Adam Lesinski6b372992017-08-09 10:54:23 -0700252bool CollectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keep_set,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700253 bool main_dex_only) {
254 ManifestVisitor visitor(source, keep_set, main_dex_only);
255 if (res->root) {
256 res->root->Accept(&visitor);
257 return true;
258 }
259 return false;
260}
261
Adam Lesinski6b372992017-08-09 10:54:23 -0700262bool CollectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keep_set) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700263 if (!res->root) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700264 return false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700265 }
266
267 switch (res->file.name.type) {
268 case ResourceType::kLayout: {
269 LayoutVisitor visitor(source, keep_set);
270 res->root->Accept(&visitor);
271 break;
272 }
273
274 case ResourceType::kXml: {
275 XmlResourceVisitor visitor(source, keep_set);
276 res->root->Accept(&visitor);
277 break;
278 }
279
280 case ResourceType::kTransition: {
281 TransitionVisitor visitor(source, keep_set);
282 res->root->Accept(&visitor);
283 break;
284 }
285
Adam Lesinskif762df22017-06-26 16:39:03 -0700286 case ResourceType::kMenu: {
287 MenuVisitor visitor(source, keep_set);
288 res->root->Accept(&visitor);
289 break;
290 }
291
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700292 default:
293 break;
294 }
295 return true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700296}
297
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700298bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
299 for (const auto& entry : keep_set.keep_set_) {
300 for (const Source& source : entry.second) {
301 *out << "# Referenced at " << source << "\n";
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700302 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700303 *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
304 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700305
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700306 for (const auto& entry : keep_set.keep_method_set_) {
307 for (const Source& source : entry.second) {
308 *out << "# Referenced at " << source << "\n";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700309 }
Adam Lesinski6b372992017-08-09 10:54:23 -0700310 *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700311 }
312 return true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700313}
314
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700315} // namespace proguard
316} // namespace aapt