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