• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 #include "Convert.h"
18 
19 #include <vector>
20 
21 #include "android-base/macros.h"
22 #include "androidfw/StringPiece.h"
23 
24 #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"
30 #include "format/proto/ProtoSerialize.h"
31 #include "io/BigBufferStream.h"
32 #include "io/Util.h"
33 #include "process/IResourceTableConsumer.h"
34 #include "process/SymbolTable.h"
35 #include "util/Util.h"
36 
37 using ::android::StringPiece;
38 using ::android::base::StringPrintf;
39 using ::std::unique_ptr;
40 using ::std::vector;
41 
42 namespace aapt {
43 
44 class IApkSerializer {
45  public:
IApkSerializer(IAaptContext * context,const Source & source)46   IApkSerializer(IAaptContext* context, const Source& source) : context_(context),
47                                                                 source_(source) {}
48 
49   virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
50                             IArchiveWriter* writer, uint32_t compression_flags) = 0;
51   virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0;
52   virtual bool SerializeFile(FileReference* file, IArchiveWriter* writer) = 0;
53 
54   virtual ~IApkSerializer() = default;
55 
56  protected:
57   IAaptContext* context_;
58   Source source_;
59 };
60 
61 class BinaryApkSerializer : public IApkSerializer {
62  public:
BinaryApkSerializer(IAaptContext * context,const Source & source,const TableFlattenerOptions & table_flattener_options,const XmlFlattenerOptions & xml_flattener_options)63   BinaryApkSerializer(IAaptContext* context, const Source& source,
64                       const TableFlattenerOptions& table_flattener_options,
65                       const XmlFlattenerOptions& xml_flattener_options)
66       : IApkSerializer(context, source),
67         table_flattener_options_(table_flattener_options),
68         xml_flattener_options_(xml_flattener_options) {}
69 
SerializeXml(const xml::XmlResource * xml,const std::string & path,bool utf16,IArchiveWriter * writer,uint32_t compression_flags)70   bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
71                     IArchiveWriter* writer, uint32_t compression_flags) override {
72     BigBuffer buffer(4096);
73     xml_flattener_options_.use_utf16 = utf16;
74     XmlFlattener flattener(&buffer, xml_flattener_options_);
75     if (!flattener.Consume(context_, xml)) {
76       return false;
77     }
78 
79     io::BigBufferInputStream input_stream(&buffer);
80     return io::CopyInputStreamToArchive(context_, &input_stream, path, compression_flags, writer);
81   }
82 
SerializeTable(ResourceTable * table,IArchiveWriter * writer)83   bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
84     BigBuffer buffer(4096);
85     TableFlattener table_flattener(table_flattener_options_, &buffer);
86     if (!table_flattener.Consume(context_, table)) {
87       return false;
88     }
89 
90     io::BigBufferInputStream input_stream(&buffer);
91     return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
92                                         ArchiveEntry::kAlign, writer);
93   }
94 
SerializeFile(FileReference * file,IArchiveWriter * writer)95   bool SerializeFile(FileReference* file, IArchiveWriter* writer) override {
96     if (file->type == ResourceFile::Type::kProtoXml) {
97       unique_ptr<io::InputStream> in = file->file->OpenInputStream();
98       if (in == nullptr) {
99         context_->GetDiagnostics()->Error(DiagMessage(source_)
100                                           << "failed to open file " << *file->path);
101         return false;
102       }
103 
104       pb::XmlNode pb_node;
105       io::ProtoInputStreamReader proto_reader(in.get());
106       if (!proto_reader.ReadMessage(&pb_node)) {
107         context_->GetDiagnostics()->Error(DiagMessage(source_)
108                                           << "failed to parse proto XML " << *file->path);
109         return false;
110       }
111 
112       std::string error;
113       unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
114       if (xml == nullptr) {
115         context_->GetDiagnostics()->Error(DiagMessage(source_)
116                                           << "failed to deserialize proto XML "
117                                           << *file->path << ": " << error);
118         return false;
119       }
120 
121       if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer,
122                         file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) {
123         context_->GetDiagnostics()->Error(DiagMessage(source_)
124                                           << "failed to serialize to binary XML: " << *file->path);
125         return false;
126       }
127 
128       file->type = ResourceFile::Type::kBinaryXml;
129     } else {
130       if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
131         context_->GetDiagnostics()->Error(DiagMessage(source_)
132                                           << "failed to copy file " << *file->path);
133         return false;
134       }
135     }
136 
137     return true;
138   }
139 
140  private:
141   TableFlattenerOptions table_flattener_options_;
142   XmlFlattenerOptions xml_flattener_options_;
143 
144   DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer);
145 };
146 
147 class ProtoApkSerializer : public IApkSerializer {
148  public:
ProtoApkSerializer(IAaptContext * context,const Source & source)149   ProtoApkSerializer(IAaptContext* context, const Source& source)
150       : IApkSerializer(context, source) {}
151 
SerializeXml(const xml::XmlResource * xml,const std::string & path,bool utf16,IArchiveWriter * writer,uint32_t compression_flags)152   bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
153                     IArchiveWriter* writer, uint32_t compression_flags) override {
154     pb::XmlNode pb_node;
155     SerializeXmlResourceToPb(*xml, &pb_node);
156     return io::CopyProtoToArchive(context_, &pb_node, path, compression_flags, writer);
157   }
158 
SerializeTable(ResourceTable * table,IArchiveWriter * writer)159   bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
160     pb::ResourceTable pb_table;
161     SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics());
162     return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
163                                   ArchiveEntry::kCompress, writer);
164   }
165 
SerializeFile(FileReference * file,IArchiveWriter * writer)166   bool SerializeFile(FileReference* file, IArchiveWriter* writer) override {
167     if (file->type == ResourceFile::Type::kBinaryXml) {
168       std::unique_ptr<io::IData> data = file->file->OpenAsData();
169       if (!data) {
170         context_->GetDiagnostics()->Error(DiagMessage(source_)
171                                           << "failed to open file " << *file->path);
172         return false;
173       }
174 
175       std::string error;
176       std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
177       if (xml == nullptr) {
178         context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: "
179                                                                << error);
180         return false;
181       }
182 
183       if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer,
184                         file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) {
185         context_->GetDiagnostics()->Error(DiagMessage(source_)
186                                           << "failed to serialize to proto XML: " << *file->path);
187         return false;
188       }
189 
190       file->type = ResourceFile::Type::kProtoXml;
191     } else {
192       if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
193         context_->GetDiagnostics()->Error(DiagMessage(source_)
194                                           << "failed to copy file " << *file->path);
195         return false;
196       }
197     }
198 
199     return true;
200   }
201 
202  private:
203   DISALLOW_COPY_AND_ASSIGN(ProtoApkSerializer);
204 };
205 
206 class Context : public IAaptContext {
207  public:
Context()208   Context() : mangler_({}), symbols_(&mangler_) {
209   }
210 
GetPackageType()211   PackageType GetPackageType() override {
212     return PackageType::kApp;
213   }
214 
GetExternalSymbols()215   SymbolTable* GetExternalSymbols() override {
216     return &symbols_;
217   }
218 
GetDiagnostics()219   IDiagnostics* GetDiagnostics() override {
220     return &diag_;
221   }
222 
GetCompilationPackage()223   const std::string& GetCompilationPackage() override {
224     return package_;
225   }
226 
GetPackageId()227   uint8_t GetPackageId() override {
228     // Nothing should call this.
229     UNIMPLEMENTED(FATAL) << "PackageID should not be necessary";
230     return 0;
231   }
232 
GetNameMangler()233   NameMangler* GetNameMangler() override {
234     UNIMPLEMENTED(FATAL);
235     return nullptr;
236   }
237 
IsVerbose()238   bool IsVerbose() override {
239     return verbose_;
240   }
241 
GetMinSdkVersion()242   int GetMinSdkVersion() override {
243     return 0u;
244   }
245 
GetSplitNameDependencies()246   const std::set<std::string>& GetSplitNameDependencies() override {
247     UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
248     static std::set<std::string> empty;
249     return empty;
250   }
251 
252   bool verbose_ = false;
253   std::string package_;
254 
255  private:
256   DISALLOW_COPY_AND_ASSIGN(Context);
257 
258   NameMangler mangler_;
259   SymbolTable symbols_;
260   StdErrDiagnostics diag_;
261 };
262 
Convert(IAaptContext * context,LoadedApk * apk,IArchiveWriter * output_writer,ApkFormat output_format,TableFlattenerOptions table_flattener_options,XmlFlattenerOptions xml_flattener_options)263 int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer,
264             ApkFormat output_format, TableFlattenerOptions table_flattener_options,
265             XmlFlattenerOptions xml_flattener_options) {
266   unique_ptr<IApkSerializer> serializer;
267   if (output_format == ApkFormat::kBinary) {
268     serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), table_flattener_options,
269                                              xml_flattener_options));
270   } else if (output_format == ApkFormat::kProto) {
271     serializer.reset(new ProtoApkSerializer(context, apk->GetSource()));
272   } else {
273     context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
274                                      << "Cannot convert APK to unknown format");
275     return 1;
276   }
277 
278   io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath);
279   if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/,
280                                 output_writer, (manifest != nullptr && manifest->WasCompressed())
281                                                ? ArchiveEntry::kCompress : 0u)) {
282     context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
283                                      << "failed to serialize AndroidManifest.xml");
284     return 1;
285   }
286 
287   if (apk->GetResourceTable() != nullptr) {
288     // The table might be modified by below code.
289     auto converted_table = apk->GetResourceTable();
290 
291     std::unordered_set<std::string> files_written;
292 
293     // Resources
294     for (const auto& package : converted_table->packages) {
295       for (const auto& type : package->types) {
296         for (const auto& entry : type->entries) {
297           for (const auto& config_value : entry->values) {
298             FileReference* file = ValueCast<FileReference>(config_value->value.get());
299             if (file != nullptr) {
300               if (file->file == nullptr) {
301                 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
302                                                  << "no file associated with " << *file);
303                 return 1;
304               }
305 
306               // Only serialize if we haven't seen this file before
307               if (files_written.insert(*file->path).second) {
308                 if (!serializer->SerializeFile(file, output_writer)) {
309                   context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
310                                                    << "failed to serialize file " << *file->path);
311                   return 1;
312                 }
313               }
314             } // file
315           } // config_value
316         } // entry
317       } // type
318     } // package
319 
320     // Converted resource table
321     if (!serializer->SerializeTable(converted_table, output_writer)) {
322       context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
323                                        << "failed to serialize the resource table");
324       return 1;
325     }
326   }
327 
328   // Other files
329   std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
330   while (iterator->HasNext()) {
331     io::IFile* file = iterator->Next();
332     std::string path = file->GetSource().path;
333 
334     // Manifest, resource table and resources have already been taken care of.
335     if (path == kAndroidManifestPath ||
336         path == kApkResourceTablePath ||
337         path == kProtoResourceTablePath ||
338         path.find("res/") == 0) {
339       continue;
340     }
341 
342     if (!io::CopyFileToArchivePreserveCompression(context, file, path, output_writer)) {
343       context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
344                                            << "failed to copy file " << path);
345       return 1;
346     }
347   }
348 
349   return 0;
350 }
351 
352 const char* ConvertCommand::kOutputFormatProto = "proto";
353 const char* ConvertCommand::kOutputFormatBinary = "binary";
354 
Action(const std::vector<std::string> & args)355 int ConvertCommand::Action(const std::vector<std::string>& args) {
356   if (args.size() != 1) {
357     std::cerr << "must supply a single APK\n";
358     Usage(&std::cerr);
359     return 1;
360   }
361 
362   Context context;
363   const StringPiece& path = args[0];
364   unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
365   if (apk == nullptr) {
366     context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK");
367     return 1;
368   }
369 
370   Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(),
371                                                              context.GetDiagnostics());
372   if (!app_info) {
373     return 1;
374   }
375 
376   context.package_ = app_info.value().package;
377   unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context.GetDiagnostics(),
378                                                                  output_path_);
379   if (writer == nullptr) {
380     return 1;
381   }
382 
383   ApkFormat format;
384   if (!output_format_ || output_format_.value() == ConvertCommand::kOutputFormatBinary) {
385     format = ApkFormat::kBinary;
386   } else if (output_format_.value() == ConvertCommand::kOutputFormatProto) {
387     format = ApkFormat::kProto;
388   } else {
389     context.GetDiagnostics()->Error(DiagMessage(path) << "Invalid value for flag --output-format: "
390                                                       << output_format_.value());
391     return 1;
392   }
393 
394   return Convert(&context, apk.get(), writer.get(), format, table_flattener_options_,
395                  xml_flattener_options_);
396 }
397 
398 }  // namespace aapt
399