1 /*
2  * Copyright (C) 2018 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 #ifndef AAPT2_DUMP_H
18 #define AAPT2_DUMP_H
19 
20 #include "Command.h"
21 #include "Debug.h"
22 #include "LoadedApk.h"
23 #include "dump/DumpManifest.h"
24 
25 namespace aapt {
26 
27 /**
28  * The base command for dumping information about apks. When the command is executed, the command
29  * performs the DumpApkCommand::Dump() operation on each apk provided as a file argument.
30  **/
31 class DumpApkCommand : public Command {
32  public:
DumpApkCommand(const std::string && name,text::Printer * printer,IDiagnostics * diag)33   explicit DumpApkCommand(const std::string&& name, text::Printer* printer, IDiagnostics* diag)
34       : Command(name), printer_(printer), diag_(diag) {
35         SetDescription("Dump information about an APK or APC.");
36   }
37 
GetPrinter()38   text::Printer* GetPrinter() {
39     return printer_;
40   }
41 
GetDiagnostics()42   IDiagnostics* GetDiagnostics() {
43     return diag_;
44   }
45 
GetPackageName(LoadedApk * apk)46   Maybe<std::string> GetPackageName(LoadedApk* apk) {
47     xml::Element* manifest_el = apk->GetManifest()->root.get();
48     if (!manifest_el) {
49       GetDiagnostics()->Error(DiagMessage() << "No AndroidManifest.");
50       return Maybe<std::string>();
51     }
52 
53     xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
54     if (!attr) {
55       GetDiagnostics()->Error(DiagMessage() << "No package name.");
56       return Maybe<std::string>();
57     }
58     return attr->value;
59   }
60 
61   /** Perform the dump operation on the apk. */
62   virtual int Dump(LoadedApk* apk) = 0;
63 
Action(const std::vector<std::string> & args)64   int Action(const std::vector<std::string>& args) final {
65     if (args.size() < 1) {
66       diag_->Error(DiagMessage() << "No dump apk specified.");
67       return 1;
68     }
69 
70     bool error = false;
71     for (auto apk : args) {
72       auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
73       if (!loaded_apk) {
74         error = true;
75         continue;
76       }
77 
78       error |= Dump(loaded_apk.get());
79     }
80 
81     return error;
82   }
83 
84  private:
85   text::Printer* printer_;
86   IDiagnostics* diag_;
87 };
88 
89 /** Command that prints contents of files generated from the compilation stage. */
90 class DumpAPCCommand : public Command {
91  public:
DumpAPCCommand(text::Printer * printer,IDiagnostics * diag)92   explicit DumpAPCCommand(text::Printer* printer, IDiagnostics* diag)
93       : Command("apc"), printer_(printer), diag_(diag) {
94     SetDescription("Print the contents of the AAPT2 Container (APC) generated fom compilation.");
95     AddOptionalSwitch("--no-values", "Suppresses output of values when displaying resource tables.",
96                       &no_values_);
97     AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
98   }
99 
100   int Action(const std::vector<std::string>& args) override;
101 
102  private:
103   text::Printer* printer_;
104   IDiagnostics* diag_;
105   bool no_values_ = false;
106   bool verbose_ = false;
107 };
108 
109 /** Easter egg command shown when users enter "badger" instead of "badging". */
110 class DumpBadgerCommand : public Command {
111  public:
DumpBadgerCommand(text::Printer * printer)112   explicit DumpBadgerCommand(text::Printer* printer) : Command("badger"), printer_(printer) {
113   }
114 
115   int Action(const std::vector<std::string>& args) override;
116 
117  private:
118   text::Printer* printer_;
119   const static char kBadgerData[2925];
120 };
121 
122 class DumpBadgingCommand : public DumpApkCommand {
123  public:
DumpBadgingCommand(text::Printer * printer,IDiagnostics * diag)124   explicit DumpBadgingCommand(text::Printer* printer, IDiagnostics* diag)
125       : DumpApkCommand("badging", printer, diag) {
126     SetDescription("Print information extracted from the manifest of the APK.");
127     AddOptionalSwitch("--include-meta-data", "Include meta-data information.",
128                       &options_.include_meta_data);
129   }
130 
Dump(LoadedApk * apk)131   int Dump(LoadedApk* apk) override {
132     return DumpManifest(apk, options_, GetPrinter(), GetDiagnostics());
133   }
134 
135  private:
136   DumpManifestOptions options_;
137 };
138 
139 class DumpConfigsCommand : public DumpApkCommand {
140  public:
DumpConfigsCommand(text::Printer * printer,IDiagnostics * diag)141   explicit DumpConfigsCommand(text::Printer* printer, IDiagnostics* diag)
142       : DumpApkCommand("configurations", printer, diag) {
143     SetDescription("Print every configuration used by a resource in the APK.");
144   }
145 
146   int Dump(LoadedApk* apk) override;
147 };
148 
149 class DumpPackageNameCommand : public DumpApkCommand {
150  public:
DumpPackageNameCommand(text::Printer * printer,IDiagnostics * diag)151   explicit DumpPackageNameCommand(text::Printer* printer, IDiagnostics* diag)
152       : DumpApkCommand("packagename", printer, diag) {
153     SetDescription("Print the package name of the APK.");
154   }
155 
156   int Dump(LoadedApk* apk) override;
157 };
158 
159 class DumpPermissionsCommand : public DumpApkCommand {
160  public:
DumpPermissionsCommand(text::Printer * printer,IDiagnostics * diag)161   explicit DumpPermissionsCommand(text::Printer* printer, IDiagnostics* diag)
162       : DumpApkCommand("permissions", printer, diag) {
163     SetDescription("Print the permissions extracted from the manifest of the APK.");
164   }
165 
Dump(LoadedApk * apk)166   int Dump(LoadedApk* apk) override {
167     DumpManifestOptions options;
168     options.only_permissions = true;
169     return DumpManifest(apk, options, GetPrinter(), GetDiagnostics());
170   }
171 };
172 
173 class DumpStringsCommand : public DumpApkCommand {
174  public:
DumpStringsCommand(text::Printer * printer,IDiagnostics * diag)175   explicit DumpStringsCommand(text::Printer* printer, IDiagnostics* diag)
176       : DumpApkCommand("strings", printer, diag) {
177     SetDescription("Print the contents of the resource table string pool in the APK.");
178   }
179 
180   int Dump(LoadedApk* apk) override;
181 };
182 
183 /** Prints the graph of parents of a style in an APK. */
184 class DumpStyleParentCommand : public DumpApkCommand {
185  public:
DumpStyleParentCommand(text::Printer * printer,IDiagnostics * diag)186   explicit DumpStyleParentCommand(text::Printer* printer, IDiagnostics* diag)
187       : DumpApkCommand("styleparents", printer, diag) {
188     SetDescription("Print the parents of a style in an APK.");
189     AddRequiredFlag("--style", "The name of the style to print", &style_);
190   }
191 
192   int Dump(LoadedApk* apk) override;
193 
194  private:
195   std::string style_;
196 };
197 
198 class DumpTableCommand : public DumpApkCommand {
199  public:
DumpTableCommand(text::Printer * printer,IDiagnostics * diag)200   explicit DumpTableCommand(text::Printer* printer, IDiagnostics* diag)
201       : DumpApkCommand("resources", printer, diag) {
202     SetDescription("Print the contents of the resource table from the APK.");
203     AddOptionalSwitch("--no-values", "Suppresses output of values when displaying resource tables.",
204                       &no_values_);
205     AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
206   }
207 
208   int Dump(LoadedApk* apk) override;
209 
210  private:
211   bool no_values_ = false;
212   bool verbose_ = false;
213 };
214 
215 class DumpXmlStringsCommand : public DumpApkCommand {
216  public:
DumpXmlStringsCommand(text::Printer * printer,IDiagnostics * diag)217   explicit DumpXmlStringsCommand(text::Printer* printer, IDiagnostics* diag)
218       : DumpApkCommand("xmlstrings", printer, diag) {
219     SetDescription("Print the string pool of a compiled xml in an APK.");
220     AddRequiredFlagList("--file", "A compiled xml file to print", &files_);
221   }
222 
223   int Dump(LoadedApk* apk) override;
224 
225  private:
226   std::vector<std::string> files_;
227 };
228 
229 class DumpXmlTreeCommand : public DumpApkCommand {
230  public:
DumpXmlTreeCommand(text::Printer * printer,IDiagnostics * diag)231   explicit DumpXmlTreeCommand(text::Printer* printer, IDiagnostics* diag)
232       : DumpApkCommand("xmltree", printer, diag) {
233     SetDescription("Print the tree of a compiled xml in an APK.");
234     AddRequiredFlagList("--file", "A compiled xml file to print", &files_);
235   }
236 
237   int Dump(LoadedApk* apk) override;
238 
239  private:
240   std::vector<std::string> files_;
241 };
242 
243 class DumpOverlayableCommand : public DumpApkCommand {
244  public:
DumpOverlayableCommand(text::Printer * printer,IDiagnostics * diag)245   explicit DumpOverlayableCommand(text::Printer* printer, IDiagnostics* diag)
246       : DumpApkCommand("overlayable", printer, diag) {
247     SetDescription("Print the <overlayable> resources of an APK.");
248   }
249 
250   int Dump(LoadedApk* apk) override;
251 };
252 
253 /** The default dump command. Performs no action because a subcommand is required. */
254 class DumpCommand : public Command {
255  public:
DumpCommand(text::Printer * printer,IDiagnostics * diag)256   explicit DumpCommand(text::Printer* printer, IDiagnostics* diag)
257       : Command("dump", "d"), diag_(diag) {
258     AddOptionalSubcommand(util::make_unique<DumpAPCCommand>(printer, diag_));
259     AddOptionalSubcommand(util::make_unique<DumpBadgingCommand>(printer, diag_));
260     AddOptionalSubcommand(util::make_unique<DumpConfigsCommand>(printer, diag_));
261     AddOptionalSubcommand(util::make_unique<DumpPackageNameCommand>(printer, diag_));
262     AddOptionalSubcommand(util::make_unique<DumpPermissionsCommand>(printer, diag_));
263     AddOptionalSubcommand(util::make_unique<DumpStringsCommand>(printer, diag_));
264     AddOptionalSubcommand(util::make_unique<DumpStyleParentCommand>(printer, diag_));
265     AddOptionalSubcommand(util::make_unique<DumpTableCommand>(printer, diag_));
266     AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_));
267     AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_));
268     AddOptionalSubcommand(util::make_unique<DumpOverlayableCommand>(printer, diag_));
269     AddOptionalSubcommand(util::make_unique<DumpBadgerCommand>(printer), /* hidden */ true);
270   }
271 
Action(const std::vector<std::string> & args)272   int Action(const std::vector<std::string>& args) override {
273     if (args.size() == 0) {
274       diag_->Error(DiagMessage() << "no subcommand specified");
275     } else {
276       diag_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'");
277     }
278     Usage(&std::cerr);
279     return 1;
280   }
281 
282  private:
283   IDiagnostics* diag_;
284 };
285 
286 }  // namespace aapt
287 
288 #endif  // AAPT2_DUMP_H
289