1/*
2 * Copyright (C) 2016 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 <vector>
18
19#include "androidfw/StringPiece.h"
20
21#include "Debug.h"
22#include "Diagnostics.h"
23#include "Flags.h"
24#include "io/ZipArchive.h"
25#include "process/IResourceTableConsumer.h"
26#include "proto/ProtoSerialize.h"
27#include "unflatten/BinaryResourceParser.h"
28#include "util/Files.h"
29
30using android::StringPiece;
31
32namespace aapt {
33
34void DumpCompiledFile(const pb::CompiledFile& pb_file, const void* data, size_t len,
35                      const Source& source, IAaptContext* context) {
36  std::unique_ptr<ResourceFile> file =
37      DeserializeCompiledFileFromPb(pb_file, source, context->GetDiagnostics());
38  if (!file) {
39    context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
40    return;
41  }
42
43  std::cout << "Resource: " << file->name << "\n"
44            << "Config:   " << file->config << "\n"
45            << "Source:   " << file->source << "\n";
46}
47
48void TryDumpFile(IAaptContext* context, const std::string& file_path) {
49  std::unique_ptr<ResourceTable> table;
50
51  std::string err;
52  std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
53  if (zip) {
54    io::IFile* file = zip->FindFile("resources.arsc.flat");
55    if (file) {
56      std::unique_ptr<io::IData> data = file->OpenAsData();
57      if (!data) {
58        context->GetDiagnostics()->Error(DiagMessage(file_path)
59                                         << "failed to open resources.arsc.flat");
60        return;
61      }
62
63      pb::ResourceTable pb_table;
64      if (!pb_table.ParseFromArray(data->data(), data->size())) {
65        context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid resources.arsc.flat");
66        return;
67      }
68
69      table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics());
70      if (!table) {
71        return;
72      }
73    }
74
75    if (!table) {
76      file = zip->FindFile("resources.arsc");
77      if (file) {
78        std::unique_ptr<io::IData> data = file->OpenAsData();
79        if (!data) {
80          context->GetDiagnostics()->Error(DiagMessage(file_path)
81                                           << "failed to open resources.arsc");
82          return;
83        }
84
85        table = util::make_unique<ResourceTable>();
86        BinaryResourceParser parser(context, table.get(), Source(file_path), data->data(),
87                                    data->size());
88        if (!parser.Parse()) {
89          return;
90        }
91      }
92    }
93  }
94
95  if (!table) {
96    Maybe<android::FileMap> file = file::MmapPath(file_path, &err);
97    if (!file) {
98      context->GetDiagnostics()->Error(DiagMessage(file_path) << err);
99      return;
100    }
101
102    android::FileMap* file_map = &file.value();
103
104    // Try as a compiled table.
105    pb::ResourceTable pb_table;
106    if (pb_table.ParseFromArray(file_map->getDataPtr(), file_map->getDataLength())) {
107      table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics());
108    }
109
110    if (!table) {
111      // Try as a compiled file.
112      CompiledFileInputStream input(file_map->getDataPtr(), file_map->getDataLength());
113
114      uint32_t num_files = 0;
115      if (!input.ReadLittleEndian32(&num_files)) {
116        return;
117      }
118
119      for (uint32_t i = 0; i < num_files; i++) {
120        pb::CompiledFile compiled_file;
121        if (!input.ReadCompiledFile(&compiled_file)) {
122          context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
123          return;
124        }
125
126        uint64_t offset, len;
127        if (!input.ReadDataMetaData(&offset, &len)) {
128          context->GetDiagnostics()->Warn(DiagMessage() << "failed to read meta data");
129          return;
130        }
131
132        const void* data = static_cast<const uint8_t*>(file_map->getDataPtr()) + offset;
133        DumpCompiledFile(compiled_file, data, len, Source(file_path), context);
134      }
135    }
136  }
137
138  if (table) {
139    DebugPrintTableOptions options;
140    options.show_sources = true;
141    Debug::PrintTable(table.get(), options);
142  }
143}
144
145class DumpContext : public IAaptContext {
146 public:
147  PackageType GetPackageType() override {
148    // Doesn't matter.
149    return PackageType::kApp;
150  }
151
152  IDiagnostics* GetDiagnostics() override {
153    return &diagnostics_;
154  }
155
156  NameMangler* GetNameMangler() override {
157    abort();
158    return nullptr;
159  }
160
161  const std::string& GetCompilationPackage() override {
162    static std::string empty;
163    return empty;
164  }
165
166  uint8_t GetPackageId() override {
167    return 0;
168  }
169
170  SymbolTable* GetExternalSymbols() override {
171    abort();
172    return nullptr;
173  }
174
175  bool IsVerbose() override {
176    return verbose_;
177  }
178
179  void SetVerbose(bool val) {
180    verbose_ = val;
181  }
182
183  int GetMinSdkVersion() override {
184    return 0;
185  }
186
187 private:
188  StdErrDiagnostics diagnostics_;
189  bool verbose_ = false;
190};
191
192/**
193 * Entry point for dump command.
194 */
195int Dump(const std::vector<StringPiece>& args) {
196  bool verbose = false;
197  Flags flags = Flags().OptionalSwitch("-v", "increase verbosity of output", &verbose);
198  if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
199    return 1;
200  }
201
202  DumpContext context;
203  context.SetVerbose(verbose);
204
205  for (const std::string& arg : flags.GetArgs()) {
206    TryDumpFile(&context, arg);
207  }
208  return 0;
209}
210
211}  // namespace aapt
212