| /* |
| * Copyright (C) 2016 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 "androidfw/StringPiece.h" |
| |
| #include "Debug.h" |
| #include "Diagnostics.h" |
| #include "Flags.h" |
| #include "io/ZipArchive.h" |
| #include "process/IResourceTableConsumer.h" |
| #include "proto/ProtoSerialize.h" |
| #include "unflatten/BinaryResourceParser.h" |
| #include "util/Files.h" |
| |
| using android::StringPiece; |
| |
| namespace aapt { |
| |
| void DumpCompiledFile(const pb::CompiledFile& pb_file, const void* data, size_t len, |
| const Source& source, IAaptContext* context) { |
| std::unique_ptr<ResourceFile> file = |
| DeserializeCompiledFileFromPb(pb_file, source, context->GetDiagnostics()); |
| if (!file) { |
| context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file"); |
| return; |
| } |
| |
| std::cout << "Resource: " << file->name << "\n" |
| << "Config: " << file->config << "\n" |
| << "Source: " << file->source << "\n"; |
| } |
| |
| void TryDumpFile(IAaptContext* context, const std::string& file_path) { |
| std::unique_ptr<ResourceTable> table; |
| |
| std::string err; |
| std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err); |
| if (zip) { |
| io::IFile* file = zip->FindFile("resources.arsc.flat"); |
| if (file) { |
| std::unique_ptr<io::IData> data = file->OpenAsData(); |
| if (!data) { |
| context->GetDiagnostics()->Error(DiagMessage(file_path) |
| << "failed to open resources.arsc.flat"); |
| return; |
| } |
| |
| pb::ResourceTable pb_table; |
| if (!pb_table.ParseFromArray(data->data(), data->size())) { |
| context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid resources.arsc.flat"); |
| return; |
| } |
| |
| table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics()); |
| if (!table) { |
| return; |
| } |
| } |
| |
| if (!table) { |
| file = zip->FindFile("resources.arsc"); |
| if (file) { |
| std::unique_ptr<io::IData> data = file->OpenAsData(); |
| if (!data) { |
| context->GetDiagnostics()->Error(DiagMessage(file_path) |
| << "failed to open resources.arsc"); |
| return; |
| } |
| |
| table = util::make_unique<ResourceTable>(); |
| BinaryResourceParser parser(context, table.get(), Source(file_path), data->data(), |
| data->size()); |
| if (!parser.Parse()) { |
| return; |
| } |
| } |
| } |
| } |
| |
| if (!table) { |
| Maybe<android::FileMap> file = file::MmapPath(file_path, &err); |
| if (!file) { |
| context->GetDiagnostics()->Error(DiagMessage(file_path) << err); |
| return; |
| } |
| |
| android::FileMap* file_map = &file.value(); |
| |
| // Try as a compiled table. |
| pb::ResourceTable pb_table; |
| if (pb_table.ParseFromArray(file_map->getDataPtr(), file_map->getDataLength())) { |
| table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics()); |
| } |
| |
| if (!table) { |
| // Try as a compiled file. |
| CompiledFileInputStream input(file_map->getDataPtr(), file_map->getDataLength()); |
| |
| uint32_t num_files = 0; |
| if (!input.ReadLittleEndian32(&num_files)) { |
| return; |
| } |
| |
| for (uint32_t i = 0; i < num_files; i++) { |
| pb::CompiledFile compiled_file; |
| if (!input.ReadCompiledFile(&compiled_file)) { |
| context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file"); |
| return; |
| } |
| |
| uint64_t offset, len; |
| if (!input.ReadDataMetaData(&offset, &len)) { |
| context->GetDiagnostics()->Warn(DiagMessage() << "failed to read meta data"); |
| return; |
| } |
| |
| const void* data = static_cast<const uint8_t*>(file_map->getDataPtr()) + offset; |
| DumpCompiledFile(compiled_file, data, len, Source(file_path), context); |
| } |
| } |
| } |
| |
| if (table) { |
| DebugPrintTableOptions options; |
| options.show_sources = true; |
| Debug::PrintTable(table.get(), options); |
| } |
| } |
| |
| class DumpContext : public IAaptContext { |
| public: |
| PackageType GetPackageType() override { |
| // Doesn't matter. |
| return PackageType::kApp; |
| } |
| |
| IDiagnostics* GetDiagnostics() override { |
| return &diagnostics_; |
| } |
| |
| NameMangler* GetNameMangler() override { |
| abort(); |
| return nullptr; |
| } |
| |
| const std::string& GetCompilationPackage() override { |
| static std::string empty; |
| return empty; |
| } |
| |
| uint8_t GetPackageId() override { |
| return 0; |
| } |
| |
| SymbolTable* GetExternalSymbols() override { |
| abort(); |
| return nullptr; |
| } |
| |
| bool IsVerbose() override { |
| return verbose_; |
| } |
| |
| void SetVerbose(bool val) { |
| verbose_ = val; |
| } |
| |
| int GetMinSdkVersion() override { |
| return 0; |
| } |
| |
| private: |
| StdErrDiagnostics diagnostics_; |
| bool verbose_ = false; |
| }; |
| |
| /** |
| * Entry point for dump command. |
| */ |
| int Dump(const std::vector<StringPiece>& args) { |
| bool verbose = false; |
| Flags flags = Flags().OptionalSwitch("-v", "increase verbosity of output", &verbose); |
| if (!flags.Parse("aapt2 dump", args, &std::cerr)) { |
| return 1; |
| } |
| |
| DumpContext context; |
| context.SetVerbose(verbose); |
| |
| for (const std::string& arg : flags.GetArgs()) { |
| TryDumpFile(&context, arg); |
| } |
| return 0; |
| } |
| |
| } // namespace aapt |