1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #include "Convert.h"
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include <vector>
20*d57664e9SAndroid Build Coastguard Worker
21*d57664e9SAndroid Build Coastguard Worker #include "Diagnostics.h"
22*d57664e9SAndroid Build Coastguard Worker #include "LoadedApk.h"
23*d57664e9SAndroid Build Coastguard Worker #include "ValueVisitor.h"
24*d57664e9SAndroid Build Coastguard Worker #include "android-base/file.h"
25*d57664e9SAndroid Build Coastguard Worker #include "android-base/macros.h"
26*d57664e9SAndroid Build Coastguard Worker #include "android-base/stringprintf.h"
27*d57664e9SAndroid Build Coastguard Worker #include "androidfw/BigBufferStream.h"
28*d57664e9SAndroid Build Coastguard Worker #include "androidfw/StringPiece.h"
29*d57664e9SAndroid Build Coastguard Worker #include "cmd/Util.h"
30*d57664e9SAndroid Build Coastguard Worker #include "format/binary/TableFlattener.h"
31*d57664e9SAndroid Build Coastguard Worker #include "format/binary/XmlFlattener.h"
32*d57664e9SAndroid Build Coastguard Worker #include "format/proto/ProtoDeserialize.h"
33*d57664e9SAndroid Build Coastguard Worker #include "format/proto/ProtoSerialize.h"
34*d57664e9SAndroid Build Coastguard Worker #include "io/Util.h"
35*d57664e9SAndroid Build Coastguard Worker #include "process/IResourceTableConsumer.h"
36*d57664e9SAndroid Build Coastguard Worker #include "process/SymbolTable.h"
37*d57664e9SAndroid Build Coastguard Worker #include "util/Util.h"
38*d57664e9SAndroid Build Coastguard Worker
39*d57664e9SAndroid Build Coastguard Worker using ::android::StringPiece;
40*d57664e9SAndroid Build Coastguard Worker using ::android::base::StringPrintf;
41*d57664e9SAndroid Build Coastguard Worker using ::std::unique_ptr;
42*d57664e9SAndroid Build Coastguard Worker using ::std::vector;
43*d57664e9SAndroid Build Coastguard Worker
44*d57664e9SAndroid Build Coastguard Worker namespace aapt {
45*d57664e9SAndroid Build Coastguard Worker
46*d57664e9SAndroid Build Coastguard Worker class IApkSerializer {
47*d57664e9SAndroid Build Coastguard Worker public:
IApkSerializer(IAaptContext * context,const android::Source & source)48*d57664e9SAndroid Build Coastguard Worker IApkSerializer(IAaptContext* context, const android::Source& source)
49*d57664e9SAndroid Build Coastguard Worker : context_(context), source_(source) {
50*d57664e9SAndroid Build Coastguard Worker }
51*d57664e9SAndroid Build Coastguard Worker
52*d57664e9SAndroid Build Coastguard Worker virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
53*d57664e9SAndroid Build Coastguard Worker IArchiveWriter* writer, uint32_t compression_flags) = 0;
54*d57664e9SAndroid Build Coastguard Worker virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0;
55*d57664e9SAndroid Build Coastguard Worker virtual bool SerializeFile(FileReference* file, IArchiveWriter* writer) = 0;
56*d57664e9SAndroid Build Coastguard Worker
57*d57664e9SAndroid Build Coastguard Worker virtual ~IApkSerializer() = default;
58*d57664e9SAndroid Build Coastguard Worker
59*d57664e9SAndroid Build Coastguard Worker protected:
60*d57664e9SAndroid Build Coastguard Worker IAaptContext* context_;
61*d57664e9SAndroid Build Coastguard Worker android::Source source_;
62*d57664e9SAndroid Build Coastguard Worker };
63*d57664e9SAndroid Build Coastguard Worker
64*d57664e9SAndroid Build Coastguard Worker class BinaryApkSerializer : public IApkSerializer {
65*d57664e9SAndroid Build Coastguard Worker public:
BinaryApkSerializer(IAaptContext * context,const android::Source & source,const TableFlattenerOptions & table_flattener_options,const XmlFlattenerOptions & xml_flattener_options)66*d57664e9SAndroid Build Coastguard Worker BinaryApkSerializer(IAaptContext* context, const android::Source& source,
67*d57664e9SAndroid Build Coastguard Worker const TableFlattenerOptions& table_flattener_options,
68*d57664e9SAndroid Build Coastguard Worker const XmlFlattenerOptions& xml_flattener_options)
69*d57664e9SAndroid Build Coastguard Worker : IApkSerializer(context, source),
70*d57664e9SAndroid Build Coastguard Worker table_flattener_options_(table_flattener_options),
71*d57664e9SAndroid Build Coastguard Worker xml_flattener_options_(xml_flattener_options) {
72*d57664e9SAndroid Build Coastguard Worker }
73*d57664e9SAndroid Build Coastguard Worker
SerializeXml(const xml::XmlResource * xml,const std::string & path,bool utf16,IArchiveWriter * writer,uint32_t compression_flags)74*d57664e9SAndroid Build Coastguard Worker bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
75*d57664e9SAndroid Build Coastguard Worker IArchiveWriter* writer, uint32_t compression_flags) override {
76*d57664e9SAndroid Build Coastguard Worker android::BigBuffer buffer(4096);
77*d57664e9SAndroid Build Coastguard Worker xml_flattener_options_.use_utf16 = utf16;
78*d57664e9SAndroid Build Coastguard Worker XmlFlattener flattener(&buffer, xml_flattener_options_);
79*d57664e9SAndroid Build Coastguard Worker if (!flattener.Consume(context_, xml)) {
80*d57664e9SAndroid Build Coastguard Worker return false;
81*d57664e9SAndroid Build Coastguard Worker }
82*d57664e9SAndroid Build Coastguard Worker
83*d57664e9SAndroid Build Coastguard Worker android::BigBufferInputStream input_stream(&buffer);
84*d57664e9SAndroid Build Coastguard Worker return io::CopyInputStreamToArchive(context_, &input_stream, path, compression_flags, writer);
85*d57664e9SAndroid Build Coastguard Worker }
86*d57664e9SAndroid Build Coastguard Worker
SerializeTable(ResourceTable * table,IArchiveWriter * writer)87*d57664e9SAndroid Build Coastguard Worker bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
88*d57664e9SAndroid Build Coastguard Worker android::BigBuffer buffer(4096);
89*d57664e9SAndroid Build Coastguard Worker TableFlattener table_flattener(table_flattener_options_, &buffer);
90*d57664e9SAndroid Build Coastguard Worker if (!table_flattener.Consume(context_, table)) {
91*d57664e9SAndroid Build Coastguard Worker return false;
92*d57664e9SAndroid Build Coastguard Worker }
93*d57664e9SAndroid Build Coastguard Worker
94*d57664e9SAndroid Build Coastguard Worker android::BigBufferInputStream input_stream(&buffer);
95*d57664e9SAndroid Build Coastguard Worker return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
96*d57664e9SAndroid Build Coastguard Worker ArchiveEntry::kAlign, writer);
97*d57664e9SAndroid Build Coastguard Worker }
98*d57664e9SAndroid Build Coastguard Worker
SerializeFile(FileReference * file,IArchiveWriter * writer)99*d57664e9SAndroid Build Coastguard Worker bool SerializeFile(FileReference* file, IArchiveWriter* writer) override {
100*d57664e9SAndroid Build Coastguard Worker if (file->type == ResourceFile::Type::kProtoXml) {
101*d57664e9SAndroid Build Coastguard Worker unique_ptr<android::InputStream> in = file->file->OpenInputStream();
102*d57664e9SAndroid Build Coastguard Worker if (in == nullptr) {
103*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(source_)
104*d57664e9SAndroid Build Coastguard Worker << "failed to open file " << *file->path);
105*d57664e9SAndroid Build Coastguard Worker return false;
106*d57664e9SAndroid Build Coastguard Worker }
107*d57664e9SAndroid Build Coastguard Worker
108*d57664e9SAndroid Build Coastguard Worker pb::XmlNode pb_node;
109*d57664e9SAndroid Build Coastguard Worker io::ProtoInputStreamReader proto_reader(in.get());
110*d57664e9SAndroid Build Coastguard Worker if (!proto_reader.ReadMessage(&pb_node)) {
111*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(source_)
112*d57664e9SAndroid Build Coastguard Worker << "failed to parse proto XML " << *file->path);
113*d57664e9SAndroid Build Coastguard Worker return false;
114*d57664e9SAndroid Build Coastguard Worker }
115*d57664e9SAndroid Build Coastguard Worker
116*d57664e9SAndroid Build Coastguard Worker std::string error;
117*d57664e9SAndroid Build Coastguard Worker unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
118*d57664e9SAndroid Build Coastguard Worker if (xml == nullptr) {
119*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(source_)
120*d57664e9SAndroid Build Coastguard Worker << "failed to deserialize proto XML " << *file->path
121*d57664e9SAndroid Build Coastguard Worker << ": " << error);
122*d57664e9SAndroid Build Coastguard Worker return false;
123*d57664e9SAndroid Build Coastguard Worker }
124*d57664e9SAndroid Build Coastguard Worker
125*d57664e9SAndroid Build Coastguard Worker if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer,
126*d57664e9SAndroid Build Coastguard Worker file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) {
127*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(source_)
128*d57664e9SAndroid Build Coastguard Worker << "failed to serialize to binary XML: " << *file->path);
129*d57664e9SAndroid Build Coastguard Worker return false;
130*d57664e9SAndroid Build Coastguard Worker }
131*d57664e9SAndroid Build Coastguard Worker
132*d57664e9SAndroid Build Coastguard Worker file->type = ResourceFile::Type::kBinaryXml;
133*d57664e9SAndroid Build Coastguard Worker } else {
134*d57664e9SAndroid Build Coastguard Worker if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
135*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(source_)
136*d57664e9SAndroid Build Coastguard Worker << "failed to copy file " << *file->path);
137*d57664e9SAndroid Build Coastguard Worker return false;
138*d57664e9SAndroid Build Coastguard Worker }
139*d57664e9SAndroid Build Coastguard Worker }
140*d57664e9SAndroid Build Coastguard Worker
141*d57664e9SAndroid Build Coastguard Worker return true;
142*d57664e9SAndroid Build Coastguard Worker }
143*d57664e9SAndroid Build Coastguard Worker
144*d57664e9SAndroid Build Coastguard Worker private:
145*d57664e9SAndroid Build Coastguard Worker TableFlattenerOptions table_flattener_options_;
146*d57664e9SAndroid Build Coastguard Worker XmlFlattenerOptions xml_flattener_options_;
147*d57664e9SAndroid Build Coastguard Worker
148*d57664e9SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer);
149*d57664e9SAndroid Build Coastguard Worker };
150*d57664e9SAndroid Build Coastguard Worker
151*d57664e9SAndroid Build Coastguard Worker class ProtoApkSerializer : public IApkSerializer {
152*d57664e9SAndroid Build Coastguard Worker public:
ProtoApkSerializer(IAaptContext * context,const android::Source & source)153*d57664e9SAndroid Build Coastguard Worker ProtoApkSerializer(IAaptContext* context, const android::Source& source)
154*d57664e9SAndroid Build Coastguard Worker : IApkSerializer(context, source) {
155*d57664e9SAndroid Build Coastguard Worker }
156*d57664e9SAndroid Build Coastguard Worker
SerializeXml(const xml::XmlResource * xml,const std::string & path,bool utf16,IArchiveWriter * writer,uint32_t compression_flags)157*d57664e9SAndroid Build Coastguard Worker bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
158*d57664e9SAndroid Build Coastguard Worker IArchiveWriter* writer, uint32_t compression_flags) override {
159*d57664e9SAndroid Build Coastguard Worker pb::XmlNode pb_node;
160*d57664e9SAndroid Build Coastguard Worker SerializeXmlResourceToPb(*xml, &pb_node);
161*d57664e9SAndroid Build Coastguard Worker return io::CopyProtoToArchive(context_, &pb_node, path, compression_flags, writer);
162*d57664e9SAndroid Build Coastguard Worker }
163*d57664e9SAndroid Build Coastguard Worker
SerializeTable(ResourceTable * table,IArchiveWriter * writer)164*d57664e9SAndroid Build Coastguard Worker bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
165*d57664e9SAndroid Build Coastguard Worker pb::ResourceTable pb_table;
166*d57664e9SAndroid Build Coastguard Worker SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics());
167*d57664e9SAndroid Build Coastguard Worker return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
168*d57664e9SAndroid Build Coastguard Worker ArchiveEntry::kCompress, writer);
169*d57664e9SAndroid Build Coastguard Worker }
170*d57664e9SAndroid Build Coastguard Worker
SerializeFile(FileReference * file,IArchiveWriter * writer)171*d57664e9SAndroid Build Coastguard Worker bool SerializeFile(FileReference* file, IArchiveWriter* writer) override {
172*d57664e9SAndroid Build Coastguard Worker if (file->type == ResourceFile::Type::kBinaryXml) {
173*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IData> data = file->file->OpenAsData();
174*d57664e9SAndroid Build Coastguard Worker if (!data) {
175*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(source_)
176*d57664e9SAndroid Build Coastguard Worker << "failed to open file " << *file->path);
177*d57664e9SAndroid Build Coastguard Worker return false;
178*d57664e9SAndroid Build Coastguard Worker }
179*d57664e9SAndroid Build Coastguard Worker
180*d57664e9SAndroid Build Coastguard Worker std::string error;
181*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
182*d57664e9SAndroid Build Coastguard Worker if (xml == nullptr) {
183*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(source_)
184*d57664e9SAndroid Build Coastguard Worker << "failed to parse binary XML: " << error);
185*d57664e9SAndroid Build Coastguard Worker return false;
186*d57664e9SAndroid Build Coastguard Worker }
187*d57664e9SAndroid Build Coastguard Worker
188*d57664e9SAndroid Build Coastguard Worker if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer,
189*d57664e9SAndroid Build Coastguard Worker file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) {
190*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(source_)
191*d57664e9SAndroid Build Coastguard Worker << "failed to serialize to proto XML: " << *file->path);
192*d57664e9SAndroid Build Coastguard Worker return false;
193*d57664e9SAndroid Build Coastguard Worker }
194*d57664e9SAndroid Build Coastguard Worker
195*d57664e9SAndroid Build Coastguard Worker file->type = ResourceFile::Type::kProtoXml;
196*d57664e9SAndroid Build Coastguard Worker } else {
197*d57664e9SAndroid Build Coastguard Worker if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
198*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(source_)
199*d57664e9SAndroid Build Coastguard Worker << "failed to copy file " << *file->path);
200*d57664e9SAndroid Build Coastguard Worker return false;
201*d57664e9SAndroid Build Coastguard Worker }
202*d57664e9SAndroid Build Coastguard Worker }
203*d57664e9SAndroid Build Coastguard Worker
204*d57664e9SAndroid Build Coastguard Worker return true;
205*d57664e9SAndroid Build Coastguard Worker }
206*d57664e9SAndroid Build Coastguard Worker
207*d57664e9SAndroid Build Coastguard Worker private:
208*d57664e9SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(ProtoApkSerializer);
209*d57664e9SAndroid Build Coastguard Worker };
210*d57664e9SAndroid Build Coastguard Worker
211*d57664e9SAndroid Build Coastguard Worker class Context : public IAaptContext {
212*d57664e9SAndroid Build Coastguard Worker public:
Context()213*d57664e9SAndroid Build Coastguard Worker Context() : mangler_({}), symbols_(&mangler_) {
214*d57664e9SAndroid Build Coastguard Worker }
215*d57664e9SAndroid Build Coastguard Worker
GetPackageType()216*d57664e9SAndroid Build Coastguard Worker PackageType GetPackageType() override {
217*d57664e9SAndroid Build Coastguard Worker return PackageType::kApp;
218*d57664e9SAndroid Build Coastguard Worker }
219*d57664e9SAndroid Build Coastguard Worker
GetExternalSymbols()220*d57664e9SAndroid Build Coastguard Worker SymbolTable* GetExternalSymbols() override {
221*d57664e9SAndroid Build Coastguard Worker return &symbols_;
222*d57664e9SAndroid Build Coastguard Worker }
223*d57664e9SAndroid Build Coastguard Worker
GetDiagnostics()224*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* GetDiagnostics() override {
225*d57664e9SAndroid Build Coastguard Worker return &diag_;
226*d57664e9SAndroid Build Coastguard Worker }
227*d57664e9SAndroid Build Coastguard Worker
GetCompilationPackage()228*d57664e9SAndroid Build Coastguard Worker const std::string& GetCompilationPackage() override {
229*d57664e9SAndroid Build Coastguard Worker return package_;
230*d57664e9SAndroid Build Coastguard Worker }
231*d57664e9SAndroid Build Coastguard Worker
GetPackageId()232*d57664e9SAndroid Build Coastguard Worker uint8_t GetPackageId() override {
233*d57664e9SAndroid Build Coastguard Worker // Nothing should call this.
234*d57664e9SAndroid Build Coastguard Worker UNIMPLEMENTED(FATAL) << "PackageID should not be necessary";
235*d57664e9SAndroid Build Coastguard Worker return 0;
236*d57664e9SAndroid Build Coastguard Worker }
237*d57664e9SAndroid Build Coastguard Worker
GetNameMangler()238*d57664e9SAndroid Build Coastguard Worker NameMangler* GetNameMangler() override {
239*d57664e9SAndroid Build Coastguard Worker UNIMPLEMENTED(FATAL);
240*d57664e9SAndroid Build Coastguard Worker return nullptr;
241*d57664e9SAndroid Build Coastguard Worker }
242*d57664e9SAndroid Build Coastguard Worker
IsVerbose()243*d57664e9SAndroid Build Coastguard Worker bool IsVerbose() override {
244*d57664e9SAndroid Build Coastguard Worker return verbose_;
245*d57664e9SAndroid Build Coastguard Worker }
246*d57664e9SAndroid Build Coastguard Worker
SetVerbose(bool verbose)247*d57664e9SAndroid Build Coastguard Worker void SetVerbose(bool verbose) {
248*d57664e9SAndroid Build Coastguard Worker verbose_ = verbose;
249*d57664e9SAndroid Build Coastguard Worker }
250*d57664e9SAndroid Build Coastguard Worker
GetMinSdkVersion()251*d57664e9SAndroid Build Coastguard Worker int GetMinSdkVersion() override {
252*d57664e9SAndroid Build Coastguard Worker return min_sdk_;
253*d57664e9SAndroid Build Coastguard Worker }
254*d57664e9SAndroid Build Coastguard Worker
GetSplitNameDependencies()255*d57664e9SAndroid Build Coastguard Worker const std::set<std::string>& GetSplitNameDependencies() override {
256*d57664e9SAndroid Build Coastguard Worker UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
257*d57664e9SAndroid Build Coastguard Worker static std::set<std::string> empty;
258*d57664e9SAndroid Build Coastguard Worker return empty;
259*d57664e9SAndroid Build Coastguard Worker }
260*d57664e9SAndroid Build Coastguard Worker
261*d57664e9SAndroid Build Coastguard Worker bool verbose_ = false;
262*d57664e9SAndroid Build Coastguard Worker std::string package_;
263*d57664e9SAndroid Build Coastguard Worker int32_t min_sdk_ = 0;
264*d57664e9SAndroid Build Coastguard Worker
265*d57664e9SAndroid Build Coastguard Worker private:
266*d57664e9SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(Context);
267*d57664e9SAndroid Build Coastguard Worker
268*d57664e9SAndroid Build Coastguard Worker NameMangler mangler_;
269*d57664e9SAndroid Build Coastguard Worker SymbolTable symbols_;
270*d57664e9SAndroid Build Coastguard Worker StdErrDiagnostics diag_;
271*d57664e9SAndroid Build Coastguard Worker };
272*d57664e9SAndroid Build Coastguard Worker
Convert(IAaptContext * context,LoadedApk * apk,IArchiveWriter * output_writer,ApkFormat output_format,TableFlattenerOptions table_flattener_options,XmlFlattenerOptions xml_flattener_options)273*d57664e9SAndroid Build Coastguard Worker int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer,
274*d57664e9SAndroid Build Coastguard Worker ApkFormat output_format, TableFlattenerOptions table_flattener_options,
275*d57664e9SAndroid Build Coastguard Worker XmlFlattenerOptions xml_flattener_options) {
276*d57664e9SAndroid Build Coastguard Worker unique_ptr<IApkSerializer> serializer;
277*d57664e9SAndroid Build Coastguard Worker if (output_format == ApkFormat::kBinary) {
278*d57664e9SAndroid Build Coastguard Worker serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), table_flattener_options,
279*d57664e9SAndroid Build Coastguard Worker xml_flattener_options));
280*d57664e9SAndroid Build Coastguard Worker } else if (output_format == ApkFormat::kProto) {
281*d57664e9SAndroid Build Coastguard Worker serializer.reset(new ProtoApkSerializer(context, apk->GetSource()));
282*d57664e9SAndroid Build Coastguard Worker } else {
283*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(apk->GetSource())
284*d57664e9SAndroid Build Coastguard Worker << "Cannot convert APK to unknown format");
285*d57664e9SAndroid Build Coastguard Worker return 1;
286*d57664e9SAndroid Build Coastguard Worker }
287*d57664e9SAndroid Build Coastguard Worker
288*d57664e9SAndroid Build Coastguard Worker io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath);
289*d57664e9SAndroid Build Coastguard Worker if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/,
290*d57664e9SAndroid Build Coastguard Worker output_writer, (manifest != nullptr && manifest->WasCompressed())
291*d57664e9SAndroid Build Coastguard Worker ? ArchiveEntry::kCompress : 0u)) {
292*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(apk->GetSource())
293*d57664e9SAndroid Build Coastguard Worker << "failed to serialize AndroidManifest.xml");
294*d57664e9SAndroid Build Coastguard Worker return 1;
295*d57664e9SAndroid Build Coastguard Worker }
296*d57664e9SAndroid Build Coastguard Worker
297*d57664e9SAndroid Build Coastguard Worker if (apk->GetResourceTable() != nullptr) {
298*d57664e9SAndroid Build Coastguard Worker // The table might be modified by below code.
299*d57664e9SAndroid Build Coastguard Worker auto converted_table = apk->GetResourceTable();
300*d57664e9SAndroid Build Coastguard Worker
301*d57664e9SAndroid Build Coastguard Worker std::unordered_set<std::string> files_written;
302*d57664e9SAndroid Build Coastguard Worker
303*d57664e9SAndroid Build Coastguard Worker // Resources
304*d57664e9SAndroid Build Coastguard Worker for (const auto& package : converted_table->packages) {
305*d57664e9SAndroid Build Coastguard Worker for (const auto& type : package->types) {
306*d57664e9SAndroid Build Coastguard Worker for (const auto& entry : type->entries) {
307*d57664e9SAndroid Build Coastguard Worker for (const auto& config_value : entry->values) {
308*d57664e9SAndroid Build Coastguard Worker FileReference* file = ValueCast<FileReference>(config_value->value.get());
309*d57664e9SAndroid Build Coastguard Worker if (file != nullptr) {
310*d57664e9SAndroid Build Coastguard Worker if (file->file == nullptr) {
311*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(apk->GetSource())
312*d57664e9SAndroid Build Coastguard Worker << "no file associated with " << *file);
313*d57664e9SAndroid Build Coastguard Worker return 1;
314*d57664e9SAndroid Build Coastguard Worker }
315*d57664e9SAndroid Build Coastguard Worker
316*d57664e9SAndroid Build Coastguard Worker // Only serialize if we haven't seen this file before
317*d57664e9SAndroid Build Coastguard Worker if (files_written.insert(*file->path).second) {
318*d57664e9SAndroid Build Coastguard Worker if (!serializer->SerializeFile(file, output_writer)) {
319*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(apk->GetSource())
320*d57664e9SAndroid Build Coastguard Worker << "failed to serialize file " << *file->path);
321*d57664e9SAndroid Build Coastguard Worker return 1;
322*d57664e9SAndroid Build Coastguard Worker }
323*d57664e9SAndroid Build Coastguard Worker }
324*d57664e9SAndroid Build Coastguard Worker } // file
325*d57664e9SAndroid Build Coastguard Worker } // config_value
326*d57664e9SAndroid Build Coastguard Worker } // entry
327*d57664e9SAndroid Build Coastguard Worker } // type
328*d57664e9SAndroid Build Coastguard Worker } // package
329*d57664e9SAndroid Build Coastguard Worker
330*d57664e9SAndroid Build Coastguard Worker // Converted resource table
331*d57664e9SAndroid Build Coastguard Worker if (!serializer->SerializeTable(converted_table, output_writer)) {
332*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(apk->GetSource())
333*d57664e9SAndroid Build Coastguard Worker << "failed to serialize the resource table");
334*d57664e9SAndroid Build Coastguard Worker return 1;
335*d57664e9SAndroid Build Coastguard Worker }
336*d57664e9SAndroid Build Coastguard Worker }
337*d57664e9SAndroid Build Coastguard Worker
338*d57664e9SAndroid Build Coastguard Worker // Other files
339*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
340*d57664e9SAndroid Build Coastguard Worker while (iterator->HasNext()) {
341*d57664e9SAndroid Build Coastguard Worker io::IFile* file = iterator->Next();
342*d57664e9SAndroid Build Coastguard Worker std::string path = file->GetSource().path;
343*d57664e9SAndroid Build Coastguard Worker
344*d57664e9SAndroid Build Coastguard Worker // Manifest, resource table and resources have already been taken care of.
345*d57664e9SAndroid Build Coastguard Worker if (path == kAndroidManifestPath ||
346*d57664e9SAndroid Build Coastguard Worker path == kApkResourceTablePath ||
347*d57664e9SAndroid Build Coastguard Worker path == kProtoResourceTablePath ||
348*d57664e9SAndroid Build Coastguard Worker path.find("res/") == 0) {
349*d57664e9SAndroid Build Coastguard Worker continue;
350*d57664e9SAndroid Build Coastguard Worker }
351*d57664e9SAndroid Build Coastguard Worker
352*d57664e9SAndroid Build Coastguard Worker if (!io::CopyFileToArchivePreserveCompression(context, file, path, output_writer)) {
353*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(apk->GetSource())
354*d57664e9SAndroid Build Coastguard Worker << "failed to copy file " << path);
355*d57664e9SAndroid Build Coastguard Worker return 1;
356*d57664e9SAndroid Build Coastguard Worker }
357*d57664e9SAndroid Build Coastguard Worker }
358*d57664e9SAndroid Build Coastguard Worker
359*d57664e9SAndroid Build Coastguard Worker return 0;
360*d57664e9SAndroid Build Coastguard Worker }
361*d57664e9SAndroid Build Coastguard Worker
ExtractResourceConfig(const std::string & path,IAaptContext * context,TableFlattenerOptions & out_options)362*d57664e9SAndroid Build Coastguard Worker bool ExtractResourceConfig(const std::string& path, IAaptContext* context,
363*d57664e9SAndroid Build Coastguard Worker TableFlattenerOptions& out_options) {
364*d57664e9SAndroid Build Coastguard Worker std::string content;
365*d57664e9SAndroid Build Coastguard Worker if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
366*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path) << "failed reading config file");
367*d57664e9SAndroid Build Coastguard Worker return false;
368*d57664e9SAndroid Build Coastguard Worker }
369*d57664e9SAndroid Build Coastguard Worker std::unordered_set<ResourceName> resources_exclude_list;
370*d57664e9SAndroid Build Coastguard Worker bool result = ParseResourceConfig(content, context, resources_exclude_list,
371*d57664e9SAndroid Build Coastguard Worker out_options.name_collapse_exemptions,
372*d57664e9SAndroid Build Coastguard Worker out_options.path_shorten_exemptions);
373*d57664e9SAndroid Build Coastguard Worker if (!result) {
374*d57664e9SAndroid Build Coastguard Worker return false;
375*d57664e9SAndroid Build Coastguard Worker }
376*d57664e9SAndroid Build Coastguard Worker if (!resources_exclude_list.empty()) {
377*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path)
378*d57664e9SAndroid Build Coastguard Worker << "Unsupported '#remove' directive in resource config.");
379*d57664e9SAndroid Build Coastguard Worker return false;
380*d57664e9SAndroid Build Coastguard Worker }
381*d57664e9SAndroid Build Coastguard Worker return true;
382*d57664e9SAndroid Build Coastguard Worker }
383*d57664e9SAndroid Build Coastguard Worker
384*d57664e9SAndroid Build Coastguard Worker const char* ConvertCommand::kOutputFormatProto = "proto";
385*d57664e9SAndroid Build Coastguard Worker const char* ConvertCommand::kOutputFormatBinary = "binary";
386*d57664e9SAndroid Build Coastguard Worker
Action(const std::vector<std::string> & args)387*d57664e9SAndroid Build Coastguard Worker int ConvertCommand::Action(const std::vector<std::string>& args) {
388*d57664e9SAndroid Build Coastguard Worker if (args.size() != 1) {
389*d57664e9SAndroid Build Coastguard Worker std::cerr << "must supply a single APK\n";
390*d57664e9SAndroid Build Coastguard Worker Usage(&std::cerr);
391*d57664e9SAndroid Build Coastguard Worker return 1;
392*d57664e9SAndroid Build Coastguard Worker }
393*d57664e9SAndroid Build Coastguard Worker
394*d57664e9SAndroid Build Coastguard Worker Context context;
395*d57664e9SAndroid Build Coastguard Worker context.SetVerbose(verbose_);
396*d57664e9SAndroid Build Coastguard Worker
397*d57664e9SAndroid Build Coastguard Worker StringPiece path = args[0];
398*d57664e9SAndroid Build Coastguard Worker unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
399*d57664e9SAndroid Build Coastguard Worker if (apk == nullptr) {
400*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(path) << "failed to load APK");
401*d57664e9SAndroid Build Coastguard Worker return 1;
402*d57664e9SAndroid Build Coastguard Worker }
403*d57664e9SAndroid Build Coastguard Worker
404*d57664e9SAndroid Build Coastguard Worker auto app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
405*d57664e9SAndroid Build Coastguard Worker if (!app_info) {
406*d57664e9SAndroid Build Coastguard Worker return 1;
407*d57664e9SAndroid Build Coastguard Worker }
408*d57664e9SAndroid Build Coastguard Worker
409*d57664e9SAndroid Build Coastguard Worker context.package_ = app_info.value().package;
410*d57664e9SAndroid Build Coastguard Worker context.min_sdk_ = app_info.value().min_sdk_version.value_or(0);
411*d57664e9SAndroid Build Coastguard Worker unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context.GetDiagnostics(),
412*d57664e9SAndroid Build Coastguard Worker output_path_);
413*d57664e9SAndroid Build Coastguard Worker if (writer == nullptr) {
414*d57664e9SAndroid Build Coastguard Worker return 1;
415*d57664e9SAndroid Build Coastguard Worker }
416*d57664e9SAndroid Build Coastguard Worker
417*d57664e9SAndroid Build Coastguard Worker ApkFormat format;
418*d57664e9SAndroid Build Coastguard Worker if (!output_format_ || output_format_.value() == ConvertCommand::kOutputFormatBinary) {
419*d57664e9SAndroid Build Coastguard Worker format = ApkFormat::kBinary;
420*d57664e9SAndroid Build Coastguard Worker } else if (output_format_.value() == ConvertCommand::kOutputFormatProto) {
421*d57664e9SAndroid Build Coastguard Worker format = ApkFormat::kProto;
422*d57664e9SAndroid Build Coastguard Worker } else {
423*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(path)
424*d57664e9SAndroid Build Coastguard Worker << "Invalid value for flag --output-format: "
425*d57664e9SAndroid Build Coastguard Worker << output_format_.value());
426*d57664e9SAndroid Build Coastguard Worker return 1;
427*d57664e9SAndroid Build Coastguard Worker }
428*d57664e9SAndroid Build Coastguard Worker if (enable_sparse_encoding_) {
429*d57664e9SAndroid Build Coastguard Worker table_flattener_options_.sparse_entries = SparseEntriesMode::Enabled;
430*d57664e9SAndroid Build Coastguard Worker }
431*d57664e9SAndroid Build Coastguard Worker if (force_sparse_encoding_) {
432*d57664e9SAndroid Build Coastguard Worker table_flattener_options_.sparse_entries = SparseEntriesMode::Forced;
433*d57664e9SAndroid Build Coastguard Worker }
434*d57664e9SAndroid Build Coastguard Worker table_flattener_options_.use_compact_entries = enable_compact_entries_;
435*d57664e9SAndroid Build Coastguard Worker if (resources_config_path_) {
436*d57664e9SAndroid Build Coastguard Worker if (!ExtractResourceConfig(*resources_config_path_, &context, table_flattener_options_)) {
437*d57664e9SAndroid Build Coastguard Worker return 1;
438*d57664e9SAndroid Build Coastguard Worker }
439*d57664e9SAndroid Build Coastguard Worker }
440*d57664e9SAndroid Build Coastguard Worker
441*d57664e9SAndroid Build Coastguard Worker return Convert(&context, apk.get(), writer.get(), format, table_flattener_options_,
442*d57664e9SAndroid Build Coastguard Worker xml_flattener_options_);
443*d57664e9SAndroid Build Coastguard Worker }
444*d57664e9SAndroid Build Coastguard Worker
445*d57664e9SAndroid Build Coastguard Worker } // namespace aapt
446