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 #include "util/Util.h" 19 #include "xml/XmlDom.h" 20 21 #include <memory> 22 #include <string> 23 24 namespace aapt { 25 namespace proguard { 26 27 class BaseVisitor : public xml::Visitor { 28 public: 29 BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) { 30 } 31 32 virtual void visit(xml::Text*) override {}; 33 34 virtual void visit(xml::Namespace* node) override { 35 for (const auto& child : node->children) { 36 child->accept(this); 37 } 38 } 39 40 virtual void visit(xml::Element* node) override { 41 if (!node->namespaceUri.empty()) { 42 Maybe<xml::ExtractedPackage> maybePackage = xml::extractPackageFromNamespace( 43 node->namespaceUri); 44 if (maybePackage) { 45 // This is a custom view, let's figure out the class name from this. 46 std::u16string package = maybePackage.value().package + u"." + node->name; 47 if (util::isJavaClassName(package)) { 48 addClass(node->lineNumber, package); 49 } 50 } 51 } else if (util::isJavaClassName(node->name)) { 52 addClass(node->lineNumber, node->name); 53 } 54 55 for (const auto& child: node->children) { 56 child->accept(this); 57 } 58 } 59 60 protected: 61 void addClass(size_t lineNumber, const std::u16string& className) { 62 mKeepSet->addClass(Source(mSource.path, lineNumber), className); 63 } 64 65 void addMethod(size_t lineNumber, const std::u16string& methodName) { 66 mKeepSet->addMethod(Source(mSource.path, lineNumber), methodName); 67 } 68 69 private: 70 Source mSource; 71 KeepSet* mKeepSet; 72 }; 73 74 struct LayoutVisitor : public BaseVisitor { 75 LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) { 76 } 77 78 virtual void visit(xml::Element* node) override { 79 bool checkClass = false; 80 bool checkName = false; 81 if (node->namespaceUri.empty()) { 82 checkClass = node->name == u"view" || node->name == u"fragment"; 83 } else if (node->namespaceUri == xml::kSchemaAndroid) { 84 checkName = node->name == u"fragment"; 85 } 86 87 for (const auto& attr : node->attributes) { 88 if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" && 89 util::isJavaClassName(attr.value)) { 90 addClass(node->lineNumber, attr.value); 91 } else if (checkName && attr.namespaceUri == xml::kSchemaAndroid && 92 attr.name == u"name" && util::isJavaClassName(attr.value)) { 93 addClass(node->lineNumber, attr.value); 94 } else if (attr.namespaceUri == xml::kSchemaAndroid && attr.name == u"onClick") { 95 addMethod(node->lineNumber, attr.value); 96 } 97 } 98 99 BaseVisitor::visit(node); 100 } 101 }; 102 103 struct XmlResourceVisitor : public BaseVisitor { 104 XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) { 105 } 106 107 virtual void visit(xml::Element* node) override { 108 bool checkFragment = false; 109 if (node->namespaceUri.empty()) { 110 checkFragment = node->name == u"PreferenceScreen" || node->name == u"header"; 111 } 112 113 if (checkFragment) { 114 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"fragment"); 115 if (attr && util::isJavaClassName(attr->value)) { 116 addClass(node->lineNumber, attr->value); 117 } 118 } 119 120 BaseVisitor::visit(node); 121 } 122 }; 123 124 struct TransitionVisitor : public BaseVisitor { 125 TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) { 126 } 127 128 virtual void visit(xml::Element* node) override { 129 bool checkClass = node->namespaceUri.empty() && 130 (node->name == u"transition" || node->name == u"pathMotion"); 131 if (checkClass) { 132 xml::Attribute* attr = node->findAttribute({}, u"class"); 133 if (attr && util::isJavaClassName(attr->value)) { 134 addClass(node->lineNumber, attr->value); 135 } 136 } 137 138 BaseVisitor::visit(node); 139 } 140 }; 141 142 struct ManifestVisitor : public BaseVisitor { 143 ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) { 144 } 145 146 virtual void visit(xml::Element* node) override { 147 if (node->namespaceUri.empty()) { 148 bool getName = false; 149 if (node->name == u"manifest") { 150 xml::Attribute* attr = node->findAttribute({}, u"package"); 151 if (attr) { 152 mPackage = attr->value; 153 } 154 } else if (node->name == u"application") { 155 getName = true; 156 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"backupAgent"); 157 if (attr) { 158 Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage, 159 attr->value); 160 if (result) { 161 addClass(node->lineNumber, result.value()); 162 } 163 } 164 } else if (node->name == u"activity" || node->name == u"service" || 165 node->name == u"receiver" || node->name == u"provider" || 166 node->name == u"instrumentation") { 167 getName = true; 168 } 169 170 if (getName) { 171 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"name"); 172 if (attr) { 173 Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage, 174 attr->value); 175 if (result) { 176 addClass(node->lineNumber, result.value()); 177 } 178 } 179 } 180 } 181 BaseVisitor::visit(node); 182 } 183 184 std::u16string mPackage; 185 }; 186 187 bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, 188 KeepSet* keepSet) { 189 ManifestVisitor visitor(source, keepSet); 190 if (res->root) { 191 res->root->accept(&visitor); 192 return true; 193 } 194 return false; 195 } 196 197 bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet) { 198 if (!res->root) { 199 return false; 200 } 201 202 switch (res->file.name.type) { 203 case ResourceType::kLayout: { 204 LayoutVisitor visitor(source, keepSet); 205 res->root->accept(&visitor); 206 break; 207 } 208 209 case ResourceType::kXml: { 210 XmlResourceVisitor visitor(source, keepSet); 211 res->root->accept(&visitor); 212 break; 213 } 214 215 case ResourceType::kTransition: { 216 TransitionVisitor visitor(source, keepSet); 217 res->root->accept(&visitor); 218 break; 219 } 220 221 default: 222 break; 223 } 224 return true; 225 } 226 227 bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) { 228 for (const auto& entry : keepSet.mKeepSet) { 229 for (const Source& source : entry.second) { 230 *out << "# Referenced at " << source << "\n"; 231 } 232 *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl; 233 } 234 235 for (const auto& entry : keepSet.mKeepMethodSet) { 236 for (const Source& source : entry.second) { 237 *out << "# Referenced at " << source << "\n"; 238 } 239 *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl; 240 } 241 return true; 242 } 243 244 } // namespace proguard 245 } // namespace aapt 246