Home | History | Annotate | Download | only in cmd
      1 /*
      2  * Copyright (C) 2016 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 "Diff.h"
     18 
     19 #include "android-base/macros.h"
     20 
     21 #include "LoadedApk.h"
     22 #include "ValueVisitor.h"
     23 #include "process/IResourceTableConsumer.h"
     24 #include "process/SymbolTable.h"
     25 
     26 using ::android::StringPiece;
     27 
     28 namespace aapt {
     29 
     30 class DiffContext : public IAaptContext {
     31  public:
     32   DiffContext() : name_mangler_({}), symbol_table_(&name_mangler_) {
     33   }
     34 
     35   PackageType GetPackageType() override {
     36     // Doesn't matter.
     37     return PackageType::kApp;
     38   }
     39 
     40   const std::string& GetCompilationPackage() override {
     41     return empty_;
     42   }
     43 
     44   uint8_t GetPackageId() override {
     45     return 0x0;
     46   }
     47 
     48   IDiagnostics* GetDiagnostics() override {
     49     return &diagnostics_;
     50   }
     51 
     52   NameMangler* GetNameMangler() override {
     53     return &name_mangler_;
     54   }
     55 
     56   SymbolTable* GetExternalSymbols() override {
     57     return &symbol_table_;
     58   }
     59 
     60   bool IsVerbose() override {
     61     return false;
     62   }
     63 
     64   int GetMinSdkVersion() override {
     65     return 0;
     66   }
     67 
     68  private:
     69   std::string empty_;
     70   StdErrDiagnostics diagnostics_;
     71   NameMangler name_mangler_;
     72   SymbolTable symbol_table_;
     73 };
     74 
     75 static void EmitDiffLine(const Source& source, const StringPiece& message) {
     76   std::cerr << source << ": " << message << "\n";
     77 }
     78 
     79 static bool IsSymbolVisibilityDifferent(const Visibility& vis_a, const Visibility& vis_b) {
     80   return vis_a.level != vis_b.level;
     81 }
     82 
     83 template <typename Id>
     84 static bool IsIdDiff(const Visibility::Level& level_a, const Maybe<Id>& id_a,
     85                      const Visibility::Level& level_b, const Maybe<Id>& id_b) {
     86   if (level_a == Visibility::Level::kPublic || level_b == Visibility::Level::kPublic) {
     87     return id_a != id_b;
     88   }
     89   return false;
     90 }
     91 
     92 static bool EmitResourceConfigValueDiff(IAaptContext* context, LoadedApk* apk_a,
     93                                         ResourceTablePackage* pkg_a, ResourceTableType* type_a,
     94                                         ResourceEntry* entry_a, ResourceConfigValue* config_value_a,
     95                                         LoadedApk* apk_b, ResourceTablePackage* pkg_b,
     96                                         ResourceTableType* type_b, ResourceEntry* entry_b,
     97                                         ResourceConfigValue* config_value_b) {
     98   Value* value_a = config_value_a->value.get();
     99   Value* value_b = config_value_b->value.get();
    100   if (!value_a->Equals(value_b)) {
    101     std::stringstream str_stream;
    102     str_stream << "value " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
    103                << " config=" << config_value_a->config << " does not match:\n";
    104     value_a->Print(&str_stream);
    105     str_stream << "\n vs \n";
    106     value_b->Print(&str_stream);
    107     EmitDiffLine(apk_b->GetSource(), str_stream.str());
    108     return true;
    109   }
    110   return false;
    111 }
    112 
    113 static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
    114                                   ResourceTablePackage* pkg_a, ResourceTableType* type_a,
    115                                   ResourceEntry* entry_a, LoadedApk* apk_b,
    116                                   ResourceTablePackage* pkg_b, ResourceTableType* type_b,
    117                                   ResourceEntry* entry_b) {
    118   bool diff = false;
    119   for (std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) {
    120     ResourceConfigValue* config_value_b = entry_b->FindValue(config_value_a->config);
    121     if (!config_value_b) {
    122       std::stringstream str_stream;
    123       str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
    124                  << " config=" << config_value_a->config;
    125       EmitDiffLine(apk_b->GetSource(), str_stream.str());
    126       diff = true;
    127     } else {
    128       diff |=
    129           EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a.get(),
    130                                       apk_b, pkg_b, type_b, entry_b, config_value_b);
    131     }
    132   }
    133 
    134   // Check for any newly added config values.
    135   for (std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) {
    136     ResourceConfigValue* config_value_a = entry_a->FindValue(config_value_b->config);
    137     if (!config_value_a) {
    138       std::stringstream str_stream;
    139       str_stream << "new config " << pkg_b->name << ":" << type_b->type << "/" << entry_b->name
    140                  << " config=" << config_value_b->config;
    141       EmitDiffLine(apk_b->GetSource(), str_stream.str());
    142       diff = true;
    143     }
    144   }
    145   return false;
    146 }
    147 
    148 static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a,
    149                                  ResourceTablePackage* pkg_a, ResourceTableType* type_a,
    150                                  LoadedApk* apk_b, ResourceTablePackage* pkg_b,
    151                                  ResourceTableType* type_b) {
    152   bool diff = false;
    153   for (std::unique_ptr<ResourceEntry>& entry_a : type_a->entries) {
    154     ResourceEntry* entry_b = type_b->FindEntry(entry_a->name);
    155     if (!entry_b) {
    156       std::stringstream str_stream;
    157       str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name;
    158       EmitDiffLine(apk_b->GetSource(), str_stream.str());
    159       diff = true;
    160     } else {
    161       if (IsSymbolVisibilityDifferent(entry_a->visibility, entry_b->visibility)) {
    162         std::stringstream str_stream;
    163         str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
    164                    << " has different visibility (";
    165         if (entry_b->visibility.level == Visibility::Level::kPublic) {
    166           str_stream << "PUBLIC";
    167         } else {
    168           str_stream << "PRIVATE";
    169         }
    170         str_stream << " vs ";
    171         if (entry_a->visibility.level == Visibility::Level::kPublic) {
    172           str_stream << "PUBLIC";
    173         } else {
    174           str_stream << "PRIVATE";
    175         }
    176         str_stream << ")";
    177         EmitDiffLine(apk_b->GetSource(), str_stream.str());
    178         diff = true;
    179       } else if (IsIdDiff(entry_a->visibility.level, entry_a->id, entry_b->visibility.level,
    180                           entry_b->id)) {
    181         std::stringstream str_stream;
    182         str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
    183                    << " has different public ID (";
    184         if (entry_b->id) {
    185           str_stream << "0x" << std::hex << entry_b->id.value();
    186         } else {
    187           str_stream << "none";
    188         }
    189         str_stream << " vs ";
    190         if (entry_a->id) {
    191           str_stream << "0x " << std::hex << entry_a->id.value();
    192         } else {
    193           str_stream << "none";
    194         }
    195         str_stream << ")";
    196         EmitDiffLine(apk_b->GetSource(), str_stream.str());
    197         diff = true;
    198       }
    199       diff |= EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a.get(), apk_b, pkg_b,
    200                                     type_b, entry_b);
    201     }
    202   }
    203 
    204   // Check for any newly added entries.
    205   for (std::unique_ptr<ResourceEntry>& entry_b : type_b->entries) {
    206     ResourceEntry* entry_a = type_a->FindEntry(entry_b->name);
    207     if (!entry_a) {
    208       std::stringstream str_stream;
    209       str_stream << "new entry " << pkg_b->name << ":" << type_b->type << "/" << entry_b->name;
    210       EmitDiffLine(apk_b->GetSource(), str_stream.str());
    211       diff = true;
    212     }
    213   }
    214   return diff;
    215 }
    216 
    217 static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a,
    218                                     ResourceTablePackage* pkg_a, LoadedApk* apk_b,
    219                                     ResourceTablePackage* pkg_b) {
    220   bool diff = false;
    221   for (std::unique_ptr<ResourceTableType>& type_a : pkg_a->types) {
    222     ResourceTableType* type_b = pkg_b->FindType(type_a->type);
    223     if (!type_b) {
    224       std::stringstream str_stream;
    225       str_stream << "missing " << pkg_a->name << ":" << type_a->type;
    226       EmitDiffLine(apk_a->GetSource(), str_stream.str());
    227       diff = true;
    228     } else {
    229       if (type_a->visibility_level != type_b->visibility_level) {
    230         std::stringstream str_stream;
    231         str_stream << pkg_a->name << ":" << type_a->type << " has different visibility (";
    232         if (type_b->visibility_level == Visibility::Level::kPublic) {
    233           str_stream << "PUBLIC";
    234         } else {
    235           str_stream << "PRIVATE";
    236         }
    237         str_stream << " vs ";
    238         if (type_a->visibility_level == Visibility::Level::kPublic) {
    239           str_stream << "PUBLIC";
    240         } else {
    241           str_stream << "PRIVATE";
    242         }
    243         str_stream << ")";
    244         EmitDiffLine(apk_b->GetSource(), str_stream.str());
    245         diff = true;
    246       } else if (IsIdDiff(type_a->visibility_level, type_a->id, type_b->visibility_level,
    247                           type_b->id)) {
    248         std::stringstream str_stream;
    249         str_stream << pkg_a->name << ":" << type_a->type << " has different public ID (";
    250         if (type_b->id) {
    251           str_stream << "0x" << std::hex << type_b->id.value();
    252         } else {
    253           str_stream << "none";
    254         }
    255         str_stream << " vs ";
    256         if (type_a->id) {
    257           str_stream << "0x " << std::hex << type_a->id.value();
    258         } else {
    259           str_stream << "none";
    260         }
    261         str_stream << ")";
    262         EmitDiffLine(apk_b->GetSource(), str_stream.str());
    263         diff = true;
    264       }
    265       diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a.get(), apk_b, pkg_b, type_b);
    266     }
    267   }
    268 
    269   // Check for any newly added types.
    270   for (std::unique_ptr<ResourceTableType>& type_b : pkg_b->types) {
    271     ResourceTableType* type_a = pkg_a->FindType(type_b->type);
    272     if (!type_a) {
    273       std::stringstream str_stream;
    274       str_stream << "new type " << pkg_b->name << ":" << type_b->type;
    275       EmitDiffLine(apk_b->GetSource(), str_stream.str());
    276       diff = true;
    277     }
    278   }
    279   return diff;
    280 }
    281 
    282 static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, LoadedApk* apk_b) {
    283   ResourceTable* table_a = apk_a->GetResourceTable();
    284   ResourceTable* table_b = apk_b->GetResourceTable();
    285 
    286   bool diff = false;
    287   for (std::unique_ptr<ResourceTablePackage>& pkg_a : table_a->packages) {
    288     ResourceTablePackage* pkg_b = table_b->FindPackage(pkg_a->name);
    289     if (!pkg_b) {
    290       std::stringstream str_stream;
    291       str_stream << "missing package " << pkg_a->name;
    292       EmitDiffLine(apk_b->GetSource(), str_stream.str());
    293       diff = true;
    294     } else {
    295       if (pkg_a->id != pkg_b->id) {
    296         std::stringstream str_stream;
    297         str_stream << "package '" << pkg_a->name << "' has different id (";
    298         if (pkg_b->id) {
    299           str_stream << "0x" << std::hex << pkg_b->id.value();
    300         } else {
    301           str_stream << "none";
    302         }
    303         str_stream << " vs ";
    304         if (pkg_a->id) {
    305           str_stream << "0x" << std::hex << pkg_a->id.value();
    306         } else {
    307           str_stream << "none";
    308         }
    309         str_stream << ")";
    310         EmitDiffLine(apk_b->GetSource(), str_stream.str());
    311         diff = true;
    312       }
    313       diff |= EmitResourcePackageDiff(context, apk_a, pkg_a.get(), apk_b, pkg_b);
    314     }
    315   }
    316 
    317   // Check for any newly added packages.
    318   for (std::unique_ptr<ResourceTablePackage>& pkg_b : table_b->packages) {
    319     ResourceTablePackage* pkg_a = table_a->FindPackage(pkg_b->name);
    320     if (!pkg_a) {
    321       std::stringstream str_stream;
    322       str_stream << "new package " << pkg_b->name;
    323       EmitDiffLine(apk_b->GetSource(), str_stream.str());
    324       diff = true;
    325     }
    326   }
    327   return diff;
    328 }
    329 
    330 class ZeroingReferenceVisitor : public DescendingValueVisitor {
    331  public:
    332   using DescendingValueVisitor::Visit;
    333 
    334   void Visit(Reference* ref) override {
    335     if (ref->name && ref->id) {
    336       if (ref->id.value().package_id() == kAppPackageId) {
    337         ref->id = {};
    338       }
    339     }
    340   }
    341 };
    342 
    343 static void ZeroOutAppReferences(ResourceTable* table) {
    344   ZeroingReferenceVisitor visitor;
    345   VisitAllValuesInTable(table, &visitor);
    346 }
    347 
    348 int DiffCommand::Action(const std::vector<std::string>& args) {
    349   DiffContext context;
    350 
    351   if (args.size() != 2u) {
    352     std::cerr << "must have two apks as arguments.\n\n";
    353     Usage(&std::cerr);
    354     return 1;
    355   }
    356 
    357   IDiagnostics* diag = context.GetDiagnostics();
    358   std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(args[0], diag);
    359   std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(args[1], diag);
    360   if (!apk_a || !apk_b) {
    361     return 1;
    362   }
    363 
    364   // Zero out Application IDs in references.
    365   ZeroOutAppReferences(apk_a->GetResourceTable());
    366   ZeroOutAppReferences(apk_b->GetResourceTable());
    367 
    368   if (EmitResourceTableDiff(&context, apk_a.get(), apk_b.get())) {
    369     // We emitted a diff, so return 1 (failure).
    370     return 1;
    371   }
    372   return 0;
    373 }
    374 
    375 }  // namespace aapt
    376