xref: /aosp_15_r20/frameworks/base/tools/aapt2/cmd/Convert.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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