Home | History | Annotate | Download | only in aapt2
      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