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 27namespace aapt { 28namespace proguard { 29 30class BaseVisitor : public xml::Visitor { 31 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); 40 } 41 } 42 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: 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 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 79class 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()) { 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 113class XmlResourceVisitor : public BaseVisitor { 114 public: 115 XmlResourceVisitor(const Source& source, KeepSet* keep_set) 116 : BaseVisitor(source, keep_set) {} 117 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 140class TransitionVisitor : public BaseVisitor { 141 public: 142 TransitionVisitor(const Source& source, KeepSet* keep_set) 143 : BaseVisitor(source, keep_set) {} 144 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 163class ManifestVisitor : public BaseVisitor { 164 public: 165 ManifestVisitor(const Source& source, KeepSet* keep_set, bool main_dex_only) 166 : BaseVisitor(source, keep_set), main_dex_only_(main_dex_only) {} 167 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 234bool 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 245bool 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 276bool 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