Loading tools/aapt2/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ toolSources = [ "cmd/Compile.cpp", "cmd/Convert.cpp", "cmd/Diff.cpp", "cmd/Dump.cpp", "cmd/Link.cpp", Loading tools/aapt2/LoadedApk.cpp +110 −36 Original line number Diff line number Diff line Loading @@ -21,43 +21,139 @@ #include "format/Archive.h" #include "format/binary/TableFlattener.h" #include "format/binary/XmlFlattener.h" #include "format/proto/ProtoDeserialize.h" #include "io/BigBufferStream.h" #include "io/Util.h" #include "xml/XmlDom.h" namespace aapt { using ::aapt::io::IFile; using ::aapt::io::IFileCollection; using ::aapt::xml::XmlResource; using ::android::StringPiece; using ::std::unique_ptr; using xml::XmlResource; namespace aapt { std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context, const android::StringPiece& path) { std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) { Source source(path); std::string error; std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::Create(path, &error); if (!apk) { context->GetDiagnostics()->Error(DiagMessage(source) << error); if (apk == nullptr) { diag->Error(DiagMessage(path) << "failed opening zip: " << error); return {}; } if (apk->FindFile("resources.arsc") != nullptr) { return LoadBinaryApkFromFileCollection(source, std::move(apk), diag); } else if (apk->FindFile("resources.pb") != nullptr) { return LoadProtoApkFromFileCollection(source, std::move(apk), diag); } diag->Error(DiagMessage(path) << "no resource table found"); return {}; } std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection( const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) { io::IFile* table_file = collection->FindFile(kProtoResourceTablePath); if (table_file == nullptr) { diag->Error(DiagMessage(source) << "failed to find " << kProtoResourceTablePath); return {}; } std::unique_ptr<io::InputStream> in = table_file->OpenInputStream(); if (in == nullptr) { diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath); return {}; } pb::ResourceTable pb_table; io::ZeroCopyInputAdaptor adaptor(in.get()); if (!pb_table.ParseFromZeroCopyStream(&adaptor)) { diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath); return {}; } std::string error; std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) { diag->Error(DiagMessage(source) << "failed to deserialize " << kProtoResourceTablePath << ": " << error); return {}; } io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); if (manifest_file == nullptr) { diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath); return {}; } std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream(); if (manifest_in == nullptr) { diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath); return {}; } pb::XmlNode pb_node; io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get()); if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) { diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath); return {}; } std::unique_ptr<xml::XmlResource> manifest = DeserializeXmlResourceFromPb(pb_node, &error); if (manifest == nullptr) { diag->Error(DiagMessage(source) << "failed to deserialize proto " << kAndroidManifestPath << ": " << error); return {}; } return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table), std::move(manifest)); } std::unique_ptr<LoadedApk> LoadedApk::LoadBinaryApkFromFileCollection( const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) { io::IFile* table_file = collection->FindFile(kApkResourceTablePath); if (table_file == nullptr) { diag->Error(DiagMessage(source) << "failed to find " << kApkResourceTablePath); io::IFile* file = apk->FindFile("resources.arsc"); if (!file) { context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found"); return {}; } std::unique_ptr<io::IData> data = file->OpenAsData(); if (!data) { context->GetDiagnostics()->Error(DiagMessage(source) << "could not open resources.arsc"); std::unique_ptr<io::IData> data = table_file->OpenAsData(); if (data == nullptr) { diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath); return {}; } std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); BinaryResourceParser parser(context, table.get(), source, data->data(), data->size(), apk.get()); BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(), collection.get()); if (!parser.Parse()) { return {}; } return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table)); io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); if (manifest_file == nullptr) { diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath); return {}; } std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData(); if (manifest_data == nullptr) { diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath); return {}; } std::string error; std::unique_ptr<xml::XmlResource> manifest = xml::Inflate(manifest_data->data(), manifest_data->size(), &error); if (manifest == nullptr) { diag->Error(DiagMessage(source) << "failed to parse binary " << kAndroidManifestPath << ": " << error); return {}; } return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table), std::move(manifest)); } bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, Loading Loading @@ -148,26 +244,4 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table return true; } std::unique_ptr<xml::XmlResource> LoadedApk::InflateManifest(IAaptContext* context) { IDiagnostics* diag = context->GetDiagnostics(); io::IFile* manifest_file = GetFileCollection()->FindFile("AndroidManifest.xml"); if (manifest_file == nullptr) { diag->Error(DiagMessage(source_) << "no AndroidManifest.xml found"); return {}; } std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData(); if (manifest_data == nullptr) { diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml"); return {}; } std::unique_ptr<xml::XmlResource> manifest = xml::Inflate(manifest_data->data(), manifest_data->size(), diag, manifest_file->GetSource()); if (manifest == nullptr) { diag->Error(DiagMessage() << "failed to read binary AndroidManifest.xml"); } return manifest; } } // namespace aapt tools/aapt2/LoadedApk.h +32 −11 Original line number Diff line number Diff line Loading @@ -29,20 +29,41 @@ namespace aapt { constexpr static const char kApkResourceTablePath[] = "resources.arsc"; constexpr static const char kProtoResourceTablePath[] = "resources.pb"; constexpr static const char kAndroidManifestPath[] = "AndroidManifest.xml"; // Info about an APK loaded in memory. class LoadedApk { public: LoadedApk( const Source& source, std::unique_ptr<io::IFileCollection> apk, std::unique_ptr<ResourceTable> table) : source_(source), apk_(std::move(apk)), table_(std::move(table)) {} virtual ~LoadedApk() = default; // Loads both binary and proto APKs from disk. static std::unique_ptr<LoadedApk> LoadApkFromPath(const ::android::StringPiece& path, IDiagnostics* diag); // Loads a proto APK from the given file collection. static std::unique_ptr<LoadedApk> LoadProtoApkFromFileCollection( const Source& source, std::unique_ptr<io::IFileCollection> collection, IDiagnostics* diag); // Loads a binary APK from the given file collection. static std::unique_ptr<LoadedApk> LoadBinaryApkFromFileCollection( const Source& source, std::unique_ptr<io::IFileCollection> collection, IDiagnostics* diag); LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk, std::unique_ptr<ResourceTable> table, std::unique_ptr<xml::XmlResource> manifest) : source_(source), apk_(std::move(apk)), table_(std::move(table)), manifest_(std::move(manifest)) { } io::IFileCollection* GetFileCollection() { return apk_.get(); } const ResourceTable* GetResourceTable() const { return table_.get(); } ResourceTable* GetResourceTable() { return table_.get(); } Loading @@ -51,6 +72,10 @@ class LoadedApk { return source_; } const xml::XmlResource* GetManifest() const { return manifest_.get(); } /** * Writes the APK on disk at the given path, while also removing the resource * files that are not referenced in the resource table. Loading @@ -71,11 +96,6 @@ class LoadedApk { const TableFlattenerOptions& options, FilterChain* filters, IArchiveWriter* writer, xml::XmlResource* manifest = nullptr); /** Inflates the AndroidManifest.xml file from the APK. */ std::unique_ptr<xml::XmlResource> InflateManifest(IAaptContext* context); static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context, const android::StringPiece& path); private: DISALLOW_COPY_AND_ASSIGN(LoadedApk); Loading @@ -83,6 +103,7 @@ class LoadedApk { Source source_; std::unique_ptr<io::IFileCollection> apk_; std::unique_ptr<ResourceTable> table_; std::unique_ptr<xml::XmlResource> manifest_; }; } // namespace aapt Loading tools/aapt2/Main.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ static void PrintVersion() { } static void PrintUsage() { std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl; std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl; } extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics); Loading @@ -58,6 +58,7 @@ extern int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) extern int Dump(const std::vector<StringPiece>& args); extern int Diff(const std::vector<StringPiece>& args); extern int Optimize(const std::vector<StringPiece>& args); extern int Convert(const std::vector<StringPiece>& args); static int ExecuteCommand(const StringPiece& command, const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { Loading @@ -71,6 +72,8 @@ static int ExecuteCommand(const StringPiece& command, const std::vector<StringPi return Diff(args); } else if (command == "optimize") { return Optimize(args); } else if (command == "convert") { return Convert(args); } else if (command == "version") { PrintVersion(); return 0; Loading tools/aapt2/cmd/Convert.cpp 0 → 100644 +228 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <vector> #include "android-base/macros.h" #include "androidfw/StringPiece.h" #include "Flags.h" #include "LoadedApk.h" #include "ValueVisitor.h" #include "cmd/Util.h" #include "format/binary/TableFlattener.h" #include "format/binary/XmlFlattener.h" #include "format/proto/ProtoDeserialize.h" #include "io/BigBufferStream.h" #include "io/Util.h" #include "process/IResourceTableConsumer.h" #include "process/SymbolTable.h" using ::android::StringPiece; using ::std::unique_ptr; using ::std::vector; namespace aapt { static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml, const std::string& entry_path, bool utf16, IArchiveWriter* writer) { BigBuffer buffer(4096); XmlFlattenerOptions options = {}; options.use_utf16 = utf16; XmlFlattener flattener(&buffer, options); if (!flattener.Consume(context, &xml)) { return false; } io::BigBufferInputStream input_stream(&buffer); return io::CopyInputStreamToArchive(context, &input_stream, entry_path, ArchiveEntry::kCompress, writer); } bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk, const TableFlattenerOptions& options, IArchiveWriter* writer) { if (!FlattenXml(context, *apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) { return false; } BigBuffer buffer(4096); TableFlattener table_flattener(options, &buffer); if (!table_flattener.Consume(context, apk->GetResourceTable())) { return false; } io::BigBufferInputStream input_stream(&buffer); if (!io::CopyInputStreamToArchive(context, &input_stream, kApkResourceTablePath, ArchiveEntry::kAlign, writer)) { return false; } for (const auto& package : apk->GetResourceTable()->packages) { for (const auto& type : package->types) { for (const auto& entry : type->entries) { for (const auto& config_value : entry->values) { const FileReference* file = ValueCast<FileReference>(config_value->value.get()); if (file != nullptr) { if (file->file == nullptr) { context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource()) << "no file associated with " << *file); return false; } if (file->type == ResourceFile::Type::kProtoXml) { unique_ptr<io::InputStream> in = file->file->OpenInputStream(); if (in == nullptr) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to open file " << *file->path); return false; } pb::XmlNode pb_node; io::ZeroCopyInputAdaptor adaptor(in.get()); if (!pb_node.ParseFromZeroCopyStream(&adaptor)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to parse proto XML " << *file->path); return false; } std::string error; unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error); if (xml == nullptr) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to deserialize proto XML " << *file->path << ": " << error); return false; } if (!FlattenXml(context, *xml, *file->path, false /*utf16*/, writer)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to serialize XML " << *file->path); return false; } } else { if (!io::CopyFileToArchive(context, file->file, *file->path, file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u, writer)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to copy file " << *file->path); return false; } } } // file } // config_value } // entry } // type } // package return true; } class Context : public IAaptContext { public: Context() : mangler_({}), symbols_(&mangler_) { } PackageType GetPackageType() override { return PackageType::kApp; } SymbolTable* GetExternalSymbols() override { return &symbols_; } IDiagnostics* GetDiagnostics() override { return &diag_; } const std::string& GetCompilationPackage() override { return package_; } uint8_t GetPackageId() override { // Nothing should call this. UNIMPLEMENTED(FATAL) << "PackageID should not be necessary"; return 0; } NameMangler* GetNameMangler() override { UNIMPLEMENTED(FATAL); return nullptr; } bool IsVerbose() override { return verbose_; } int GetMinSdkVersion() override { return 0u; } bool verbose_ = false; std::string package_; private: DISALLOW_COPY_AND_ASSIGN(Context); NameMangler mangler_; SymbolTable symbols_; StdErrDiagnostics diag_; }; int Convert(const vector<StringPiece>& args) { Context context; std::string output_path; TableFlattenerOptions options; Flags flags = Flags() .RequiredFlag("-o", "Output path", &output_path) .OptionalSwitch("--enable-sparse-encoding", "Enables encoding sparse entries using a binary search tree.\n" "This decreases APK size at the cost of resource retrieval performance.", &options.use_sparse_entries) .OptionalSwitch("-v", "Enables verbose logging", &context.verbose_); if (!flags.Parse("aapt2 convert", args, &std::cerr)) { return 1; } if (flags.GetArgs().size() != 1) { std::cerr << "must supply a single proto APK\n"; flags.Usage("aapt2 convert", &std::cerr); return 1; } const StringPiece& path = flags.GetArgs()[0]; unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics()); if (apk == nullptr) { context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK"); return 1; } Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics()); if (!app_info) { return 1; } context.package_ = app_info.value().package; unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path); if (writer == nullptr) { return 1; } return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1; } } // namespace aapt Loading
tools/aapt2/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ toolSources = [ "cmd/Compile.cpp", "cmd/Convert.cpp", "cmd/Diff.cpp", "cmd/Dump.cpp", "cmd/Link.cpp", Loading
tools/aapt2/LoadedApk.cpp +110 −36 Original line number Diff line number Diff line Loading @@ -21,43 +21,139 @@ #include "format/Archive.h" #include "format/binary/TableFlattener.h" #include "format/binary/XmlFlattener.h" #include "format/proto/ProtoDeserialize.h" #include "io/BigBufferStream.h" #include "io/Util.h" #include "xml/XmlDom.h" namespace aapt { using ::aapt::io::IFile; using ::aapt::io::IFileCollection; using ::aapt::xml::XmlResource; using ::android::StringPiece; using ::std::unique_ptr; using xml::XmlResource; namespace aapt { std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context, const android::StringPiece& path) { std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) { Source source(path); std::string error; std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::Create(path, &error); if (!apk) { context->GetDiagnostics()->Error(DiagMessage(source) << error); if (apk == nullptr) { diag->Error(DiagMessage(path) << "failed opening zip: " << error); return {}; } if (apk->FindFile("resources.arsc") != nullptr) { return LoadBinaryApkFromFileCollection(source, std::move(apk), diag); } else if (apk->FindFile("resources.pb") != nullptr) { return LoadProtoApkFromFileCollection(source, std::move(apk), diag); } diag->Error(DiagMessage(path) << "no resource table found"); return {}; } std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection( const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) { io::IFile* table_file = collection->FindFile(kProtoResourceTablePath); if (table_file == nullptr) { diag->Error(DiagMessage(source) << "failed to find " << kProtoResourceTablePath); return {}; } std::unique_ptr<io::InputStream> in = table_file->OpenInputStream(); if (in == nullptr) { diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath); return {}; } pb::ResourceTable pb_table; io::ZeroCopyInputAdaptor adaptor(in.get()); if (!pb_table.ParseFromZeroCopyStream(&adaptor)) { diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath); return {}; } std::string error; std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) { diag->Error(DiagMessage(source) << "failed to deserialize " << kProtoResourceTablePath << ": " << error); return {}; } io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); if (manifest_file == nullptr) { diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath); return {}; } std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream(); if (manifest_in == nullptr) { diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath); return {}; } pb::XmlNode pb_node; io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get()); if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) { diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath); return {}; } std::unique_ptr<xml::XmlResource> manifest = DeserializeXmlResourceFromPb(pb_node, &error); if (manifest == nullptr) { diag->Error(DiagMessage(source) << "failed to deserialize proto " << kAndroidManifestPath << ": " << error); return {}; } return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table), std::move(manifest)); } std::unique_ptr<LoadedApk> LoadedApk::LoadBinaryApkFromFileCollection( const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) { io::IFile* table_file = collection->FindFile(kApkResourceTablePath); if (table_file == nullptr) { diag->Error(DiagMessage(source) << "failed to find " << kApkResourceTablePath); io::IFile* file = apk->FindFile("resources.arsc"); if (!file) { context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found"); return {}; } std::unique_ptr<io::IData> data = file->OpenAsData(); if (!data) { context->GetDiagnostics()->Error(DiagMessage(source) << "could not open resources.arsc"); std::unique_ptr<io::IData> data = table_file->OpenAsData(); if (data == nullptr) { diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath); return {}; } std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); BinaryResourceParser parser(context, table.get(), source, data->data(), data->size(), apk.get()); BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(), collection.get()); if (!parser.Parse()) { return {}; } return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table)); io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); if (manifest_file == nullptr) { diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath); return {}; } std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData(); if (manifest_data == nullptr) { diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath); return {}; } std::string error; std::unique_ptr<xml::XmlResource> manifest = xml::Inflate(manifest_data->data(), manifest_data->size(), &error); if (manifest == nullptr) { diag->Error(DiagMessage(source) << "failed to parse binary " << kAndroidManifestPath << ": " << error); return {}; } return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table), std::move(manifest)); } bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, Loading Loading @@ -148,26 +244,4 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table return true; } std::unique_ptr<xml::XmlResource> LoadedApk::InflateManifest(IAaptContext* context) { IDiagnostics* diag = context->GetDiagnostics(); io::IFile* manifest_file = GetFileCollection()->FindFile("AndroidManifest.xml"); if (manifest_file == nullptr) { diag->Error(DiagMessage(source_) << "no AndroidManifest.xml found"); return {}; } std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData(); if (manifest_data == nullptr) { diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml"); return {}; } std::unique_ptr<xml::XmlResource> manifest = xml::Inflate(manifest_data->data(), manifest_data->size(), diag, manifest_file->GetSource()); if (manifest == nullptr) { diag->Error(DiagMessage() << "failed to read binary AndroidManifest.xml"); } return manifest; } } // namespace aapt
tools/aapt2/LoadedApk.h +32 −11 Original line number Diff line number Diff line Loading @@ -29,20 +29,41 @@ namespace aapt { constexpr static const char kApkResourceTablePath[] = "resources.arsc"; constexpr static const char kProtoResourceTablePath[] = "resources.pb"; constexpr static const char kAndroidManifestPath[] = "AndroidManifest.xml"; // Info about an APK loaded in memory. class LoadedApk { public: LoadedApk( const Source& source, std::unique_ptr<io::IFileCollection> apk, std::unique_ptr<ResourceTable> table) : source_(source), apk_(std::move(apk)), table_(std::move(table)) {} virtual ~LoadedApk() = default; // Loads both binary and proto APKs from disk. static std::unique_ptr<LoadedApk> LoadApkFromPath(const ::android::StringPiece& path, IDiagnostics* diag); // Loads a proto APK from the given file collection. static std::unique_ptr<LoadedApk> LoadProtoApkFromFileCollection( const Source& source, std::unique_ptr<io::IFileCollection> collection, IDiagnostics* diag); // Loads a binary APK from the given file collection. static std::unique_ptr<LoadedApk> LoadBinaryApkFromFileCollection( const Source& source, std::unique_ptr<io::IFileCollection> collection, IDiagnostics* diag); LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk, std::unique_ptr<ResourceTable> table, std::unique_ptr<xml::XmlResource> manifest) : source_(source), apk_(std::move(apk)), table_(std::move(table)), manifest_(std::move(manifest)) { } io::IFileCollection* GetFileCollection() { return apk_.get(); } const ResourceTable* GetResourceTable() const { return table_.get(); } ResourceTable* GetResourceTable() { return table_.get(); } Loading @@ -51,6 +72,10 @@ class LoadedApk { return source_; } const xml::XmlResource* GetManifest() const { return manifest_.get(); } /** * Writes the APK on disk at the given path, while also removing the resource * files that are not referenced in the resource table. Loading @@ -71,11 +96,6 @@ class LoadedApk { const TableFlattenerOptions& options, FilterChain* filters, IArchiveWriter* writer, xml::XmlResource* manifest = nullptr); /** Inflates the AndroidManifest.xml file from the APK. */ std::unique_ptr<xml::XmlResource> InflateManifest(IAaptContext* context); static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context, const android::StringPiece& path); private: DISALLOW_COPY_AND_ASSIGN(LoadedApk); Loading @@ -83,6 +103,7 @@ class LoadedApk { Source source_; std::unique_ptr<io::IFileCollection> apk_; std::unique_ptr<ResourceTable> table_; std::unique_ptr<xml::XmlResource> manifest_; }; } // namespace aapt Loading
tools/aapt2/Main.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ static void PrintVersion() { } static void PrintUsage() { std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl; std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl; } extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics); Loading @@ -58,6 +58,7 @@ extern int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) extern int Dump(const std::vector<StringPiece>& args); extern int Diff(const std::vector<StringPiece>& args); extern int Optimize(const std::vector<StringPiece>& args); extern int Convert(const std::vector<StringPiece>& args); static int ExecuteCommand(const StringPiece& command, const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { Loading @@ -71,6 +72,8 @@ static int ExecuteCommand(const StringPiece& command, const std::vector<StringPi return Diff(args); } else if (command == "optimize") { return Optimize(args); } else if (command == "convert") { return Convert(args); } else if (command == "version") { PrintVersion(); return 0; Loading
tools/aapt2/cmd/Convert.cpp 0 → 100644 +228 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <vector> #include "android-base/macros.h" #include "androidfw/StringPiece.h" #include "Flags.h" #include "LoadedApk.h" #include "ValueVisitor.h" #include "cmd/Util.h" #include "format/binary/TableFlattener.h" #include "format/binary/XmlFlattener.h" #include "format/proto/ProtoDeserialize.h" #include "io/BigBufferStream.h" #include "io/Util.h" #include "process/IResourceTableConsumer.h" #include "process/SymbolTable.h" using ::android::StringPiece; using ::std::unique_ptr; using ::std::vector; namespace aapt { static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml, const std::string& entry_path, bool utf16, IArchiveWriter* writer) { BigBuffer buffer(4096); XmlFlattenerOptions options = {}; options.use_utf16 = utf16; XmlFlattener flattener(&buffer, options); if (!flattener.Consume(context, &xml)) { return false; } io::BigBufferInputStream input_stream(&buffer); return io::CopyInputStreamToArchive(context, &input_stream, entry_path, ArchiveEntry::kCompress, writer); } bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk, const TableFlattenerOptions& options, IArchiveWriter* writer) { if (!FlattenXml(context, *apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) { return false; } BigBuffer buffer(4096); TableFlattener table_flattener(options, &buffer); if (!table_flattener.Consume(context, apk->GetResourceTable())) { return false; } io::BigBufferInputStream input_stream(&buffer); if (!io::CopyInputStreamToArchive(context, &input_stream, kApkResourceTablePath, ArchiveEntry::kAlign, writer)) { return false; } for (const auto& package : apk->GetResourceTable()->packages) { for (const auto& type : package->types) { for (const auto& entry : type->entries) { for (const auto& config_value : entry->values) { const FileReference* file = ValueCast<FileReference>(config_value->value.get()); if (file != nullptr) { if (file->file == nullptr) { context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource()) << "no file associated with " << *file); return false; } if (file->type == ResourceFile::Type::kProtoXml) { unique_ptr<io::InputStream> in = file->file->OpenInputStream(); if (in == nullptr) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to open file " << *file->path); return false; } pb::XmlNode pb_node; io::ZeroCopyInputAdaptor adaptor(in.get()); if (!pb_node.ParseFromZeroCopyStream(&adaptor)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to parse proto XML " << *file->path); return false; } std::string error; unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error); if (xml == nullptr) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to deserialize proto XML " << *file->path << ": " << error); return false; } if (!FlattenXml(context, *xml, *file->path, false /*utf16*/, writer)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to serialize XML " << *file->path); return false; } } else { if (!io::CopyFileToArchive(context, file->file, *file->path, file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u, writer)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to copy file " << *file->path); return false; } } } // file } // config_value } // entry } // type } // package return true; } class Context : public IAaptContext { public: Context() : mangler_({}), symbols_(&mangler_) { } PackageType GetPackageType() override { return PackageType::kApp; } SymbolTable* GetExternalSymbols() override { return &symbols_; } IDiagnostics* GetDiagnostics() override { return &diag_; } const std::string& GetCompilationPackage() override { return package_; } uint8_t GetPackageId() override { // Nothing should call this. UNIMPLEMENTED(FATAL) << "PackageID should not be necessary"; return 0; } NameMangler* GetNameMangler() override { UNIMPLEMENTED(FATAL); return nullptr; } bool IsVerbose() override { return verbose_; } int GetMinSdkVersion() override { return 0u; } bool verbose_ = false; std::string package_; private: DISALLOW_COPY_AND_ASSIGN(Context); NameMangler mangler_; SymbolTable symbols_; StdErrDiagnostics diag_; }; int Convert(const vector<StringPiece>& args) { Context context; std::string output_path; TableFlattenerOptions options; Flags flags = Flags() .RequiredFlag("-o", "Output path", &output_path) .OptionalSwitch("--enable-sparse-encoding", "Enables encoding sparse entries using a binary search tree.\n" "This decreases APK size at the cost of resource retrieval performance.", &options.use_sparse_entries) .OptionalSwitch("-v", "Enables verbose logging", &context.verbose_); if (!flags.Parse("aapt2 convert", args, &std::cerr)) { return 1; } if (flags.GetArgs().size() != 1) { std::cerr << "must supply a single proto APK\n"; flags.Usage("aapt2 convert", &std::cerr); return 1; } const StringPiece& path = flags.GetArgs()[0]; unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics()); if (apk == nullptr) { context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK"); return 1; } Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics()); if (!app_info) { return 1; } context.package_ = app_info.value().package; unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path); if (writer == nullptr) { return 1; } return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1; } } // namespace aapt