Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2dadf0c authored by Yifan Hong's avatar Yifan Hong
Browse files

lshal: Add timeout for IPC calls.

IPC calls into interfaces should be done fault-torelently.
Add a timeout for each IPC call made so that lshal won't be
indefinitely blocked even if the interface don't reply
promptly.

Bug: 35317039

Test: lshal

Change-Id: Icb8157716ad68bddb5b33304b9063aa6f233985d
parent 38d53e03
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@
#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;

@@ -163,7 +165,7 @@ 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 = manager->list([&] (const auto &fqInstanceNames) {
    auto ret = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
        for (const auto &fqInstanceName : fqInstanceNames) {
            putEntry({
                .interfaceName = fqInstanceName,
@@ -186,7 +188,7 @@ 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 = manager->debugDump([&] (const auto &infos) {
    auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
        for (const auto &info : infos) {
            putEntry({
                .interfaceName =
@@ -214,7 +216,7 @@ Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
    using namespace ::android::hidl::base::V1_0;
    const std::string mode = "hwbinder";
    Status status = OK;
    auto listRet = manager->list([&] (const auto &fqInstanceNames) {
    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;
@@ -222,7 +224,7 @@ Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
            const auto pair = split(fqInstanceName, '/');
            const auto &serviceName = pair.first;
            const auto &instanceName = pair.second;
            auto getRet = manager->get(serviceName, instanceName);
            auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
            if (!getRet.isOk()) {
                mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
                     << "cannot be fetched from service manager:"
@@ -237,7 +239,7 @@ Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
                status |= DUMP_BINDERIZED_ERROR;
                continue;
            }
            auto debugRet = service->getDebugInfo([&] (const auto &debugInfo) {
            auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
                allDebugInfos[fqInstanceName] = debugInfo;
                if (debugInfo.pid >= 0) {
                    allPids[static_cast<pid_t>(debugInfo.pid)].clear();

cmds/lshal/Timeout.h

0 → 100644
+81 −0
Original line number Diff line number Diff line
/*
 * 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