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

Commit 4255862b authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk Committed by Android (Google) Code Review
Browse files

Merge "Improve error handling with separate ICanErrorListener"

parents c71f0f17 a9061966
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ hidl_interface {
        "types.hal",
        "ICanBus.hal",
        "ICanController.hal",
        "ICanErrorListener.hal",
        "ICanMessageListener.hal",
        "ICloseHandle.hal",
    ],
+12 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package android.hardware.automotive.can@1.0;

import ICanErrorListener;
import ICanMessageListener;
import ICloseHandle;

@@ -57,4 +58,15 @@ interface ICanBus {
     */
    listen(vec<CanMessageFilter> filter, ICanMessageListener listener)
            generates (Result result, ICloseHandle close);

    /**
     * Adds a new listener for CAN bus or interface errors.
     *
     * If the error is fatal, the client is supposed to drop any references to
     * this specific ICanBus instance (see ICanErrorListener).
     *
     * @param listener The interface to receive the error events on
     * @return close A handle to call in order to remove the listener
     */
    listenForErrors(ICanErrorListener listener) generates (ICloseHandle close);
};
+32 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */
package android.hardware.automotive.can@1.0;

/**
 * CAN error listener.
 */
interface ICanErrorListener {
    /**
     * Called on error event.
     *
     * If the error is fatal, the client is supposed to drop any references to
     * this specific ICanBus instance.
     *
     * @param error Error type
     * @param isFatal Whether an error would result with ICanBus instance being unusable.
     */
    onError(ErrorEvent error, bool isFatal);
};
+0 −7
Original line number Diff line number Diff line
@@ -30,11 +30,4 @@ interface ICanMessageListener {
     * @param message Received CAN message
     */
    onReceive(CanMessage message);

    /**
     * Called on error event.
     *
     * @param error Error type
     */
    onError(ErrorEvent error);
};
+75 −28
Original line number Diff line number Diff line
@@ -68,14 +68,14 @@ Return<void> CanBus::listen(const hidl_vec<CanMessageFilter>& filter,
        return {};
    }

    std::lock_guard<std::mutex> lckListeners(mListenersGuard);
    std::lock_guard<std::mutex> lckListeners(mMsgListenersGuard);

    sp<CloseHandle> closeHandle = new CloseHandle([this, listenerCb]() {
        std::lock_guard<std::mutex> lck(mListenersGuard);
        std::erase_if(mListeners, [&](const auto& e) { return e.callback == listenerCb; });
        std::lock_guard<std::mutex> lck(mMsgListenersGuard);
        std::erase_if(mMsgListeners, [&](const auto& e) { return e.callback == listenerCb; });
    });
    mListeners.emplace_back(CanMessageListener{listenerCb, filter, closeHandle});
    auto& listener = mListeners.back();
    mMsgListeners.emplace_back(CanMessageListener{listenerCb, filter, closeHandle});
    auto& listener = mMsgListeners.back();

    // fix message IDs to have all zeros on bits not covered by mask
    std::for_each(listener.filter.begin(), listener.filter.end(),
@@ -91,8 +91,15 @@ CanBus::~CanBus() {
    std::lock_guard<std::mutex> lck(mIsUpGuard);
    CHECK(!mIsUp) << "Interface is still up while being destroyed";

    std::lock_guard<std::mutex> lckListeners(mListenersGuard);
    CHECK(mListeners.empty()) << "Listeners list is not empty while interface is being destroyed";
    std::lock_guard<std::mutex> lckListeners(mMsgListenersGuard);
    CHECK(mMsgListeners.empty()) << "Listener list is not empty while interface is being destroyed";
}

void CanBus::setErrorCallback(ErrorCallback errcb) {
    CHECK(!mIsUp) << "Can't set error callback while interface is up";
    CHECK(mErrCb == nullptr) << "Error callback is already set";
    mErrCb = errcb;
    CHECK(!mIsUp) << "Can't set error callback while interface is up";
}

ICanController::Result CanBus::preUp() {
@@ -120,19 +127,19 @@ ICanController::Result CanBus::up() {
        LOG(ERROR) << "Interface " << mIfname << " didn't get prepared";
        return ICanController::Result::BAD_ADDRESS;
    }
    mWasUpInitially = *isUp;

    if (!mWasUpInitially && !netdevice::up(mIfname)) {
    if (!*isUp && !netdevice::up(mIfname)) {
        LOG(ERROR) << "Can't bring " << mIfname << " up";
        return ICanController::Result::UNKNOWN_ERROR;
    }
    mDownAfterUse = !*isUp;

    using namespace std::placeholders;
    CanSocket::ReadCallback rdcb = std::bind(&CanBus::onRead, this, _1, _2);
    CanSocket::ErrorCallback errcb = std::bind(&CanBus::onError, this);
    CanSocket::ErrorCallback errcb = std::bind(&CanBus::onError, this, _1);
    mSocket = CanSocket::open(mIfname, rdcb, errcb);
    if (!mSocket) {
        if (!mWasUpInitially) netdevice::down(mIfname);
        if (mDownAfterUse) netdevice::down(mIfname);
        return ICanController::Result::UNKNOWN_ERROR;
    }

@@ -140,24 +147,50 @@ ICanController::Result CanBus::up() {
    return ICanController::Result::OK;
}

void CanBus::clearListeners() {
void CanBus::clearMsgListeners() {
    std::vector<wp<ICloseHandle>> listenersToClose;
    {
        std::lock_guard<std::mutex> lck(mListenersGuard);
        std::transform(mListeners.begin(), mListeners.end(), std::back_inserter(listenersToClose),
        std::lock_guard<std::mutex> lck(mMsgListenersGuard);
        std::transform(mMsgListeners.begin(), mMsgListeners.end(),
                       std::back_inserter(listenersToClose),
                       [](const auto& e) { return e.closeHandle; });
    }

    for (auto& weakListener : listenersToClose) {
        /* Between populating listenersToClose and calling close method here, some listeners might
         * have been already removed from the original mListeners list (resulting in a dangling weak
         * pointer here). It's fine - we just want to clean them up. */
         * have been already removed from the original mMsgListeners list (resulting in a dangling
         * weak pointer here). It's fine - we just want to clean them up. */
        auto listener = weakListener.promote();
        if (listener != nullptr) listener->close();
    }

    std::lock_guard<std::mutex> lck(mListenersGuard);
    CHECK(mListeners.empty()) << "Listeners list wasn't emptied";
    std::lock_guard<std::mutex> lck(mMsgListenersGuard);
    CHECK(mMsgListeners.empty()) << "Listeners list wasn't emptied";
}

void CanBus::clearErrListeners() {
    std::lock_guard<std::mutex> lck(mErrListenersGuard);
    mErrListeners.clear();
}

Return<sp<ICloseHandle>> CanBus::listenForErrors(const sp<ICanErrorListener>& listener) {
    if (listener == nullptr) {
        return new CloseHandle();
    }

    std::lock_guard<std::mutex> upLck(mIsUpGuard);
    if (!mIsUp) {
        listener->onError(ErrorEvent::INTERFACE_DOWN, true);
        return new CloseHandle();
    }

    std::lock_guard<std::mutex> errLck(mErrListenersGuard);
    mErrListeners.emplace_back(listener);

    return new CloseHandle([this, listener]() {
        std::lock_guard<std::mutex> lck(mErrListenersGuard);
        std::erase(mErrListeners, listener);
    });
}

bool CanBus::down() {
@@ -169,12 +202,13 @@ bool CanBus::down() {
    }
    mIsUp = false;

    clearListeners();
    clearMsgListeners();
    clearErrListeners();
    mSocket.reset();

    bool success = true;

    if (!mWasUpInitially && !netdevice::down(mIfname)) {
    if (mDownAfterUse && !netdevice::down(mIfname)) {
        LOG(ERROR) << "Can't bring " << mIfname << " down";
        // don't return yet, let's try to do best-effort cleanup
        success = false;
@@ -223,24 +257,37 @@ void CanBus::onRead(const struct canfd_frame& frame, std::chrono::nanoseconds ti
        LOG(VERBOSE) << "Got message " << toString(message);
    }

    std::lock_guard<std::mutex> lck(mListenersGuard);
    for (auto& listener : mListeners) {
    std::lock_guard<std::mutex> lck(mMsgListenersGuard);
    for (auto& listener : mMsgListeners) {
        if (!match(listener.filter, message.id)) continue;
        if (!listener.callback->onReceive(message).isOk()) {
        if (!listener.callback->onReceive(message).isOk() && !listener.failedOnce) {
            listener.failedOnce = true;
            LOG(WARNING) << "Failed to notify listener about message";
        }
    }
}

void CanBus::onError() {
    std::lock_guard<std::mutex> lck(mListenersGuard);
    for (auto& listener : mListeners) {
        if (!listener.callback->onError(ErrorEvent::HARDWARE_ERROR).isOk()) {
void CanBus::onError(int errnoVal) {
    auto eventType = ErrorEvent::HARDWARE_ERROR;

    if (errnoVal == ENODEV || errnoVal == ENETDOWN) {
        mDownAfterUse = false;
        eventType = ErrorEvent::INTERFACE_DOWN;
    }

    {
        std::lock_guard<std::mutex> lck(mErrListenersGuard);
        for (auto& listener : mErrListeners) {
            if (!listener->onError(eventType, true).isOk()) {
                LOG(WARNING) << "Failed to notify listener about error";
            }
        }
    }

    const auto errcb = mErrCb;
    if (errcb != nullptr) errcb();
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace can
Loading