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