lshal: Lshal register commands

Register commands to the controller class Lshal to autogenerate
help messages and select the correct Command.

Test: lshal_test
Change-Id: I846aef13d77bcee328c8410dc61f2e8b4c0e0d69
diff --git a/cmds/lshal/Command.h b/cmds/lshal/Command.h
index aff4975..4f128ab 100644
--- a/cmds/lshal/Command.h
+++ b/cmds/lshal/Command.h
@@ -35,6 +35,12 @@
 
     virtual void usage() const = 0;
 
+    // e.g. "list"
+    virtual std::string getName() const = 0;
+
+    // e.g. "list HALs"
+    virtual std::string getSimpleDescription() const = 0;
+
 protected:
     Lshal& mLshal;
 };
diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp
index d21764c..622f866 100644
--- a/cmds/lshal/DebugCommand.cpp
+++ b/cmds/lshal/DebugCommand.cpp
@@ -21,6 +21,14 @@
 namespace android {
 namespace lshal {
 
+std::string DebugCommand::getName() const {
+    return "debug";
+}
+
+std::string DebugCommand::getSimpleDescription() const {
+    return "Debug a specified HAL.";
+}
+
 Status DebugCommand::parseArgs(const Arg &arg) {
     if (optind >= arg.argc) {
         return USAGE;
diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h
index 6b70713..9b91084 100644
--- a/cmds/lshal/DebugCommand.h
+++ b/cmds/lshal/DebugCommand.h
@@ -35,6 +35,8 @@
     ~DebugCommand() = default;
     Status main(const Arg &arg) override;
     void usage() const override;
+    std::string getSimpleDescription() const override;
+    std::string getName() const override;
 private:
     Status parseArgs(const Arg &arg);
 
diff --git a/cmds/lshal/HelpCommand.cpp b/cmds/lshal/HelpCommand.cpp
index b393f05..6773ace 100644
--- a/cmds/lshal/HelpCommand.cpp
+++ b/cmds/lshal/HelpCommand.cpp
@@ -21,6 +21,14 @@
 namespace android {
 namespace lshal {
 
+std::string HelpCommand::GetName() {
+    return "help";
+}
+
+std::string HelpCommand::getSimpleDescription() const {
+    return "Print help message.";
+}
+
 Status HelpCommand::main(const Arg &arg) {
     if (optind >= arg.argc) {
         // `lshal help` prints global usage.
@@ -49,18 +57,17 @@
 }
 
 void HelpCommand::usage() const {
-    static const std::string help =
-            "help:\n"
-            "    lshal -h\n"
-            "    lshal --help\n"
-            "    lshal help\n"
-            "        Print this help message\n"
-            "    lshal help list\n"
-            "        Print help message for list\n"
-            "    lshal help debug\n"
-            "        Print help message for debug\n";
+    mLshal.err()
+            << "help:" << std::endl
+            << "    lshal -h" << std::endl
+            << "    lshal --help" << std::endl
+            << "    lshal help" << std::endl
+            << "        Print this help message" << std::endl;
+    mLshal.forEachCommand([&](const Command* e) {
+        mLshal.err() << "    lshal help " << e->getName() << std::endl
+                     << "        Print help message for " << e->getName() << std::endl;
+    });
 
-    mLshal.err() << help;
 }
 
 }  // namespace lshal
diff --git a/cmds/lshal/HelpCommand.h b/cmds/lshal/HelpCommand.h
index 3cc0d80..cc709f8 100644
--- a/cmds/lshal/HelpCommand.h
+++ b/cmds/lshal/HelpCommand.h
@@ -35,6 +35,9 @@
     ~HelpCommand() = default;
     Status main(const Arg &arg) override;
     void usage() const override;
+    std::string getSimpleDescription() const override;
+    std::string getName() const override { return GetName(); }
+    static std::string GetName();
     Status usageOfCommand(const std::string& c) const;
 };
 
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 71ac25b..c7a41a3 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -52,6 +52,13 @@
     return mLshal.err();
 }
 
+std::string ListCommand::GetName() {
+    return "list";
+}
+std::string ListCommand::getSimpleDescription() const {
+    return "List HALs.";
+}
+
 std::string ListCommand::parseCmdline(pid_t pid) const {
     std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
     std::string cmdline;
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index 6defb0a..346af28 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -49,6 +49,10 @@
     virtual ~ListCommand() = default;
     Status main(const Arg &arg) override;
     void usage() const override;
+    std::string getSimpleDescription() const override;
+    std::string getName() const override { return GetName(); }
+
+    static std::string GetName();
 protected:
     Status parseArgs(const Arg &arg);
     Status fetch();
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index a08a02c..c6f28ac 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -34,9 +34,8 @@
 using ::android::hidl::manager::V1_0::IServiceManager;
 
 Lshal::Lshal()
-    : mOut(std::cout), mErr(std::cerr),
-      mServiceManager(::android::hardware::defaultServiceManager()),
-      mPassthroughManager(::android::hardware::getPassthroughServiceManager()) {
+    : Lshal(std::cout, std::cerr, ::android::hardware::defaultServiceManager(),
+            ::android::hardware::getPassthroughServiceManager()) {
 }
 
 Lshal::Lshal(std::ostream &out, std::ostream &err,
@@ -46,25 +45,39 @@
       mServiceManager(serviceManager),
       mPassthroughManager(passthroughManager) {
 
+    mRegisteredCommands.push_back({std::make_unique<ListCommand>(*this)});
+    mRegisteredCommands.push_back({std::make_unique<DebugCommand>(*this)});
+    mRegisteredCommands.push_back({std::make_unique<HelpCommand>(*this)});
+}
+
+void Lshal::forEachCommand(const std::function<void(const Command* c)>& f) const {
+    for (const auto& e : mRegisteredCommands) f(e.get());
 }
 
 void Lshal::usage() {
-    static const std::string helpSummary =
-            "lshal: List and debug HALs.\n"
-            "\n"
-            "commands:\n"
-            "    help            Print help message\n"
-            "    list            list HALs\n"
-            "    debug           debug a specified HAL\n"
-            "\n"
-            "If no command is specified, `list` is the default.\n";
+    err() << "lshal: List and debug HALs." << std::endl << std::endl
+          << "commands:" << std::endl;
 
-    err() << helpSummary << "\n";
-    selectCommand("list")->usage();
-    err() << "\n";
-    selectCommand("debug")->usage();
-    err() << "\n";
-    selectCommand("help")->usage();
+    size_t nameMaxLength = 0;
+    forEachCommand([&](const Command* e) {
+        nameMaxLength = std::max(nameMaxLength, e->getName().length());
+    });
+    bool first = true;
+    forEachCommand([&](const Command* e) {
+        if (!first) err() << std::endl;
+        first = false;
+        err() << "    " << std::left << std::setw(nameMaxLength + 8) << e->getName()
+              << e->getSimpleDescription();
+    });
+    err() << std::endl << "If no command is specified, `" << ListCommand::GetName()
+          << "` is the default." << std::endl << std::endl;
+
+    first = true;
+    forEachCommand([&](const Command* e) {
+        if (!first) err() << std::endl;
+        first = false;
+        e->usage();
+    });
 }
 
 // A unique_ptr type using a custom deleter function.
@@ -135,21 +148,20 @@
 }
 
 Status Lshal::parseArgs(const Arg &arg) {
-    static std::set<std::string> sAllCommands{"list", "debug", "help"};
     optind = 1;
     if (optind >= arg.argc) {
         // no options at all.
         return OK;
     }
     mCommand = arg.argv[optind];
-    if (sAllCommands.find(mCommand) != sAllCommands.end()) {
+    if (selectCommand(mCommand) != nullptr) {
         ++optind;
         return OK; // mCommand is set correctly
     }
 
     if (mCommand.size() > 0 && mCommand[0] == '-') {
         // first argument is an option, set command to "" (which is recognized as "list")
-        mCommand = "";
+        mCommand.clear();
         return OK;
     }
 
@@ -164,20 +176,14 @@
     }
 }
 
-std::unique_ptr<HelpCommand> Lshal::selectHelpCommand() {
-    return std::make_unique<HelpCommand>(*this);
-}
-
-std::unique_ptr<Command> Lshal::selectCommand(const std::string& command) {
-    // Default command is list
-    if (command == "list" || command == "") {
-        return std::make_unique<ListCommand>(*this);
+Command* Lshal::selectCommand(const std::string& command) const {
+    if (command.empty()) {
+        return selectCommand(ListCommand::GetName());
     }
-    if (command == "debug") {
-        return std::make_unique<DebugCommand>(*this);
-    }
-    if (command == "help") {
-        return selectHelpCommand();
+    for (const auto& e : mRegisteredCommands) {
+        if (e->getName() == command) {
+            return e.get();
+        }
     }
     return nullptr;
 }
@@ -202,7 +208,8 @@
         // bad options. Run `lshal help ${mCommand}` instead.
         // For example, `lshal --unknown-option` becomes `lshal help` (prints global help)
         // and `lshal list --unknown-option` becomes `lshal help list`
-        return selectHelpCommand()->usageOfCommand(mCommand);
+        auto&& help = selectCommand(HelpCommand::GetName());
+        return static_cast<HelpCommand*>(help)->usageOfCommand(mCommand);
     }
 
     return status;
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
index 89b38db..9f8eeaa 100644
--- a/cmds/lshal/Lshal.h
+++ b/cmds/lshal/Lshal.h
@@ -54,11 +54,12 @@
             std::ostream &out,
             NullableOStream<std::ostream> err) const;
 
-    std::unique_ptr<Command> selectCommand(const std::string& command);
+    Command* selectCommand(const std::string& command) const;
+
+    void forEachCommand(const std::function<void(const Command* c)>& f) const;
 
 private:
     Status parseArgs(const Arg &arg);
-    std::unique_ptr<HelpCommand> selectHelpCommand();
 
     std::string mCommand;
     Arg mCmdArgs;
@@ -68,6 +69,8 @@
     sp<hidl::manager::V1_0::IServiceManager> mServiceManager;
     sp<hidl::manager::V1_0::IServiceManager> mPassthroughManager;
 
+    std::vector<std::unique_ptr<Command>> mRegisteredCommands;
+
     DISALLOW_COPY_AND_ASSIGN(Lshal);
 };