blob: 63ebc60ccc3a3d86eb9943907bc1add0062f3698 [file] [log] [blame]
Adam Lesinskid0f492d2017-04-03 18:12:45 -07001/*
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 <memory>
18#include <vector>
19
Luke Nicholsonb0643302017-12-01 15:29:03 -080020#include "android-base/file.h"
Shane Farmer57669432017-06-19 12:52:04 -070021#include "android-base/stringprintf.h"
Adam Lesinskid3ffa8442017-09-28 13:34:35 -070022
Shane Farmer0a5b2012017-06-22 12:24:12 -070023#include "androidfw/ResourceTypes.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070024#include "androidfw/StringPiece.h"
25
26#include "Diagnostics.h"
27#include "Flags.h"
28#include "LoadedApk.h"
29#include "ResourceUtils.h"
30#include "SdkConstants.h"
31#include "ValueVisitor.h"
32#include "cmd/Util.h"
Shane Farmer57669432017-06-19 12:52:04 -070033#include "configuration/ConfigurationParser.h"
34#include "filter/AbiFilter.h"
Adam Lesinski46708052017-09-29 14:49:15 -070035#include "format/binary/TableFlattener.h"
36#include "format/binary/XmlFlattener.h"
Adam Lesinski00451162017-10-03 07:44:08 -070037#include "io/BigBufferStream.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070038#include "io/Util.h"
Shane Farmer0a5b2012017-06-22 12:24:12 -070039#include "optimize/MultiApkGenerator.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070040#include "optimize/ResourceDeduper.h"
Mohamed Heikald3c5fb62018-01-12 11:37:26 -050041#include "optimize/ResourceFilter.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070042#include "optimize/VersionCollapser.h"
43#include "split/TableSplitter.h"
Shane Farmer57669432017-06-19 12:52:04 -070044#include "util/Files.h"
Shane Farmer0a5b2012017-06-22 12:24:12 -070045#include "util/Util.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070046
Shane Farmer57669432017-06-19 12:52:04 -070047using ::aapt::configuration::Abi;
Shane Farmercb6c3f92017-11-27 13:19:36 -080048using ::aapt::configuration::OutputArtifact;
Shane Farmer0a5b2012017-06-22 12:24:12 -070049using ::android::ResTable_config;
Shane Farmer57669432017-06-19 12:52:04 -070050using ::android::StringPiece;
Luke Nicholsonb0643302017-12-01 15:29:03 -080051using ::android::base::ReadFileToString;
Shane Farmer0a5b2012017-06-22 12:24:12 -070052using ::android::base::StringAppendF;
Shane Farmer57669432017-06-19 12:52:04 -070053using ::android::base::StringPrintf;
Adam Lesinskid0f492d2017-04-03 18:12:45 -070054
55namespace aapt {
56
57struct OptimizeOptions {
58 // Path to the output APK.
Shane Farmer57669432017-06-19 12:52:04 -070059 Maybe<std::string> output_path;
60 // Path to the output APK directory for splits.
61 Maybe<std::string> output_dir;
Adam Lesinskid0f492d2017-04-03 18:12:45 -070062
63 // Details of the app extracted from the AndroidManifest.xml
64 AppInfo app_info;
65
Mohamed Heikald3c5fb62018-01-12 11:37:26 -050066 // Blacklist of unused resources that should be removed from the apk.
67 std::unordered_set<ResourceName> resources_blacklist;
68
Adam Lesinskid0f492d2017-04-03 18:12:45 -070069 // Split APK options.
70 TableSplitterOptions table_splitter_options;
71
72 // List of output split paths. These are in the same order as `split_constraints`.
73 std::vector<std::string> split_paths;
74
75 // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`.
76 std::vector<SplitConstraints> split_constraints;
77
78 TableFlattenerOptions table_flattener_options;
Shane Farmer57669432017-06-19 12:52:04 -070079
Shane Farmercb6c3f92017-11-27 13:19:36 -080080 Maybe<std::vector<OutputArtifact>> apk_artifacts;
Shane Farmer666de342017-11-29 16:07:51 -080081
82 // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
83 // are kept and will be written as output.
84 std::unordered_set<std::string> kept_artifacts;
Adam Lesinskid0f492d2017-04-03 18:12:45 -070085};
86
87class OptimizeContext : public IAaptContext {
88 public:
Adam Lesinskib522f042017-04-21 16:57:59 -070089 OptimizeContext() = default;
90
91 PackageType GetPackageType() override {
92 // Not important here. Using anything other than kApp adds EXTRA validation, which we want to
93 // avoid.
94 return PackageType::kApp;
95 }
96
Adam Lesinskid0f492d2017-04-03 18:12:45 -070097 IDiagnostics* GetDiagnostics() override {
98 return &diagnostics_;
99 }
100
101 NameMangler* GetNameMangler() override {
102 UNIMPLEMENTED(FATAL);
103 return nullptr;
104 }
105
106 const std::string& GetCompilationPackage() override {
107 static std::string empty;
108 return empty;
109 }
110
111 uint8_t GetPackageId() override {
112 return 0;
113 }
114
115 SymbolTable* GetExternalSymbols() override {
116 UNIMPLEMENTED(FATAL);
117 return nullptr;
118 }
119
120 bool IsVerbose() override {
121 return verbose_;
122 }
123
124 void SetVerbose(bool val) {
125 verbose_ = val;
126 }
127
128 void SetMinSdkVersion(int sdk_version) {
129 sdk_version_ = sdk_version;
130 }
131
132 int GetMinSdkVersion() override {
133 return sdk_version_;
134 }
135
136 private:
Adam Lesinskib522f042017-04-21 16:57:59 -0700137 DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
138
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700139 StdErrDiagnostics diagnostics_;
140 bool verbose_ = false;
141 int sdk_version_ = 0;
142};
143
144class OptimizeCommand {
145 public:
146 OptimizeCommand(OptimizeContext* context, const OptimizeOptions& options)
147 : options_(options), context_(context) {
148 }
149
150 int Run(std::unique_ptr<LoadedApk> apk) {
151 if (context_->IsVerbose()) {
152 context_->GetDiagnostics()->Note(DiagMessage() << "Optimizing APK...");
153 }
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500154 if (!options_.resources_blacklist.empty()) {
155 ResourceFilter filter(options_.resources_blacklist);
156 if (!filter.Consume(context_, apk->GetResourceTable())) {
157 context_->GetDiagnostics()->Error(DiagMessage() << "failed filtering resources");
158 return 1;
159 }
160 }
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700161
162 VersionCollapser collapser;
163 if (!collapser.Consume(context_, apk->GetResourceTable())) {
164 return 1;
165 }
166
167 ResourceDeduper deduper;
168 if (!deduper.Consume(context_, apk->GetResourceTable())) {
169 context_->GetDiagnostics()->Error(DiagMessage() << "failed deduping resources");
170 return 1;
171 }
172
173 // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
174 // equal to the minSdk.
175 options_.split_constraints =
176 AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints);
177
178 // Stripping the APK using the TableSplitter. The resource table is modified in place in the
179 // LoadedApk.
180 TableSplitter splitter(options_.split_constraints, options_.table_splitter_options);
181 if (!splitter.VerifySplitConstraints(context_)) {
182 return 1;
183 }
184 splitter.SplitTable(apk->GetResourceTable());
185
186 auto path_iter = options_.split_paths.begin();
187 auto split_constraints_iter = options_.split_constraints.begin();
188 for (std::unique_ptr<ResourceTable>& split_table : splitter.splits()) {
189 if (context_->IsVerbose()) {
190 context_->GetDiagnostics()->Note(
191 DiagMessage(*path_iter) << "generating split with configurations '"
192 << util::Joiner(split_constraints_iter->configs, ", ") << "'");
193 }
194
195 // Generate an AndroidManifest.xml for each split.
196 std::unique_ptr<xml::XmlResource> split_manifest =
197 GenerateSplitManifest(options_.app_info, *split_constraints_iter);
198 std::unique_ptr<IArchiveWriter> split_writer =
199 CreateZipFileArchiveWriter(context_->GetDiagnostics(), *path_iter);
200 if (!split_writer) {
201 return 1;
202 }
203
204 if (!WriteSplitApk(split_table.get(), split_manifest.get(), split_writer.get())) {
205 return 1;
206 }
207
208 ++path_iter;
209 ++split_constraints_iter;
210 }
211
Shane Farmercb6c3f92017-11-27 13:19:36 -0800212 if (options_.apk_artifacts && options_.output_dir) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700213 MultiApkGenerator generator{apk.get(), context_};
Shane Farmer666de342017-11-29 16:07:51 -0800214 MultiApkGeneratorOptions generator_options = {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800215 options_.output_dir.value(), options_.apk_artifacts.value(),
216 options_.table_flattener_options, options_.kept_artifacts};
Shane Farmerefe45392017-08-21 14:39:28 -0700217 if (!generator.FromBaseApk(generator_options)) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700218 return 1;
Shane Farmer57669432017-06-19 12:52:04 -0700219 }
220 }
221
222 if (options_.output_path) {
223 std::unique_ptr<IArchiveWriter> writer =
224 CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path.value());
225 if (!apk->WriteToArchive(context_, options_.table_flattener_options, writer.get())) {
226 return 1;
227 }
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700228 }
229
230 return 0;
231 }
232
233 private:
234 bool WriteSplitApk(ResourceTable* table, xml::XmlResource* manifest, IArchiveWriter* writer) {
235 BigBuffer manifest_buffer(4096);
236 XmlFlattener xml_flattener(&manifest_buffer, {});
237 if (!xml_flattener.Consume(context_, manifest)) {
238 return false;
239 }
240
241 io::BigBufferInputStream manifest_buffer_in(&manifest_buffer);
242 if (!io::CopyInputStreamToArchive(context_, &manifest_buffer_in, "AndroidManifest.xml",
243 ArchiveEntry::kCompress, writer)) {
244 return false;
245 }
246
247 std::map<std::pair<ConfigDescription, StringPiece>, FileReference*> config_sorted_files;
248 for (auto& pkg : table->packages) {
249 for (auto& type : pkg->types) {
250 // Sort by config and name, so that we get better locality in the zip file.
251 config_sorted_files.clear();
252
253 for (auto& entry : type->entries) {
254 for (auto& config_value : entry->values) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700255 auto* file_ref = ValueCast<FileReference>(config_value->value.get());
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700256 if (file_ref == nullptr) {
257 continue;
258 }
259
260 if (file_ref->file == nullptr) {
261 ResourceNameRef name(pkg->name, type->type, entry->name);
Adam Lesinski742888f2017-04-28 15:34:52 -0700262 context_->GetDiagnostics()->Warn(DiagMessage(file_ref->GetSource())
Shane Farmer57669432017-06-19 12:52:04 -0700263 << "file for resource " << name << " with config '"
264 << config_value->config << "' not found");
Adam Lesinski742888f2017-04-28 15:34:52 -0700265 continue;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700266 }
267
268 const StringPiece entry_name = entry->name;
269 config_sorted_files[std::make_pair(config_value->config, entry_name)] = file_ref;
270 }
271 }
272
273 for (auto& entry : config_sorted_files) {
274 FileReference* file_ref = entry.second;
Pierre Lecesned55bef72017-11-10 22:31:01 +0000275 if (!io::CopyFileToArchivePreserveCompression(context_, file_ref->file, *file_ref->path,
276 writer)) {
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700277 return false;
278 }
279 }
280 }
281 }
282
283 BigBuffer table_buffer(4096);
284 TableFlattener table_flattener(options_.table_flattener_options, &table_buffer);
285 if (!table_flattener.Consume(context_, table)) {
286 return false;
287 }
288
289 io::BigBufferInputStream table_buffer_in(&table_buffer);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700290 return io::CopyInputStreamToArchive(context_, &table_buffer_in, "resources.arsc",
291 ArchiveEntry::kAlign, writer);
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700292 }
293
294 OptimizeOptions options_;
295 OptimizeContext* context_;
296};
297
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500298bool ExtractObfuscationWhitelistFromConfig(const std::string& path, OptimizeContext* context,
299 OptimizeOptions* options) {
Luke Nicholsonb0643302017-12-01 15:29:03 -0800300 std::string contents;
301 if (!ReadFileToString(path, &contents, true)) {
302 context->GetDiagnostics()->Error(DiagMessage()
303 << "failed to parse whitelist from config file: " << path);
304 return false;
305 }
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500306 for (StringPiece resource_name : util::Tokenize(contents, ',')) {
307 options->table_flattener_options.whitelisted_resources.insert(
308 resource_name.to_string());
309 }
310 return true;
311}
312
313bool ExtractConfig(const std::string& path, OptimizeContext* context,
314 OptimizeOptions* options) {
315 std::string content;
316 if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
317 context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading whitelist");
318 return false;
319 }
320
321 size_t line_no = 0;
322 for (StringPiece line : util::Tokenize(content, '\n')) {
323 line_no++;
324 line = util::TrimWhitespace(line);
325 if (line.empty()) {
326 continue;
327 }
328
329 auto split_line = util::Split(line, '#');
330 if (split_line.size() < 2) {
331 context->GetDiagnostics()->Error(DiagMessage(line) << "No # found in line");
332 return false;
333 }
334 StringPiece resource_string = split_line[0];
335 StringPiece directives = split_line[1];
336 ResourceNameRef resource_name;
337 if (!ResourceUtils::ParseResourceName(resource_string, &resource_name)) {
338 context->GetDiagnostics()->Error(DiagMessage(line) << "Malformed resource name");
339 return false;
340 }
341 if (!resource_name.package.empty()) {
342 context->GetDiagnostics()->Error(DiagMessage(line)
343 << "Package set for resource. Only use type/name");
344 return false;
345 }
346 for (StringPiece directive : util::Tokenize(directives, ',')) {
347 if (directive == "remove") {
348 options->resources_blacklist.insert(resource_name.ToResourceName());
349 } else if (directive == "no_obfuscate") {
350 options->table_flattener_options.whitelisted_resources.insert(
351 resource_name.entry.to_string());
352 }
353 }
Luke Nicholsonb0643302017-12-01 15:29:03 -0800354 }
355 return true;
356}
357
Adam Lesinski8780eb62017-10-31 17:44:39 -0700358bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700359 OptimizeOptions* out_options) {
Adam Lesinski8780eb62017-10-31 17:44:39 -0700360 const xml::XmlResource* manifest = apk->GetManifest();
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700361 if (manifest == nullptr) {
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700362 return false;
363 }
364
Adam Lesinski8780eb62017-10-31 17:44:39 -0700365 Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics());
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700366 if (!app_info) {
367 context->GetDiagnostics()->Error(DiagMessage()
368 << "failed to extract data from AndroidManifest.xml");
369 return false;
370 }
371
372 out_options->app_info = std::move(app_info.value());
373 context->SetMinSdkVersion(out_options->app_info.min_sdk_version.value_or_default(0));
374 return true;
375}
376
377int Optimize(const std::vector<StringPiece>& args) {
378 OptimizeContext context;
379 OptimizeOptions options;
Shane Farmer57669432017-06-19 12:52:04 -0700380 Maybe<std::string> config_path;
Luke Nicholsonb0643302017-12-01 15:29:03 -0800381 Maybe<std::string> whitelist_path;
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500382 Maybe<std::string> resources_config_path;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700383 Maybe<std::string> target_densities;
384 std::vector<std::string> configs;
385 std::vector<std::string> split_args;
Shane Farmer666de342017-11-29 16:07:51 -0800386 std::unordered_set<std::string> kept_artifacts;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700387 bool verbose = false;
Shane Farmer9ecc0752017-08-24 15:55:36 -0700388 bool print_only = false;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700389 Flags flags =
390 Flags()
Shane Farmer57669432017-06-19 12:52:04 -0700391 .OptionalFlag("-o", "Path to the output APK.", &options.output_path)
392 .OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
393 .OptionalFlag("-x", "Path to XML configuration file.", &config_path)
Shane Farmer9ecc0752017-08-24 15:55:36 -0700394 .OptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only)
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700395 .OptionalFlag(
396 "--target-densities",
397 "Comma separated list of the screen densities that the APK will be optimized for.\n"
398 "All the resources that would be unused on devices of the given densities will be \n"
399 "removed from the APK.",
400 &target_densities)
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500401 .OptionalFlag("--whitelist-path",
Luke Nicholsonb0643302017-12-01 15:29:03 -0800402 "Path to the whitelist.cfg file containing whitelisted resources \n"
403 "whose names should not be altered in final resource tables.",
404 &whitelist_path)
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500405 .OptionalFlag("--resources-config-path",
406 "Path to the resources.cfg file containing the list of resources and \n"
407 "directives to each resource. \n"
408 "Format: type/resource_name#[directive][,directive]",
409 &resources_config_path)
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700410 .OptionalFlagList("-c",
411 "Comma separated list of configurations to include. The default\n"
412 "is all configurations.",
413 &configs)
414 .OptionalFlagList("--split",
415 "Split resources matching a set of configs out to a "
Adam Lesinskidb091572017-04-13 12:48:56 -0700416 "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
417 "On Windows, use a semicolon ';' separator instead.",
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700418 &split_args)
Shane Farmer666de342017-11-29 16:07:51 -0800419 .OptionalFlagList("--keep-artifacts",
420 "Comma separated list of artifacts to keep. If none are specified,\n"
421 "all artifacts will be kept.",
422 &kept_artifacts)
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700423 .OptionalSwitch("--enable-sparse-encoding",
424 "Enables encoding sparse entries using a binary search tree.\n"
425 "This decreases APK size at the cost of resource retrieval performance.",
426 &options.table_flattener_options.use_sparse_entries)
Luke Nicholsonb0643302017-12-01 15:29:03 -0800427 .OptionalSwitch("--enable-resource-obfuscation",
428 "Enables obfuscation of key string pool to single value",
429 &options.table_flattener_options.collapse_key_stringpool)
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700430 .OptionalSwitch("-v", "Enables verbose logging", &verbose);
431
432 if (!flags.Parse("aapt2 optimize", args, &std::cerr)) {
433 return 1;
434 }
435
436 if (flags.GetArgs().size() != 1u) {
437 std::cerr << "must have one APK as argument.\n\n";
438 flags.Usage("aapt2 optimize", &std::cerr);
439 return 1;
440 }
441
Shane Farmer0a5b2012017-06-22 12:24:12 -0700442 const std::string& apk_path = flags.GetArgs()[0];
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700443
444 context.SetVerbose(verbose);
Shane Farmer9ecc0752017-08-24 15:55:36 -0700445 IDiagnostics* diag = context.GetDiagnostics();
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700446
Shane Farmer57669432017-06-19 12:52:04 -0700447 if (config_path) {
Shane Farmer57669432017-06-19 12:52:04 -0700448 std::string& path = config_path.value();
449 Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
450 if (for_path) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800451 options.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
452 if (!options.apk_artifacts) {
453 diag->Error(DiagMessage() << "Failed to parse the output artifact list");
454 return 1;
455 }
456
Shane Farmer57669432017-06-19 12:52:04 -0700457 } else {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700458 diag->Error(DiagMessage() << "Could not parse config file " << path);
Shane Farmer57669432017-06-19 12:52:04 -0700459 return 1;
460 }
Shane Farmer9ecc0752017-08-24 15:55:36 -0700461
462 if (print_only) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800463 for (const OutputArtifact& artifact : options.apk_artifacts.value()) {
464 std::cout << artifact.name << std::endl;
Shane Farmer9ecc0752017-08-24 15:55:36 -0700465 }
466 return 0;
467 }
468
Shane Farmer666de342017-11-29 16:07:51 -0800469 if (!kept_artifacts.empty()) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800470 for (const std::string& artifact_str : kept_artifacts) {
471 for (const StringPiece& artifact : util::Tokenize(artifact_str, ',')) {
Shane Farmer666de342017-11-29 16:07:51 -0800472 options.kept_artifacts.insert(artifact.to_string());
473 }
474 }
475 }
476
Shane Farmer9ecc0752017-08-24 15:55:36 -0700477 // Since we know that we are going to process the APK (not just print targets), make sure we
478 // have somewhere to write them to.
479 if (!options.output_dir) {
480 diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
481 return 1;
482 }
483 } else if (print_only) {
484 diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
485 return 1;
Shane Farmer57669432017-06-19 12:52:04 -0700486 }
487
Shane Farmer2c122412017-12-15 16:55:54 -0800488 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, context.GetDiagnostics());
489 if (!apk) {
490 return 1;
491 }
492
493 if (target_densities) {
494 // Parse the target screen densities.
495 for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) {
496 Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
497 if (!target_density) {
498 return 1;
499 }
500 options.table_splitter_options.preferred_densities.push_back(target_density.value());
501 }
502 }
503
504 std::unique_ptr<IConfigFilter> filter;
505 if (!configs.empty()) {
506 filter = ParseConfigFilterParameters(configs, diag);
507 if (filter == nullptr) {
508 return 1;
509 }
510 options.table_splitter_options.config_filter = filter.get();
511 }
512
513 // Parse the split parameters.
514 for (const std::string& split_arg : split_args) {
515 options.split_paths.emplace_back();
516 options.split_constraints.emplace_back();
517 if (!ParseSplitParameter(split_arg, diag, &options.split_paths.back(),
518 &options.split_constraints.back())) {
519 return 1;
520 }
521 }
522
Luke Nicholsonb0643302017-12-01 15:29:03 -0800523 if (options.table_flattener_options.collapse_key_stringpool) {
524 if (whitelist_path) {
525 std::string& path = whitelist_path.value();
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500526 if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options)) {
Luke Nicholsonb0643302017-12-01 15:29:03 -0800527 return 1;
528 }
529 }
530 }
531
Mohamed Heikald3c5fb62018-01-12 11:37:26 -0500532 if (resources_config_path) {
533 std::string& path = resources_config_path.value();
534 if (!ExtractConfig(path, &context, &options)) {
535 return 1;
536 }
537 }
538
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700539 if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
540 return 1;
541 }
542
543 OptimizeCommand cmd(&context, options);
544 return cmd.Run(std::move(apk));
545}
546
547} // namespace aapt