blob: 3ea17552ea7ce7b49aadc72f51b487b18dedccf8 [file] [log] [blame]
Adam Lesinski8780eb62017-10-31 17:44:39 -07001/*
2 * Copyright (C) 2017 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 "Convert.h"
18
Adam Lesinski8780eb62017-10-31 17:44:39 -070019#include <vector>
20
21#include "android-base/macros.h"
22#include "androidfw/StringPiece.h"
23
Adam Lesinski8780eb62017-10-31 17:44:39 -070024#include "LoadedApk.h"
25#include "ValueVisitor.h"
26#include "cmd/Util.h"
27#include "format/binary/TableFlattener.h"
28#include "format/binary/XmlFlattener.h"
29#include "format/proto/ProtoDeserialize.h"
Pierre Lecesned8b4bea2017-11-10 23:50:17 +000030#include "format/proto/ProtoSerialize.h"
Adam Lesinski8780eb62017-10-31 17:44:39 -070031#include "io/BigBufferStream.h"
32#include "io/Util.h"
33#include "process/IResourceTableConsumer.h"
34#include "process/SymbolTable.h"
Pierre Lecesned8b4bea2017-11-10 23:50:17 +000035#include "util/Util.h"
Adam Lesinski8780eb62017-10-31 17:44:39 -070036
37using ::android::StringPiece;
Pierre Lecesned8b4bea2017-11-10 23:50:17 +000038using ::android::base::StringPrintf;
Adam Lesinski8780eb62017-10-31 17:44:39 -070039using ::std::unique_ptr;
40using ::std::vector;
41
42namespace aapt {
43
Pierre Lecesned8b4bea2017-11-10 23:50:17 +000044class IApkSerializer {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000045 public:
Pierre Lecesned8b4bea2017-11-10 23:50:17 +000046 IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000047
48 virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
Ryan Mitchell05aebf42018-10-09 15:08:41 -070049 IArchiveWriter* writer, uint32_t compression_flags) = 0;
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000050 virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0;
David Chaloupkab66db4e2018-01-15 12:35:41 +000051 virtual bool SerializeFile(FileReference* file, IArchiveWriter* writer) = 0;
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000052
Pierre Lecesned8b4bea2017-11-10 23:50:17 +000053 virtual ~IApkSerializer() = default;
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000054
55 protected:
56 IAaptContext* context_;
57 Source source_;
58};
Adam Lesinski8780eb62017-10-31 17:44:39 -070059
Pierre Lecesned8b4bea2017-11-10 23:50:17 +000060bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer* serializer,
61 IArchiveWriter* writer) {
Ryan Mitchell05aebf42018-10-09 15:08:41 -070062 io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath);
63 if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer,
64 (manifest != nullptr && manifest->WasCompressed())
65 ? ArchiveEntry::kCompress : 0u)) {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000066 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
67 << "failed to serialize AndroidManifest.xml");
Adam Lesinski8780eb62017-10-31 17:44:39 -070068 return false;
69 }
70
Tom Dobek725fb122017-12-08 14:19:01 +000071 if (apk->GetResourceTable() != nullptr) {
David Chaloupkab66db4e2018-01-15 12:35:41 +000072 // The table might be modified by below code.
73 auto converted_table = apk->GetResourceTable();
Tom Dobek725fb122017-12-08 14:19:01 +000074
75 // Resources
David Chaloupkab66db4e2018-01-15 12:35:41 +000076 for (const auto& package : converted_table->packages) {
Tom Dobek725fb122017-12-08 14:19:01 +000077 for (const auto& type : package->types) {
78 for (const auto& entry : type->entries) {
79 for (const auto& config_value : entry->values) {
David Chaloupkab66db4e2018-01-15 12:35:41 +000080 FileReference* file = ValueCast<FileReference>(config_value->value.get());
Tom Dobek725fb122017-12-08 14:19:01 +000081 if (file != nullptr) {
82 if (file->file == nullptr) {
83 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
84 << "no file associated with " << *file);
85 return false;
86 }
87
88 if (!serializer->SerializeFile(file, writer)) {
89 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
90 << "failed to serialize file " << *file->path);
91 return false;
92 }
93 } // file
94 } // config_value
95 } // entry
96 } // type
97 } // package
David Chaloupkab66db4e2018-01-15 12:35:41 +000098
99 // Converted resource table
100 if (!serializer->SerializeTable(converted_table, writer)) {
101 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
102 << "failed to serialize the resource table");
103 return false;
104 }
Adam Lesinski8780eb62017-10-31 17:44:39 -0700105 }
106
Pierre Lecesned55bef72017-11-10 22:31:01 +0000107 // Other files
108 std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
109 while (iterator->HasNext()) {
110 io::IFile* file = iterator->Next();
Pierre Lecesned55bef72017-11-10 22:31:01 +0000111 std::string path = file->GetSource().path;
Pierre Lecesned55bef72017-11-10 22:31:01 +0000112
113 // Manifest, resource table and resources have already been taken care of.
114 if (path == kAndroidManifestPath ||
115 path == kApkResourceTablePath ||
116 path == kProtoResourceTablePath ||
117 path.find("res/") == 0) {
118 continue;
119 }
120
121 if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
122 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
123 << "failed to copy file " << path);
124 return false;
125 }
126 }
127
Adam Lesinski8780eb62017-10-31 17:44:39 -0700128 return true;
129}
130
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000131
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000132class BinaryApkSerializer : public IApkSerializer {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000133 public:
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000134 BinaryApkSerializer(IAaptContext* context, const Source& source,
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000135 const TableFlattenerOptions& options)
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000136 : IApkSerializer(context, source), tableFlattenerOptions_(options) {}
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000137
138 bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
Ryan Mitchell05aebf42018-10-09 15:08:41 -0700139 IArchiveWriter* writer, uint32_t compression_flags) override {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000140 BigBuffer buffer(4096);
141 XmlFlattenerOptions options = {};
142 options.use_utf16 = utf16;
Adam Lesinskibbf42972018-02-14 13:36:09 -0800143 options.keep_raw_values = true;
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000144 XmlFlattener flattener(&buffer, options);
145 if (!flattener.Consume(context_, xml)) {
146 return false;
147 }
148
149 io::BigBufferInputStream input_stream(&buffer);
Ryan Mitchell05aebf42018-10-09 15:08:41 -0700150 return io::CopyInputStreamToArchive(context_, &input_stream, path, compression_flags, writer);
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000151 }
152
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000153 bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000154 BigBuffer buffer(4096);
155 TableFlattener table_flattener(tableFlattenerOptions_, &buffer);
156 if (!table_flattener.Consume(context_, table)) {
157 return false;
158 }
159
160 io::BigBufferInputStream input_stream(&buffer);
161 return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
162 ArchiveEntry::kAlign, writer);
163 }
164
David Chaloupkab66db4e2018-01-15 12:35:41 +0000165 bool SerializeFile(FileReference* file, IArchiveWriter* writer) override {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000166 if (file->type == ResourceFile::Type::kProtoXml) {
167 unique_ptr<io::InputStream> in = file->file->OpenInputStream();
168 if (in == nullptr) {
169 context_->GetDiagnostics()->Error(DiagMessage(source_)
170 << "failed to open file " << *file->path);
171 return false;
172 }
173
174 pb::XmlNode pb_node;
Ryan Mitchelle0eba7a2018-09-12 08:54:07 -0700175 io::ProtoInputStreamReader proto_reader(in.get());
176 if (!proto_reader.ReadMessage(&pb_node)) {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000177 context_->GetDiagnostics()->Error(DiagMessage(source_)
178 << "failed to parse proto XML " << *file->path);
179 return false;
180 }
181
182 std::string error;
183 unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
184 if (xml == nullptr) {
185 context_->GetDiagnostics()->Error(DiagMessage(source_)
186 << "failed to deserialize proto XML "
187 << *file->path << ": " << error);
188 return false;
189 }
190
Ryan Mitchell05aebf42018-10-09 15:08:41 -0700191 if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer,
192 file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000193 context_->GetDiagnostics()->Error(DiagMessage(source_)
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000194 << "failed to serialize to binary XML: " << *file->path);
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000195 return false;
196 }
David Chaloupkab66db4e2018-01-15 12:35:41 +0000197
198 file->type = ResourceFile::Type::kBinaryXml;
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000199 } else {
Pierre Lecesned55bef72017-11-10 22:31:01 +0000200 if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
201 context_->GetDiagnostics()->Error(DiagMessage(source_)
202 << "failed to copy file " << *file->path);
203 return false;
204 }
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000205 }
206
207 return true;
208 }
209
210 private:
211 TableFlattenerOptions tableFlattenerOptions_;
212
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000213 DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer);
214};
215
216class ProtoApkSerializer : public IApkSerializer {
217 public:
218 ProtoApkSerializer(IAaptContext* context, const Source& source)
219 : IApkSerializer(context, source) {}
220
221 bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
Ryan Mitchell05aebf42018-10-09 15:08:41 -0700222 IArchiveWriter* writer, uint32_t compression_flags) override {
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000223 pb::XmlNode pb_node;
224 SerializeXmlResourceToPb(*xml, &pb_node);
Ryan Mitchell05aebf42018-10-09 15:08:41 -0700225 return io::CopyProtoToArchive(context_, &pb_node, path, compression_flags, writer);
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000226 }
227
228 bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
229 pb::ResourceTable pb_table;
Ryan Mitchella15c2a82018-03-26 11:05:31 -0700230 SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics());
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000231 return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
232 ArchiveEntry::kCompress, writer);
233 }
234
David Chaloupkab66db4e2018-01-15 12:35:41 +0000235 bool SerializeFile(FileReference* file, IArchiveWriter* writer) override {
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000236 if (file->type == ResourceFile::Type::kBinaryXml) {
237 std::unique_ptr<io::IData> data = file->file->OpenAsData();
238 if (!data) {
239 context_->GetDiagnostics()->Error(DiagMessage(source_)
240 << "failed to open file " << *file->path);
241 return false;
242 }
243
244 std::string error;
245 std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
246 if (xml == nullptr) {
247 context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: "
248 << error);
249 return false;
250 }
251
Ryan Mitchell05aebf42018-10-09 15:08:41 -0700252 if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer,
253 file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) {
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000254 context_->GetDiagnostics()->Error(DiagMessage(source_)
255 << "failed to serialize to proto XML: " << *file->path);
256 return false;
257 }
David Chaloupkab66db4e2018-01-15 12:35:41 +0000258
259 file->type = ResourceFile::Type::kProtoXml;
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000260 } else {
261 if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
262 context_->GetDiagnostics()->Error(DiagMessage(source_)
263 << "failed to copy file " << *file->path);
264 return false;
265 }
266 }
267
268 return true;
269 }
270
271 private:
272 DISALLOW_COPY_AND_ASSIGN(ProtoApkSerializer);
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000273};
274
Adam Lesinski8780eb62017-10-31 17:44:39 -0700275class Context : public IAaptContext {
276 public:
277 Context() : mangler_({}), symbols_(&mangler_) {
278 }
279
280 PackageType GetPackageType() override {
281 return PackageType::kApp;
282 }
283
284 SymbolTable* GetExternalSymbols() override {
285 return &symbols_;
286 }
287
288 IDiagnostics* GetDiagnostics() override {
289 return &diag_;
290 }
291
292 const std::string& GetCompilationPackage() override {
293 return package_;
294 }
295
296 uint8_t GetPackageId() override {
297 // Nothing should call this.
298 UNIMPLEMENTED(FATAL) << "PackageID should not be necessary";
299 return 0;
300 }
301
302 NameMangler* GetNameMangler() override {
303 UNIMPLEMENTED(FATAL);
304 return nullptr;
305 }
306
307 bool IsVerbose() override {
308 return verbose_;
309 }
310
311 int GetMinSdkVersion() override {
312 return 0u;
313 }
314
315 bool verbose_ = false;
316 std::string package_;
317
318 private:
319 DISALLOW_COPY_AND_ASSIGN(Context);
320
321 NameMangler mangler_;
322 SymbolTable symbols_;
323 StdErrDiagnostics diag_;
324};
325
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700326const char* ConvertCommand::kOutputFormatProto = "proto";
327const char* ConvertCommand::kOutputFormatBinary = "binary";
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000328
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700329int ConvertCommand::Action(const std::vector<std::string>& args) {
330 if (args.size() != 1) {
331 std::cerr << "must supply a single proto APK\n";
332 Usage(&std::cerr);
333 return 1;
334 }
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000335
Adam Lesinski8780eb62017-10-31 17:44:39 -0700336 Context context;
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700337 const StringPiece& path = args[0];
Adam Lesinski8780eb62017-10-31 17:44:39 -0700338 unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
339 if (apk == nullptr) {
340 context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK");
341 return 1;
342 }
343
344 Maybe<AppInfo> app_info =
345 ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
346 if (!app_info) {
347 return 1;
348 }
349
350 context.package_ = app_info.value().package;
351
352 unique_ptr<IArchiveWriter> writer =
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700353 CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path_);
Adam Lesinski8780eb62017-10-31 17:44:39 -0700354 if (writer == nullptr) {
355 return 1;
356 }
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000357
358 unique_ptr<IApkSerializer> serializer;
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700359 if (!output_format_ || output_format_.value() == ConvertCommand::kOutputFormatBinary) {
360
361 serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options_));
362 } else if (output_format_.value() == ConvertCommand::kOutputFormatProto) {
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000363 serializer.reset(new ProtoApkSerializer(&context, apk->GetSource()));
364 } else {
365 context.GetDiagnostics()->Error(DiagMessage(path)
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700366 << "Invalid value for flag --output-format: "
367 << output_format_.value());
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000368 return 1;
369 }
370
Pierre Lecesned8b4bea2017-11-10 23:50:17 +0000371 return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1;
Adam Lesinski8780eb62017-10-31 17:44:39 -0700372}
373
374} // namespace aapt