blob: 5f61faeeebe75346c6b5c3772b6c3fb79234ccc7 [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:
32 BaseVisitor(const Source& source, KeepSet* keep_set)
33 : source_(source), keep_set_(keep_set) {}
34
35 virtual void Visit(xml::Text*) override{};
36
37 virtual void Visit(xml::Namespace* node) override {
38 for (const auto& child : node->children) {
39 child->Accept(this);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070040 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070041 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070042
Adam Lesinskice5e56e2016-10-21 17:56:45 -070043 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);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070052 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070053 }
54 } else if (util::IsJavaClassName(node->name)) {
55 AddClass(node->line_number, node->name);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070056 }
57
Adam Lesinskice5e56e2016-10-21 17:56:45 -070058 for (const auto& child : node->children) {
59 child->Accept(this);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070060 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070061 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070062
Adam Lesinskice5e56e2016-10-21 17:56:45 -070063 protected:
64 void AddClass(size_t line_number, const std::string& class_name) {
65 keep_set_->AddClass(Source(source_.path, line_number), class_name);
66 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070067
Adam Lesinskice5e56e2016-10-21 17:56:45 -070068 void AddMethod(size_t line_number, const std::string& method_name) {
69 keep_set_->AddMethod(Source(source_.path, line_number), method_name);
70 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070071
Adam Lesinskice5e56e2016-10-21 17:56:45 -070072 private:
73 DISALLOW_COPY_AND_ASSIGN(BaseVisitor);
74
75 Source source_;
76 KeepSet* keep_set_;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070077};
78
Adam Lesinskice5e56e2016-10-21 17:56:45 -070079class LayoutVisitor : public BaseVisitor {
80 public:
81 LayoutVisitor(const Source& source, KeepSet* keep_set)
82 : BaseVisitor(source, keep_set) {}
83
84 virtual void Visit(xml::Element* node) override {
85 bool check_class = false;
86 bool check_name = false;
87 if (node->namespace_uri.empty()) {
Adam Lesinskif762df22017-06-26 16:39:03 -070088 if (node->name == "view") {
89 check_class = true;
90 } else if (node->name == "fragment") {
91 check_class = check_name = true;
92 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070093 } else if (node->namespace_uri == xml::kSchemaAndroid) {
94 check_name = node->name == "fragment";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070095 }
96
Adam Lesinskice5e56e2016-10-21 17:56:45 -070097 for (const auto& attr : node->attributes) {
98 if (check_class && attr.namespace_uri.empty() && attr.name == "class" &&
99 util::IsJavaClassName(attr.value)) {
100 AddClass(node->line_number, attr.value);
101 } else if (check_name && attr.namespace_uri == xml::kSchemaAndroid &&
102 attr.name == "name" && util::IsJavaClassName(attr.value)) {
103 AddClass(node->line_number, attr.value);
104 } else if (attr.namespace_uri == xml::kSchemaAndroid &&
105 attr.name == "onClick") {
106 AddMethod(node->line_number, attr.value);
107 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700108 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700109
110 BaseVisitor::Visit(node);
111 }
112
113 private:
114 DISALLOW_COPY_AND_ASSIGN(LayoutVisitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700115};
116
Adam Lesinskif762df22017-06-26 16:39:03 -0700117class MenuVisitor : public BaseVisitor {
118 public:
119 MenuVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
120 }
121
122 virtual void Visit(xml::Element* node) override {
123 if (node->namespace_uri.empty() && node->name == "item") {
124 for (const auto& attr : node->attributes) {
125 if (attr.namespace_uri == xml::kSchemaAndroid) {
126 if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
127 util::IsJavaClassName(attr.value)) {
128 AddClass(node->line_number, attr.value);
129 } else if (attr.name == "onClick") {
130 AddMethod(node->line_number, attr.value);
131 }
132 }
133 }
134 }
135
136 BaseVisitor::Visit(node);
137 }
138
139 private:
140 DISALLOW_COPY_AND_ASSIGN(MenuVisitor);
141};
142
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700143class XmlResourceVisitor : public BaseVisitor {
144 public:
145 XmlResourceVisitor(const Source& source, KeepSet* keep_set)
146 : BaseVisitor(source, keep_set) {}
147
148 virtual void Visit(xml::Element* node) override {
149 bool check_fragment = false;
150 if (node->namespace_uri.empty()) {
151 check_fragment =
152 node->name == "PreferenceScreen" || node->name == "header";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700153 }
154
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700155 if (check_fragment) {
156 xml::Attribute* attr =
157 node->FindAttribute(xml::kSchemaAndroid, "fragment");
158 if (attr && util::IsJavaClassName(attr->value)) {
159 AddClass(node->line_number, attr->value);
160 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700161 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700162
163 BaseVisitor::Visit(node);
164 }
165
166 private:
167 DISALLOW_COPY_AND_ASSIGN(XmlResourceVisitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700168};
169
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700170class TransitionVisitor : public BaseVisitor {
171 public:
172 TransitionVisitor(const Source& source, KeepSet* keep_set)
173 : BaseVisitor(source, keep_set) {}
174
175 virtual void Visit(xml::Element* node) override {
176 bool check_class =
177 node->namespace_uri.empty() &&
178 (node->name == "transition" || node->name == "pathMotion");
179 if (check_class) {
180 xml::Attribute* attr = node->FindAttribute({}, "class");
181 if (attr && util::IsJavaClassName(attr->value)) {
182 AddClass(node->line_number, attr->value);
183 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700184 }
185
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700186 BaseVisitor::Visit(node);
187 }
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700188
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700189 private:
190 DISALLOW_COPY_AND_ASSIGN(TransitionVisitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700191};
192
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700193class ManifestVisitor : public BaseVisitor {
194 public:
195 ManifestVisitor(const Source& source, KeepSet* keep_set, bool main_dex_only)
196 : BaseVisitor(source, keep_set), main_dex_only_(main_dex_only) {}
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700197
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700198 virtual void Visit(xml::Element* node) override {
199 if (node->namespace_uri.empty()) {
200 bool get_name = false;
201 if (node->name == "manifest") {
202 xml::Attribute* attr = node->FindAttribute({}, "package");
203 if (attr) {
204 package_ = attr->value;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700205 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700206 } else if (node->name == "application") {
207 get_name = true;
208 xml::Attribute* attr =
209 node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
210 if (attr) {
211 Maybe<std::string> result =
212 util::GetFullyQualifiedClassName(package_, attr->value);
213 if (result) {
214 AddClass(node->line_number, result.value());
215 }
216 }
217 if (main_dex_only_) {
218 xml::Attribute* default_process =
219 node->FindAttribute(xml::kSchemaAndroid, "process");
220 if (default_process) {
221 default_process_ = default_process->value;
222 }
223 }
224 } else if (node->name == "activity" || node->name == "service" ||
225 node->name == "receiver" || node->name == "provider") {
226 get_name = true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700227
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700228 if (main_dex_only_) {
229 xml::Attribute* component_process =
230 node->FindAttribute(xml::kSchemaAndroid, "process");
231
232 const std::string& process =
233 component_process ? component_process->value : default_process_;
234 get_name = !process.empty() && process[0] != ':';
235 }
236 } else if (node->name == "instrumentation") {
237 get_name = true;
238 }
239
240 if (get_name) {
241 xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "name");
242 get_name = attr != nullptr;
243
244 if (get_name) {
245 Maybe<std::string> result =
246 util::GetFullyQualifiedClassName(package_, attr->value);
247 if (result) {
248 AddClass(node->line_number, result.value());
249 }
250 }
251 }
252 }
253 BaseVisitor::Visit(node);
254 }
255
256 private:
257 DISALLOW_COPY_AND_ASSIGN(ManifestVisitor);
258
259 std::string package_;
260 const bool main_dex_only_;
261 std::string default_process_;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700262};
263
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700264bool CollectProguardRulesForManifest(const Source& source,
265 xml::XmlResource* res, KeepSet* keep_set,
266 bool main_dex_only) {
267 ManifestVisitor visitor(source, keep_set, main_dex_only);
268 if (res->root) {
269 res->root->Accept(&visitor);
270 return true;
271 }
272 return false;
273}
274
275bool CollectProguardRules(const Source& source, xml::XmlResource* res,
276 KeepSet* keep_set) {
277 if (!res->root) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700278 return false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700279 }
280
281 switch (res->file.name.type) {
282 case ResourceType::kLayout: {
283 LayoutVisitor visitor(source, keep_set);
284 res->root->Accept(&visitor);
285 break;
286 }
287
288 case ResourceType::kXml: {
289 XmlResourceVisitor visitor(source, keep_set);
290 res->root->Accept(&visitor);
291 break;
292 }
293
294 case ResourceType::kTransition: {
295 TransitionVisitor visitor(source, keep_set);
296 res->root->Accept(&visitor);
297 break;
298 }
299
Adam Lesinskif762df22017-06-26 16:39:03 -0700300 case ResourceType::kMenu: {
301 MenuVisitor visitor(source, keep_set);
302 res->root->Accept(&visitor);
303 break;
304 }
305
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700306 default:
307 break;
308 }
309 return true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700310}
311
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700312bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
313 for (const auto& entry : keep_set.keep_set_) {
314 for (const Source& source : entry.second) {
315 *out << "# Referenced at " << source << "\n";
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700316 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700317 *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
318 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700319
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700320 for (const auto& entry : keep_set.keep_method_set_) {
321 for (const Source& source : entry.second) {
322 *out << "# Referenced at " << source << "\n";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700323 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700324 *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n"
325 << std::endl;
326 }
327 return true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700328}
329
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700330} // namespace proguard
331} // namespace aapt