blob: 6da3176b2bee46be2c25a8b1631704892caa8dc2 [file] [log] [blame]
Adam Lesinski458b8772016-04-25 14:20:21 -07001/*
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
Ryan Mitchell833a1a62018-07-10 13:51:36 -070017#include "Diff.h"
18
Jeremy Meyer56f36e82022-05-20 20:35:42 +000019#include "Diagnostics.h"
Pierre Lecesneff759e62017-02-01 00:29:25 +000020#include "LoadedApk.h"
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -070021#include "ValueVisitor.h"
Jeremy Meyer56f36e82022-05-20 20:35:42 +000022#include "android-base/macros.h"
Adam Lesinski458b8772016-04-25 14:20:21 -070023#include "process/IResourceTableConsumer.h"
24#include "process/SymbolTable.h"
Adam Lesinski458b8772016-04-25 14:20:21 -070025
Adam Lesinskid3ffa8442017-09-28 13:34:35 -070026using ::android::StringPiece;
Adam Lesinskid5083f62017-01-16 15:07:21 -080027
Adam Lesinski458b8772016-04-25 14:20:21 -070028namespace aapt {
29
30class DiffContext : public IAaptContext {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070031 public:
Adam Lesinskid0f492d2017-04-03 18:12:45 -070032 DiffContext() : name_mangler_({}), symbol_table_(&name_mangler_) {
33 }
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080034
Adam Lesinskib522f042017-04-21 16:57:59 -070035 PackageType GetPackageType() override {
36 // Doesn't matter.
37 return PackageType::kApp;
38 }
39
Adam Lesinskid0f492d2017-04-03 18:12:45 -070040 const std::string& GetCompilationPackage() override {
41 return empty_;
42 }
Adam Lesinski458b8772016-04-25 14:20:21 -070043
Adam Lesinskid0f492d2017-04-03 18:12:45 -070044 uint8_t GetPackageId() override {
45 return 0x0;
46 }
Adam Lesinski458b8772016-04-25 14:20:21 -070047
Jeremy Meyer56f36e82022-05-20 20:35:42 +000048 android::IDiagnostics* GetDiagnostics() override {
Adam Lesinskid0f492d2017-04-03 18:12:45 -070049 return &diagnostics_;
50 }
Adam Lesinski458b8772016-04-25 14:20:21 -070051
Adam Lesinskid0f492d2017-04-03 18:12:45 -070052 NameMangler* GetNameMangler() override {
53 return &name_mangler_;
54 }
Adam Lesinski458b8772016-04-25 14:20:21 -070055
Adam Lesinskid0f492d2017-04-03 18:12:45 -070056 SymbolTable* GetExternalSymbols() override {
57 return &symbol_table_;
58 }
Adam Lesinski458b8772016-04-25 14:20:21 -070059
Adam Lesinskid0f492d2017-04-03 18:12:45 -070060 bool IsVerbose() override {
61 return false;
62 }
Adam Lesinski458b8772016-04-25 14:20:21 -070063
Adam Lesinskid0f492d2017-04-03 18:12:45 -070064 int GetMinSdkVersion() override {
65 return 0;
66 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -070067
Udam Sainib228df32019-06-18 16:50:34 -070068 const std::set<std::string>& GetSplitNameDependencies() override {
69 UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
70 static std::set<std::string> empty;
71 return empty;
72 }
73
Adam Lesinskicacb28f2016-10-19 12:18:14 -070074 private:
Adam Lesinskice5e56e22016-10-21 17:56:45 -070075 std::string empty_;
76 StdErrDiagnostics diagnostics_;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080077 NameMangler name_mangler_;
Adam Lesinskice5e56e22016-10-21 17:56:45 -070078 SymbolTable symbol_table_;
Adam Lesinski458b8772016-04-25 14:20:21 -070079};
80
Yurii Zubrytskyia5775142022-11-02 17:49:49 -070081static void EmitDiffLine(const android::Source& source, StringPiece message) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070082 std::cerr << source << ": " << message << "\n";
Adam Lesinski458b8772016-04-25 14:20:21 -070083}
84
Adam Lesinski71be7052017-12-12 16:48:07 -080085static bool IsSymbolVisibilityDifferent(const Visibility& vis_a, const Visibility& vis_b) {
Ryan Mitchell1499f502021-03-30 12:20:08 -070086 return vis_a.level != vis_b.level || vis_a.staged_api != vis_b.staged_api;
Adam Lesinski458b8772016-04-25 14:20:21 -070087}
88
89template <typename Id>
Ryan Mitchell4382e442021-07-14 12:53:01 -070090static bool IsIdDiff(const Visibility::Level& level_a, const std::optional<Id>& id_a,
91 const Visibility::Level& level_b, const std::optional<Id>& id_b) {
Adam Lesinski71be7052017-12-12 16:48:07 -080092 if (level_a == Visibility::Level::kPublic || level_b == Visibility::Level::kPublic) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -070093 return id_a != id_b;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070094 }
95 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -070096}
97
Ryan Mitchell9634efb2021-03-19 14:53:17 -070098static bool EmitResourceConfigValueDiff(
99 IAaptContext* context, LoadedApk* apk_a, const ResourceTablePackageView& pkg_a,
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700100 const ResourceTableTypeView& type_a, const ResourceTableEntryView& entry_a,
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700101 const ResourceConfigValue* config_value_a, LoadedApk* apk_b,
102 const ResourceTablePackageView& pkg_b, const ResourceTableTypeView& type_b,
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700103 const ResourceTableEntryView& entry_b, const ResourceConfigValue* config_value_b) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700104 Value* value_a = config_value_a->value.get();
105 Value* value_b = config_value_b->value.get();
106 if (!value_a->Equals(value_b)) {
107 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000108 str_stream << "value " << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
Yurii Zubrytskyi97f0f1e2024-07-02 15:31:01 -0700109 << " config='" << config_value_a->config << "' does not match:\n";
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700110 value_a->Print(&str_stream);
111 str_stream << "\n vs \n";
112 value_b->Print(&str_stream);
113 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700114 return true;
115 }
116 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700117}
118
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700119static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700120 const ResourceTablePackageView& pkg_a,
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700121 const ResourceTableTypeView& type_a,
122 const ResourceTableEntryView& entry_a, LoadedApk* apk_b,
123 const ResourceTablePackageView& pkg_b,
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700124 const ResourceTableTypeView& type_b,
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700125 const ResourceTableEntryView& entry_b) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700126 bool diff = false;
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700127 for (const ResourceConfigValue* config_value_a : entry_a.values) {
128 auto config_value_b = entry_b.FindValue(config_value_a->config);
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700129 if (!config_value_b) {
130 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000131 str_stream << "missing " << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700132 << " config=" << config_value_a->config;
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700133 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700134 diff = true;
135 } else {
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700136 diff |= EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a,
137 apk_b, pkg_b, type_b, entry_b, config_value_b);
Adam Lesinski458b8772016-04-25 14:20:21 -0700138 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700139 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700140
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700141 // Check for any newly added config values.
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700142 for (const ResourceConfigValue* config_value_b : entry_b.values) {
143 auto config_value_a = entry_a.FindValue(config_value_b->config);
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700144 if (!config_value_a) {
145 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000146 str_stream << "new config " << pkg_b.name << ":" << type_b.named_type << "/" << entry_b.name
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700147 << " config=" << config_value_b->config;
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700148 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700149 diff = true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700150 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700151 }
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700152 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700153}
154
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700155static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a,
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700156 const ResourceTablePackageView& pkg_a,
157 const ResourceTableTypeView& type_a, LoadedApk* apk_b,
158 const ResourceTablePackageView& pkg_b,
159 const ResourceTableTypeView& type_b) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700160 bool diff = false;
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700161 auto entry_a_iter = type_a.entries.begin();
162 auto entry_b_iter = type_b.entries.begin();
163 while (entry_a_iter != type_a.entries.end() || entry_b_iter != type_b.entries.end()) {
164 if (entry_b_iter == type_b.entries.end()) {
165 // Type A contains a type that type B does not have.
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700166 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000167 str_stream << "missing " << pkg_a.name << ":" << type_a.named_type << "/"
168 << entry_a_iter->name;
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700169 EmitDiffLine(apk_a->GetSource(), str_stream.str());
170 diff = true;
171 } else if (entry_a_iter == type_a.entries.end()) {
172 // Type B contains a type that type A does not have.
173 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000174 str_stream << "new entry " << pkg_b.name << ":" << type_b.named_type << "/"
175 << entry_b_iter->name;
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700176 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700177 diff = true;
178 } else {
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700179 const auto& entry_a = *entry_a_iter;
Ryan Mitchell1499f502021-03-30 12:20:08 -0700180 const auto& entry_b = *entry_b_iter;
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700181 if (IsSymbolVisibilityDifferent(entry_a.visibility, entry_b.visibility)) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700182 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000183 str_stream << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700184 << " has different visibility (";
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700185 if (entry_b.visibility.staged_api) {
Ryan Mitchell1499f502021-03-30 12:20:08 -0700186 str_stream << "STAGED ";
187 }
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700188 if (entry_b.visibility.level == Visibility::Level::kPublic) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700189 str_stream << "PUBLIC";
Adam Lesinski458b8772016-04-25 14:20:21 -0700190 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700191 str_stream << "PRIVATE";
Adam Lesinski458b8772016-04-25 14:20:21 -0700192 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700193 str_stream << " vs ";
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700194 if (entry_a.visibility.staged_api) {
Ryan Mitchell1499f502021-03-30 12:20:08 -0700195 str_stream << "STAGED ";
196 }
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700197 if (entry_a.visibility.level == Visibility::Level::kPublic) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700198 str_stream << "PUBLIC";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700199 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700200 str_stream << "PRIVATE";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700201 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700202 str_stream << ")";
203 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700204 diff = true;
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700205 } else if (IsIdDiff(entry_a.visibility.level, entry_a.id, entry_b.visibility.level,
206 entry_b.id)) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700207 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000208 str_stream << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700209 << " has different public ID (";
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700210 if (entry_b.id) {
211 str_stream << "0x" << std::hex << entry_b.id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700212 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700213 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700214 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700215 str_stream << " vs ";
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700216 if (entry_a.id) {
217 str_stream << "0x " << std::hex << entry_a.id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700218 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700219 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700220 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700221 str_stream << ")";
222 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700223 diff = true;
224 }
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700225 diff |= EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a, apk_b, pkg_b, type_b,
226 entry_b);
Adam Lesinski458b8772016-04-25 14:20:21 -0700227 }
Ryan Mitchell1499f502021-03-30 12:20:08 -0700228 if (entry_a_iter != type_a.entries.end()) {
229 ++entry_a_iter;
230 }
231 if (entry_b_iter != type_b.entries.end()) {
232 ++entry_b_iter;
233 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700234 }
235 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700236}
237
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700238static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a,
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700239 const ResourceTablePackageView& pkg_a, LoadedApk* apk_b,
240 const ResourceTablePackageView& pkg_b) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700241 bool diff = false;
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700242 auto type_a_iter = pkg_a.types.begin();
243 auto type_b_iter = pkg_b.types.begin();
244 while (type_a_iter != pkg_a.types.end() || type_b_iter != pkg_b.types.end()) {
245 if (type_b_iter == pkg_b.types.end()) {
246 // Type A contains a type that type B does not have.
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700247 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000248 str_stream << "missing " << pkg_a.name << ":" << type_a_iter->named_type;
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700249 EmitDiffLine(apk_a->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700250 diff = true;
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700251 } else if (type_a_iter == pkg_a.types.end()) {
252 // Type B contains a type that type A does not have.
253 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000254 str_stream << "new type " << pkg_b.name << ":" << type_b_iter->named_type;
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700255 EmitDiffLine(apk_b->GetSource(), str_stream.str());
256 diff = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700257 } else {
Ryan Mitchell1499f502021-03-30 12:20:08 -0700258 const auto& type_a = *type_a_iter;
259 const auto& type_b = *type_b_iter;
260 if (type_a.visibility_level != type_b.visibility_level) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700261 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000262 str_stream << pkg_a.name << ":" << type_a.named_type << " has different visibility (";
Ryan Mitchell1499f502021-03-30 12:20:08 -0700263 if (type_b.visibility_level == Visibility::Level::kPublic) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700264 str_stream << "PUBLIC";
Adam Lesinski458b8772016-04-25 14:20:21 -0700265 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700266 str_stream << "PRIVATE";
Adam Lesinski458b8772016-04-25 14:20:21 -0700267 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700268 str_stream << " vs ";
Ryan Mitchell1499f502021-03-30 12:20:08 -0700269 if (type_a.visibility_level == Visibility::Level::kPublic) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700270 str_stream << "PUBLIC";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700271 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700272 str_stream << "PRIVATE";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700273 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700274 str_stream << ")";
275 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700276 diff = true;
Ryan Mitchell1499f502021-03-30 12:20:08 -0700277 } else if (IsIdDiff(type_a.visibility_level, type_a.id, type_b.visibility_level, type_b.id)) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700278 std::stringstream str_stream;
Iurii Makhnof0c5ff42022-02-22 13:31:02 +0000279 str_stream << pkg_a.name << ":" << type_a.named_type << " has different public ID (";
Ryan Mitchell1499f502021-03-30 12:20:08 -0700280 if (type_b.id) {
281 str_stream << "0x" << std::hex << type_b.id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700282 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700283 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700284 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700285 str_stream << " vs ";
Ryan Mitchell1499f502021-03-30 12:20:08 -0700286 if (type_a.id) {
287 str_stream << "0x " << std::hex << type_a.id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700288 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700289 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700290 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700291 str_stream << ")";
292 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700293 diff = true;
294 }
Ryan Mitchell1499f502021-03-30 12:20:08 -0700295 diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a, apk_b, pkg_b, type_b);
296 }
297 if (type_a_iter != pkg_a.types.end()) {
298 ++type_a_iter;
299 }
300 if (type_b_iter != pkg_b.types.end()) {
301 ++type_b_iter;
Adam Lesinski458b8772016-04-25 14:20:21 -0700302 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700303 }
304 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700305}
306
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700307static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, LoadedApk* apk_b) {
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700308 const auto table_a = apk_a->GetResourceTable()->GetPartitionedView();
309 const auto table_b = apk_b->GetResourceTable()->GetPartitionedView();
Adam Lesinski458b8772016-04-25 14:20:21 -0700310
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700311 bool diff = false;
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700312 auto package_a_iter = table_a.packages.begin();
313 auto package_b_iter = table_b.packages.begin();
314 while (package_a_iter != table_a.packages.end() || package_b_iter != table_b.packages.end()) {
315 if (package_b_iter == table_b.packages.end()) {
316 // Table A contains a package that table B does not have.
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700317 std::stringstream str_stream;
Ryan Mitchell9634efb2021-03-19 14:53:17 -0700318 str_stream << "missing package " << package_a_iter->name;
319 EmitDiffLine(apk_a->GetSource(), str_stream.str());
320 diff = true;
321 } else if (package_a_iter == table_a.packages.end()) {
322 // Table B contains a package that table A does not have.
323 std::stringstream str_stream;
324 str_stream << "new package " << package_b_iter->name;
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700325 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700326 diff = true;
327 } else {
Ryan Mitchell1499f502021-03-30 12:20:08 -0700328 const auto& package_a = *package_a_iter;
329 const auto& package_b = *package_b_iter;
330 if (package_a.id != package_b.id) {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700331 std::stringstream str_stream;
Ryan Mitchell1499f502021-03-30 12:20:08 -0700332 str_stream << "package '" << package_a.name << "' has different id (";
333 if (package_b.id) {
334 str_stream << "0x" << std::hex << package_b.id.value();
Adam Lesinski458b8772016-04-25 14:20:21 -0700335 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700336 str_stream << "none";
Adam Lesinski458b8772016-04-25 14:20:21 -0700337 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700338 str_stream << " vs ";
Ryan Mitchell1499f502021-03-30 12:20:08 -0700339 if (package_a.id) {
340 str_stream << "0x" << std::hex << package_b.id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700341 } else {
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700342 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700343 }
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700344 str_stream << ")";
345 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700346 diff = true;
347 }
Ryan Mitchell1499f502021-03-30 12:20:08 -0700348 diff |= EmitResourcePackageDiff(context, apk_a, package_a, apk_b, package_b);
349 }
350 if (package_a_iter != table_a.packages.end()) {
351 ++package_a_iter;
352 }
353 if (package_b_iter != table_b.packages.end()) {
354 ++package_b_iter;
Adam Lesinski458b8772016-04-25 14:20:21 -0700355 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700356 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700357
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700358 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700359}
360
Adam Lesinskid3ffa8442017-09-28 13:34:35 -0700361class ZeroingReferenceVisitor : public DescendingValueVisitor {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700362 public:
Adam Lesinskid3ffa8442017-09-28 13:34:35 -0700363 using DescendingValueVisitor::Visit;
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700364
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700365 void Visit(Reference* ref) override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700366 if (ref->name && ref->id) {
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800367 if (ref->id.value().package_id() == kAppPackageId) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700368 ref->id = {};
369 }
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700370 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700371 }
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700372};
373
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700374static void ZeroOutAppReferences(ResourceTable* table) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700375 ZeroingReferenceVisitor visitor;
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700376 VisitAllValuesInTable(table, &visitor);
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700377}
378
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700379int DiffCommand::Action(const std::vector<std::string>& args) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700380 DiffContext context;
Adam Lesinski458b8772016-04-25 14:20:21 -0700381
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700382 if (args.size() != 2u) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700383 std::cerr << "must have two apks as arguments.\n\n";
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700384 Usage(&std::cerr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700385 return 1;
386 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700387
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000388 android::IDiagnostics* diag = context.GetDiagnostics();
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700389 std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(args[0], diag);
390 std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(args[1], diag);
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700391 if (!apk_a || !apk_b) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700392 return 1;
393 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700394
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700395 // Zero out Application IDs in references.
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700396 ZeroOutAppReferences(apk_a->GetResourceTable());
397 ZeroOutAppReferences(apk_b->GetResourceTable());
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700398
Adam Lesinskice5e56e22016-10-21 17:56:45 -0700399 if (EmitResourceTableDiff(&context, apk_a.get(), apk_b.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700400 // We emitted a diff, so return 1 (failure).
401 return 1;
402 }
403 return 0;
Adam Lesinski458b8772016-04-25 14:20:21 -0700404}
405
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700406} // namespace aapt