AAPT2: Refactor flags into commands

Refactors the flag based command invocation into classes that make using
subcommands easier.

Test: manual tests of printing
Change-Id: Ic8df6af0be30db552e32150afebecbfeec7e1075
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 7a33de0..2ecf25b 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -15,6 +15,7 @@
 //
 
 toolSources = [
+    "cmd/Command.cpp",
     "cmd/Compile.cpp",
     "cmd/Convert.cpp",
     "cmd/Diff.cpp",
@@ -124,7 +125,6 @@
         "ConfigDescription.cpp",
         "Debug.cpp",
         "DominatorTree.cpp",
-        "Flags.cpp",
         "java/AnnotationProcessor.cpp",
         "java/ClassDefinition.cpp",
         "java/JavaClassGenerator.cpp",
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
deleted file mode 100644
index 84977ab..0000000
--- a/tools/aapt2/Flags.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2015 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 "Flags.h"
-
-#include <iomanip>
-#include <iostream>
-#include <string>
-#include <vector>
-
-#include "androidfw/StringPiece.h"
-
-#include "util/Util.h"
-
-using android::StringPiece;
-
-namespace aapt {
-
-Flags& Flags::RequiredFlag(const StringPiece& name,
-                           const StringPiece& description, std::string* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    *value = arg.to_string();
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
-  return *this;
-}
-
-Flags& Flags::RequiredFlagList(const StringPiece& name,
-                               const StringPiece& description,
-                               std::vector<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    value->push_back(arg.to_string());
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
-  return *this;
-}
-
-Flags& Flags::OptionalFlag(const StringPiece& name,
-                           const StringPiece& description,
-                           Maybe<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    *value = arg.to_string();
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
-  return *this;
-}
-
-Flags& Flags::OptionalFlagList(const StringPiece& name,
-                               const StringPiece& description,
-                               std::vector<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    value->push_back(arg.to_string());
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
-  return *this;
-}
-
-Flags& Flags::OptionalFlagList(const StringPiece& name,
-                               const StringPiece& description,
-                               std::unordered_set<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    value->insert(arg.to_string());
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
-  return *this;
-}
-
-Flags& Flags::OptionalSwitch(const StringPiece& name,
-                             const StringPiece& description, bool* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    *value = true;
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false});
-  return *this;
-}
-
-void Flags::Usage(const StringPiece& command, std::ostream* out) {
-  constexpr size_t kWidth = 50;
-
-  *out << command << " [options]";
-  for (const Flag& flag : flags_) {
-    if (flag.required) {
-      *out << " " << flag.name << " arg";
-    }
-  }
-
-  *out << " files...\n\nOptions:\n";
-
-  for (const Flag& flag : flags_) {
-    std::string argline = flag.name;
-    if (flag.num_args > 0) {
-      argline += " arg";
-    }
-
-    // Split the description by newlines and write out the argument (which is
-    // empty after
-    // the first line) followed by the description line. This will make sure
-    // that multiline
-    // descriptions are still right justified and aligned.
-    for (StringPiece line : util::Tokenize(flag.description, '\n')) {
-      *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
-      argline = " ";
-    }
-  }
-  *out << " " << std::setw(kWidth) << std::left << "-h"
-       << "Displays this help menu\n";
-  out->flush();
-}
-
-bool Flags::Parse(const StringPiece& command,
-                  const std::vector<StringPiece>& args,
-                  std::ostream* out_error) {
-  for (size_t i = 0; i < args.size(); i++) {
-    StringPiece arg = args[i];
-    if (*(arg.data()) != '-') {
-      args_.push_back(arg.to_string());
-      continue;
-    }
-
-    if (arg == "-h" || arg == "--help") {
-      Usage(command, out_error);
-      return false;
-    }
-
-    bool match = false;
-    for (Flag& flag : flags_) {
-      if (arg == flag.name) {
-        if (flag.num_args > 0) {
-          i++;
-          if (i >= args.size()) {
-            *out_error << flag.name << " missing argument.\n\n";
-            Usage(command, out_error);
-            return false;
-          }
-          flag.action(args[i]);
-        } else {
-          flag.action({});
-        }
-        flag.parsed = true;
-        match = true;
-        break;
-      }
-    }
-
-    if (!match) {
-      *out_error << "unknown option '" << arg << "'.\n\n";
-      Usage(command, out_error);
-      return false;
-    }
-  }
-
-  for (const Flag& flag : flags_) {
-    if (flag.required && !flag.parsed) {
-      *out_error << "missing required flag " << flag.name << "\n\n";
-      Usage(command, out_error);
-      return false;
-    }
-  }
-  return true;
-}
-
-const std::vector<std::string>& Flags::GetArgs() { return args_; }
-
-}  // namespace aapt
diff --git a/tools/aapt2/Flags.h b/tools/aapt2/Flags.h
deleted file mode 100644
index 3b3ae71..0000000
--- a/tools/aapt2/Flags.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef AAPT_FLAGS_H
-#define AAPT_FLAGS_H
-
-#include <functional>
-#include <ostream>
-#include <string>
-#include <unordered_set>
-#include <vector>
-
-#include "androidfw/StringPiece.h"
-
-#include "util/Maybe.h"
-
-namespace aapt {
-
-class Flags {
- public:
-  Flags& RequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
-                      std::string* value);
-  Flags& RequiredFlagList(const android::StringPiece& name, const android::StringPiece& description,
-                          std::vector<std::string>* value);
-  Flags& OptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
-                      Maybe<std::string>* value);
-  Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
-                          std::vector<std::string>* value);
-  Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
-                          std::unordered_set<std::string>* value);
-  Flags& OptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
-                        bool* value);
-
-  void Usage(const android::StringPiece& command, std::ostream* out);
-
-  bool Parse(const android::StringPiece& command, const std::vector<android::StringPiece>& args,
-             std::ostream* outError);
-
-  const std::vector<std::string>& GetArgs();
-
- private:
-  struct Flag {
-    std::string name;
-    std::string description;
-    std::function<bool(const android::StringPiece& value)> action;
-    bool required;
-    size_t num_args;
-
-    bool parsed;
-  };
-
-  std::vector<Flag> flags_;
-  std::vector<std::string> args_;
-};
-
-}  // namespace aapt
-
-#endif  // AAPT_FLAGS_H
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 808b29c..23903c9e 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -29,6 +29,13 @@
 #include "androidfw/StringPiece.h"
 
 #include "Diagnostics.h"
+#include "cmd/Command.h"
+#include "cmd/Compile.h"
+#include "cmd/Convert.h"
+#include "cmd/Diff.h"
+#include "cmd/Dump.h"
+#include "cmd/Link.h"
+#include "cmd/Optimize.h"
 #include "util/Files.h"
 #include "util/Util.h"
 
@@ -43,114 +50,121 @@
 // Update minor version whenever a feature or flag is added.
 static const char* sMinorVersion = "19";
 
-static void PrintVersion() {
-  std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion,
-                            sMinorVersion)
-            << std::endl;
-}
+/** Prints the version information of AAPT2. */
+class VersionCommand : public Command {
+ public:
+  explicit VersionCommand() : Command("version") {
+    SetDescription("Prints the version of aapt.");
+  }
 
-static void PrintUsage() {
-  std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl;
-}
-
-extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
-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) {
-  if (command == "compile" || command == "c") {
-    return Compile(args, diagnostics);
-  } else if (command == "link" || command == "l") {
-    return Link(args, diagnostics);
-  } else if (command == "dump" || command == "d") {
-    return Dump(args);
-  } else if (command == "diff") {
-    return Diff(args);
-  } else if (command == "optimize") {
-    return Optimize(args);
-  } else if (command == "convert") {
-    return Convert(args);
-  } else if (command == "version") {
-    PrintVersion();
+  int Action(const std::vector<std::string>& /* args */) override {
+    std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion,
+                              sMinorVersion)
+              << std::endl;
     return 0;
   }
-  diagnostics->Error(DiagMessage() << "unknown command '" << command << "'");
-  return -1;
-}
+};
 
-static void RunDaemon(IDiagnostics* diagnostics) {
-  std::cout << "Ready" << std::endl;
-
-  // Run in daemon mode. The first line of input is the command. This can be 'quit' which ends
-  // the daemon mode. Each subsequent line is a single parameter to the command. The end of a
-  // invocation is signaled by providing an empty line. At any point, an EOF signal or the
-  // command 'quit' will end the daemon mode.
-  while (true) {
-    std::vector<std::string> raw_args;
-    for (std::string line; std::getline(std::cin, line) && !line.empty();) {
-      raw_args.push_back(line);
-    }
-
-    if (!std::cin) {
-      break;
-    }
-
-    // An empty command does nothing.
-    if (raw_args.empty()) {
-      continue;
-    }
-
-    if (raw_args[0] == "quit") {
-      break;
-    }
-
-    std::vector<StringPiece> args;
-    args.insert(args.end(), ++raw_args.begin(), raw_args.end());
-    int ret = ExecuteCommand(raw_args[0], args, diagnostics);
-    if (ret != 0) {
-      std::cerr << "Error" << std::endl;
-    }
-    std::cerr << "Done" << std::endl;
+/** The main entry point of AAPT. */
+class MainCommand : public Command {
+ public:
+  explicit MainCommand(IDiagnostics* diagnostics) : Command("aapt2"), diagnostics_(diagnostics) {
+    AddOptionalSubcommand(util::make_unique<CompileCommand>(diagnostics));
+    AddOptionalSubcommand(util::make_unique<LinkCommand>(diagnostics));
+    AddOptionalSubcommand(util::make_unique<DumpCommand>());
+    AddOptionalSubcommand(util::make_unique<DiffCommand>());
+    AddOptionalSubcommand(util::make_unique<OptimizeCommand>());
+    AddOptionalSubcommand(util::make_unique<ConvertCommand>());
+    AddOptionalSubcommand(util::make_unique<VersionCommand>());
   }
-  std::cout << "Exiting daemon" << std::endl;
-}
+
+  int Action(const std::vector<std::string>& args) override {
+    if (args.size() == 0) {
+      diagnostics_->Error(DiagMessage() << "no subcommand specified");
+    } else {
+      diagnostics_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'");
+    }
+
+    Usage(&std::cerr);
+    return -1;
+  }
+
+ private:
+  IDiagnostics* diagnostics_;
+};
+
+/*
+ * Run in daemon mode. The first line of input is the command. This can be 'quit' which ends
+ * the daemon mode. Each subsequent line is a single parameter to the command. The end of a
+ * invocation is signaled by providing an empty line. At any point, an EOF signal or the
+ * command 'quit' will end the daemon mode.
+ */
+class DaemonCommand : public Command {
+ public:
+  explicit DaemonCommand(IDiagnostics* diagnostics) : Command("daemon", "m"),
+                                                      diagnostics_(diagnostics) {
+    SetDescription("Runs aapt in daemon mode. Each subsequent line is a single parameter to the\n"
+        "command. The end of an invocation is signaled by providing an empty line.");
+  }
+
+  int Action(const std::vector<std::string>& /* args */) override {
+    std::cout << "Ready" << std::endl;
+
+    while (true) {
+      std::vector<std::string> raw_args;
+      for (std::string line; std::getline(std::cin, line) && !line.empty();) {
+        raw_args.push_back(line);
+      }
+
+      if (!std::cin) {
+        break;
+      }
+
+      // An empty command does nothing.
+      if (raw_args.empty()) {
+        continue;
+      }
+
+      // End the dameon
+      if (raw_args[0] == "quit") {
+        break;
+      }
+
+      std::vector<StringPiece> args;
+      args.insert(args.end(), raw_args.begin(), raw_args.end());
+      if (MainCommand(diagnostics_).Execute(args, &std::cerr) != 0) {
+        std::cerr << "Error" << std::endl;
+      }
+      std::cerr << "Done" << std::endl;
+    }
+    std::cout << "Exiting daemon" << std::endl;
+
+    return 0;
+  }
+
+ private:
+  IDiagnostics* diagnostics_;
+};
 
 }  // namespace aapt
 
 int MainImpl(int argc, char** argv) {
-  if (argc < 2) {
-    std::cerr << "no command specified\n";
-    aapt::PrintUsage();
+  if (argc < 1) {
     return -1;
   }
 
-  argv += 1;
-  argc -= 1;
-
-  aapt::StdErrDiagnostics diagnostics;
-
   // Collect the arguments starting after the program name and command name.
   std::vector<StringPiece> args;
   for (int i = 1; i < argc; i++) {
     args.push_back(argv[i]);
   }
 
-  const StringPiece command(argv[0]);
-  if (command != "daemon" && command != "m") {
-    // Single execution.
-    const int result = aapt::ExecuteCommand(command, args, &diagnostics);
-    if (result < 0) {
-      aapt::PrintUsage();
-    }
-    return result;
-  }
+  // Add the daemon subcommand here so it cannot be called while executing the daemon
+  aapt::StdErrDiagnostics diagnostics;
+  auto main_command = new aapt::MainCommand(&diagnostics);
+  main_command->AddOptionalSubcommand(aapt::util::make_unique<aapt::DaemonCommand>(&diagnostics));
 
-  aapt::RunDaemon(&diagnostics);
-  return 0;
+  return main_command->Execute(args, &std::cerr);
 }
 
 int main(int argc, char** argv) {
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
new file mode 100644
index 0000000..09411b9
--- /dev/null
+++ b/tools/aapt2/cmd/Command.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2018 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 "Command.h"
+
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "androidfw/StringPiece.h"
+
+#include "util/Util.h"
+
+using android::StringPiece;
+
+namespace aapt {
+
+void Command::AddRequiredFlag(const StringPiece& name,
+    const StringPiece& description, std::string* value) {
+  auto func = [value](const StringPiece& arg) -> bool {
+    *value = arg.to_string();
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+}
+
+void Command::AddRequiredFlagList(const StringPiece& name,
+    const StringPiece& description,
+    std::vector<std::string>* value) {
+  auto func = [value](const StringPiece& arg) -> bool {
+    value->push_back(arg.to_string());
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+}
+
+void Command::AddOptionalFlag(const StringPiece& name,
+    const StringPiece& description,
+    Maybe<std::string>* value) {
+  auto func = [value](const StringPiece& arg) -> bool {
+    *value = arg.to_string();
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+}
+
+void Command::AddOptionalFlagList(const StringPiece& name,
+    const StringPiece& description,
+    std::vector<std::string>* value) {
+  auto func = [value](const StringPiece& arg) -> bool {
+    value->push_back(arg.to_string());
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+}
+
+void Command::AddOptionalFlagList(const StringPiece& name,
+    const StringPiece& description,
+    std::unordered_set<std::string>* value) {
+  auto func = [value](const StringPiece& arg) -> bool {
+    value->insert(arg.to_string());
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+}
+
+void Command::AddOptionalSwitch(const StringPiece& name,
+    const StringPiece& description, bool* value) {
+  auto func = [value](const StringPiece& arg) -> bool {
+    *value = true;
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false});
+}
+
+void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand) {
+  subcommand->fullname_ = name_ + " " + subcommand->name_;
+  subcommands_.push_back(std::move(subcommand));
+}
+
+void Command::SetDescription(const android::StringPiece& description) {
+  description_ = description.to_string();
+}
+
+void Command::Usage(std::ostream* out) {
+  constexpr size_t kWidth = 50;
+
+  *out << fullname_;
+
+  if (!subcommands_.empty()) {
+    *out << " [subcommand]";
+  }
+
+  *out << " [options]";
+  for (const Flag& flag : flags_) {
+    if (flag.required) {
+      *out << " " << flag.name << " arg";
+    }
+  }
+
+  *out << " files...\n";
+
+  if (!subcommands_.empty()) {
+    *out << "\nSubcommands:\n";
+    for (auto& subcommand : subcommands_) {
+      std::string argline = subcommand->name_;
+
+      // Split the description by newlines and write out the argument (which is
+      // empty after the first line) followed by the description line. This will make sure
+      // that multiline descriptions are still right justified and aligned.
+      for (StringPiece line : util::Tokenize(subcommand->description_, '\n')) {
+        *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
+        argline = " ";
+      }
+    }
+  }
+
+  *out << "\nOptions:\n";
+
+  for (const Flag& flag : flags_) {
+    std::string argline = flag.name;
+    if (flag.num_args > 0) {
+      argline += " arg";
+    }
+
+    // Split the description by newlines and write out the argument (which is
+    // empty after the first line) followed by the description line. This will make sure
+    // that multiline descriptions are still right justified and aligned.
+    for (StringPiece line : util::Tokenize(flag.description, '\n')) {
+      *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
+      argline = " ";
+    }
+  }
+  *out << " " << std::setw(kWidth) << std::left << "-h"
+       << "Displays this help menu\n";
+  out->flush();
+}
+
+int Command::Execute(const std::vector<android::StringPiece>& args, std::ostream* out_error) {
+  std::vector<std::string> file_args;
+
+  for (size_t i = 0; i < args.size(); i++) {
+    StringPiece arg = args[i];
+    if (*(arg.data()) != '-') {
+      // Continue parsing as the sub command if the first argument matches one of the subcommands
+      if (i == 0) {
+        for (auto& subcommand : subcommands_) {
+          if (arg == subcommand->name_ || arg==subcommand->short_name_) {
+            return subcommand->Execute(
+                std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
+          }
+        }
+      }
+
+      file_args.push_back(arg.to_string());
+      continue;
+    }
+
+    if (arg == "-h" || arg == "--help") {
+      Usage(out_error);
+      return 1;
+    }
+
+    bool match = false;
+    for (Flag& flag : flags_) {
+      if (arg == flag.name) {
+        if (flag.num_args > 0) {
+          i++;
+          if (i >= args.size()) {
+            *out_error << flag.name << " missing argument.\n\n";
+            Usage(out_error);
+            return false;
+          }
+          flag.action(args[i]);
+        } else {
+          flag.action({});
+        }
+        flag.parsed = true;
+        match = true;
+        break;
+      }
+    }
+
+    if (!match) {
+      *out_error << "unknown option '" << arg << "'.\n\n";
+      Usage(out_error);
+      return 1;
+    }
+  }
+
+  for (const Flag& flag : flags_) {
+    if (flag.required && !flag.parsed) {
+      *out_error << "missing required flag " << flag.name << "\n\n";
+      Usage(out_error);
+      return 1;
+    }
+  }
+
+  return Action(file_args);
+}
+
+}  // namespace aapt
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
new file mode 100644
index 0000000..71dc6fe
--- /dev/null
+++ b/tools/aapt2/cmd/Command.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef AAPT_COMMAND_H
+#define AAPT_COMMAND_H
+
+#include <functional>
+#include <ostream>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "androidfw/StringPiece.h"
+
+#include "util/Maybe.h"
+
+namespace aapt {
+
+class Command {
+ public:
+  explicit Command(const android::StringPiece& name) : name_(name.to_string()),
+                                                       fullname_(name.to_string()) { }
+  explicit Command(const android::StringPiece& name, const android::StringPiece& short_name)
+      : name_(name.to_string()), short_name_(short_name.to_string()), fullname_(name.to_string()) {}
+  virtual ~Command() = default;
+
+  void AddRequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
+      std::string* value);
+  void AddRequiredFlagList(const android::StringPiece& name, const android::StringPiece&
+      description, std::vector<std::string>* value);
+  void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
+      Maybe<std::string>* value);
+  void AddOptionalFlagList(const android::StringPiece& name,
+      const android::StringPiece& description, std::vector<std::string>* value);
+  void AddOptionalFlagList(const android::StringPiece& name,
+      const android::StringPiece& description, std::unordered_set<std::string>* value);
+  void AddOptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
+      bool* value);
+  void AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand);
+
+  void SetDescription(const android::StringPiece& name);
+
+  /** Prints the help menu of the command. */
+  void Usage(std::ostream* out);
+
+  /**
+   * Parses the command line arguments, sets the flag variable values, and runs the action of
+   * the command. If the arguments fail to parse to the command and its subcommands, then the action
+   * will not be run and the usage will be printed instead.
+   **/
+  int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError);
+
+  /** The action to preform when the command is executed. */
+  virtual int Action(const std::vector<std::string>& args) = 0;
+
+ private:
+  struct Flag {
+    std::string name;
+    std::string description;
+    std::function<bool(const android::StringPiece& value)> action;
+    bool required;
+    size_t num_args;
+
+    bool parsed;
+  };
+
+  std::string description_;
+  std::string name_;
+  std::string short_name_;
+  std::string fullname_;
+  std::vector<Flag> flags_;
+  std::vector<std::unique_ptr<Command>> subcommands_;
+};
+
+}  // namespace aapt
+
+#endif  // AAPT_COMMAND_H
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index a17a0d3..8c1fa9a 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#include <dirent.h>
+#include "Compile.h"
 
+#include <dirent.h>
 #include <string>
 
 #include "android-base/errors.h"
@@ -27,7 +28,6 @@
 
 #include "ConfigDescription.h"
 #include "Diagnostics.h"
-#include "Flags.h"
 #include "ResourceParser.h"
 #include "ResourceTable.h"
 #include "cmd/Util.h"
@@ -121,17 +121,6 @@
                           extension.to_string(), config_str.to_string(), config};
 }
 
-struct CompileOptions {
-  std::string output_path;
-  Maybe<std::string> res_dir;
-  Maybe<std::string> generate_text_symbols_path;
-  Maybe<Visibility::Level> visibility;
-  bool pseudolocalize = false;
-  bool no_png_crunch = false;
-  bool legacy_mode = false;
-  bool verbose = false;
-};
-
 static std::string BuildIntermediateContainerFilename(const ResourcePathData& data) {
   std::stringstream name;
   name << data.resource_dir;
@@ -712,50 +701,21 @@
   bool verbose_ = false;
 };
 
-// Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
-int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
-  CompileContext context(diagnostics);
-  CompileOptions options;
+int CompileCommand::Action(const std::vector<std::string>& args) {
+  CompileContext context(diagnostic_);
+  context.SetVerbose(options_.verbose);
 
-  bool verbose = false;
-  Maybe<std::string> visibility;
-  Flags flags =
-      Flags()
-          .RequiredFlag("-o", "Output path", &options.output_path)
-          .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir)
-          .OptionalFlag("--output-text-symbols",
-                        "Generates a text file containing the resource symbols in the\n"
-                        "specified file",
-                        &options.generate_text_symbols_path)
-          .OptionalSwitch("--pseudo-localize",
-                          "Generate resources for pseudo-locales "
-                          "(en-XA and ar-XB)",
-                          &options.pseudolocalize)
-          .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)
-          .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
-                          &options.legacy_mode)
-          .OptionalSwitch("-v", "Enables verbose logging", &verbose)
-          .OptionalFlag("--visibility",
-                        "Sets the visibility of the compiled resources to the specified\n"
-                        "level. Accepted levels: public, private, default",
-                        &visibility);
-  if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
-    return 1;
-  }
-
-  context.SetVerbose(verbose);
-
-  if (visibility) {
-    if (visibility.value() == "public") {
-      options.visibility = Visibility::Level::kPublic;
-    } else if (visibility.value() == "private") {
-      options.visibility = Visibility::Level::kPrivate;
-    } else if (visibility.value() == "default") {
-      options.visibility = Visibility::Level::kUndefined;
+  if (visibility_) {
+    if (visibility_.value() == "public") {
+      options_.visibility = Visibility::Level::kPublic;
+    } else if (visibility_.value() == "private") {
+      options_.visibility = Visibility::Level::kPrivate;
+    } else if (visibility_.value() == "default") {
+      options_.visibility = Visibility::Level::kUndefined;
     } else {
       context.GetDiagnostics()->Error(
           DiagMessage() << "Unrecognized visibility level passes to --visibility: '"
-                        << visibility.value() << "'. Accepted levels: public, private, default");
+                        << visibility_.value() << "'. Accepted levels: public, private, default");
       return 1;
     }
   }
@@ -763,25 +723,25 @@
   std::unique_ptr<IArchiveWriter> archive_writer;
 
   std::vector<ResourcePathData> input_data;
-  if (options.res_dir) {
-    if (!flags.GetArgs().empty()) {
+  if (options_.res_dir) {
+    if (!args.empty()) {
       // Can't have both files and a resource directory.
       context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
-      flags.Usage("aapt2 compile", &std::cerr);
+      Usage(&std::cerr);
       return 1;
     }
 
-    if (!LoadInputFilesFromDir(&context, options, &input_data)) {
+    if (!LoadInputFilesFromDir(&context, options_, &input_data)) {
       return 1;
     }
 
-    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path);
+    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
 
   } else {
-    input_data.reserve(flags.GetArgs().size());
+    input_data.reserve(args.size());
 
     // Collect data from the path for each input file.
-    for (const std::string& arg : flags.GetArgs()) {
+    for (const std::string& arg : args) {
       std::string error_str;
       if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) {
         input_data.push_back(std::move(path_data.value()));
@@ -791,7 +751,7 @@
       }
     }
 
-    archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path);
+    archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path);
   }
 
   if (!archive_writer) {
@@ -800,7 +760,7 @@
 
   bool error = false;
   for (ResourcePathData& path_data : input_data) {
-    if (options.verbose) {
+    if (options_.verbose) {
       context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing");
     }
 
@@ -821,21 +781,21 @@
       if (*type != ResourceType::kRaw) {
         if (path_data.extension == "xml") {
           compile_func = &CompileXml;
-        } else if ((!options.no_png_crunch && path_data.extension == "png")
+        } else if ((!options_.no_png_crunch && path_data.extension == "png")
             || path_data.extension == "9.png") {
           compile_func = &CompilePng;
         }
       }
     } else {
       context.GetDiagnostics()->Error(DiagMessage()
-                                      << "invalid file path '" << path_data.source << "'");
+          << "invalid file path '" << path_data.source << "'");
       error = true;
       continue;
     }
 
     // Treat periods as a reserved character that should not be present in a file name
     // Legacy support for AAPT which did not reserve periods
-    if (compile_func != &CompileFile && !options.legacy_mode
+    if (compile_func != &CompileFile && !options_.legacy_mode
         && std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) {
       error = true;
       context.GetDiagnostics()->Error(DiagMessage() << "resource file '" << path_data.source.path
@@ -846,7 +806,7 @@
 
     // Compile the file.
     const std::string out_path = BuildIntermediateContainerFilename(path_data);
-    error |= !compile_func(&context, options, path_data, archive_writer.get(), out_path);
+    error |= !compile_func(&context, options_, path_data, archive_writer.get(), out_path);
   }
   return error ? 1 : 0;
 }
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index d95cf1c..4151952 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -1,13 +1,69 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
 #ifndef AAPT2_COMPILE_H
 #define AAPT2_COMPILE_H
 
 #include "androidfw/StringPiece.h"
 
+#include "Command.h"
 #include "Diagnostics.h"
+#include "ResourceTable.h"
 
 namespace aapt {
 
-  int Compile(const std::vector<android::StringPiece>& args, IDiagnostics* diagnostics);
+struct CompileOptions {
+  std::string output_path;
+  Maybe<std::string> res_dir;
+  Maybe<std::string> generate_text_symbols_path;
+  Maybe<Visibility::Level> visibility;
+  bool pseudolocalize = false;
+  bool no_png_crunch = false;
+  bool legacy_mode = false;
+  bool verbose = false;
+};
+
+class CompileCommand : public Command {
+ public:
+  explicit CompileCommand(IDiagnostics* diagnostic) : Command("compile", "c"),
+                                                      diagnostic_(diagnostic) {
+    SetDescription("Compiles resources to be linked into an apk.");
+    AddRequiredFlag("-o", "Output path", &options_.output_path);
+    AddOptionalFlag("--dir", "Directory to scan for resources", &options_.res_dir);
+    AddOptionalFlag("--output-text-symbols",
+        "Generates a text file containing the resource symbols in the\n"
+            "specified file", &options_.generate_text_symbols_path);
+    AddOptionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
+        "(en-XA and ar-XB)", &options_.pseudolocalize);
+    AddOptionalSwitch("--no-crunch", "Disables PNG processing", &options_.no_png_crunch);
+    AddOptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
+        &options_.legacy_mode);
+    AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
+    AddOptionalFlag("--visibility",
+        "Sets the visibility of the compiled resources to the specified\n"
+            "level. Accepted levels: public, private, default", &visibility_);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  IDiagnostics* diagnostic_;
+  CompileOptions options_;
+  Maybe<std::string> visibility_;
+};
 
 }// namespace aapt
 
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 212f2cf..d21addf 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -23,7 +23,8 @@
 
 namespace aapt {
 
-int TestCompile(std::string path, std::string outDir, bool legacy, StdErrDiagnostics& diag) {
+int TestCompile(const std::string& path, const std::string& outDir, bool legacy,
+    StdErrDiagnostics& diag) {
   std::vector<android::StringPiece> args;
   args.push_back(path);
   args.push_back("-o");
@@ -32,7 +33,7 @@
   if (legacy) {
     args.push_back("--legacy");
   }
-  return aapt::Compile(args, &diag);
+  return CompileCommand(&diag).Execute(args, &std::cerr);
 }
 
 TEST(CompilerTest, MultiplePeriods) {
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 3c8beaa..4b82eef 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
+#include "Convert.h"
+
 #include <vector>
 
 #include "android-base/macros.h"
 #include "androidfw/StringPiece.h"
 
-#include "Flags.h"
 #include "LoadedApk.h"
 #include "ValueVisitor.h"
 #include "cmd/Util.h"
@@ -325,37 +326,18 @@
   StdErrDiagnostics diag_;
 };
 
-int Convert(const vector<StringPiece>& args) {
+const char* ConvertCommand::kOutputFormatProto = "proto";
+const char* ConvertCommand::kOutputFormatBinary = "binary";
 
-  static const char* kOutputFormatProto = "proto";
-  static const char* kOutputFormatBinary = "binary";
+int ConvertCommand::Action(const std::vector<std::string>& args) {
+  if (args.size() != 1) {
+    std::cerr << "must supply a single proto APK\n";
+    Usage(&std::cerr);
+    return 1;
+  }
 
   Context context;
-  std::string output_path;
-  Maybe<std::string> output_format;
-  TableFlattenerOptions options;
-  Flags flags =
-      Flags()
-          .RequiredFlag("-o", "Output path", &output_path)
-          .OptionalFlag("--output-format", StringPrintf("Format of the output. Accepted values are "
-                        "'%s' and '%s'. When not set, defaults to '%s'.", kOutputFormatProto,
-                        kOutputFormatBinary, kOutputFormatBinary), &output_format)
-          .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];
+  const StringPiece& path = args[0];
   unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
   if (apk == nullptr) {
     context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK");
@@ -371,24 +353,24 @@
   context.package_ = app_info.value().package;
 
   unique_ptr<IArchiveWriter> writer =
-      CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path);
+      CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path_);
   if (writer == nullptr) {
     return 1;
   }
 
   unique_ptr<IApkSerializer> serializer;
-  if (!output_format || output_format.value() == kOutputFormatBinary) {
-    serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options));
-  } else if (output_format.value() == kOutputFormatProto) {
+  if (!output_format_ || output_format_.value() == ConvertCommand::kOutputFormatBinary) {
+
+    serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options_));
+  } else if (output_format_.value() == ConvertCommand::kOutputFormatProto) {
     serializer.reset(new ProtoApkSerializer(&context, apk->GetSource()));
   } else {
     context.GetDiagnostics()->Error(DiagMessage(path)
-                                    << "Invalid value for flag --output-format: "
-                                    << output_format.value());
+        << "Invalid value for flag --output-format: "
+        << output_format_.value());
     return 1;
   }
 
-
   return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1;
 }
 
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
new file mode 100644
index 0000000..fcec23d
--- /dev/null
+++ b/tools/aapt2/cmd/Convert.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef AAPT2_CONVERT_H
+#define AAPT2_CONVERT_H
+
+#include "Command.h"
+#include "format/binary/TableFlattener.h"
+
+namespace aapt {
+
+class ConvertCommand : public Command {
+ public:
+  explicit ConvertCommand() : Command("convert") {
+    SetDescription("Converts an apk between binary and proto formats.");
+    AddRequiredFlag("-o", "Output path", &output_path_);
+    AddOptionalFlag("--output-format", android::base::StringPrintf("Format of the output. "
+            "Accepted values are '%s' and '%s'. When not set, defaults to '%s'.",
+        kOutputFormatProto, kOutputFormatBinary, kOutputFormatBinary), &output_format_);
+    AddOptionalSwitch("--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);
+    AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  const static char* kOutputFormatProto;
+  const static char* kOutputFormatBinary;
+
+  TableFlattenerOptions options_;
+  std::string output_path_;
+  Maybe<std::string> output_format_;
+  bool verbose_ = false;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_CONVERT_H
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 16c7bba..7875a2b 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
+#include "Diff.h"
+
 #include "android-base/macros.h"
 
-#include "Flags.h"
 #include "LoadedApk.h"
 #include "ValueVisitor.h"
 #include "process/IResourceTableConsumer.h"
@@ -348,23 +349,18 @@
   VisitAllValuesInTable(table, &visitor);
 }
 
-int Diff(const std::vector<StringPiece>& args) {
+int DiffCommand::Action(const std::vector<std::string>& args) {
   DiffContext context;
 
-  Flags flags;
-  if (!flags.Parse("aapt2 diff", args, &std::cerr)) {
-    return 1;
-  }
-
-  if (flags.GetArgs().size() != 2u) {
+  if (args.size() != 2u) {
     std::cerr << "must have two apks as arguments.\n\n";
-    flags.Usage("aapt2 diff", &std::cerr);
+    Usage(&std::cerr);
     return 1;
   }
 
   IDiagnostics* diag = context.GetDiagnostics();
-  std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(flags.GetArgs()[0], diag);
-  std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(flags.GetArgs()[1], diag);
+  std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(args[0], diag);
+  std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(args[1], diag);
   if (!apk_a || !apk_b) {
     return 1;
   }
diff --git a/tools/aapt2/cmd/Diff.h b/tools/aapt2/cmd/Diff.h
new file mode 100644
index 0000000..c388888
--- /dev/null
+++ b/tools/aapt2/cmd/Diff.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef AAPT2_DIFF_H
+#define AAPT2_DIFF_H
+
+#include "Command.h"
+
+namespace aapt {
+
+class DiffCommand : public Command {
+ public:
+  explicit DiffCommand() : Command("diff") {
+    SetDescription("Prints the differences in resources of two apks.");
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_DIFF_H
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index fd133f3..717e757 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "Dump.h"
+
 #include <cinttypes>
 #include <vector>
 
@@ -22,7 +24,6 @@
 
 #include "Debug.h"
 #include "Diagnostics.h"
-#include "Flags.h"
 #include "format/Container.h"
 #include "format/binary/BinaryResourceParser.h"
 #include "format/proto/ProtoDeserialize.h"
@@ -38,13 +39,6 @@
 
 namespace aapt {
 
-struct DumpOptions {
-  DebugPrintTableOptions print_options;
-
-  // The path to a file within an APK to dump.
-  Maybe<std::string> file_to_dump_path;
-};
-
 static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
   switch (type) {
     case ResourceFile::Type::kPng:
@@ -309,29 +303,13 @@
 
 }  // namespace
 
-// Entry point for dump command.
-int Dump(const std::vector<StringPiece>& args) {
-  bool verbose = false;
-  bool no_values = false;
-  DumpOptions options;
-  Flags flags = Flags()
-                    .OptionalSwitch("--no-values",
-                                    "Suppresses output of values when displaying resource tables.",
-                                    &no_values)
-                    .OptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
-                                  &options.file_to_dump_path)
-                    .OptionalSwitch("-v", "increase verbosity of output", &verbose);
-  if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
-    return 1;
-  }
-
+int DumpCommand::Action(const std::vector<std::string>& args) {
   DumpContext context;
-  context.SetVerbose(verbose);
-
-  options.print_options.show_sources = true;
-  options.print_options.show_values = !no_values;
-  for (const std::string& arg : flags.GetArgs()) {
-    if (!TryDumpFile(&context, arg, options)) {
+  context.SetVerbose(verbose_);
+  options_.print_options.show_sources = true;
+  options_.print_options.show_values = !no_values_;
+  for (const std::string& arg : args) {
+    if (!TryDumpFile(&context, arg, options_)) {
       return 1;
     }
   }
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
new file mode 100644
index 0000000..4893c8b
--- /dev/null
+++ b/tools/aapt2/cmd/Dump.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef AAPT2_DUMP_H
+#define AAPT2_DUMP_H
+
+#include "Command.h"
+#include "Debug.h"
+
+namespace aapt {
+
+struct DumpOptions {
+  DebugPrintTableOptions print_options;
+
+  // The path to a file within an APK to dump.
+  Maybe<std::string> file_to_dump_path;
+};
+
+class DumpCommand : public Command {
+ public:
+  DumpCommand() : Command("dump", "d") {
+    SetDescription("Prints resource and manifest information.");
+    AddOptionalSwitch("--no-values", "Suppresses output of values when displaying resource tables.",
+        &no_values_);
+    AddOptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
+        &options_.file_to_dump_path);
+    AddOptionalSwitch("-v", "increase verbosity of output", &verbose_);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  DumpOptions options_;
+
+  bool verbose_ = false;
+  bool no_values_ = false;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_DUMP_H
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 1a2da7f..26770d1 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "Link.h"
+
 #include <sys/stat.h>
 #include <cinttypes>
 
@@ -29,7 +31,6 @@
 
 #include "AppInfo.h"
 #include "Debug.h"
-#include "Flags.h"
 #include "LoadedApk.h"
 #include "Locale.h"
 #include "NameMangler.h"
@@ -74,74 +75,6 @@
 
 namespace aapt {
 
-enum class OutputFormat {
-  kApk,
-  kProto,
-};
-
-struct LinkOptions {
-  std::string output_path;
-  std::string manifest_path;
-  std::vector<std::string> include_paths;
-  std::vector<std::string> overlay_files;
-  std::vector<std::string> assets_dirs;
-  bool output_to_directory = false;
-  bool auto_add_overlay = false;
-  OutputFormat output_format = OutputFormat::kApk;
-
-  // Java/Proguard options.
-  Maybe<std::string> generate_java_class_path;
-  Maybe<std::string> custom_java_package;
-  std::set<std::string> extra_java_packages;
-  Maybe<std::string> generate_text_symbols_path;
-  Maybe<std::string> generate_proguard_rules_path;
-  Maybe<std::string> generate_main_dex_proguard_rules_path;
-  bool generate_conditional_proguard_rules = false;
-  bool generate_non_final_ids = false;
-  std::vector<std::string> javadoc_annotations;
-  Maybe<std::string> private_symbols;
-
-  // Optimizations/features.
-  bool no_auto_version = false;
-  bool no_version_vectors = false;
-  bool no_version_transitions = false;
-  bool no_resource_deduping = false;
-  bool no_xml_namespaces = false;
-  bool do_not_compress_anything = false;
-  std::unordered_set<std::string> extensions_to_not_compress;
-
-  // Static lib options.
-  bool no_static_lib_packages = false;
-  bool auto_namespace_static_lib = false;
-
-  // AndroidManifest.xml massaging options.
-  ManifestFixerOptions manifest_fixer_options;
-
-  // Products to use/filter on.
-  std::unordered_set<std::string> products;
-
-  // Flattening options.
-  TableFlattenerOptions table_flattener_options;
-
-  // Split APK options.
-  TableSplitterOptions table_splitter_options;
-  std::vector<SplitConstraints> split_constraints;
-  std::vector<std::string> split_paths;
-
-  // Stable ID options.
-  std::unordered_map<ResourceName, ResourceId> stable_id_map;
-  Maybe<std::string> resource_id_map_path;
-
-  // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
-  // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
-  // In order to work around this limitation, we allow the use of traditionally reserved
-  // resource IDs [those between 0x02 and 0x7E].
-  bool allow_reserved_package_id = false;
-
-  // Whether we should fail on definitions of a resource with conflicting visibility.
-  bool strict_visibility = false;
-};
-
 class LinkContext : public IAaptContext {
  public:
   LinkContext(IDiagnostics* diagnostics)
@@ -785,9 +718,9 @@
   return table.getTableCookie(idx);
 }
 
-class LinkCommand {
+class Linker {
  public:
-  LinkCommand(LinkContext* context, const LinkOptions& options)
+  Linker(LinkContext* context, const LinkOptions& options)
       : options_(options),
         context_(context),
         final_table_(),
@@ -2040,197 +1973,12 @@
   Maybe<std::string> included_feature_base_;
 };
 
-int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
-  LinkContext context(diagnostics);
-  LinkOptions options;
-  std::vector<std::string> overlay_arg_list;
-  std::vector<std::string> extra_java_packages;
-  Maybe<std::string> package_id;
-  std::vector<std::string> configs;
-  Maybe<std::string> preferred_density;
-  Maybe<std::string> product_list;
-  bool legacy_x_flag = false;
-  bool require_localization = false;
-  bool verbose = false;
-  bool shared_lib = false;
-  bool static_lib = false;
-  bool proto_format = false;
-  Maybe<std::string> stable_id_file_path;
-  std::vector<std::string> split_args;
-  Flags flags =
-      Flags()
-          .RequiredFlag("-o", "Output path.", &options.output_path)
-          .RequiredFlag("--manifest", "Path to the Android manifest to build.",
-                        &options.manifest_path)
-          .OptionalFlagList("-I", "Adds an Android APK to link against.", &options.include_paths)
-          .OptionalFlagList("-A",
-                            "An assets directory to include in the APK. These are unprocessed.",
-                            &options.assets_dirs)
-          .OptionalFlagList("-R",
-                            "Compilation unit to link, using `overlay` semantics.\n"
-                            "The last conflicting resource given takes precedence.",
-                            &overlay_arg_list)
-          .OptionalFlag("--package-id",
-                        "Specify the package ID to use for this app. Must be greater or equal to\n"
-                        "0x7f and can't be used with --static-lib or --shared-lib.",
-                        &package_id)
-          .OptionalFlag("--java", "Directory in which to generate R.java.",
-                        &options.generate_java_class_path)
-          .OptionalFlag("--proguard", "Output file for generated Proguard rules.",
-                        &options.generate_proguard_rules_path)
-          .OptionalFlag("--proguard-main-dex",
-                        "Output file for generated Proguard rules for the main dex.",
-                        &options.generate_main_dex_proguard_rules_path)
-          .OptionalSwitch("--proguard-conditional-keep-rules",
-                          "Generate conditional Proguard keep rules.",
-                          &options.generate_conditional_proguard_rules)
-          .OptionalSwitch("--no-auto-version",
-                          "Disables automatic style and layout SDK versioning.",
-                          &options.no_auto_version)
-          .OptionalSwitch("--no-version-vectors",
-                          "Disables automatic versioning of vector drawables. Use this only\n"
-                          "when building with vector drawable support library.",
-                          &options.no_version_vectors)
-          .OptionalSwitch("--no-version-transitions",
-                          "Disables automatic versioning of transition resources. Use this only\n"
-                          "when building with transition support library.",
-                          &options.no_version_transitions)
-          .OptionalSwitch("--no-resource-deduping",
-                          "Disables automatic deduping of resources with\n"
-                          "identical values across compatible configurations.",
-                          &options.no_resource_deduping)
-          .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.table_flattener_options.use_sparse_entries)
-          .OptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.",
-                          &legacy_x_flag)
-          .OptionalSwitch("-z", "Require localization of strings marked 'suggested'.",
-                          &require_localization)
-          .OptionalFlagList("-c",
-                            "Comma separated list of configurations to include. The default\n"
-                            "is all configurations.",
-                            &configs)
-          .OptionalFlag("--preferred-density",
-                        "Selects the closest matching density and strips out all others.",
-                        &preferred_density)
-          .OptionalFlag("--product", "Comma separated list of product names to keep", &product_list)
-          .OptionalSwitch("--output-to-dir",
-                          "Outputs the APK contents to a directory specified by -o.",
-                          &options.output_to_directory)
-          .OptionalSwitch("--no-xml-namespaces",
-                          "Removes XML namespace prefix and URI information from\n"
-                          "AndroidManifest.xml and XML binaries in res/*.",
-                          &options.no_xml_namespaces)
-          .OptionalFlag("--min-sdk-version",
-                        "Default minimum SDK version to use for AndroidManifest.xml.",
-                        &options.manifest_fixer_options.min_sdk_version_default)
-          .OptionalFlag("--target-sdk-version",
-                        "Default target SDK version to use for AndroidManifest.xml.",
-                        &options.manifest_fixer_options.target_sdk_version_default)
-          .OptionalFlag("--version-code",
-                        "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
-                        "present.",
-                        &options.manifest_fixer_options.version_code_default)
-          .OptionalFlag("--version-name",
-                        "Version name to inject into the AndroidManifest.xml if none is present.",
-                        &options.manifest_fixer_options.version_name_default)
-          .OptionalSwitch("--replace-version",
-                         "If --version-code and/or --version-name are specified, these\n"
-                         "values will replace any value already in the manifest. By\n"
-                         "default, nothing is changed if the manifest already defines\n"
-                         "these attributes.",
-                         &options.manifest_fixer_options.replace_version)
-          .OptionalFlag("--compile-sdk-version-code",
-                        "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
-                        "present.",
-                        &options.manifest_fixer_options.compile_sdk_version)
-          .OptionalFlag("--compile-sdk-version-name",
-                        "Version name to inject into the AndroidManifest.xml if none is present.",
-                        &options.manifest_fixer_options.compile_sdk_version_codename)
-          .OptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
-                          &shared_lib)
-          .OptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib)
-          .OptionalSwitch("--proto-format",
-                          "Generates compiled resources in Protobuf format.\n"
-                          "Suitable as input to the bundle tool for generating an App Bundle.",
-                          &proto_format)
-          .OptionalSwitch("--no-static-lib-packages",
-                          "Merge all library resources under the app's package.",
-                          &options.no_static_lib_packages)
-          .OptionalSwitch("--auto-namespace-static-lib",
-                          "Automatically namespace resource references when building a static\n"
-                          "library.",
-                          &options.auto_namespace_static_lib)
-          .OptionalSwitch("--non-final-ids",
-                          "Generates R.java without the final modifier. This is implied when\n"
-                          "--static-lib is specified.",
-                          &options.generate_non_final_ids)
-          .OptionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
-                        &stable_id_file_path)
-          .OptionalFlag("--emit-ids",
-                        "Emit a file at the given path with a list of name to ID mappings,\n"
-                        "suitable for use with --stable-ids.",
-                        &options.resource_id_map_path)
-          .OptionalFlag("--private-symbols",
-                        "Package name to use when generating R.java for private symbols.\n"
-                        "If not specified, public and private symbols will use the application's\n"
-                        "package name.",
-                        &options.private_symbols)
-          .OptionalFlag("--custom-package", "Custom Java package under which to generate R.java.",
-                        &options.custom_java_package)
-          .OptionalFlagList("--extra-packages",
-                            "Generate the same R.java but with different package names.",
-                            &extra_java_packages)
-          .OptionalFlagList("--add-javadoc-annotation",
-                            "Adds a JavaDoc annotation to all generated Java classes.",
-                            &options.javadoc_annotations)
-          .OptionalFlag("--output-text-symbols",
-                        "Generates a text file containing the resource symbols of the R class in\n"
-                        "the specified folder.",
-                        &options.generate_text_symbols_path)
-          .OptionalSwitch("--allow-reserved-package-id",
-                          "Allows the use of a reserved package ID. This should on be used for\n"
-                          "packages with a pre-O min-sdk\n",
-                          &options.allow_reserved_package_id)
-          .OptionalSwitch("--auto-add-overlay",
-                          "Allows the addition of new resources in overlays without\n"
-                          "<add-resource> tags.",
-                          &options.auto_add_overlay)
-          .OptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
-                        &options.manifest_fixer_options.rename_manifest_package)
-          .OptionalFlag("--rename-instrumentation-target-package",
-                        "Changes the name of the target package for instrumentation. Most useful\n"
-                        "when used in conjunction with --rename-manifest-package.",
-                        &options.manifest_fixer_options.rename_instrumentation_target_package)
-          .OptionalFlagList("-0", "File extensions not to compress.",
-                            &options.extensions_to_not_compress)
-          .OptionalSwitch("--no-compress", "Do not compress any resources.",
-                          &options.do_not_compress_anything)
-          .OptionalSwitch("--warn-manifest-validation",
-                          "Treat manifest validation errors as warnings.",
-                          &options.manifest_fixer_options.warn_validation)
-          .OptionalFlagList("--split",
-                            "Split resources matching a set of configs out to a Split APK.\n"
-                            "Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
-                            "On Windows, use a semicolon ';' separator instead.",
-                            &split_args)
-          .OptionalSwitch("-v", "Enables verbose logging.", &verbose)
-          .OptionalSwitch("--debug-mode",
-                          "Inserts android:debuggable=\"true\" in to the application node of the\n"
-                          "manifest, making the application debuggable even on production devices.",
-                          &options.manifest_fixer_options.debug_mode)
-          .OptionalSwitch("--strict-visibility",
-                          "Do not allow overlays with different visibility levels.",
-                          &options.strict_visibility);
-
-  if (!flags.Parse("aapt2 link", args, &std::cerr)) {
-    return 1;
-  }
+int LinkCommand::Action(const std::vector<std::string>& args) {
+  LinkContext context(diag_);
 
   // Expand all argument-files passed into the command line. These start with '@'.
   std::vector<std::string> arg_list;
-  for (const std::string& arg : flags.GetArgs()) {
+  for (const std::string& arg : args) {
     if (util::StartsWith(arg, "@")) {
       const std::string path = arg.substr(1, arg.size() - 1);
       std::string error;
@@ -2244,27 +1992,27 @@
   }
 
   // Expand all argument-files passed to -R.
-  for (const std::string& arg : overlay_arg_list) {
+  for (const std::string& arg : overlay_arg_list_) {
     if (util::StartsWith(arg, "@")) {
       const std::string path = arg.substr(1, arg.size() - 1);
       std::string error;
-      if (!file::AppendArgsFromFile(path, &options.overlay_files, &error)) {
+      if (!file::AppendArgsFromFile(path, &options_.overlay_files, &error)) {
         context.GetDiagnostics()->Error(DiagMessage(path) << error);
         return 1;
       }
     } else {
-      options.overlay_files.push_back(arg);
+      options_.overlay_files.push_back(arg);
     }
   }
 
-  if (verbose) {
-    context.SetVerbose(verbose);
+  if (verbose_) {
+    context.SetVerbose(verbose_);
   }
 
-  if (int{shared_lib} + int{static_lib} + int{proto_format} > 1) {
+  if (int{shared_lib_} + int{static_lib_} + int{proto_format_} > 1) {
     context.GetDiagnostics()->Error(
         DiagMessage()
-        << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
+            << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
     return 1;
   }
 
@@ -2272,18 +2020,18 @@
   context.SetPackageType(PackageType::kApp);
   context.SetPackageId(kAppPackageId);
 
-  if (shared_lib) {
+  if (shared_lib_) {
     context.SetPackageType(PackageType::kSharedLib);
     context.SetPackageId(0x00);
-  } else if (static_lib) {
+  } else if (static_lib_) {
     context.SetPackageType(PackageType::kStaticLib);
-    options.output_format = OutputFormat::kProto;
-  } else if (proto_format) {
-    options.output_format = OutputFormat::kProto;
+    options_.output_format = OutputFormat::kProto;
+  } else if (proto_format_) {
+    options_.output_format = OutputFormat::kProto;
   }
 
-  if (options.auto_namespace_static_lib) {
-    if (!static_lib) {
+  if (options_.auto_namespace_static_lib) {
+    if (!static_lib_) {
       context.GetDiagnostics()->Error(
           DiagMessage() << "--auto-namespace-static-lib can only be used with --static-lib");
       return 1;
@@ -2291,16 +2039,16 @@
     context.SetAutoNamespace(true);
   }
 
-  if (package_id) {
+  if (package_id_) {
     if (context.GetPackageType() != PackageType::kApp) {
       context.GetDiagnostics()->Error(
           DiagMessage() << "can't specify --package-id when not building a regular app");
       return 1;
     }
 
-    const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id.value());
+    const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id_.value());
     if (!maybe_package_id_int) {
-      context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id.value()
+      context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id_.value()
                                                     << "' is not a valid integer");
       return 1;
     }
@@ -2308,7 +2056,7 @@
     const uint32_t package_id_int = maybe_package_id_int.value();
     if (package_id_int > std::numeric_limits<uint8_t>::max()
         || package_id_int == kFrameworkPackageId
-        || (!options.allow_reserved_package_id && package_id_int < kAppPackageId)) {
+        || (!options_.allow_reserved_package_id && package_id_int < kAppPackageId)) {
       context.GetDiagnostics()->Error(
           DiagMessage() << StringPrintf(
               "invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int));
@@ -2318,71 +2066,71 @@
   }
 
   // Populate the set of extra packages for which to generate R.java.
-  for (std::string& extra_package : extra_java_packages) {
+  for (std::string& extra_package : extra_java_packages_) {
     // A given package can actually be a colon separated list of packages.
     for (StringPiece package : util::Split(extra_package, ':')) {
-      options.extra_java_packages.insert(package.to_string());
+      options_.extra_java_packages.insert(package.to_string());
     }
   }
 
-  if (product_list) {
-    for (StringPiece product : util::Tokenize(product_list.value(), ',')) {
+  if (product_list_) {
+    for (StringPiece product : util::Tokenize(product_list_.value(), ',')) {
       if (product != "" && product != "default") {
-        options.products.insert(product.to_string());
+        options_.products.insert(product.to_string());
       }
     }
   }
 
   std::unique_ptr<IConfigFilter> filter;
-  if (!configs.empty()) {
-    filter = ParseConfigFilterParameters(configs, context.GetDiagnostics());
+  if (!configs_.empty()) {
+    filter = ParseConfigFilterParameters(configs_, context.GetDiagnostics());
     if (filter == nullptr) {
       return 1;
     }
-    options.table_splitter_options.config_filter = filter.get();
+    options_.table_splitter_options.config_filter = filter.get();
   }
 
-  if (preferred_density) {
+  if (preferred_density_) {
     Maybe<uint16_t> density =
-        ParseTargetDensityParameter(preferred_density.value(), context.GetDiagnostics());
+        ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics());
     if (!density) {
       return 1;
     }
-    options.table_splitter_options.preferred_densities.push_back(density.value());
+    options_.table_splitter_options.preferred_densities.push_back(density.value());
   }
 
   // Parse the split parameters.
-  for (const std::string& split_arg : split_args) {
-    options.split_paths.push_back({});
-    options.split_constraints.push_back({});
-    if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options.split_paths.back(),
-                             &options.split_constraints.back())) {
+  for (const std::string& split_arg : split_args_) {
+    options_.split_paths.push_back({});
+    options_.split_constraints.push_back({});
+    if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options_.split_paths.back(),
+        &options_.split_constraints.back())) {
       return 1;
     }
   }
 
-  if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path) {
-    if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path.value(),
-                         &options.stable_id_map)) {
+  if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
+    if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
+        &options_.stable_id_map)) {
       return 1;
     }
   }
 
   // Populate some default no-compress extensions that are already compressed.
-  options.extensions_to_not_compress.insert(
+  options_.extensions_to_not_compress.insert(
       {".jpg",   ".jpeg", ".png",  ".gif", ".wav",  ".mp2",  ".mp3",  ".ogg",
-       ".aac",   ".mpg",  ".mpeg", ".mid", ".midi", ".smf",  ".jet",  ".rtttl",
-       ".imy",   ".xmf",  ".mp4",  ".m4a", ".m4v",  ".3gp",  ".3gpp", ".3g2",
-       ".3gpp2", ".amr",  ".awb",  ".wma", ".wmv",  ".webm", ".mkv"});
+          ".aac",   ".mpg",  ".mpeg", ".mid", ".midi", ".smf",  ".jet",  ".rtttl",
+          ".imy",   ".xmf",  ".mp4",  ".m4a", ".m4v",  ".3gp",  ".3gpp", ".3g2",
+          ".3gpp2", ".amr",  ".awb",  ".wma", ".wmv",  ".webm", ".mkv"});
 
   // Turn off auto versioning for static-libs.
   if (context.GetPackageType() == PackageType::kStaticLib) {
-    options.no_auto_version = true;
-    options.no_version_vectors = true;
-    options.no_version_transitions = true;
+    options_.no_auto_version = true;
+    options_.no_version_vectors = true;
+    options_.no_version_transitions = true;
   }
 
-  LinkCommand cmd(&context, options);
+  Linker cmd(&context, options_);
   return cmd.Run(arg_list);
 }
 
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
new file mode 100644
index 0000000..434475e
--- /dev/null
+++ b/tools/aapt2/cmd/Link.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef AAPT2_LINK_H
+#define AAPT2_LINK_H
+
+#include "Command.h"
+#include "Diagnostics.h"
+#include "Resource.h"
+#include "split/TableSplitter.h"
+#include "format/binary/TableFlattener.h"
+#include "link/ManifestFixer.h"
+
+namespace aapt {
+
+enum class OutputFormat {
+  kApk,
+  kProto,
+};
+
+struct LinkOptions {
+  std::string output_path;
+  std::string manifest_path;
+  std::vector<std::string> include_paths;
+  std::vector<std::string> overlay_files;
+  std::vector<std::string> assets_dirs;
+  bool output_to_directory = false;
+  bool auto_add_overlay = false;
+  OutputFormat output_format = OutputFormat::kApk;
+
+  // Java/Proguard options.
+  Maybe<std::string> generate_java_class_path;
+  Maybe<std::string> custom_java_package;
+  std::set<std::string> extra_java_packages;
+  Maybe<std::string> generate_text_symbols_path;
+  Maybe<std::string> generate_proguard_rules_path;
+  Maybe<std::string> generate_main_dex_proguard_rules_path;
+  bool generate_conditional_proguard_rules = false;
+  bool generate_non_final_ids = false;
+  std::vector<std::string> javadoc_annotations;
+  Maybe<std::string> private_symbols;
+
+  // Optimizations/features.
+  bool no_auto_version = false;
+  bool no_version_vectors = false;
+  bool no_version_transitions = false;
+  bool no_resource_deduping = false;
+  bool no_xml_namespaces = false;
+  bool do_not_compress_anything = false;
+  std::unordered_set<std::string> extensions_to_not_compress;
+
+  // Static lib options.
+  bool no_static_lib_packages = false;
+  bool auto_namespace_static_lib = false;
+
+  // AndroidManifest.xml massaging options.
+  ManifestFixerOptions manifest_fixer_options;
+
+  // Products to use/filter on.
+  std::unordered_set<std::string> products;
+
+  // Flattening options.
+  TableFlattenerOptions table_flattener_options;
+
+  // Split APK options.
+  TableSplitterOptions table_splitter_options;
+  std::vector<SplitConstraints> split_constraints;
+  std::vector<std::string> split_paths;
+
+  // Stable ID options.
+  std::unordered_map<ResourceName, ResourceId> stable_id_map;
+  Maybe<std::string> resource_id_map_path;
+
+  // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
+  // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
+  // In order to work around this limitation, we allow the use of traditionally reserved
+  // resource IDs [those between 0x02 and 0x7E].
+  bool allow_reserved_package_id = false;
+
+  // Whether we should fail on definitions of a resource with conflicting visibility.
+  bool strict_visibility = false;
+};
+
+class LinkCommand : public Command {
+ public:
+  explicit LinkCommand(IDiagnostics* diag) : Command("link", "l"),
+                                             diag_(diag) {
+    SetDescription("Links resources into an apk.");
+    AddRequiredFlag("-o", "Output path.", &options_.output_path);
+    AddRequiredFlag("--manifest", "Path to the Android manifest to build.",
+        &options_.manifest_path);
+    AddOptionalFlagList("-I", "Adds an Android APK to link against.", &options_.include_paths);
+    AddOptionalFlagList("-A", "An assets directory to include in the APK. These are unprocessed.",
+        &options_.assets_dirs);
+    AddOptionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
+        "The last conflicting resource given takes precedence.", &overlay_arg_list_);
+    AddOptionalFlag("--package-id",
+        "Specify the package ID to use for this app. Must be greater or equal to\n"
+            "0x7f and can't be used with --static-lib or --shared-lib.", &package_id_);
+    AddOptionalFlag("--java", "Directory in which to generate R.java.",
+        &options_.generate_java_class_path);
+    AddOptionalFlag("--proguard", "Output file for generated Proguard rules.",
+        &options_.generate_proguard_rules_path);
+    AddOptionalFlag("--proguard-main-dex",
+        "Output file for generated Proguard rules for the main dex.",
+        &options_.generate_main_dex_proguard_rules_path);
+    AddOptionalSwitch("--proguard-conditional-keep-rules",
+        "Generate conditional Proguard keep rules.",
+        &options_.generate_conditional_proguard_rules);
+    AddOptionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning.",
+        &options_.no_auto_version);
+    AddOptionalSwitch("--no-version-vectors",
+        "Disables automatic versioning of vector drawables. Use this only\n"
+            "when building with vector drawable support library.",
+        &options_.no_version_vectors);
+    AddOptionalSwitch("--no-version-transitions",
+        "Disables automatic versioning of transition resources. Use this only\n"
+            "when building with transition support library.",
+        &options_.no_version_transitions);
+    AddOptionalSwitch("--no-resource-deduping", "Disables automatic deduping of resources with\n"
+            "identical values across compatible configurations.",
+        &options_.no_resource_deduping);
+    AddOptionalSwitch("--enable-sparse-encoding",
+        "This decreases APK size at the cost of resource retrieval performance.",
+        &options_.table_flattener_options.use_sparse_entries);
+    AddOptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.",
+        &legacy_x_flag_);
+    AddOptionalSwitch("-z", "Require localization of strings marked 'suggested'.",
+        &require_localization_);
+    AddOptionalFlagList("-c",
+        "Comma separated list of configurations to include. The default\n"
+            "is all configurations.", &configs_);
+    AddOptionalFlag("--preferred-density",
+        "Selects the closest matching density and strips out all others.",
+        &preferred_density_);
+    AddOptionalFlag("--product", "Comma separated list of product names to keep", &product_list_);
+    AddOptionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified by -o.",
+        &options_.output_to_directory);
+    AddOptionalSwitch("--no-xml-namespaces", "Removes XML namespace prefix and URI information\n"
+            "from AndroidManifest.xml and XML binaries in res/*.",
+        &options_.no_xml_namespaces);
+    AddOptionalFlag("--min-sdk-version",
+        "Default minimum SDK version to use for AndroidManifest.xml.",
+        &options_.manifest_fixer_options.min_sdk_version_default);
+    AddOptionalFlag("--target-sdk-version",
+        "Default target SDK version to use for AndroidManifest.xml.",
+        &options_.manifest_fixer_options.target_sdk_version_default);
+    AddOptionalFlag("--version-code",
+        "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
+            "present.",
+        &options_.manifest_fixer_options.version_code_default);
+    AddOptionalFlag("--version-name",
+        "Version name to inject into the AndroidManifest.xml if none is present.",
+        &options_.manifest_fixer_options.version_name_default);
+    AddOptionalSwitch("--replace-version",
+        "If --version-code and/or --version-name are specified, these\n"
+            "values will replace any value already in the manifest. By\n"
+            "default, nothing is changed if the manifest already defines\n"
+            "these attributes.",
+        &options_.manifest_fixer_options.replace_version);
+    AddOptionalFlag("--compile-sdk-version-code",
+        "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
+            "present.",
+        &options_.manifest_fixer_options.compile_sdk_version);
+    AddOptionalFlag("--compile-sdk-version-name",
+        "Version name to inject into the AndroidManifest.xml if none is present.",
+        &options_.manifest_fixer_options.compile_sdk_version_codename);
+    AddOptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
+        &shared_lib_);
+    AddOptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib_);
+    AddOptionalSwitch("--proto-format",
+        "Generates compiled resources in Protobuf format.\n"
+            "Suitable as input to the bundle tool for generating an App Bundle.",
+        &proto_format_);
+    AddOptionalSwitch("--no-static-lib-packages",
+        "Merge all library resources under the app's package.",
+        &options_.no_static_lib_packages);
+    AddOptionalSwitch("--auto-namespace-static-lib",
+        "Automatically namespace resource references when building a static\n"
+            "library.",
+        &options_.auto_namespace_static_lib);
+    AddOptionalSwitch("--non-final-ids",
+        "Generates R.java without the final modifier. This is implied when\n"
+            "--static-lib is specified.",
+        &options_.generate_non_final_ids);
+    AddOptionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
+        &stable_id_file_path_);
+    AddOptionalFlag("--emit-ids",
+        "Emit a file at the given path with a list of name to ID mappings,\n"
+            "suitable for use with --stable-ids.",
+        &options_.resource_id_map_path);
+    AddOptionalFlag("--private-symbols",
+        "Package name to use when generating R.java for private symbols.\n"
+            "If not specified, public and private symbols will use the application's\n"
+            "package name.",
+        &options_.private_symbols);
+    AddOptionalFlag("--custom-package", "Custom Java package under which to generate R.java.",
+        &options_.custom_java_package);
+    AddOptionalFlagList("--extra-packages",
+        "Generate the same R.java but with different package names.",
+        &extra_java_packages_);
+    AddOptionalFlagList("--add-javadoc-annotation",
+        "Adds a JavaDoc annotation to all generated Java classes.",
+        &options_.javadoc_annotations);
+    AddOptionalFlag("--output-text-symbols",
+        "Generates a text file containing the resource symbols of the R class in\n"
+            "the specified folder.",
+        &options_.generate_text_symbols_path);
+    AddOptionalSwitch("--allow-reserved-package-id",
+        "Allows the use of a reserved package ID. This should on be used for\n"
+            "packages with a pre-O min-sdk\n",
+        &options_.allow_reserved_package_id);
+    AddOptionalSwitch("--auto-add-overlay",
+        "Allows the addition of new resources in overlays without\n"
+            "<add-resource> tags.",
+        &options_.auto_add_overlay);
+    AddOptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
+        &options_.manifest_fixer_options.rename_manifest_package);
+    AddOptionalFlag("--rename-instrumentation-target-package",
+        "Changes the name of the target package for instrumentation. Most useful\n"
+            "when used in conjunction with --rename-manifest-package.",
+        &options_.manifest_fixer_options.rename_instrumentation_target_package);
+    AddOptionalFlagList("-0", "File extensions not to compress.",
+        &options_.extensions_to_not_compress);
+    AddOptionalSwitch("--no-compress", "Do not compress any resources.",
+        &options_.do_not_compress_anything);
+    AddOptionalSwitch("--warn-manifest-validation",
+        "Treat manifest validation errors as warnings.",
+        &options_.manifest_fixer_options.warn_validation);
+    AddOptionalFlagList("--split",
+        "Split resources matching a set of configs out to a Split APK.\n"
+            "Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
+            "On Windows, use a semicolon ';' separator instead.",
+        &split_args_);
+    AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
+    AddOptionalSwitch("--debug-mode",
+        "Inserts android:debuggable=\"true\" in to the application node of the\n"
+            "manifest, making the application debuggable even on production devices.",
+        &options_.manifest_fixer_options.debug_mode);
+    AddOptionalSwitch("--strict-visibility",
+        "Do not allow overlays with different visibility levels.",
+        &options_.strict_visibility);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  IDiagnostics* diag_;
+  LinkOptions options_;
+
+  std::vector<std::string> overlay_arg_list_;
+  std::vector<std::string> extra_java_packages_;
+  Maybe<std::string> package_id_;
+  std::vector<std::string> configs_;
+  Maybe<std::string> preferred_density_;
+  Maybe<std::string> product_list_;
+  bool legacy_x_flag_ = false;
+  bool require_localization_ = false;
+  bool verbose_ = false;
+  bool shared_lib_ = false;
+  bool static_lib_ = false;
+  bool proto_format_ = false;
+  Maybe<std::string> stable_id_file_path_;
+  std::vector<std::string> split_args_;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_LINK_H
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 4afa8f0..b4cba8c 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "Optimize.h"
+
 #include <memory>
 #include <vector>
 
@@ -24,7 +26,6 @@
 #include "androidfw/StringPiece.h"
 
 #include "Diagnostics.h"
-#include "Flags.h"
 #include "LoadedApk.h"
 #include "ResourceUtils.h"
 #include "SdkConstants.h"
@@ -54,36 +55,6 @@
 
 namespace aapt {
 
-struct OptimizeOptions {
-  // Path to the output APK.
-  Maybe<std::string> output_path;
-  // Path to the output APK directory for splits.
-  Maybe<std::string> output_dir;
-
-  // Details of the app extracted from the AndroidManifest.xml
-  AppInfo app_info;
-
-  // Blacklist of unused resources that should be removed from the apk.
-  std::unordered_set<ResourceName> resources_blacklist;
-
-  // Split APK options.
-  TableSplitterOptions table_splitter_options;
-
-  // List of output split paths. These are in the same order as `split_constraints`.
-  std::vector<std::string> split_paths;
-
-  // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`.
-  std::vector<SplitConstraints> split_constraints;
-
-  TableFlattenerOptions table_flattener_options;
-
-  Maybe<std::vector<OutputArtifact>> apk_artifacts;
-
-  // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
-  // are kept and will be written as output.
-  std::unordered_set<std::string> kept_artifacts;
-};
-
 class OptimizeContext : public IAaptContext {
  public:
   OptimizeContext() = default;
@@ -145,9 +116,9 @@
   int sdk_version_ = 0;
 };
 
-class OptimizeCommand {
+class Optimizer {
  public:
-  OptimizeCommand(OptimizeContext* context, const OptimizeOptions& options)
+  Optimizer(OptimizeContext* context, const OptimizeOptions& options)
       : options_(options), context_(context) {
   }
 
@@ -378,82 +349,24 @@
   return true;
 }
 
-int Optimize(const std::vector<StringPiece>& args) {
-  OptimizeContext context;
-  OptimizeOptions options;
-  Maybe<std::string> config_path;
-  Maybe<std::string> whitelist_path;
-  Maybe<std::string> resources_config_path;
-  Maybe<std::string> target_densities;
-  std::vector<std::string> configs;
-  std::vector<std::string> split_args;
-  std::unordered_set<std::string> kept_artifacts;
-  bool verbose = false;
-  bool print_only = false;
-  Flags flags =
-      Flags()
-          .OptionalFlag("-o", "Path to the output APK.", &options.output_path)
-          .OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
-          .OptionalFlag("-x", "Path to XML configuration file.", &config_path)
-          .OptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only)
-          .OptionalFlag(
-              "--target-densities",
-              "Comma separated list of the screen densities that the APK will be optimized for.\n"
-              "All the resources that would be unused on devices of the given densities will be \n"
-              "removed from the APK.",
-              &target_densities)
-          .OptionalFlag("--whitelist-path",
-                        "Path to the whitelist.cfg file containing whitelisted resources \n"
-                        "whose names should not be altered in final resource tables.",
-                        &whitelist_path)
-          .OptionalFlag("--resources-config-path",
-                        "Path to the resources.cfg file containing the list of resources and \n"
-                        "directives to each resource. \n"
-                        "Format: type/resource_name#[directive][,directive]",
-                        &resources_config_path)
-          .OptionalFlagList("-c",
-                            "Comma separated list of configurations to include. The default\n"
-                            "is all configurations.",
-                            &configs)
-          .OptionalFlagList("--split",
-                            "Split resources matching a set of configs out to a "
-                            "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
-                            "On Windows, use a semicolon ';' separator instead.",
-                            &split_args)
-          .OptionalFlagList("--keep-artifacts",
-                            "Comma separated list of artifacts to keep. If none are specified,\n"
-                            "all artifacts will be kept.",
-                            &kept_artifacts)
-          .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.table_flattener_options.use_sparse_entries)
-          .OptionalSwitch("--enable-resource-obfuscation",
-                          "Enables obfuscation of key string pool to single value",
-                          &options.table_flattener_options.collapse_key_stringpool)
-          .OptionalSwitch("-v", "Enables verbose logging", &verbose);
-
-  if (!flags.Parse("aapt2 optimize", args, &std::cerr)) {
-    return 1;
-  }
-
-  if (flags.GetArgs().size() != 1u) {
+int OptimizeCommand::Action(const std::vector<std::string>& args) {
+  if (args.size() != 1u) {
     std::cerr << "must have one APK as argument.\n\n";
-    flags.Usage("aapt2 optimize", &std::cerr);
+    Usage(&std::cerr);
     return 1;
   }
 
-  const std::string& apk_path = flags.GetArgs()[0];
-
-  context.SetVerbose(verbose);
+  const std::string& apk_path = args[0];
+  OptimizeContext context;
+  context.SetVerbose(verbose_);
   IDiagnostics* diag = context.GetDiagnostics();
 
-  if (config_path) {
-    std::string& path = config_path.value();
+  if (config_path_) {
+    std::string& path = config_path_.value();
     Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
     if (for_path) {
-      options.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
-      if (!options.apk_artifacts) {
+      options_.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
+      if (!options_.apk_artifacts) {
         diag->Error(DiagMessage() << "Failed to parse the output artifact list");
         return 1;
       }
@@ -463,28 +376,28 @@
       return 1;
     }
 
-    if (print_only) {
-      for (const OutputArtifact& artifact : options.apk_artifacts.value()) {
+    if (print_only_) {
+      for (const OutputArtifact& artifact : options_.apk_artifacts.value()) {
         std::cout << artifact.name << std::endl;
       }
       return 0;
     }
 
-    if (!kept_artifacts.empty()) {
-      for (const std::string& artifact_str : kept_artifacts) {
+    if (!kept_artifacts_.empty()) {
+      for (const std::string& artifact_str : kept_artifacts_) {
         for (const StringPiece& artifact : util::Tokenize(artifact_str, ',')) {
-          options.kept_artifacts.insert(artifact.to_string());
+          options_.kept_artifacts.insert(artifact.to_string());
         }
       }
     }
 
     // Since we know that we are going to process the APK (not just print targets), make sure we
     // have somewhere to write them to.
-    if (!options.output_dir) {
+    if (!options_.output_dir) {
       diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
       return 1;
     }
-  } else if (print_only) {
+  } else if (print_only_) {
     diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
     return 1;
   }
@@ -494,57 +407,57 @@
     return 1;
   }
 
-  if (target_densities) {
+  if (target_densities_) {
     // Parse the target screen densities.
-    for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) {
+    for (const StringPiece& config_str : util::Tokenize(target_densities_.value(), ',')) {
       Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
       if (!target_density) {
         return 1;
       }
-      options.table_splitter_options.preferred_densities.push_back(target_density.value());
+      options_.table_splitter_options.preferred_densities.push_back(target_density.value());
     }
   }
 
   std::unique_ptr<IConfigFilter> filter;
-  if (!configs.empty()) {
-    filter = ParseConfigFilterParameters(configs, diag);
+  if (!configs_.empty()) {
+    filter = ParseConfigFilterParameters(configs_, diag);
     if (filter == nullptr) {
       return 1;
     }
-    options.table_splitter_options.config_filter = filter.get();
+    options_.table_splitter_options.config_filter = filter.get();
   }
 
   // Parse the split parameters.
-  for (const std::string& split_arg : split_args) {
-    options.split_paths.emplace_back();
-    options.split_constraints.emplace_back();
-    if (!ParseSplitParameter(split_arg, diag, &options.split_paths.back(),
-                             &options.split_constraints.back())) {
+  for (const std::string& split_arg : split_args_) {
+    options_.split_paths.emplace_back();
+    options_.split_constraints.emplace_back();
+    if (!ParseSplitParameter(split_arg, diag, &options_.split_paths.back(),
+        &options_.split_constraints.back())) {
       return 1;
     }
   }
 
-  if (options.table_flattener_options.collapse_key_stringpool) {
-    if (whitelist_path) {
-      std::string& path = whitelist_path.value();
-      if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options)) {
+  if (options_.table_flattener_options.collapse_key_stringpool) {
+    if (whitelist_path_) {
+      std::string& path = whitelist_path_.value();
+      if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options_)) {
         return 1;
       }
     }
   }
 
-  if (resources_config_path) {
-    std::string& path = resources_config_path.value();
-    if (!ExtractConfig(path, &context, &options)) {
+  if (resources_config_path_) {
+    std::string& path = resources_config_path_.value();
+    if (!ExtractConfig(path, &context, &options_)) {
       return 1;
     }
   }
 
-  if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
+  if (!ExtractAppDataFromManifest(&context, apk.get(), &options_)) {
     return 1;
   }
 
-  OptimizeCommand cmd(&context, options);
+  Optimizer cmd(&context, options_);
   return cmd.Run(std::move(apk));
 }
 
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
new file mode 100644
index 0000000..43bc216
--- /dev/null
+++ b/tools/aapt2/cmd/Optimize.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef AAPT2_OPTIMIZE_H
+#define AAPT2_OPTIMIZE_H
+
+#include "AppInfo.h"
+#include "Command.h"
+#include "configuration/ConfigurationParser.h"
+#include "format/binary/TableFlattener.h"
+#include "split/TableSplitter.h"
+
+namespace aapt {
+
+struct OptimizeOptions {
+  friend class OptimizeCommand;
+
+  // Path to the output APK.
+  Maybe<std::string> output_path;
+  // Path to the output APK directory for splits.
+  Maybe<std::string> output_dir;
+
+  // Details of the app extracted from the AndroidManifest.xml
+  AppInfo app_info;
+
+  // Blacklist of unused resources that should be removed from the apk.
+  std::unordered_set<ResourceName> resources_blacklist;
+
+  // Split APK options.
+  TableSplitterOptions table_splitter_options;
+
+  // List of output split paths. These are in the same order as `split_constraints`.
+  std::vector<std::string> split_paths;
+
+  // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`.
+  std::vector<SplitConstraints> split_constraints;
+
+  TableFlattenerOptions table_flattener_options;
+
+  Maybe<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts;
+
+  // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
+  // are kept and will be written as output.
+  std::unordered_set<std::string> kept_artifacts;
+};
+
+class OptimizeCommand : public Command {
+ public:
+  explicit OptimizeCommand() : Command("optimize") {
+    SetDescription("Preforms resource optimizations on an apk.");
+    AddOptionalFlag("-o", "Path to the output APK.", &options_.output_path);
+    AddOptionalFlag("-d", "Path to the output directory (for splits).", &options_.output_dir);
+    AddOptionalFlag("-x", "Path to XML configuration file.", &config_path_);
+    AddOptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only_);
+    AddOptionalFlag(
+        "--target-densities",
+        "Comma separated list of the screen densities that the APK will be optimized for.\n"
+            "All the resources that would be unused on devices of the given densities will be \n"
+            "removed from the APK.",
+        &target_densities_);
+    AddOptionalFlag("--whitelist-path",
+        "Path to the whitelist.cfg file containing whitelisted resources \n"
+            "whose names should not be altered in final resource tables.",
+        &whitelist_path_);
+    AddOptionalFlag("--resources-config-path",
+        "Path to the resources.cfg file containing the list of resources and \n"
+            "directives to each resource. \n"
+            "Format: type/resource_name#[directive][,directive]",
+        &resources_config_path_);
+    AddOptionalFlagList("-c",
+        "Comma separated list of configurations to include. The default\n"
+            "is all configurations.",
+        &configs_);
+    AddOptionalFlagList("--split",
+        "Split resources matching a set of configs out to a "
+            "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
+            "On Windows, use a semicolon ';' separator instead.",
+        &split_args_);
+    AddOptionalFlagList("--keep-artifacts",
+        "Comma separated list of artifacts to keep. If none are specified,\n"
+            "all artifacts will be kept.",
+        &kept_artifacts_);
+    AddOptionalSwitch("--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_.table_flattener_options.use_sparse_entries);
+    AddOptionalSwitch("--enable-resource-obfuscation",
+        "Enables obfuscation of key string pool to single value",
+        &options_.table_flattener_options.collapse_key_stringpool);
+    AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  OptimizeOptions options_;
+
+  Maybe<std::string> config_path_;
+  Maybe<std::string> whitelist_path_;
+  Maybe<std::string> resources_config_path_;
+  Maybe<std::string> target_densities_;
+  std::vector<std::string> configs_;
+  std::vector<std::string> split_args_;
+  std::unordered_set<std::string> kept_artifacts_;
+  bool print_only_ = false;
+  bool verbose_ = false;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_OPTIMIZE_H
\ No newline at end of file
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
index ad5ad4c..ba9646f 100644
--- a/tools/aapt2/jni/aapt2_jni.cpp
+++ b/tools/aapt2/jni/aapt2_jni.cpp
@@ -25,15 +25,12 @@
 #include "ScopedUtfChars.h"
 
 #include "Diagnostics.h"
+#include "cmd/Compile.h"
+#include "cmd/Link.h"
 #include "util/Util.h"
 
 using android::StringPiece;
 
-namespace aapt {
-extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* iDiagnostics);
-extern int Link(const std::vector<StringPiece>& args, IDiagnostics* iDiagnostics);
-}
-
 /*
  * Converts a java List<String> into C++ vector<ScopedUtfChars>.
  */
@@ -126,7 +123,7 @@
       list_to_utfchars(env, arguments_obj);
   std::vector<StringPiece> compile_args = extract_pieces(compile_args_jni);
   JniDiagnostics diagnostics(env, diagnostics_obj);
-  return aapt::Compile(compile_args, &diagnostics);
+  return aapt::CompileCommand(&diagnostics).Execute(compile_args, &std::cerr);
 }
 
 JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv* env,
@@ -137,7 +134,7 @@
       list_to_utfchars(env, arguments_obj);
   std::vector<StringPiece> link_args = extract_pieces(link_args_jni);
   JniDiagnostics diagnostics(env, diagnostics_obj);
-  return aapt::Link(link_args, &diagnostics);
+  return aapt::LinkCommand(&diagnostics).Execute(link_args, &std::cerr);
 }
 
 JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping(