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