Merge changes from topic 'lshal'

* changes:
  lshal: Add option to print cmd lines instead of pids.
  lshal: Add timeout for IPC calls.
  lshal: Allow selecting columns and sorting by column.
  lshal: Refactor lshal to use an Lshal class; combined instance column with interface column
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index c380598..5aab35a 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -22,6 +22,6 @@
         "libhidltransport",
     ],
     srcs: [
-        "lshal.cpp"
+        "Lshal.cpp"
     ],
 }
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
new file mode 100644
index 0000000..ce058c8
--- /dev/null
+++ b/cmds/lshal/Lshal.cpp
@@ -0,0 +1,477 @@
+/*
+ * 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.
+ */
+
+#include "Lshal.h"
+
+#include <getopt.h>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <regex>
+
+#include <android-base/parseint.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
+
+#include "Timeout.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hidl::manager::V1_0::IServiceManager;
+
+namespace android {
+namespace lshal {
+
+template <typename A>
+std::string join(const A &components, const std::string &separator) {
+    std::stringstream out;
+    bool first = true;
+    for (const auto &component : components) {
+        if (!first) {
+            out << separator;
+        }
+        out << component;
+
+        first = false;
+    }
+    return out.str();
+}
+
+static std::string toHexString(uint64_t t) {
+    std::ostringstream os;
+    os << std::hex << std::setfill('0') << std::setw(16) << t;
+    return os.str();
+}
+
+static std::pair<hidl_string, hidl_string> split(const hidl_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)};
+}
+
+static std::vector<std::string> split(const std::string &s, char c) {
+    std::vector<std::string> components{};
+    size_t startPos = 0;
+    size_t matchPos;
+    while ((matchPos = s.find(c, startPos)) != std::string::npos) {
+        components.push_back(s.substr(startPos, matchPos - startPos));
+        startPos = matchPos + 1;
+    }
+
+    if (startPos <= s.length()) {
+        components.push_back(s.substr(startPos));
+    }
+    return components;
+}
+
+std::string getCmdline(pid_t pid) {
+    std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
+    std::string cmdline;
+    if (!ifs.is_open()) {
+        return "";
+    }
+    ifs >> cmdline;
+    return cmdline;
+}
+
+const std::string &Lshal::getCmdline(pid_t pid) {
+    auto pair = mCmdlines.find(pid);
+    if (pair != mCmdlines.end()) {
+        return pair->second;
+    }
+    mCmdlines[pid] = ::android::lshal::getCmdline(pid);
+    return mCmdlines[pid];
+}
+
+void Lshal::removeDeadProcesses(Pids *pids) {
+    static const pid_t myPid = getpid();
+    std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
+        return pid == myPid || this->getCmdline(pid).empty();
+    });
+}
+
+bool Lshal::getReferencedPids(
+        pid_t serverPid, std::map<uint64_t, Pids> *objects) const {
+
+    std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
+    if (!ifs.is_open()) {
+        return false;
+    }
+
+    static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
+
+    std::string line;
+    std::smatch match;
+    while(getline(ifs, line)) {
+        if (!std::regex_search(line, match, prefix)) {
+            // the line doesn't start with the correct prefix
+            continue;
+        }
+        std::string ptrString = "0x" + match.str(2); // use number after c
+        uint64_t ptr;
+        if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
+            // Should not reach here, but just be tolerant.
+            mErr << "Could not parse number " << ptrString << std::endl;
+            continue;
+        }
+        const std::string proc = " proc ";
+        auto pos = line.rfind(proc);
+        if (pos != std::string::npos) {
+            for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
+                int32_t pid;
+                if (!::android::base::ParseInt(pidStr, &pid)) {
+                    mErr << "Could not parse number " << pidStr << std::endl;
+                    continue;
+                }
+                (*objects)[ptr].push_back(pid);
+            }
+        }
+    }
+    return true;
+}
+
+void Lshal::postprocess() {
+    if (mSortColumn) {
+        std::sort(mTable.begin(), mTable.end(), mSortColumn);
+    }
+    for (TableEntry &entry : mTable) {
+        entry.serverCmdline = getCmdline(entry.serverPid);
+        removeDeadProcesses(&entry.clientPids);
+        for (auto pid : entry.clientPids) {
+            entry.clientCmdlines.push_back(this->getCmdline(pid));
+        }
+    }
+}
+
+void Lshal::printLine(
+        const std::string &interfaceName,
+        const std::string &transport, const std::string &server,
+        const std::string &serverCmdline,
+        const std::string &address, const std::string &clients,
+        const std::string &clientCmdlines) const {
+    if (mSelectedColumns & ENABLE_INTERFACE_NAME)
+        mOut << std::setw(80) << interfaceName << "\t";
+    if (mSelectedColumns & ENABLE_TRANSPORT)
+        mOut << std::setw(10) << transport << "\t";
+    if (mSelectedColumns & ENABLE_SERVER_PID) {
+        if (mEnableCmdlines) {
+            mOut << std::setw(15) << serverCmdline << "\t";
+        } else {
+            mOut << std::setw(5)  << server << "\t";
+        }
+    }
+    if (mSelectedColumns & ENABLE_SERVER_ADDR)
+        mOut << std::setw(16) << address << "\t";
+    if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
+        if (mEnableCmdlines) {
+            mOut << std::setw(0)  << clientCmdlines;
+        } else {
+            mOut << std::setw(0)  << clients;
+        }
+    }
+    mOut << std::endl;
+}
+
+void Lshal::dump() const {
+    mOut << "All services:" << std::endl;
+    mOut << std::left;
+    printLine("Interface", "Transport", "Server", "Server CMD", "PTR", "Clients", "Clients CMD");
+    for (const auto &entry : mTable) {
+        printLine(entry.interfaceName,
+                entry.transport,
+                entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
+                entry.serverCmdline,
+                entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
+                join(entry.clientPids, " "),
+                join(entry.clientCmdlines, ";"));
+    }
+}
+
+void Lshal::putEntry(TableEntry &&entry) {
+    mTable.push_back(std::forward<TableEntry>(entry));
+}
+
+Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) {
+    using namespace ::android::hardware;
+    using namespace ::android::hidl::manager::V1_0;
+    using namespace ::android::hidl::base::V1_0;
+    auto ret = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
+        for (const auto &fqInstanceName : fqInstanceNames) {
+            putEntry({
+                .interfaceName = fqInstanceName,
+                .transport = "passthrough",
+                .serverPid = NO_PID,
+                .serverObjectAddress = NO_PTR,
+                .clientPids = {}
+            });
+        }
+    });
+    if (!ret.isOk()) {
+        mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
+             << ret.description() << std::endl;
+        return DUMP_ALL_LIBS_ERROR;
+    }
+    return OK;
+}
+
+Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) {
+    using namespace ::android::hardware;
+    using namespace ::android::hidl::manager::V1_0;
+    using namespace ::android::hidl::base::V1_0;
+    auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
+        for (const auto &info : infos) {
+            putEntry({
+                .interfaceName =
+                        std::string{info.interfaceName.c_str()} + "/" +
+                        std::string{info.instanceName.c_str()},
+                .transport = "passthrough",
+                .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
+                .serverObjectAddress = NO_PTR,
+                .clientPids = info.clientPids
+            });
+        }
+    });
+    if (!ret.isOk()) {
+        mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
+             << ret.description() << std::endl;
+        return DUMP_PASSTHROUGH_ERROR;
+    }
+    return OK;
+}
+
+Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
+    using namespace ::std;
+    using namespace ::android::hardware;
+    using namespace ::android::hidl::manager::V1_0;
+    using namespace ::android::hidl::base::V1_0;
+    const std::string mode = "hwbinder";
+    Status status = OK;
+    auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
+        // server pid, .ptr value of binder object, child pids
+        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 &serviceName = pair.first;
+            const auto &instanceName = pair.second;
+            auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
+            if (!getRet.isOk()) {
+                mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+                     << "cannot be fetched from service manager:"
+                     << getRet.description() << std::endl;
+                status |= DUMP_BINDERIZED_ERROR;
+                continue;
+            }
+            sp<IBase> service = getRet;
+            if (service == nullptr) {
+                mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+                     << "cannot be fetched from service manager (null)";
+                status |= DUMP_BINDERIZED_ERROR;
+                continue;
+            }
+            auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
+                allDebugInfos[fqInstanceName] = debugInfo;
+                if (debugInfo.pid >= 0) {
+                    allPids[static_cast<pid_t>(debugInfo.pid)].clear();
+                }
+            });
+            if (!debugRet.isOk()) {
+                mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+                     << "debugging information cannot be retrieved:"
+                     << debugRet.description() << std::endl;
+                status |= DUMP_BINDERIZED_ERROR;
+            }
+        }
+        for (auto &pair : allPids) {
+            pid_t serverPid = pair.first;
+            if (!getReferencedPids(serverPid, &allPids[serverPid])) {
+                mErr << "Warning: no information for PID " << serverPid
+                          << ", are you root?" << std::endl;
+                status |= DUMP_BINDERIZED_ERROR;
+            }
+        }
+        for (const auto &fqInstanceName : fqInstanceNames) {
+            auto it = allDebugInfos.find(fqInstanceName);
+            if (it == allDebugInfos.end()) {
+                putEntry({
+                    .interfaceName = fqInstanceName,
+                    .transport = mode,
+                    .serverPid = NO_PID,
+                    .serverObjectAddress = NO_PTR,
+                    .clientPids = {}
+                });
+                continue;
+            }
+            const DebugInfo &info = it->second;
+            putEntry({
+                .interfaceName = fqInstanceName,
+                .transport = mode,
+                .serverPid = info.pid,
+                .serverObjectAddress = info.ptr,
+                .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
+                        ? Pids{} : allPids[info.pid][info.ptr]
+            });
+        }
+
+    });
+    if (!listRet.isOk()) {
+        mErr << "Error: Failed to list services for " << mode << ": "
+             << listRet.description() << std::endl;
+        status |= DUMP_BINDERIZED_ERROR;
+    }
+    return status;
+}
+
+Status Lshal::fetch() {
+    Status status = OK;
+    auto bManager = ::android::hardware::defaultServiceManager();
+    if (bManager == nullptr) {
+        mErr << "Failed to get defaultServiceManager()!" << std::endl;
+        status |= NO_BINDERIZED_MANAGER;
+    } else {
+        status |= fetchBinderized(bManager);
+        // Passthrough PIDs are registered to the binderized manager as well.
+        status |= fetchPassthrough(bManager);
+    }
+
+    auto pManager = ::android::hardware::getPassthroughServiceManager();
+    if (pManager == nullptr) {
+        mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
+        status |= NO_PASSTHROUGH_MANAGER;
+    } else {
+        status |= fetchAllLibraries(pManager);
+    }
+    return status;
+}
+
+void Lshal::usage() const {
+    mErr
+        << "usage: lshal" << std::endl
+        << "           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
+        << "           -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
+        << "           -p, --pid: print the server PID, or server cmdline if -m is set" << std::endl
+        << "           -a, --address: print the server object address column" << std::endl
+        << "           -c, --clients: print the client PIDs, or client cmdlines if -m is set"
+                                                                              << std::endl
+        << "           -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
+        << "       lshal [-h|--help]" << std::endl
+        << "           -h, --help: show this help information." << std::endl;
+}
+
+Status Lshal::parseArgs(int argc, char **argv) {
+    static struct option longOptions[] = {
+        // long options with short alternatives
+        {"help",      no_argument,       0, 'h' },
+        {"interface", no_argument,       0, 'i' },
+        {"transport", no_argument,       0, 't' },
+        {"pid",       no_argument,       0, 'p' },
+        {"address",   no_argument,       0, 'a' },
+        {"clients",   no_argument,       0, 'c' },
+        {"cmdline",   no_argument,       0, 'm' },
+
+        // long options without short alternatives
+        {"sort",      required_argument, 0, 's' },
+        { 0,          0,                 0,  0  }
+    };
+
+    int optionIndex;
+    int c;
+    optind = 1;
+    for (;;) {
+        // using getopt_long in case we want to add other options in the future
+        c = getopt_long(argc, argv, "hitpacm", longOptions, &optionIndex);
+        if (c == -1) {
+            break;
+        }
+        switch (c) {
+        case 's': {
+            if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
+                mSortColumn = TableEntry::sortByInterfaceName;
+            } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
+                mSortColumn = TableEntry::sortByServerPid;
+            } else {
+                mErr << "Unrecognized sorting column: " << optarg << std::endl;
+                usage();
+                return USAGE;
+            }
+            break;
+        }
+        case 'i': {
+            mSelectedColumns |= ENABLE_INTERFACE_NAME;
+            break;
+        }
+        case 't': {
+            mSelectedColumns |= ENABLE_TRANSPORT;
+            break;
+        }
+        case 'p': {
+            mSelectedColumns |= ENABLE_SERVER_PID;
+            break;
+        }
+        case 'a': {
+            mSelectedColumns |= ENABLE_SERVER_ADDR;
+            break;
+        }
+        case 'c': {
+            mSelectedColumns |= ENABLE_CLIENT_PIDS;
+            break;
+        }
+        case 'm': {
+            mEnableCmdlines = true;
+            break;
+        }
+        case 'h': // falls through
+        default: // see unrecognized options
+            usage();
+            return USAGE;
+        }
+    }
+
+    if (mSelectedColumns == 0) {
+        mSelectedColumns = ENABLE_INTERFACE_NAME
+                | ENABLE_TRANSPORT | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
+    }
+    return OK;
+}
+
+int Lshal::main(int argc, char **argv) {
+    Status status = parseArgs(argc, argv);
+    if (status != OK) {
+        return status;
+    }
+    status = fetch();
+    postprocess();
+    dump();
+    return status;
+}
+
+}  // namespace lshal
+}  // namespace android
+
+int main(int argc, char **argv) {
+    return ::android::lshal::Lshal{}.main(argc, argv);
+}
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
new file mode 100644
index 0000000..ead99dc
--- /dev/null
+++ b/cmds/lshal/Lshal.h
@@ -0,0 +1,89 @@
+/*
+ * 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_LSHAL_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_
+
+#include <stdint.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android/hidl/manager/1.0/IServiceManager.h>
+
+#include "TableEntry.h"
+
+namespace android {
+namespace lshal {
+
+enum : unsigned int {
+    OK                                      = 0,
+    USAGE                                   = 1 << 0,
+    NO_BINDERIZED_MANAGER                   = 1 << 1,
+    NO_PASSTHROUGH_MANAGER                  = 1 << 2,
+    DUMP_BINDERIZED_ERROR                   = 1 << 3,
+    DUMP_PASSTHROUGH_ERROR                  = 1 << 4,
+    DUMP_ALL_LIBS_ERROR                     = 1 << 5,
+};
+using Status = unsigned int;
+
+class Lshal {
+public:
+    int main(int argc, char **argv);
+
+private:
+    Status parseArgs(int argc, char **argv);
+    Status fetch();
+    void postprocess();
+    void dump() const;
+    void usage() const;
+    void putEntry(TableEntry &&entry);
+    Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
+    Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
+    Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
+    bool getReferencedPids(
+        pid_t serverPid, std::map<uint64_t, Pids> *objects) const;
+    void printLine(
+            const std::string &interfaceName,
+            const std::string &transport, const std::string &server,
+            const std::string &serverCmdline,
+            const std::string &address, const std::string &clients,
+            const std::string &clientCmdlines) const ;
+    // Return /proc/{pid}/cmdline if it exists, else empty string.
+    const std::string &getCmdline(pid_t pid);
+    // Call getCmdline on all pid in pids. If it returns empty string, the process might
+    // have died, and the pid is removed from pids.
+    void removeDeadProcesses(Pids *pids);
+
+    Table mTable{};
+    std::ostream &mErr = std::cerr;
+    std::ostream &mOut = std::cout;
+    TableEntryCompare mSortColumn = nullptr;
+    TableEntrySelect mSelectedColumns = 0;
+    // If true, cmdlines will be printed instead of pid.
+    bool mEnableCmdlines;
+    // 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.
+    std::map<pid_t, std::string> mCmdlines;
+};
+
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
new file mode 100644
index 0000000..4ec3a0c
--- /dev/null
+++ b/cmds/lshal/TableEntry.h
@@ -0,0 +1,69 @@
+/*
+ * 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_TABLE_ENTRY_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <iostream>
+
+namespace android {
+namespace lshal {
+
+using Pids = std::vector<int32_t>;
+
+struct TableEntry {
+    std::string interfaceName;
+    std::string transport;
+    int32_t serverPid;
+    std::string serverCmdline;
+    uint64_t serverObjectAddress;
+    Pids clientPids;
+    std::vector<std::string> clientCmdlines;
+
+    static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
+        return a.interfaceName < b.interfaceName;
+    };
+    static bool sortByServerPid(const TableEntry &a, const TableEntry &b) {
+        return a.serverPid < b.serverPid;
+    };
+};
+
+using Table = std::vector<TableEntry>;
+using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>;
+
+enum : unsigned int {
+    ENABLE_INTERFACE_NAME = 1 << 0,
+    ENABLE_TRANSPORT      = 1 << 1,
+    ENABLE_SERVER_PID     = 1 << 2,
+    ENABLE_SERVER_ADDR    = 1 << 3,
+    ENABLE_CLIENT_PIDS    = 1 << 4
+};
+
+using TableEntrySelect = unsigned int;
+
+enum {
+    NO_PID = -1,
+    NO_PTR = 0
+};
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
new file mode 100644
index 0000000..bf883c0
--- /dev/null
+++ b/cmds/lshal/Timeout.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#include <condition_variable>
+#include <chrono>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include <hidl/Status.h>
+
+namespace android {
+namespace lshal {
+
+static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500};
+
+class BackgroundTaskState {
+public:
+    BackgroundTaskState(){}
+    void notify() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mFinished = true;
+        lock.unlock();
+        mCondVar.notify_all();
+    }
+    template<class C, class D>
+    bool wait(std::chrono::time_point<C, D> end) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
+        return mFinished;
+    }
+private:
+    std::mutex mMutex;
+    std::condition_variable mCondVar;
+    bool mFinished = false;
+};
+
+template<class R, class P>
+bool timeout(std::chrono::duration<R, P> delay, const std::function<void(void)> &func) {
+    auto now = std::chrono::system_clock::now();
+    BackgroundTaskState state{};
+    std::thread t([&state, &func] {
+        func();
+        state.notify();
+    });
+    t.detach();
+    bool success = state.wait(now + delay);
+    return success;
+}
+
+template<class Function, class I, class... Args>
+typename std::result_of<Function(I *, Args...)>::type
+timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) {
+    using ::android::hardware::Status;
+    typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
+    auto boundFunc = std::bind(std::forward<Function>(func),
+            interfaceObject.get(), std::forward<Args>(args)...);
+    bool success = timeout(IPC_CALL_WAIT, [&ret, &boundFunc] {
+        ret = boundFunc();
+    });
+    if (!success) {
+        return Status::fromStatusT(TIMED_OUT);
+    }
+    return ret;
+}
+
+}  // namespace lshal
+}  // namespace android
diff --git a/cmds/lshal/lshal.cpp b/cmds/lshal/lshal.cpp
deleted file mode 100644
index 9998a46..0000000
--- a/cmds/lshal/lshal.cpp
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * 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.
- */
-
-
-#include <getopt.h>
-
-#include <map>
-#include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <sstream>
-#include <regex>
-
-#include <android-base/parseint.h>
-#include <android/hidl/manager/1.0/IServiceManager.h>
-#include <hidl/ServiceManagement.h>
-
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hidl::manager::V1_0::IServiceManager;
-
-template <typename A, typename B, typename C, typename D, typename E, typename F>
-void printColumn(std::stringstream &stream,
-        const A &a, const B &b, const C &c, const D &d, const E &, const F &f) {
-    using namespace ::std;
-    stream << left
-           << setw(70) << a << "\t"
-           << setw(20) << b << "\t"
-           << setw(10) << c << "\t"
-           << setw(5)  << d << "\t"
-           // TODO(b/34984175): enable selecting columns
-           // << setw(16) << e << "\t"
-           << setw(0)  << f
-           << endl;
-}
-
-template <typename A>
-std::string join(const A &components, const std::string &separator) {
-    std::stringstream out;
-    bool first = true;
-    for (const auto &component : components) {
-        if (!first) {
-            out << separator;
-        }
-        out << component;
-
-        first = false;
-    }
-    return out.str();
-}
-
-std::string toHexString(uint64_t t) {
-    std::ostringstream os;
-    os << std::hex << std::setfill('0') << std::setw(16) << t;
-    return os.str();
-}
-
-std::pair<hidl_string, hidl_string> split(const hidl_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)};
-}
-
-bool getReferencedPids(
-        pid_t serverPid, std::map<uint64_t, std::string> *objects) {
-
-    std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
-    if (!ifs.is_open()) {
-        return false;
-    }
-
-    static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
-
-    std::string line;
-    std::smatch match;
-    while(getline(ifs, line)) {
-        if (!std::regex_search(line, match, prefix)) {
-            // the line doesn't start with the correct prefix
-            continue;
-        }
-        std::string ptrString = "0x" + match.str(2); // use number after c
-        uint64_t ptr;
-        if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
-            // Should not reach here, but just be tolerant.
-            std::cerr << "Could not parse number " << ptrString << std::endl;
-            continue;
-        }
-        const std::string proc = " proc ";
-        auto pos = line.rfind(proc);
-        if (pos != std::string::npos) {
-            (*objects)[ptr] += line.substr(pos + proc.size());
-        }
-    }
-    return true;
-}
-
-void dumpAllLibraries(std::stringstream &stream, const std::string &mode,
-            const sp<IServiceManager> &manager) {
-    using namespace ::std;
-    using namespace ::android::hardware;
-    using namespace ::android::hidl::manager::V1_0;
-    using namespace ::android::hidl::base::V1_0;
-    auto ret = manager->list([&] (const auto &fqInstanceNames) {
-        for (const auto &fqInstanceName : fqInstanceNames) {
-            const auto pair = split(fqInstanceName, '/');
-            const auto &serviceName = pair.first;
-            const auto &instanceName = pair.second;
-            printColumn(stream,
-                serviceName,
-                instanceName,
-                mode,
-                "N/A",
-                "N/A",
-                "N/A");
-        }
-    });
-    if (!ret.isOk()) {
-        cerr << "Error: Failed to call debugDump on defaultServiceManager(): "
-             << ret.description() << endl;
-    }
-}
-
-void dumpPassthrough(std::stringstream &stream, const std::string &mode,
-            const sp<IServiceManager> &manager) {
-    using namespace ::std;
-    using namespace ::android::hardware;
-    using namespace ::android::hidl::manager::V1_0;
-    using namespace ::android::hidl::base::V1_0;
-    auto ret = manager->debugDump([&] (const auto &infos) {
-        for (const auto &info : infos) {
-
-            printColumn(stream,
-                info.interfaceName,
-                info.instanceName,
-                mode,
-                info.clientPids.size() == 1 ? std::to_string(info.clientPids[0]) : "N/A",
-                "N/A",
-                join(info.clientPids, " "));
-        }
-    });
-    if (!ret.isOk()) {
-        cerr << "Error: Failed to call debugDump on defaultServiceManager(): "
-             << ret.description() << endl;
-    }
-}
-
-void dumpBinderized(std::stringstream &stream, const std::string &mode,
-            const sp<IServiceManager> &manager) {
-    using namespace ::std;
-    using namespace ::android::hardware;
-    using namespace ::android::hidl::manager::V1_0;
-    using namespace ::android::hidl::base::V1_0;
-    auto listRet = manager->list([&] (const auto &fqInstanceNames) {
-        // server pid, .ptr value of binder object, child pids
-        std::map<std::string, DebugInfo> allDebugInfos;
-        std::map<pid_t, std::map<uint64_t, std::string>> allPids;
-        for (const auto &fqInstanceName : fqInstanceNames) {
-            const auto pair = split(fqInstanceName, '/');
-            const auto &serviceName = pair.first;
-            const auto &instanceName = pair.second;
-            auto getRet = manager->get(serviceName, instanceName);
-            if (!getRet.isOk()) {
-                cerr << "Warning: Skipping \"" << fqInstanceName << "\": "
-                     << "cannot be fetched from service manager:"
-                     << getRet.description() << endl;
-                continue;
-            }
-            sp<IBase> service = getRet;
-            if (service == nullptr) {
-                cerr << "Warning: Skipping \"" << fqInstanceName << "\": "
-                     << "cannot be fetched from service manager (null)";
-                continue;
-            }
-            auto debugRet = service->getDebugInfo([&] (const auto &debugInfo) {
-                allDebugInfos[fqInstanceName] = debugInfo;
-                if (debugInfo.pid >= 0) {
-                    allPids[static_cast<pid_t>(debugInfo.pid)].clear();
-                }
-            });
-            if (!debugRet.isOk()) {
-                cerr << "Warning: Skipping \"" << fqInstanceName << "\": "
-                     << "debugging information cannot be retrieved:"
-                     << debugRet.description() << endl;
-            }
-        }
-        for (auto &pair : allPids) {
-            pid_t serverPid = pair.first;
-            if (!getReferencedPids(serverPid, &allPids[serverPid])) {
-                std::cerr << "Warning: no information for PID " << serverPid
-                          << ", are you root?" << std::endl;
-            }
-        }
-        for (const auto &fqInstanceName : fqInstanceNames) {
-            const auto pair = split(fqInstanceName, '/');
-            const auto &serviceName = pair.first;
-            const auto &instanceName = pair.second;
-            auto it = allDebugInfos.find(fqInstanceName);
-            if (it == allDebugInfos.end()) {
-                printColumn(stream,
-                    serviceName,
-                    instanceName,
-                    mode,
-                    "N/A",
-                    "N/A",
-                    ""
-                );
-                continue;
-            }
-            const DebugInfo &info = it->second;
-            printColumn(stream,
-                serviceName,
-                instanceName,
-                mode,
-                info.pid < 0 ? "N/A" : std::to_string(info.pid),
-                info.ptr == 0 ? "N/A" : toHexString(info.ptr),
-                info.pid < 0 || info.ptr == 0 ? "" : allPids[info.pid][info.ptr]
-            );
-        }
-
-    });
-    if (!listRet.isOk()) {
-        cerr << "Error: Failed to list services for " << mode << ": "
-             << listRet.description() << endl;
-    }
-}
-
-int dump() {
-    using namespace ::std;
-    using namespace ::android::hardware;
-
-    std::stringstream stream;
-
-    stream << "All services:" << endl;
-    stream << left;
-    printColumn(stream, "Interface", "Instance", "Transport", "Server", "PTR", "Clients");
-
-    auto bManager = defaultServiceManager();
-    if (bManager == nullptr) {
-        cerr << "Failed to get defaultServiceManager()!" << endl;
-    } else {
-        dumpBinderized(stream, "hwbinder", bManager);
-        // Passthrough PIDs are registered to the binderized manager as well.
-        dumpPassthrough(stream, "passthrough", bManager);
-    }
-
-    auto pManager = getPassthroughServiceManager();
-    if (pManager == nullptr) {
-        cerr << "Failed to get getPassthroughServiceManager()!" << endl;
-    } else {
-        dumpAllLibraries(stream, "passthrough", pManager);
-    }
-
-    cout << stream.rdbuf();
-    return 0;
-}
-
-int usage() {
-    using namespace ::std;
-    cerr
-        << "usage: lshal" << endl
-        << "           To dump all hals." << endl
-        << "or:" << endl
-        << "       lshal [-h|--help]" << endl
-        << "           -h, --help: show this help information." << endl;
-    return -1;
-}
-
-int main(int argc, char **argv) {
-    static struct option longOptions[] = {
-        {"help", no_argument, 0, 'h' },
-        { 0,               0, 0,  0  }
-    };
-
-    int optionIndex;
-    int c;
-    optind = 1;
-    for (;;) {
-        // using getopt_long in case we want to add other options in the future
-        c = getopt_long(argc, argv, "h", longOptions, &optionIndex);
-        if (c == -1) {
-            break;
-        }
-        switch (c) {
-        case 'h': // falls through
-        default: // see unrecognized options
-            return usage();
-        }
-    }
-    return dump();
-
-}