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