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