Merge "lshal --vintf to create a skeleton hal manifest." am: 73d5aef51e am: 86c2f1cb0c
am: 4ccb9b6fb2

Change-Id: I7b34fe8c1015c48818fa293e18522b9644ffb568
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index 5aab35a..39e0ba3 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -18,8 +18,10 @@
         "libbase",
         "libutils",
         "libhidlbase",
-        "android.hidl.manager@1.0",
         "libhidltransport",
+        "libhidl-gen-utils",
+        "libvintf",
+        "android.hidl.manager@1.0",
     ],
     srcs: [
         "Lshal.cpp"
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index ae5a8a0..e953ded 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -28,6 +28,9 @@
 #include <android-base/parseint.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
+#include <hidl-util/FQName.h>
+#include <vintf/HalManifest.h>
+#include <vintf/parse_xml.h>
 
 #include "Timeout.h"
 
@@ -58,12 +61,13 @@
     return os.str();
 }
 
-static std::pair<hidl_string, hidl_string> split(const hidl_string &s, char c) {
+template<typename String>
+static std::pair<String, String> splitFirst(const String &s, char c) {
     const char *pos = strchr(s.c_str(), c);
     if (pos == nullptr) {
         return {s, {}};
     }
-    return {hidl_string(s.c_str(), pos - s.c_str()), hidl_string(pos + 1)};
+    return {String(s.c_str(), pos - s.c_str()), String(pos + 1)};
 }
 
 static std::vector<std::string> split(const std::string &s, char c) {
@@ -81,6 +85,14 @@
     return components;
 }
 
+static void replaceAll(std::string *s, char from, char to) {
+    for (size_t i = 0; i < s->size(); ++i) {
+        if (s->at(i) == from) {
+            s->at(i) = to;
+        }
+    }
+}
+
 std::string getCmdline(pid_t pid) {
     std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
     std::string cmdline;
@@ -189,7 +201,78 @@
     mOut << std::endl;
 }
 
-void Lshal::dump() const {
+void Lshal::dumpVintf() const {
+    vintf::HalManifest manifest;
+    for (const TableEntry &entry : mTable) {
+
+        std::string fqInstanceName = entry.interfaceName;
+
+        if (entry.source == LIST_DLLIB) {
+            // Quick hack to work around *'s
+            replaceAll(&fqInstanceName, '*', 'D');
+        }
+        auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
+        FQName fqName(splittedFqInstanceName.first);
+        if (!fqName.isValid()) {
+            mErr << "Warning: '" << splittedFqInstanceName.first
+                 << "' is not a valid FQName." << std::endl;
+            continue;
+        }
+        // Strip out system libs.
+        // TODO(b/34772739): might want to add other framework HAL packages
+        if (fqName.inPackage("android.hidl")) {
+            continue;
+        }
+        std::string interfaceName =
+                entry.source == LIST_DLLIB ? "" : fqName.name();
+        std::string instanceName =
+                entry.source == LIST_DLLIB ? "" : splittedFqInstanceName.second;
+
+        vintf::Transport transport;
+        if (entry.transport == "hwbinder") {
+            transport = vintf::Transport::HWBINDER;
+        } else if (entry.transport == "passthrough") {
+            transport = vintf::Transport::PASSTHROUGH;
+        } else {
+            mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
+            continue;
+        }
+
+        vintf::ManifestHal *hal = manifest.getHal(fqName.package());
+        if (hal == nullptr) {
+            if (!manifest.add(vintf::ManifestHal{
+                .format = vintf::HalFormat::HIDL,
+                .name = fqName.package(),
+                .impl = {.implLevel = vintf::ImplLevel::GENERIC, .impl = ""},
+                .transport = transport
+            })) {
+                mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
+                continue;
+            }
+            hal = manifest.getHal(fqName.package());
+        }
+        if (hal == nullptr) {
+            mErr << "Warning: cannot get hal '" << fqInstanceName
+                 << "' after adding it" << std::endl;
+            continue;
+        }
+        vintf::Version version{fqName.getPackageMajorVersion(), fqName.getPackageMinorVersion()};
+        if (std::find(hal->versions.begin(), hal->versions.end(), version) == hal->versions.end()) {
+            hal->versions.push_back(version);
+        }
+        if (entry.source != LIST_DLLIB) {
+            auto it = hal->interfaces.find(interfaceName);
+            if (it == hal->interfaces.end()) {
+                hal->interfaces.insert({interfaceName, {interfaceName, {{instanceName}}}});
+            } else {
+                it->second.instances.insert(instanceName);
+            }
+        }
+    }
+    mOut << vintf::gHalManifestConverter(manifest);
+}
+
+void Lshal::dumpTable() const {
     mOut << "All services:" << std::endl;
     mOut << std::left;
     printLine("Interface", "Transport", "Server", "Server CMD", "PTR", "Clients", "Clients CMD");
@@ -204,6 +287,20 @@
     }
 }
 
+void Lshal::dump() {
+    if (mVintf) {
+        dumpVintf();
+        if (!!mFileOutput) {
+            mFileOutput.buf().close();
+            delete &mFileOutput.buf();
+            mFileOutput = nullptr;
+        }
+        mOut = std::cout;
+    } else {
+        dumpTable();
+    }
+}
+
 void Lshal::putEntry(TableEntry &&entry) {
     mTable.push_back(std::forward<TableEntry>(entry));
 }
@@ -219,7 +316,8 @@
                 .transport = "passthrough",
                 .serverPid = NO_PID,
                 .serverObjectAddress = NO_PTR,
-                .clientPids = {}
+                .clientPids = {},
+                .source = LIST_DLLIB
             });
         }
     });
@@ -244,7 +342,8 @@
                 .transport = "passthrough",
                 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
                 .serverObjectAddress = NO_PTR,
-                .clientPids = info.clientPids
+                .clientPids = info.clientPids,
+                .source = PTSERVICEMANAGER_REG_CLIENT
             });
         }
     });
@@ -279,7 +378,7 @@
     std::map<std::string, DebugInfo> allDebugInfos;
     std::map<pid_t, std::map<uint64_t, Pids>> allPids;
     for (const auto &fqInstanceName : fqInstanceNames) {
-        const auto pair = split(fqInstanceName, '/');
+        const auto pair = splitFirst(fqInstanceName, '/');
         const auto &serviceName = pair.first;
         const auto &instanceName = pair.second;
         auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
@@ -326,7 +425,8 @@
                 .transport = mode,
                 .serverPid = NO_PID,
                 .serverObjectAddress = NO_PTR,
-                .clientPids = {}
+                .clientPids = {},
+                .source = HWSERVICEMANAGER_LIST
             });
             continue;
         }
@@ -337,7 +437,8 @@
             .serverPid = info.pid,
             .serverObjectAddress = info.ptr,
             .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
-                    ? Pids{} : allPids[info.pid][info.ptr]
+                    ? Pids{} : allPids[info.pid][info.ptr],
+            .source = HWSERVICEMANAGER_LIST
         });
     }
     return status;
@@ -371,7 +472,7 @@
         << "           Dump all hals with default ordering and columns [-itpc]." << std::endl
         << "       lshal [--interface|-i] [--transport|-t]" << std::endl
         << "             [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]" << std::endl
-        << "             [--sort={interface|i|pid|p}]" << std::endl
+        << "             [--sort={interface|i|pid|p}] [--init-vintf[=path]]" << std::endl
         << "           -i, --interface: print the interface name column" << std::endl
         << "           -n, --instance: print the instance name column" << std::endl
         << "           -t, --transport: print the transport mode column" << std::endl
@@ -382,6 +483,8 @@
         << "           -m, --cmdline: print cmdline instead of PIDs" << std::endl
         << "           --sort=i, --sort=interface: sort by interface name" << std::endl
         << "           --sort=p, --sort=pid: sort by server pid" << std::endl
+        << "           --init-vintf=path: form a skeleton HAL manifest to specified file " << std::endl
+        << "                         (stdout if no file specified)" << std::endl
         << "       lshal [-h|--help]" << std::endl
         << "           -h, --help: show this help information." << std::endl;
 }
@@ -399,6 +502,7 @@
 
         // long options without short alternatives
         {"sort",      required_argument, 0, 's' },
+        {"init-vintf",optional_argument, 0, 'v' },
         { 0,          0,                 0,  0  }
     };
 
@@ -424,6 +528,17 @@
             }
             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;
+        }
         case 'i': {
             mSelectedColumns |= ENABLE_INTERFACE_NAME;
             break;
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
index ead99dc..1c30908 100644
--- a/cmds/lshal/Lshal.h
+++ b/cmds/lshal/Lshal.h
@@ -19,12 +19,13 @@
 
 #include <stdint.h>
 
-#include <iostream>
+#include <fstream>
 #include <string>
 #include <vector>
 
 #include <android/hidl/manager/1.0/IServiceManager.h>
 
+#include "NullableOStream.h"
 #include "TableEntry.h"
 
 namespace android {
@@ -38,6 +39,7 @@
     DUMP_BINDERIZED_ERROR                   = 1 << 3,
     DUMP_PASSTHROUGH_ERROR                  = 1 << 4,
     DUMP_ALL_LIBS_ERROR                     = 1 << 5,
+    IO_ERROR                                = 1 << 6,
 };
 using Status = unsigned int;
 
@@ -49,7 +51,7 @@
     Status parseArgs(int argc, char **argv);
     Status fetch();
     void postprocess();
-    void dump() const;
+    void dump();
     void usage() const;
     void putEntry(TableEntry &&entry);
     Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
@@ -57,6 +59,8 @@
     Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
     bool getReferencedPids(
         pid_t serverPid, std::map<uint64_t, Pids> *objects) const;
+    void dumpTable() const;
+    void dumpVintf() const;
     void printLine(
             const std::string &interfaceName,
             const std::string &transport, const std::string &server,
@@ -70,12 +74,14 @@
     void removeDeadProcesses(Pids *pids);
 
     Table mTable{};
-    std::ostream &mErr = std::cerr;
-    std::ostream &mOut = std::cout;
+    NullableOStream<std::ostream> mErr = std::cerr;
+    NullableOStream<std::ostream> mOut = std::cout;
+    NullableOStream<std::ofstream> mFileOutput = nullptr;
     TableEntryCompare mSortColumn = nullptr;
     TableEntrySelect mSelectedColumns = 0;
     // If true, cmdlines will be printed instead of pid.
-    bool mEnableCmdlines;
+    bool mEnableCmdlines = false;
+    bool mVintf = false;
     // If an entry does not exist, need to ask /proc/{pid}/cmdline to get it.
     // If an entry exist but is an empty string, process might have died.
     // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline.
diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h
new file mode 100644
index 0000000..ab37a04
--- /dev/null
+++ b/cmds/lshal/NullableOStream.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 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 FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
+
+#include <iostream>
+
+namespace android {
+namespace lshal {
+
+template<typename S>
+class NullableOStream {
+public:
+    NullableOStream(S &os) : mOs(&os) {}
+    NullableOStream(S *os) : mOs(os) {}
+    NullableOStream &operator=(S &os) {
+        mOs = &os;
+        return *this;
+    }
+    NullableOStream &operator=(S *os) {
+        mOs = os;
+        return *this;
+    }
+    template<typename Other>
+    NullableOStream &operator=(const NullableOStream<Other> &other) {
+        mOs = other.mOs;
+        return *this;
+    }
+
+    const NullableOStream &operator<<(std::ostream& (*pf)(std::ostream&)) const {
+        if (mOs) {
+            (*mOs) << pf;
+        }
+        return *this;
+    }
+    template<typename T>
+    const NullableOStream &operator<<(const T &rhs) const {
+        if (mOs) {
+            (*mOs) << rhs;
+        }
+        return *this;
+    }
+    S& buf() const {
+        return *mOs;
+    }
+    operator bool() const {
+        return mOs != nullptr;
+    }
+private:
+    template<typename>
+    friend class NullableOStream;
+
+    S *mOs = nullptr;
+};
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index 4ec3a0c..e55806e 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -28,6 +28,13 @@
 
 using Pids = std::vector<int32_t>;
 
+enum : unsigned int {
+    HWSERVICEMANAGER_LIST, // through defaultServiceManager()->list()
+    PTSERVICEMANAGER_REG_CLIENT, // through registerPassthroughClient
+    LIST_DLLIB, // through listing dynamic libraries
+};
+using TableEntrySource = unsigned int;
+
 struct TableEntry {
     std::string interfaceName;
     std::string transport;
@@ -36,6 +43,7 @@
     uint64_t serverObjectAddress;
     Pids clientPids;
     std::vector<std::string> clientCmdlines;
+    TableEntrySource source;
 
     static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
         return a.interfaceName < b.interfaceName;