lshal: fix file streams for testing.

* If a file is specified for list, the file is opened and
  closed within the ListCommand::dump() function.

* Add a missing break in ListCommand::parseArgs, case
  for '--init-vintf'

Test: lshal_test
Test: lshal --init-vintf
Test: lshal --init-vintf=/data/data/a.xml ; adb shell cat \
      /data/data/a.xml
Test: lshal -d
Test: lshal --debug=/data/data/d.txt ; adb shell cat \
      /data/data/d.txt

Change-Id: I1acee5878638d25257a13244713dc416e9d8d6b2
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 4de84b7..99048af 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -212,6 +212,18 @@
             }
         }
     }
+
+    mServicesTable.setDescription(
+            "All binderized services (registered services through hwservicemanager)");
+    mPassthroughRefTable.setDescription(
+            "All interfaces that getService() has ever return as a passthrough interface;\n"
+            "PIDs / processes shown below might be inaccurate because the process\n"
+            "might have relinquished the interface or might have died.\n"
+            "The Server / Server CMD column can be ignored.\n"
+            "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
+            "the library and successfully fetched the passthrough implementation.");
+    mImplementationsTable.setDescription(
+            "All available passthrough implementations (all -impl.so files)");
 }
 
 static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
@@ -224,9 +236,9 @@
     return false;
 }
 
-void ListCommand::dumpVintf() const {
+void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const {
     using vintf::operator|=;
-    mOut << "<!-- " << std::endl
+    out << "<!-- " << std::endl
          << "    This is a skeleton device manifest. Notes: " << std::endl
          << "    1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
          << "    2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
@@ -334,7 +346,7 @@
             }
         }
     });
-    mOut << vintf::gHalManifestConverter(manifest);
+    out << vintf::gHalManifestConverter(manifest);
 }
 
 static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
@@ -349,26 +361,14 @@
     }
 }
 
-void ListCommand::dumpTable() {
+void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
     if (mNeat) {
         MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable})
-            .createTextTable().dump(mOut.buf());
+            .createTextTable().dump(out.buf());
         return;
     }
 
-    mServicesTable.setDescription(
-            "All binderized services (registered services through hwservicemanager)");
-    mPassthroughRefTable.setDescription(
-            "All interfaces that getService() has ever return as a passthrough interface;\n"
-            "PIDs / processes shown below might be inaccurate because the process\n"
-            "might have relinquished the interface or might have died.\n"
-            "The Server / Server CMD column can be ignored.\n"
-            "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
-            "the library and successfully fetched the passthrough implementation.");
-    mImplementationsTable.setDescription(
-            "All available passthrough implementations (all -impl.so files)");
-
-    forEachTable([this](const Table &table) {
+    forEachTable([this, &out](const Table &table) {
 
         // We're only interested in dumping debug info for already
         // instantiated services. There's little value in dumping the
@@ -377,30 +377,38 @@
         std::function<std::string(const std::string&)> emitDebugInfo = nullptr;
         if (mEmitDebugInfo && &table == &mServicesTable) {
             emitDebugInfo = [this](const auto& iName) {
-                std::stringstream out;
+                std::stringstream ss;
                 auto pair = splitFirst(iName, '/');
-                mLshal.emitDebugInfo(pair.first, pair.second, {}, out,
+                mLshal.emitDebugInfo(pair.first, pair.second, {}, ss,
                                      NullableOStream<std::ostream>(nullptr));
-                return out.str();
+                return ss.str();
             };
         }
-        table.createTextTable(mNeat, emitDebugInfo).dump(mOut.buf());
-        mOut << std::endl;
+        table.createTextTable(mNeat, emitDebugInfo).dump(out.buf());
+        out << std::endl;
     });
 }
 
-void ListCommand::dump() {
-    if (mVintf) {
-        dumpVintf();
-        if (!!mFileOutput) {
-            mFileOutput.buf().close();
-            delete &mFileOutput.buf();
-            mFileOutput = nullptr;
-        }
-        mOut = std::cout;
-    } else {
-        dumpTable();
+Status ListCommand::dump() {
+    auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable;
+
+    if (mFileOutputPath.empty()) {
+        (*this.*dump)(out());
+        return OK;
     }
+
+    std::ofstream fileOutput(mFileOutputPath);
+    if (!fileOutput.is_open()) {
+        err() << "Could not open file '" << mFileOutputPath << "'." << std::endl;
+        return IO_ERROR;
+    }
+    chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL);
+
+    (*this.*dump)(NullableOStream<std::ostream>(fileOutput));
+
+    fileOutput.flush();
+    fileOutput.close();
+    return OK;
 }
 
 void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
@@ -647,15 +655,9 @@
             break;
         }
         case 'v': {
-            if (optarg) {
-                mFileOutput = new std::ofstream{optarg};
-                mOut = mFileOutput;
-                if (!mFileOutput.buf().is_open()) {
-                    mErr << "Could not open file '" << optarg << "'." << std::endl;
-                    return IO_ERROR;
-                }
-            }
             mVintf = true;
+            if (optarg) mFileOutputPath = optarg;
+            break;
         }
         case 'i': {
             selectedColumns.push_back(TableColumnType::INTERFACE_NAME);
@@ -691,16 +693,7 @@
         }
         case 'd': {
             mEmitDebugInfo = true;
-
-            if (optarg) {
-                mFileOutput = new std::ofstream{optarg};
-                mOut = mFileOutput;
-                if (!mFileOutput.buf().is_open()) {
-                    mErr << "Could not open file '" << optarg << "'." << std::endl;
-                    return IO_ERROR;
-                }
-                chown(optarg, AID_SHELL, AID_SHELL);
-            }
+            if (optarg) mFileOutputPath = optarg;
             break;
         }
         case 'n': {
@@ -756,7 +749,7 @@
     }
     status = fetch();
     postprocess();
-    dump();
+    status |= dump();
     return status;
 }