blob: b214d2169f50974ce10f566b2efb8c1c5fee9a22 [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
Adam Koskidc21dea2017-07-21 10:55:27 -070024#include "JavaClassGenerator.h"
25#include "ResourceUtils.h"
26#include "ValueVisitor.h"
27#include "androidfw/StringPiece.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070028#include "util/Util.h"
29#include "xml/XmlDom.h"
30
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070031namespace aapt {
32namespace proguard {
33
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070034class BaseVisitor : public xml::Visitor {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070035 public:
Adam Lesinski6b372992017-08-09 10:54:23 -070036 using xml::Visitor::Visit;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070037
Adam Koskidc21dea2017-07-21 10:55:27 -070038 BaseVisitor(const ResourceFile& file, KeepSet* keep_set) : file_(file), keep_set_(keep_set) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070039 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070040
Adam Lesinski6b372992017-08-09 10:54:23 -070041 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070042 if (!node->namespace_uri.empty()) {
43 Maybe<xml::ExtractedPackage> maybe_package =
44 xml::ExtractPackageFromNamespace(node->namespace_uri);
45 if (maybe_package) {
46 // This is a custom view, let's figure out the class name from this.
47 std::string package = maybe_package.value().package + "." + node->name;
48 if (util::IsJavaClassName(package)) {
49 AddClass(node->line_number, package);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070050 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070051 }
52 } else if (util::IsJavaClassName(node->name)) {
53 AddClass(node->line_number, node->name);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070054 }
55
Adam Lesinskice5e56e2016-10-21 17:56:45 -070056 for (const auto& child : node->children) {
57 child->Accept(this);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070058 }
Adam Koskidc21dea2017-07-21 10:55:27 -070059
60 for (const auto& attr : node->attributes) {
61 if (attr.compiled_value) {
62 auto ref = ValueCast<Reference>(attr.compiled_value.get());
63 if (ref) {
64 AddReference(node->line_number, ref);
65 }
66 }
67 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070068 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070069
Adam Lesinskice5e56e2016-10-21 17:56:45 -070070 protected:
Adam Koskidc21dea2017-07-21 10:55:27 -070071 ResourceFile file_;
72 KeepSet* keep_set_;
73
74 virtual void AddClass(size_t line_number, const std::string& class_name) {
75 keep_set_->AddConditionalClass({file_.name, file_.source.WithLine(line_number)}, class_name);
Adam Lesinskice5e56e2016-10-21 17:56:45 -070076 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070077
Adam Lesinskice5e56e2016-10-21 17:56:45 -070078 void AddMethod(size_t line_number, const std::string& method_name) {
Adam Koskidc21dea2017-07-21 10:55:27 -070079 keep_set_->AddMethod({file_.name, file_.source.WithLine(line_number)}, method_name);
80 }
81
82 void AddReference(size_t line_number, Reference* ref) {
83 if (ref && ref->name) {
84 ResourceName ref_name = ref->name.value();
85 if (ref_name.package.empty()) {
86 ref_name = ResourceName(file_.name.package, ref_name.type, ref_name.entry);
87 }
88 keep_set_->AddReference({file_.name, file_.source.WithLine(line_number)}, ref_name);
89 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070090 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070091
Adam Lesinskice5e56e2016-10-21 17:56:45 -070092 private:
93 DISALLOW_COPY_AND_ASSIGN(BaseVisitor);
94
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070095};
96
Adam Lesinskice5e56e2016-10-21 17:56:45 -070097class LayoutVisitor : public BaseVisitor {
98 public:
Adam Koskidc21dea2017-07-21 10:55:27 -070099 LayoutVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700100 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700101
Adam Lesinski6b372992017-08-09 10:54:23 -0700102 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700103 bool check_class = false;
104 bool check_name = false;
105 if (node->namespace_uri.empty()) {
Adam Lesinskif762df22017-06-26 16:39:03 -0700106 if (node->name == "view") {
107 check_class = true;
108 } else if (node->name == "fragment") {
109 check_class = check_name = true;
110 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700111 } else if (node->namespace_uri == xml::kSchemaAndroid) {
112 check_name = node->name == "fragment";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700113 }
114
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700115 for (const auto& attr : node->attributes) {
116 if (check_class && attr.namespace_uri.empty() && attr.name == "class" &&
117 util::IsJavaClassName(attr.value)) {
118 AddClass(node->line_number, attr.value);
119 } else if (check_name && attr.namespace_uri == xml::kSchemaAndroid &&
120 attr.name == "name" && util::IsJavaClassName(attr.value)) {
121 AddClass(node->line_number, attr.value);
122 } else if (attr.namespace_uri == xml::kSchemaAndroid &&
123 attr.name == "onClick") {
124 AddMethod(node->line_number, attr.value);
125 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700126 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700127
128 BaseVisitor::Visit(node);
129 }
130
131 private:
132 DISALLOW_COPY_AND_ASSIGN(LayoutVisitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700133};
134
Adam Lesinskif762df22017-06-26 16:39:03 -0700135class MenuVisitor : public BaseVisitor {
136 public:
Adam Koskidc21dea2017-07-21 10:55:27 -0700137 MenuVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
Adam Lesinskif762df22017-06-26 16:39:03 -0700138 }
139
Adam Lesinski6b372992017-08-09 10:54:23 -0700140 void Visit(xml::Element* node) override {
Adam Lesinskif762df22017-06-26 16:39:03 -0700141 if (node->namespace_uri.empty() && node->name == "item") {
142 for (const auto& attr : node->attributes) {
143 if (attr.namespace_uri == xml::kSchemaAndroid) {
144 if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
145 util::IsJavaClassName(attr.value)) {
146 AddClass(node->line_number, attr.value);
147 } else if (attr.name == "onClick") {
148 AddMethod(node->line_number, attr.value);
149 }
150 }
151 }
152 }
153
154 BaseVisitor::Visit(node);
155 }
156
157 private:
158 DISALLOW_COPY_AND_ASSIGN(MenuVisitor);
159};
160
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700161class XmlResourceVisitor : public BaseVisitor {
162 public:
Adam Koskidc21dea2017-07-21 10:55:27 -0700163 XmlResourceVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700164 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700165
Adam Lesinski6b372992017-08-09 10:54:23 -0700166 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700167 bool check_fragment = false;
168 if (node->namespace_uri.empty()) {
169 check_fragment =
170 node->name == "PreferenceScreen" || node->name == "header";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700171 }
172
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700173 if (check_fragment) {
174 xml::Attribute* attr =
175 node->FindAttribute(xml::kSchemaAndroid, "fragment");
176 if (attr && util::IsJavaClassName(attr->value)) {
177 AddClass(node->line_number, attr->value);
178 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700179 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700180
181 BaseVisitor::Visit(node);
182 }
183
184 private:
185 DISALLOW_COPY_AND_ASSIGN(XmlResourceVisitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700186};
187
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700188class TransitionVisitor : public BaseVisitor {
189 public:
Adam Koskidc21dea2017-07-21 10:55:27 -0700190 TransitionVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700191 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700192
Adam Lesinski6b372992017-08-09 10:54:23 -0700193 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700194 bool check_class =
Adam Lesinski6b372992017-08-09 10:54:23 -0700195 node->namespace_uri.empty() && (node->name == "transition" || node->name == "pathMotion");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700196 if (check_class) {
197 xml::Attribute* attr = node->FindAttribute({}, "class");
198 if (attr && util::IsJavaClassName(attr->value)) {
199 AddClass(node->line_number, attr->value);
200 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700201 }
202
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700203 BaseVisitor::Visit(node);
204 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700205
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700206 private:
207 DISALLOW_COPY_AND_ASSIGN(TransitionVisitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700208};
209
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700210class ManifestVisitor : public BaseVisitor {
211 public:
Adam Koskidc21dea2017-07-21 10:55:27 -0700212 ManifestVisitor(const ResourceFile& file, KeepSet* keep_set, bool main_dex_only)
213 : BaseVisitor(file, keep_set), main_dex_only_(main_dex_only) {
214 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700215
Adam Lesinski6b372992017-08-09 10:54:23 -0700216 void Visit(xml::Element* node) override {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700217 if (node->namespace_uri.empty()) {
218 bool get_name = false;
219 if (node->name == "manifest") {
220 xml::Attribute* attr = node->FindAttribute({}, "package");
221 if (attr) {
222 package_ = attr->value;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700223 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700224 } else if (node->name == "application") {
225 get_name = true;
Adam Lesinski6b372992017-08-09 10:54:23 -0700226 xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700227 if (attr) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700228 Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700229 if (result) {
230 AddClass(node->line_number, result.value());
231 }
232 }
233 if (main_dex_only_) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700234 xml::Attribute* default_process = node->FindAttribute(xml::kSchemaAndroid, "process");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700235 if (default_process) {
236 default_process_ = default_process->value;
237 }
238 }
239 } else if (node->name == "activity" || node->name == "service" ||
240 node->name == "receiver" || node->name == "provider") {
241 get_name = true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700242
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700243 if (main_dex_only_) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700244 xml::Attribute* component_process = node->FindAttribute(xml::kSchemaAndroid, "process");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700245
246 const std::string& process =
247 component_process ? component_process->value : default_process_;
248 get_name = !process.empty() && process[0] != ':';
249 }
250 } else if (node->name == "instrumentation") {
251 get_name = true;
252 }
253
254 if (get_name) {
255 xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "name");
256 get_name = attr != nullptr;
257
258 if (get_name) {
Adam Lesinski6b372992017-08-09 10:54:23 -0700259 Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700260 if (result) {
261 AddClass(node->line_number, result.value());
262 }
263 }
264 }
265 }
266 BaseVisitor::Visit(node);
267 }
268
Adam Koskidc21dea2017-07-21 10:55:27 -0700269 virtual void AddClass(size_t line_number, const std::string& class_name) override {
270 keep_set_->AddManifestClass({file_.name, file_.source.WithLine(line_number)}, class_name);
271 }
272
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700273 private:
274 DISALLOW_COPY_AND_ASSIGN(ManifestVisitor);
275
276 std::string package_;
277 const bool main_dex_only_;
278 std::string default_process_;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700279};
280
Adam Koskidc21dea2017-07-21 10:55:27 -0700281bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set, bool main_dex_only) {
282 ManifestVisitor visitor(res->file, keep_set, main_dex_only);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700283 if (res->root) {
284 res->root->Accept(&visitor);
285 return true;
286 }
287 return false;
288}
289
Adam Koskidc21dea2017-07-21 10:55:27 -0700290bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700291 if (!res->root) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700292 return false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700293 }
294
295 switch (res->file.name.type) {
296 case ResourceType::kLayout: {
Adam Koskidc21dea2017-07-21 10:55:27 -0700297 LayoutVisitor visitor(res->file, keep_set);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700298 res->root->Accept(&visitor);
299 break;
300 }
301
302 case ResourceType::kXml: {
Adam Koskidc21dea2017-07-21 10:55:27 -0700303 XmlResourceVisitor visitor(res->file, keep_set);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700304 res->root->Accept(&visitor);
305 break;
306 }
307
308 case ResourceType::kTransition: {
Adam Koskidc21dea2017-07-21 10:55:27 -0700309 TransitionVisitor visitor(res->file, keep_set);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700310 res->root->Accept(&visitor);
311 break;
312 }
313
Adam Lesinskif762df22017-06-26 16:39:03 -0700314 case ResourceType::kMenu: {
Adam Koskidc21dea2017-07-21 10:55:27 -0700315 MenuVisitor visitor(res->file, keep_set);
Adam Lesinskif762df22017-06-26 16:39:03 -0700316 res->root->Accept(&visitor);
317 break;
318 }
319
Adam Koskidc21dea2017-07-21 10:55:27 -0700320 default: {
321 BaseVisitor visitor(res->file, keep_set);
322 res->root->Accept(&visitor);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700323 break;
Adam Koskidc21dea2017-07-21 10:55:27 -0700324 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700325 }
326 return true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700327}
328
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700329bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
Adam Koskidc21dea2017-07-21 10:55:27 -0700330 for (const auto& entry : keep_set.manifest_class_set_) {
331 for (const UsageLocation& location : entry.second) {
332 *out << "# Referenced at " << location.source << "\n";
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700333 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700334 *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
335 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700336
Adam Koskidc21dea2017-07-21 10:55:27 -0700337 for (const auto& entry : keep_set.conditional_class_set_) {
338 std::set<UsageLocation> locations;
339 bool can_be_conditional = true;
340 for (const UsageLocation& location : entry.second) {
341 can_be_conditional &= CollectLocations(location, keep_set, &locations);
342 }
343
344 for (const UsageLocation& location : entry.second) {
345 *out << "# Referenced at " << location.source << "\n";
346 }
347 if (keep_set.conditional_keep_rules_ && can_be_conditional) {
Adam Koski09ef94e2017-11-10 11:15:55 -0800348 *out << "-if class **.R$layout {\n";
Adam Koskidc21dea2017-07-21 10:55:27 -0700349 for (const UsageLocation& location : locations) {
350 auto transformed_name = JavaClassGenerator::TransformToFieldName(location.name.entry);
Adam Koski09ef94e2017-11-10 11:15:55 -0800351 *out << " int " << transformed_name << ";\n";
Adam Koskidc21dea2017-07-21 10:55:27 -0700352 }
Adam Koski09ef94e2017-11-10 11:15:55 -0800353 *out << "}\n";
Adam Koskidc21dea2017-07-21 10:55:27 -0700354 }
Adam Koski09ef94e2017-11-10 11:15:55 -0800355 *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
Adam Koskidc21dea2017-07-21 10:55:27 -0700356 }
357
358 for (const auto& entry : keep_set.method_set_) {
359 for (const UsageLocation& location : entry.second) {
360 *out << "# Referenced at " << location.source << "\n";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700361 }
Adam Lesinski6b372992017-08-09 10:54:23 -0700362 *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700363 }
364 return true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700365}
366
Adam Koskidc21dea2017-07-21 10:55:27 -0700367bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
368 std::set<UsageLocation>* locations) {
369 locations->insert(location);
370
371 // TODO: allow for more reference types if we can determine its safe.
372 if (location.name.type != ResourceType::kLayout) {
373 return false;
374 }
375
376 for (const auto& entry : keep_set.reference_set_) {
377 if (entry.first == location.name) {
378 for (auto& refLocation : entry.second) {
379 // Don't get stuck in loops
380 if (locations->find(refLocation) != locations->end()) {
381 return false;
382 }
383 if (!CollectLocations(refLocation, keep_set, locations)) {
384 return false;
385 }
386 }
387 }
388 }
389
390 return true;
391}
392
393class ReferenceVisitor : public ValueVisitor {
394 public:
395 using ValueVisitor::Visit;
396
397 ReferenceVisitor(aapt::IAaptContext* context, ResourceName from, KeepSet* keep_set)
398 : context_(context), from_(from), keep_set_(keep_set) {
399 }
400
401 void Visit(Reference* reference) override {
402 if (reference->name) {
403 ResourceName reference_name = reference->name.value();
404 if (reference_name.package.empty()) {
405 reference_name = ResourceName(context_->GetCompilationPackage(), reference_name.type,
406 reference_name.entry);
407 }
408 keep_set_->AddReference({from_, reference->GetSource()}, reference_name);
409 }
410 }
411
412 private:
413 aapt::IAaptContext* context_;
414 ResourceName from_;
415 KeepSet* keep_set_;
416};
417
418bool CollectResourceReferences(aapt::IAaptContext* context, ResourceTable* table,
419 KeepSet* keep_set) {
420 for (auto& pkg : table->packages) {
421 for (auto& type : pkg->types) {
422 for (auto& entry : type->entries) {
423 for (auto& config_value : entry->values) {
424 ResourceName from(pkg->name, type->type, entry->name);
425 ReferenceVisitor visitor(context, from, keep_set);
426 config_value->value->Accept(&visitor);
427 }
428 }
429 }
430 }
431 return true;
432}
433
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700434} // namespace proguard
435} // namespace aapt