1 /*
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 
17 #include "java/ProguardRules.h"
18 
19 #include <memory>
20 #include <string>
21 
22 #include "android-base/macros.h"
23 
24 #include "util/Util.h"
25 #include "xml/XmlDom.h"
26 
27 namespace aapt {
28 namespace proguard {
29 
30 class BaseVisitor : public xml::Visitor {
31  public:
BaseVisitor(const Source & source,KeepSet * keep_set)32   BaseVisitor(const Source& source, KeepSet* keep_set)
33       : source_(source), keep_set_(keep_set) {}
34 
Visit(xml::Text *)35   virtual void Visit(xml::Text*) override{};
36 
Visit(xml::Namespace * node)37   virtual void Visit(xml::Namespace* node) override {
38     for (const auto& child : node->children) {
39       child->Accept(this);
40     }
41   }
42 
Visit(xml::Element * node)43   virtual void Visit(xml::Element* node) override {
44     if (!node->namespace_uri.empty()) {
45       Maybe<xml::ExtractedPackage> maybe_package =
46           xml::ExtractPackageFromNamespace(node->namespace_uri);
47       if (maybe_package) {
48         // This is a custom view, let's figure out the class name from this.
49         std::string package = maybe_package.value().package + "." + node->name;
50         if (util::IsJavaClassName(package)) {
51           AddClass(node->line_number, package);
52         }
53       }
54     } else if (util::IsJavaClassName(node->name)) {
55       AddClass(node->line_number, node->name);
56     }
57 
58     for (const auto& child : node->children) {
59       child->Accept(this);
60     }
61   }
62 
63  protected:
AddClass(size_t line_number,const std::string & class_name)64   void AddClass(size_t line_number, const std::string& class_name) {
65     keep_set_->AddClass(Source(source_.path, line_number), class_name);
66   }
67 
AddMethod(size_t line_number,const std::string & method_name)68   void AddMethod(size_t line_number, const std::string& method_name) {
69     keep_set_->AddMethod(Source(source_.path, line_number), method_name);
70   }
71 
72  private:
73   DISALLOW_COPY_AND_ASSIGN(BaseVisitor);
74 
75   Source source_;
76   KeepSet* keep_set_;
77 };
78 
79 class LayoutVisitor : public BaseVisitor {
80  public:
LayoutVisitor(const Source & source,KeepSet * keep_set)81   LayoutVisitor(const Source& source, KeepSet* keep_set)
82       : BaseVisitor(source, keep_set) {}
83 
Visit(xml::Element * node)84   virtual void Visit(xml::Element* node) override {
85     bool check_class = false;
86     bool check_name = false;
87     if (node->namespace_uri.empty()) {
88       check_class = node->name == "view" || node->name == "fragment";
89     } else if (node->namespace_uri == xml::kSchemaAndroid) {
90       check_name = node->name == "fragment";
91     }
92 
93     for (const auto& attr : node->attributes) {
94       if (check_class && attr.namespace_uri.empty() && attr.name == "class" &&
95           util::IsJavaClassName(attr.value)) {
96         AddClass(node->line_number, attr.value);
97       } else if (check_name && attr.namespace_uri == xml::kSchemaAndroid &&
98                  attr.name == "name" && util::IsJavaClassName(attr.value)) {
99         AddClass(node->line_number, attr.value);
100       } else if (attr.namespace_uri == xml::kSchemaAndroid &&
101                  attr.name == "onClick") {
102         AddMethod(node->line_number, attr.value);
103       }
104     }
105 
106     BaseVisitor::Visit(node);
107   }
108 
109  private:
110   DISALLOW_COPY_AND_ASSIGN(LayoutVisitor);
111 };
112 
113 class XmlResourceVisitor : public BaseVisitor {
114  public:
XmlResourceVisitor(const Source & source,KeepSet * keep_set)115   XmlResourceVisitor(const Source& source, KeepSet* keep_set)
116       : BaseVisitor(source, keep_set) {}
117 
Visit(xml::Element * node)118   virtual void Visit(xml::Element* node) override {
119     bool check_fragment = false;
120     if (node->namespace_uri.empty()) {
121       check_fragment =
122           node->name == "PreferenceScreen" || node->name == "header";
123     }
124 
125     if (check_fragment) {
126       xml::Attribute* attr =
127           node->FindAttribute(xml::kSchemaAndroid, "fragment");
128       if (attr && util::IsJavaClassName(attr->value)) {
129         AddClass(node->line_number, attr->value);
130       }
131     }
132 
133     BaseVisitor::Visit(node);
134   }
135 
136  private:
137   DISALLOW_COPY_AND_ASSIGN(XmlResourceVisitor);
138 };
139 
140 class TransitionVisitor : public BaseVisitor {
141  public:
TransitionVisitor(const Source & source,KeepSet * keep_set)142   TransitionVisitor(const Source& source, KeepSet* keep_set)
143       : BaseVisitor(source, keep_set) {}
144 
Visit(xml::Element * node)145   virtual void Visit(xml::Element* node) override {
146     bool check_class =
147         node->namespace_uri.empty() &&
148         (node->name == "transition" || node->name == "pathMotion");
149     if (check_class) {
150       xml::Attribute* attr = node->FindAttribute({}, "class");
151       if (attr && util::IsJavaClassName(attr->value)) {
152         AddClass(node->line_number, attr->value);
153       }
154     }
155 
156     BaseVisitor::Visit(node);
157   }
158 
159  private:
160   DISALLOW_COPY_AND_ASSIGN(TransitionVisitor);
161 };
162 
163 class ManifestVisitor : public BaseVisitor {
164  public:
ManifestVisitor(const Source & source,KeepSet * keep_set,bool main_dex_only)165   ManifestVisitor(const Source& source, KeepSet* keep_set, bool main_dex_only)
166       : BaseVisitor(source, keep_set), main_dex_only_(main_dex_only) {}
167 
Visit(xml::Element * node)168   virtual void Visit(xml::Element* node) override {
169     if (node->namespace_uri.empty()) {
170       bool get_name = false;
171       if (node->name == "manifest") {
172         xml::Attribute* attr = node->FindAttribute({}, "package");
173         if (attr) {
174           package_ = attr->value;
175         }
176       } else if (node->name == "application") {
177         get_name = true;
178         xml::Attribute* attr =
179             node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
180         if (attr) {
181           Maybe<std::string> result =
182               util::GetFullyQualifiedClassName(package_, attr->value);
183           if (result) {
184             AddClass(node->line_number, result.value());
185           }
186         }
187         if (main_dex_only_) {
188           xml::Attribute* default_process =
189               node->FindAttribute(xml::kSchemaAndroid, "process");
190           if (default_process) {
191             default_process_ = default_process->value;
192           }
193         }
194       } else if (node->name == "activity" || node->name == "service" ||
195                  node->name == "receiver" || node->name == "provider") {
196         get_name = true;
197 
198         if (main_dex_only_) {
199           xml::Attribute* component_process =
200               node->FindAttribute(xml::kSchemaAndroid, "process");
201 
202           const std::string& process =
203               component_process ? component_process->value : default_process_;
204           get_name = !process.empty() && process[0] != ':';
205         }
206       } else if (node->name == "instrumentation") {
207         get_name = true;
208       }
209 
210       if (get_name) {
211         xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "name");
212         get_name = attr != nullptr;
213 
214         if (get_name) {
215           Maybe<std::string> result =
216               util::GetFullyQualifiedClassName(package_, attr->value);
217           if (result) {
218             AddClass(node->line_number, result.value());
219           }
220         }
221       }
222     }
223     BaseVisitor::Visit(node);
224   }
225 
226  private:
227   DISALLOW_COPY_AND_ASSIGN(ManifestVisitor);
228 
229   std::string package_;
230   const bool main_dex_only_;
231   std::string default_process_;
232 };
233 
CollectProguardRulesForManifest(const Source & source,xml::XmlResource * res,KeepSet * keep_set,bool main_dex_only)234 bool CollectProguardRulesForManifest(const Source& source,
235                                      xml::XmlResource* res, KeepSet* keep_set,
236                                      bool main_dex_only) {
237   ManifestVisitor visitor(source, keep_set, main_dex_only);
238   if (res->root) {
239     res->root->Accept(&visitor);
240     return true;
241   }
242   return false;
243 }
244 
CollectProguardRules(const Source & source,xml::XmlResource * res,KeepSet * keep_set)245 bool CollectProguardRules(const Source& source, xml::XmlResource* res,
246                           KeepSet* keep_set) {
247   if (!res->root) {
248     return false;
249   }
250 
251   switch (res->file.name.type) {
252     case ResourceType::kLayout: {
253       LayoutVisitor visitor(source, keep_set);
254       res->root->Accept(&visitor);
255       break;
256     }
257 
258     case ResourceType::kXml: {
259       XmlResourceVisitor visitor(source, keep_set);
260       res->root->Accept(&visitor);
261       break;
262     }
263 
264     case ResourceType::kTransition: {
265       TransitionVisitor visitor(source, keep_set);
266       res->root->Accept(&visitor);
267       break;
268     }
269 
270     default:
271       break;
272   }
273   return true;
274 }
275 
WriteKeepSet(std::ostream * out,const KeepSet & keep_set)276 bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
277   for (const auto& entry : keep_set.keep_set_) {
278     for (const Source& source : entry.second) {
279       *out << "# Referenced at " << source << "\n";
280     }
281     *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
282   }
283 
284   for (const auto& entry : keep_set.keep_method_set_) {
285     for (const Source& source : entry.second) {
286       *out << "# Referenced at " << source << "\n";
287     }
288     *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n"
289          << std::endl;
290   }
291   return true;
292 }
293 
294 }  // namespace proguard
295 }  // namespace aapt
296