1458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski/*
2458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski * Copyright (C) 2016 The Android Open Source Project
3458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski *
4458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski * you may not use this file except in compliance with the License.
6458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski * You may obtain a copy of the License at
7458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski *
8458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski *
10458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski * Unless required by applicable law or agreed to in writing, software
11458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski * See the License for the specific language governing permissions and
14458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski * limitations under the License.
15458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski */
16458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
17ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include "android-base/macros.h"
18ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
19458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski#include "Flags.h"
20ff759e6a6ef6dd741b174e58a0f01cb87accc897Pierre Lecesne#include "LoadedApk.h"
215e8fa3a24835a1ad39f758f630a61c83133eabf8Adam Lesinski#include "ValueVisitor.h"
22458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski#include "process/IResourceTableConsumer.h"
23458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski#include "process/SymbolTable.h"
24458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
25d3ffa844f5a07756009f019e13806e253d1bb119Adam Lesinskiusing ::android::StringPiece;
26d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski
27458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinskinamespace aapt {
28458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
29458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinskiclass DiffContext : public IAaptContext {
30cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
31d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  DiffContext() : name_mangler_({}), symbol_table_(&name_mangler_) {
32d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
33ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
34b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski  PackageType GetPackageType() override {
35b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski    // Doesn't matter.
36b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski    return PackageType::kApp;
37b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski  }
38b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski
39d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  const std::string& GetCompilationPackage() override {
40d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return empty_;
41d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
42458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
43d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  uint8_t GetPackageId() override {
44d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return 0x0;
45d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
46458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
47d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  IDiagnostics* GetDiagnostics() override {
48d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return &diagnostics_;
49d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
50458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
51d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  NameMangler* GetNameMangler() override {
52d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return &name_mangler_;
53d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
54458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
55d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  SymbolTable* GetExternalSymbols() override {
56d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return &symbol_table_;
57d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
58458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
59d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  bool IsVerbose() override {
60d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return false;
61d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
62458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
63d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  int GetMinSdkVersion() override {
64d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return 0;
65d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
66fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
67cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski private:
68ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string empty_;
69ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StdErrDiagnostics diagnostics_;
70ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  NameMangler name_mangler_;
71ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  SymbolTable symbol_table_;
72458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski};
73458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
74ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic void EmitDiffLine(const Source& source, const StringPiece& message) {
75cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::cerr << source << ": " << message << "\n";
76458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski}
77458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
7871be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinskistatic bool IsSymbolVisibilityDifferent(const Visibility& vis_a, const Visibility& vis_b) {
7971be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski  return vis_a.level != vis_b.level;
80458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski}
81458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
82458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinskitemplate <typename Id>
8371be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinskistatic bool IsIdDiff(const Visibility::Level& level_a, const Maybe<Id>& id_a,
8471be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski                     const Visibility::Level& level_b, const Maybe<Id>& id_b) {
8571be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski  if (level_a == Visibility::Level::kPublic || level_b == Visibility::Level::kPublic) {
86ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    return id_a != id_b;
87cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
88cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return false;
89458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski}
90458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
91d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool EmitResourceConfigValueDiff(IAaptContext* context, LoadedApk* apk_a,
92d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                        ResourceTablePackage* pkg_a, ResourceTableType* type_a,
93d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                        ResourceEntry* entry_a, ResourceConfigValue* config_value_a,
94d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                        LoadedApk* apk_b, ResourceTablePackage* pkg_b,
95d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                        ResourceTableType* type_b, ResourceEntry* entry_b,
96d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                        ResourceConfigValue* config_value_b) {
97ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Value* value_a = config_value_a->value.get();
98ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Value* value_b = config_value_b->value.get();
99ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!value_a->Equals(value_b)) {
100ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::stringstream str_stream;
101d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    str_stream << "value " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
102d0f492db038c6210c1138865d816bfb134376538Adam Lesinski               << " config=" << config_value_a->config << " does not match:\n";
103ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value_a->Print(&str_stream);
104ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    str_stream << "\n vs \n";
105ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value_b->Print(&str_stream);
106ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    EmitDiffLine(apk_b->GetSource(), str_stream.str());
107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return true;
108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
109cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return false;
110458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski}
111458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
112ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
113d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                  ResourceTablePackage* pkg_a, ResourceTableType* type_a,
114ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                  ResourceEntry* entry_a, LoadedApk* apk_b,
115d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                  ResourceTablePackage* pkg_b, ResourceTableType* type_b,
116ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                  ResourceEntry* entry_b) {
117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool diff = false;
118ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) {
119d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    ResourceConfigValue* config_value_b = entry_b->FindValue(config_value_a->config);
120ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!config_value_b) {
121ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream str_stream;
122d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
123d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                 << " config=" << config_value_a->config;
124ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      EmitDiffLine(apk_b->GetSource(), str_stream.str());
125cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      diff = true;
126cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
127d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      diff |=
128d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a.get(),
129d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                      apk_b, pkg_b, type_b, entry_b, config_value_b);
130458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski    }
131cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
132cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
133cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Check for any newly added config values.
134ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) {
135d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    ResourceConfigValue* config_value_a = entry_a->FindValue(config_value_b->config);
136ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!config_value_a) {
137ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream str_stream;
138d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      str_stream << "new config " << pkg_b->name << ":" << type_b->type << "/" << entry_b->name
139d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                 << " config=" << config_value_b->config;
140ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      EmitDiffLine(apk_b->GetSource(), str_stream.str());
141cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      diff = true;
142458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski    }
143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return false;
145458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski}
146458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
147ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a,
148d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                 ResourceTablePackage* pkg_a, ResourceTableType* type_a,
149d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                 LoadedApk* apk_b, ResourceTablePackage* pkg_b,
150ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                 ResourceTableType* type_b) {
151cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool diff = false;
152ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (std::unique_ptr<ResourceEntry>& entry_a : type_a->entries) {
153ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceEntry* entry_b = type_b->FindEntry(entry_a->name);
154ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!entry_b) {
155ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream str_stream;
156d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name;
157ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      EmitDiffLine(apk_b->GetSource(), str_stream.str());
158cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      diff = true;
159cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
16071be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski      if (IsSymbolVisibilityDifferent(entry_a->visibility, entry_b->visibility)) {
161ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        std::stringstream str_stream;
162ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
163ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << " has different visibility (";
16471be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski        if (entry_b->visibility.level == Visibility::Level::kPublic) {
165ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "PUBLIC";
166458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski        } else {
167ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "PRIVATE";
168458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski        }
169ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << " vs ";
17071be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski        if (entry_a->visibility.level == Visibility::Level::kPublic) {
171ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "PUBLIC";
172cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
173ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "PRIVATE";
174458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski        }
175ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << ")";
176ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        EmitDiffLine(apk_b->GetSource(), str_stream.str());
177cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        diff = true;
17871be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski      } else if (IsIdDiff(entry_a->visibility.level, entry_a->id, entry_b->visibility.level,
179d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                          entry_b->id)) {
180ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        std::stringstream str_stream;
181ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
182ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << " has different public ID (";
183ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (entry_b->id) {
184ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "0x" << std::hex << entry_b->id.value();
185cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
186ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "none";
187cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        }
188ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << " vs ";
189ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (entry_a->id) {
190ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "0x " << std::hex << entry_a->id.value();
191cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
192ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "none";
193cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        }
194ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << ")";
195ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        EmitDiffLine(apk_b->GetSource(), str_stream.str());
196cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        diff = true;
197cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
198d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      diff |= EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a.get(), apk_b, pkg_b,
199d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                    type_b, entry_b);
200cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
201cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
202cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
203cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Check for any newly added entries.
204ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (std::unique_ptr<ResourceEntry>& entry_b : type_b->entries) {
205ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceEntry* entry_a = type_a->FindEntry(entry_b->name);
206ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!entry_a) {
207ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream str_stream;
208d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      str_stream << "new entry " << pkg_b->name << ":" << type_b->type << "/" << entry_b->name;
209ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      EmitDiffLine(apk_b->GetSource(), str_stream.str());
210cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      diff = true;
211458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski    }
212cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
213cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return diff;
214458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski}
215458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
216ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a,
217d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                    ResourceTablePackage* pkg_a, LoadedApk* apk_b,
218ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                    ResourceTablePackage* pkg_b) {
219cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool diff = false;
220ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (std::unique_ptr<ResourceTableType>& type_a : pkg_a->types) {
221ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceTableType* type_b = pkg_b->FindType(type_a->type);
222ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!type_b) {
223ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream str_stream;
224ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      str_stream << "missing " << pkg_a->name << ":" << type_a->type;
225ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      EmitDiffLine(apk_a->GetSource(), str_stream.str());
226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      diff = true;
227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
22871be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski      if (type_a->visibility_level != type_b->visibility_level) {
229ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        std::stringstream str_stream;
230d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        str_stream << pkg_a->name << ":" << type_a->type << " has different visibility (";
23171be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski        if (type_b->visibility_level == Visibility::Level::kPublic) {
232ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "PUBLIC";
233458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski        } else {
234ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "PRIVATE";
235458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski        }
236ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << " vs ";
23771be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski        if (type_a->visibility_level == Visibility::Level::kPublic) {
238ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "PUBLIC";
239cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
240ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "PRIVATE";
241458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski        }
242ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << ")";
243ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        EmitDiffLine(apk_b->GetSource(), str_stream.str());
244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        diff = true;
24571be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski      } else if (IsIdDiff(type_a->visibility_level, type_a->id, type_b->visibility_level,
24671be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski                          type_b->id)) {
247ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        std::stringstream str_stream;
248d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        str_stream << pkg_a->name << ":" << type_a->type << " has different public ID (";
249ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (type_b->id) {
250ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "0x" << std::hex << type_b->id.value();
251cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
252ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "none";
253cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        }
254ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << " vs ";
255ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (type_a->id) {
256ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "0x " << std::hex << type_a->id.value();
257cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
258ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "none";
259cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        }
260ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << ")";
261ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        EmitDiffLine(apk_b->GetSource(), str_stream.str());
262cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        diff = true;
263cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
264d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a.get(), apk_b, pkg_b, type_b);
265cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
266cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
267cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Check for any newly added types.
269ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (std::unique_ptr<ResourceTableType>& type_b : pkg_b->types) {
270ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceTableType* type_a = pkg_a->FindType(type_b->type);
271ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!type_a) {
272ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream str_stream;
273ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      str_stream << "new type " << pkg_b->name << ":" << type_b->type;
274ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      EmitDiffLine(apk_b->GetSource(), str_stream.str());
275cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      diff = true;
276458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski    }
277cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
278cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return diff;
279458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski}
280458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
281d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, LoadedApk* apk_b) {
282ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceTable* table_a = apk_a->GetResourceTable();
283ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceTable* table_b = apk_b->GetResourceTable();
284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool diff = false;
286ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (std::unique_ptr<ResourceTablePackage>& pkg_a : table_a->packages) {
287ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceTablePackage* pkg_b = table_b->FindPackage(pkg_a->name);
288ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!pkg_b) {
289ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream str_stream;
290ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      str_stream << "missing package " << pkg_a->name;
291ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      EmitDiffLine(apk_b->GetSource(), str_stream.str());
292cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      diff = true;
293cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
294ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (pkg_a->id != pkg_b->id) {
295ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        std::stringstream str_stream;
296ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << "package '" << pkg_a->name << "' has different id (";
297ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (pkg_b->id) {
298ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "0x" << std::hex << pkg_b->id.value();
299458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski        } else {
300ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "none";
301458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski        }
302ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << " vs ";
303ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (pkg_a->id) {
304ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "0x" << std::hex << pkg_a->id.value();
305cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
306ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          str_stream << "none";
307458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski        }
308ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        str_stream << ")";
309ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        EmitDiffLine(apk_b->GetSource(), str_stream.str());
310cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        diff = true;
311cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
312d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      diff |= EmitResourcePackageDiff(context, apk_a, pkg_a.get(), apk_b, pkg_b);
313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
314cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
315cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Check for any newly added packages.
317ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (std::unique_ptr<ResourceTablePackage>& pkg_b : table_b->packages) {
318ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceTablePackage* pkg_a = table_a->FindPackage(pkg_b->name);
319ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!pkg_a) {
320ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream str_stream;
321ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      str_stream << "new package " << pkg_b->name;
322ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      EmitDiffLine(apk_b->GetSource(), str_stream.str());
323cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      diff = true;
324458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski    }
325cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
326cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return diff;
327458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski}
328458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
329d3ffa844f5a07756009f019e13806e253d1bb119Adam Lesinskiclass ZeroingReferenceVisitor : public DescendingValueVisitor {
330cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
331d3ffa844f5a07756009f019e13806e253d1bb119Adam Lesinski  using DescendingValueVisitor::Visit;
332cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
333ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  void Visit(Reference* ref) override {
334cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (ref->name && ref->id) {
335f34b6f4f2b969b47a3f371eb9549e92ef1680d91Adam Lesinski      if (ref->id.value().package_id() == kAppPackageId) {
336cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        ref->id = {};
337cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
3385e8fa3a24835a1ad39f758f630a61c83133eabf8Adam Lesinski    }
339cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
3405e8fa3a24835a1ad39f758f630a61c83133eabf8Adam Lesinski};
3415e8fa3a24835a1ad39f758f630a61c83133eabf8Adam Lesinski
342ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic void ZeroOutAppReferences(ResourceTable* table) {
343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ZeroingReferenceVisitor visitor;
344ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  VisitAllValuesInTable(table, &visitor);
3455e8fa3a24835a1ad39f758f630a61c83133eabf8Adam Lesinski}
3465e8fa3a24835a1ad39f758f630a61c83133eabf8Adam Lesinski
347ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskiint Diff(const std::vector<StringPiece>& args) {
348cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  DiffContext context;
349cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
350cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Flags flags;
351ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!flags.Parse("aapt2 diff", args, &std::cerr)) {
352cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
353cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
354cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
355ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (flags.GetArgs().size() != 2u) {
356cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::cerr << "must have two apks as arguments.\n\n";
357ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    flags.Usage("aapt2 diff", &std::cerr);
358cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
359cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
360cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
3618780eb6e4918ae24fb1ae74d631042c32e41dc3dAdam Lesinski  IDiagnostics* diag = context.GetDiagnostics();
3628780eb6e4918ae24fb1ae74d631042c32e41dc3dAdam Lesinski  std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(flags.GetArgs()[0], diag);
3638780eb6e4918ae24fb1ae74d631042c32e41dc3dAdam Lesinski  std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(flags.GetArgs()[1], diag);
364ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!apk_a || !apk_b) {
365cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
366cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
367cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
368cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Zero out Application IDs in references.
369ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ZeroOutAppReferences(apk_a->GetResourceTable());
370ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ZeroOutAppReferences(apk_b->GetResourceTable());
371cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
372ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (EmitResourceTableDiff(&context, apk_a.get(), apk_b.get())) {
373cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // We emitted a diff, so return 1 (failure).
374cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
375cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
376cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return 0;
377458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski}
378458b877488c12ea4336d8fc00a95d9c0298bd6d0Adam Lesinski
379cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
380