blob: 01f45399511fda24ec4a6c26cec5d8bf6448d153 [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
17#include "Flags.h"
18#include "ResourceTable.h"
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -070019#include "ValueVisitor.h"
Adam Lesinski458b8772016-04-25 14:20:21 -070020#include "io/ZipArchive.h"
21#include "process/IResourceTableConsumer.h"
22#include "process/SymbolTable.h"
23#include "unflatten/BinaryResourceParser.h"
24
25#include <android-base/macros.h>
26
27namespace aapt {
28
29class DiffContext : public IAaptContext {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070030 public:
31 const std::string& getCompilationPackage() override { return mEmpty; }
Adam Lesinski458b8772016-04-25 14:20:21 -070032
Adam Lesinskicacb28f2016-10-19 12:18:14 -070033 uint8_t getPackageId() override { return 0x0; }
Adam Lesinski458b8772016-04-25 14:20:21 -070034
Adam Lesinskicacb28f2016-10-19 12:18:14 -070035 IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
Adam Lesinski458b8772016-04-25 14:20:21 -070036
Adam Lesinskicacb28f2016-10-19 12:18:14 -070037 NameMangler* getNameMangler() override { return &mNameMangler; }
Adam Lesinski458b8772016-04-25 14:20:21 -070038
Adam Lesinskicacb28f2016-10-19 12:18:14 -070039 SymbolTable* getExternalSymbols() override { return &mSymbolTable; }
Adam Lesinski458b8772016-04-25 14:20:21 -070040
Adam Lesinskicacb28f2016-10-19 12:18:14 -070041 bool verbose() override { return false; }
Adam Lesinski458b8772016-04-25 14:20:21 -070042
Adam Lesinskicacb28f2016-10-19 12:18:14 -070043 int getMinSdkVersion() override { return 0; }
Adam Lesinskifb6312f2016-06-28 14:40:32 -070044
Adam Lesinskicacb28f2016-10-19 12:18:14 -070045 private:
46 std::string mEmpty;
47 StdErrDiagnostics mDiagnostics;
48 NameMangler mNameMangler = NameMangler(NameManglerPolicy{});
49 SymbolTable mSymbolTable;
Adam Lesinski458b8772016-04-25 14:20:21 -070050};
51
52class LoadedApk {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070053 public:
54 LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
55 std::unique_ptr<ResourceTable> table)
56 : mSource(source), mApk(std::move(apk)), mTable(std::move(table)) {}
Adam Lesinski458b8772016-04-25 14:20:21 -070057
Adam Lesinskicacb28f2016-10-19 12:18:14 -070058 io::IFileCollection* getFileCollection() { return mApk.get(); }
Adam Lesinski458b8772016-04-25 14:20:21 -070059
Adam Lesinskicacb28f2016-10-19 12:18:14 -070060 ResourceTable* getResourceTable() { return mTable.get(); }
Adam Lesinski458b8772016-04-25 14:20:21 -070061
Adam Lesinskicacb28f2016-10-19 12:18:14 -070062 const Source& getSource() { return mSource; }
Adam Lesinski458b8772016-04-25 14:20:21 -070063
Adam Lesinskicacb28f2016-10-19 12:18:14 -070064 private:
65 Source mSource;
66 std::unique_ptr<io::IFileCollection> mApk;
67 std::unique_ptr<ResourceTable> mTable;
Adam Lesinski458b8772016-04-25 14:20:21 -070068
Adam Lesinskicacb28f2016-10-19 12:18:14 -070069 DISALLOW_COPY_AND_ASSIGN(LoadedApk);
Adam Lesinski458b8772016-04-25 14:20:21 -070070};
71
Adam Lesinskicacb28f2016-10-19 12:18:14 -070072static std::unique_ptr<LoadedApk> loadApkFromPath(IAaptContext* context,
73 const StringPiece& path) {
74 Source source(path);
75 std::string error;
76 std::unique_ptr<io::ZipFileCollection> apk =
77 io::ZipFileCollection::create(path, &error);
78 if (!apk) {
79 context->getDiagnostics()->error(DiagMessage(source) << error);
80 return {};
81 }
Adam Lesinski458b8772016-04-25 14:20:21 -070082
Adam Lesinskicacb28f2016-10-19 12:18:14 -070083 io::IFile* file = apk->findFile("resources.arsc");
84 if (!file) {
85 context->getDiagnostics()->error(DiagMessage(source)
86 << "no resources.arsc found");
87 return {};
88 }
Adam Lesinski458b8772016-04-25 14:20:21 -070089
Adam Lesinskicacb28f2016-10-19 12:18:14 -070090 std::unique_ptr<io::IData> data = file->openAsData();
91 if (!data) {
92 context->getDiagnostics()->error(DiagMessage(source)
93 << "could not open resources.arsc");
94 return {};
95 }
Adam Lesinski458b8772016-04-25 14:20:21 -070096
Adam Lesinskicacb28f2016-10-19 12:18:14 -070097 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
98 BinaryResourceParser parser(context, table.get(), source, data->data(),
99 data->size());
100 if (!parser.parse()) {
101 return {};
102 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700103
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700104 return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
Adam Lesinski458b8772016-04-25 14:20:21 -0700105}
106
107static void emitDiffLine(const Source& source, const StringPiece& message) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700108 std::cerr << source << ": " << message << "\n";
Adam Lesinski458b8772016-04-25 14:20:21 -0700109}
110
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700111static bool isSymbolVisibilityDifferent(const Symbol& symbolA,
112 const Symbol& symbolB) {
113 return symbolA.state != symbolB.state;
Adam Lesinski458b8772016-04-25 14:20:21 -0700114}
115
116template <typename Id>
117static bool isIdDiff(const Symbol& symbolA, const Maybe<Id>& idA,
118 const Symbol& symbolB, const Maybe<Id>& idB) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700119 if (symbolA.state == SymbolState::kPublic ||
120 symbolB.state == SymbolState::kPublic) {
121 return idA != idB;
122 }
123 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700124}
125
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700126static bool emitResourceConfigValueDiff(
127 IAaptContext* context, LoadedApk* apkA, ResourceTablePackage* pkgA,
128 ResourceTableType* typeA, ResourceEntry* entryA,
129 ResourceConfigValue* configValueA, LoadedApk* apkB,
130 ResourceTablePackage* pkgB, ResourceTableType* typeB, ResourceEntry* entryB,
131 ResourceConfigValue* configValueB) {
132 Value* valueA = configValueA->value.get();
133 Value* valueB = configValueB->value.get();
134 if (!valueA->equals(valueB)) {
135 std::stringstream strStream;
136 strStream << "value " << pkgA->name << ":" << typeA->type << "/"
137 << entryA->name << " config=" << configValueA->config
138 << " does not match:\n";
139 valueA->print(&strStream);
140 strStream << "\n vs \n";
141 valueB->print(&strStream);
142 emitDiffLine(apkB->getSource(), strStream.str());
143 return true;
144 }
145 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700146}
147
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700148static bool emitResourceEntryDiff(IAaptContext* context, LoadedApk* apkA,
Adam Lesinski458b8772016-04-25 14:20:21 -0700149 ResourceTablePackage* pkgA,
150 ResourceTableType* typeA,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700151 ResourceEntry* entryA, LoadedApk* apkB,
Adam Lesinski458b8772016-04-25 14:20:21 -0700152 ResourceTablePackage* pkgB,
153 ResourceTableType* typeB,
154 ResourceEntry* entryB) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700155 bool diff = false;
156 for (std::unique_ptr<ResourceConfigValue>& configValueA : entryA->values) {
157 ResourceConfigValue* configValueB = entryB->findValue(configValueA->config);
158 if (!configValueB) {
159 std::stringstream strStream;
160 strStream << "missing " << pkgA->name << ":" << typeA->type << "/"
161 << entryA->name << " config=" << configValueA->config;
162 emitDiffLine(apkB->getSource(), strStream.str());
163 diff = true;
164 } else {
165 diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA,
166 configValueA.get(), apkB, pkgB, typeB,
167 entryB, configValueB);
Adam Lesinski458b8772016-04-25 14:20:21 -0700168 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700169 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700170
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700171 // Check for any newly added config values.
172 for (std::unique_ptr<ResourceConfigValue>& configValueB : entryB->values) {
173 ResourceConfigValue* configValueA = entryA->findValue(configValueB->config);
174 if (!configValueA) {
175 std::stringstream strStream;
176 strStream << "new config " << pkgB->name << ":" << typeB->type << "/"
177 << entryB->name << " config=" << configValueB->config;
178 emitDiffLine(apkB->getSource(), strStream.str());
179 diff = true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700180 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700181 }
182 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700183}
184
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700185static bool emitResourceTypeDiff(IAaptContext* context, LoadedApk* apkA,
Adam Lesinski458b8772016-04-25 14:20:21 -0700186 ResourceTablePackage* pkgA,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700187 ResourceTableType* typeA, LoadedApk* apkB,
Adam Lesinski458b8772016-04-25 14:20:21 -0700188 ResourceTablePackage* pkgB,
189 ResourceTableType* typeB) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700190 bool diff = false;
191 for (std::unique_ptr<ResourceEntry>& entryA : typeA->entries) {
192 ResourceEntry* entryB = typeB->findEntry(entryA->name);
193 if (!entryB) {
194 std::stringstream strStream;
195 strStream << "missing " << pkgA->name << ":" << typeA->type << "/"
196 << entryA->name;
197 emitDiffLine(apkB->getSource(), strStream.str());
198 diff = true;
199 } else {
200 if (isSymbolVisibilityDifferent(entryA->symbolStatus,
201 entryB->symbolStatus)) {
202 std::stringstream strStream;
203 strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
204 << " has different visibility (";
205 if (entryB->symbolStatus.state == SymbolState::kPublic) {
206 strStream << "PUBLIC";
Adam Lesinski458b8772016-04-25 14:20:21 -0700207 } else {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700208 strStream << "PRIVATE";
Adam Lesinski458b8772016-04-25 14:20:21 -0700209 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700210 strStream << " vs ";
211 if (entryA->symbolStatus.state == SymbolState::kPublic) {
212 strStream << "PUBLIC";
213 } else {
214 strStream << "PRIVATE";
215 }
216 strStream << ")";
217 emitDiffLine(apkB->getSource(), strStream.str());
218 diff = true;
219 } else if (isIdDiff(entryA->symbolStatus, entryA->id,
220 entryB->symbolStatus, entryB->id)) {
221 std::stringstream strStream;
222 strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
223 << " has different public ID (";
224 if (entryB->id) {
225 strStream << "0x" << std::hex << entryB->id.value();
226 } else {
227 strStream << "none";
228 }
229 strStream << " vs ";
230 if (entryA->id) {
231 strStream << "0x " << std::hex << entryA->id.value();
232 } else {
233 strStream << "none";
234 }
235 strStream << ")";
236 emitDiffLine(apkB->getSource(), strStream.str());
237 diff = true;
238 }
239 diff |= emitResourceEntryDiff(context, apkA, pkgA, typeA, entryA.get(),
240 apkB, pkgB, typeB, entryB);
Adam Lesinski458b8772016-04-25 14:20:21 -0700241 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700242 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700243
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700244 // Check for any newly added entries.
245 for (std::unique_ptr<ResourceEntry>& entryB : typeB->entries) {
246 ResourceEntry* entryA = typeA->findEntry(entryB->name);
247 if (!entryA) {
248 std::stringstream strStream;
249 strStream << "new entry " << pkgB->name << ":" << typeB->type << "/"
250 << entryB->name;
251 emitDiffLine(apkB->getSource(), strStream.str());
252 diff = true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700253 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700254 }
255 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700256}
257
258static bool emitResourcePackageDiff(IAaptContext* context, LoadedApk* apkA,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700259 ResourceTablePackage* pkgA, LoadedApk* apkB,
260 ResourceTablePackage* pkgB) {
261 bool diff = false;
262 for (std::unique_ptr<ResourceTableType>& typeA : pkgA->types) {
263 ResourceTableType* typeB = pkgB->findType(typeA->type);
264 if (!typeB) {
265 std::stringstream strStream;
266 strStream << "missing " << pkgA->name << ":" << typeA->type;
267 emitDiffLine(apkA->getSource(), strStream.str());
268 diff = true;
269 } else {
270 if (isSymbolVisibilityDifferent(typeA->symbolStatus,
271 typeB->symbolStatus)) {
272 std::stringstream strStream;
273 strStream << pkgA->name << ":" << typeA->type
274 << " has different visibility (";
275 if (typeB->symbolStatus.state == SymbolState::kPublic) {
276 strStream << "PUBLIC";
Adam Lesinski458b8772016-04-25 14:20:21 -0700277 } else {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700278 strStream << "PRIVATE";
Adam Lesinski458b8772016-04-25 14:20:21 -0700279 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700280 strStream << " vs ";
281 if (typeA->symbolStatus.state == SymbolState::kPublic) {
282 strStream << "PUBLIC";
283 } else {
284 strStream << "PRIVATE";
285 }
286 strStream << ")";
287 emitDiffLine(apkB->getSource(), strStream.str());
288 diff = true;
289 } else if (isIdDiff(typeA->symbolStatus, typeA->id, typeB->symbolStatus,
290 typeB->id)) {
291 std::stringstream strStream;
292 strStream << pkgA->name << ":" << typeA->type
293 << " has different public ID (";
294 if (typeB->id) {
295 strStream << "0x" << std::hex << typeB->id.value();
296 } else {
297 strStream << "none";
298 }
299 strStream << " vs ";
300 if (typeA->id) {
301 strStream << "0x " << std::hex << typeA->id.value();
302 } else {
303 strStream << "none";
304 }
305 strStream << ")";
306 emitDiffLine(apkB->getSource(), strStream.str());
307 diff = true;
308 }
309 diff |= emitResourceTypeDiff(context, apkA, pkgA, typeA.get(), apkB, pkgB,
310 typeB);
Adam Lesinski458b8772016-04-25 14:20:21 -0700311 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700312 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700313
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700314 // Check for any newly added types.
315 for (std::unique_ptr<ResourceTableType>& typeB : pkgB->types) {
316 ResourceTableType* typeA = pkgA->findType(typeB->type);
317 if (!typeA) {
318 std::stringstream strStream;
319 strStream << "new type " << pkgB->name << ":" << typeB->type;
320 emitDiffLine(apkB->getSource(), strStream.str());
321 diff = true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700322 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700323 }
324 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700325}
326
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700327static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA,
328 LoadedApk* apkB) {
329 ResourceTable* tableA = apkA->getResourceTable();
330 ResourceTable* tableB = apkB->getResourceTable();
Adam Lesinski458b8772016-04-25 14:20:21 -0700331
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700332 bool diff = false;
333 for (std::unique_ptr<ResourceTablePackage>& pkgA : tableA->packages) {
334 ResourceTablePackage* pkgB = tableB->findPackage(pkgA->name);
335 if (!pkgB) {
336 std::stringstream strStream;
337 strStream << "missing package " << pkgA->name;
338 emitDiffLine(apkB->getSource(), strStream.str());
339 diff = true;
340 } else {
341 if (pkgA->id != pkgB->id) {
342 std::stringstream strStream;
343 strStream << "package '" << pkgA->name << "' has different id (";
344 if (pkgB->id) {
345 strStream << "0x" << std::hex << pkgB->id.value();
Adam Lesinski458b8772016-04-25 14:20:21 -0700346 } else {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700347 strStream << "none";
Adam Lesinski458b8772016-04-25 14:20:21 -0700348 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700349 strStream << " vs ";
350 if (pkgA->id) {
351 strStream << "0x" << std::hex << pkgA->id.value();
352 } else {
353 strStream << "none";
354 }
355 strStream << ")";
356 emitDiffLine(apkB->getSource(), strStream.str());
357 diff = true;
358 }
359 diff |= emitResourcePackageDiff(context, apkA, pkgA.get(), apkB, pkgB);
Adam Lesinski458b8772016-04-25 14:20:21 -0700360 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700361 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700362
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700363 // Check for any newly added packages.
364 for (std::unique_ptr<ResourceTablePackage>& pkgB : tableB->packages) {
365 ResourceTablePackage* pkgA = tableA->findPackage(pkgB->name);
366 if (!pkgA) {
367 std::stringstream strStream;
368 strStream << "new package " << pkgB->name;
369 emitDiffLine(apkB->getSource(), strStream.str());
370 diff = true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700371 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700372 }
373 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700374}
375
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700376class ZeroingReferenceVisitor : public ValueVisitor {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700377 public:
378 using ValueVisitor::visit;
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700379
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700380 void visit(Reference* ref) override {
381 if (ref->name && ref->id) {
382 if (ref->id.value().packageId() == 0x7f) {
383 ref->id = {};
384 }
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700385 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700386 }
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700387};
388
389static void zeroOutAppReferences(ResourceTable* table) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700390 ZeroingReferenceVisitor visitor;
391 visitAllValuesInTable(table, &visitor);
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700392}
393
Adam Lesinski458b8772016-04-25 14:20:21 -0700394int diff(const std::vector<StringPiece>& args) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700395 DiffContext context;
Adam Lesinski458b8772016-04-25 14:20:21 -0700396
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700397 Flags flags;
398 if (!flags.parse("aapt2 diff", args, &std::cerr)) {
399 return 1;
400 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700401
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700402 if (flags.getArgs().size() != 2u) {
403 std::cerr << "must have two apks as arguments.\n\n";
404 flags.usage("aapt2 diff", &std::cerr);
405 return 1;
406 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700407
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700408 std::unique_ptr<LoadedApk> apkA =
409 loadApkFromPath(&context, flags.getArgs()[0]);
410 std::unique_ptr<LoadedApk> apkB =
411 loadApkFromPath(&context, flags.getArgs()[1]);
412 if (!apkA || !apkB) {
413 return 1;
414 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700415
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700416 // Zero out Application IDs in references.
417 zeroOutAppReferences(apkA->getResourceTable());
418 zeroOutAppReferences(apkB->getResourceTable());
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700419
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700420 if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) {
421 // We emitted a diff, so return 1 (failure).
422 return 1;
423 }
424 return 0;
Adam Lesinski458b8772016-04-25 14:20:21 -0700425}
426
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700427} // namespace aapt