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

Commit 87329670 authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk
Browse files

Default implementation for CAN bus HAL

This implementation supports SocketCAN interfaces.

Bug: 135918744
Test: VTS (separate new change)
Change-Id: I12b93e37fa64e341bee2c64eaf130b39977fcef5
parent a27d4e43
Loading
Loading
Loading
Loading
+55 −0
Original line number Original line 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.
//

cc_defaults {
    name: "android.hardware.automotive.can@defaults",
    cpp_std: "experimental",
    cflags: [
        "-Wall",
        "-Wextra",
        "-Werror",
        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
    ],
    shared_libs: [
        "libbase",
        "libutils",
    ],
}

cc_binary {
    name: "android.hardware.automotive.can@1.0-service",
    init_rc: ["android.hardware.automotive.can@1.0-service.rc"],
    defaults: ["android.hardware.automotive.can@defaults"],
    vendor: true,
    relative_install_path: "hw",
    srcs: [
        "CanBus.cpp",
        "CanBusNative.cpp",
        "CanBusVirtual.cpp",
        "CanController.cpp",
        "CanSocket.cpp",
        "CloseHandle.cpp",
        "service.cpp",
    ],
    shared_libs: [
        "android.hardware.automotive.can@1.0",
        "libhidlbase",
        "libhidltransport",
    ],
    static_libs: [
        "android.hardware.automotive.can@libnetdevice",
    ],
}
+249 −0
Original line number Original line 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.
 */

#include "CanBus.h"

#include "CloseHandle.h"

#include <android-base/logging.h>
#include <libnetdevice/can.h>
#include <libnetdevice/libnetdevice.h>
#include <linux/can.h>

namespace android {
namespace hardware {
namespace automotive {
namespace can {
namespace V1_0 {
namespace implementation {

/**
 * Whether to log sent/received packets.
 */
static constexpr bool kSuperVerbose = false;

Return<Result> CanBus::send(const CanMessage& message) {
    std::lock_guard<std::mutex> lck(mIsUpGuard);
    if (!mIsUp) return Result::INTERFACE_DOWN;

    if (UNLIKELY(kSuperVerbose)) {
        LOG(VERBOSE) << "Sending " << toString(message);
    }

    if (message.payload.size() > CAN_MAX_DLEN) return Result::PAYLOAD_TOO_LONG;

    struct canfd_frame frame = {};
    frame.can_id = message.id;
    frame.len = message.payload.size();
    memcpy(frame.data, message.payload.data(), message.payload.size());

    if (!mSocket->send(frame)) return Result::TRANSMISSION_FAILURE;

    return Result::OK;
}

Return<void> CanBus::listen(const hidl_vec<CanMessageFilter>& filter,
                            const sp<ICanMessageListener>& listenerCb, listen_cb _hidl_cb) {
    std::lock_guard<std::mutex> lck(mIsUpGuard);

    if (listenerCb == nullptr) {
        _hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
        return {};
    }
    if (!mIsUp) {
        _hidl_cb(Result::INTERFACE_DOWN, nullptr);
        return {};
    }

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

    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; });
    });
    mListeners.emplace_back(CanMessageListener{listenerCb, filter, closeHandle});
    auto& listener = mListeners.back();

    // fix message IDs to have all zeros on bits not covered by mask
    std::for_each(listener.filter.begin(), listener.filter.end(),
                  [](auto& rule) { rule.id &= rule.mask; });

    _hidl_cb(Result::OK, closeHandle);
    return {};
}

CanBus::CanBus(const std::string& ifname) : mIfname(ifname) {}

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";
}

ICanController::Result CanBus::preUp() {
    return ICanController::Result::OK;
}

bool CanBus::postDown() {
    return true;
}

ICanController::Result CanBus::up() {
    std::lock_guard<std::mutex> lck(mIsUpGuard);

    if (mIsUp) {
        LOG(WARNING) << "Interface is already up";
        return ICanController::Result::INVALID_STATE;
    }

    const auto preResult = preUp();
    if (preResult != ICanController::Result::OK) return preResult;

    const auto isUp = netdevice::isUp(mIfname);
    if (!isUp.has_value()) {
        // preUp() should prepare the interface (either create or make sure it's there)
        LOG(ERROR) << "Interface " << mIfname << " didn't get prepared";
        return ICanController::Result::BAD_ADDRESS;
    }
    mWasUpInitially = *isUp;

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

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

    mIsUp = true;
    return ICanController::Result::OK;
}

void CanBus::clearListeners() {
    std::vector<wp<ICloseHandle>> listenersToClose;
    {
        std::lock_guard<std::mutex> lck(mListenersGuard);
        std::transform(mListeners.begin(), mListeners.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. */
        auto listener = weakListener.promote();
        if (listener != nullptr) listener->close();
    }

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

bool CanBus::down() {
    std::lock_guard<std::mutex> lck(mIsUpGuard);

    if (!mIsUp) {
        LOG(WARNING) << "Interface is already down";
        return false;
    }
    mIsUp = false;

    clearListeners();
    mSocket.reset();

    bool success = true;

    if (!mWasUpInitially && !netdevice::down(mIfname)) {
        LOG(ERROR) << "Can't bring " << mIfname << " down";
        // don't return yet, let's try to do best-effort cleanup
        success = false;
    }

    if (!postDown()) success = false;

    return success;
}

/**
 * Match the filter set against message id.
 *
 * For details on the filters syntax, please see CanMessageFilter at
 * the HAL definition (types.hal).
 *
 * \param filter Filter to match against
 * \param id Message id to filter
 * \return true if the message id matches the filter, false otherwise
 */
static bool match(const hidl_vec<CanMessageFilter>& filter, CanMessageId id) {
    if (filter.size() == 0) return true;

    bool anyNonInvertedPresent = false;
    bool anyNonInvertedSatisfied = false;
    for (auto& rule : filter) {
        const bool satisfied = ((id & rule.mask) == rule.id) == !rule.inverted;
        if (rule.inverted) {
            // Any inverted (blacklist) rule not being satisfied invalidates the whole filter set.
            if (!satisfied) return false;
        } else {
            anyNonInvertedPresent = true;
            if (satisfied) anyNonInvertedSatisfied = true;
        }
    }
    return !anyNonInvertedPresent || anyNonInvertedSatisfied;
}

void CanBus::onRead(const struct canfd_frame& frame, std::chrono::nanoseconds timestamp) {
    CanMessage message = {};
    message.id = frame.can_id;
    message.payload = hidl_vec<uint8_t>(frame.data, frame.data + frame.len);
    message.timestamp = timestamp.count();

    if (UNLIKELY(kSuperVerbose)) {
        LOG(VERBOSE) << "Got message " << toString(message);
    }

    std::lock_guard<std::mutex> lck(mListenersGuard);
    for (auto& listener : mListeners) {
        if (!match(listener.filter, message.id)) continue;
        if (!listener.callback->onReceive(message).isOk()) {
            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()) {
            LOG(WARNING) << "Failed to notify listener about error";
        }
    }
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace can
}  // namespace automotive
}  // namespace hardware
}  // namespace android
+97 −0
Original line number Original line 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.
 */

#pragma once

#include "CanSocket.h"

#include <android-base/unique_fd.h>
#include <android/hardware/automotive/can/1.0/ICanBus.h>
#include <android/hardware/automotive/can/1.0/ICanController.h>
#include <utils/Mutex.h>

#include <atomic>
#include <thread>

namespace android {
namespace hardware {
namespace automotive {
namespace can {
namespace V1_0 {
namespace implementation {

struct CanBus : public ICanBus {
    virtual ~CanBus();

    Return<Result> send(const CanMessage& message) override;
    Return<void> listen(const hidl_vec<CanMessageFilter>& filter,
                        const sp<ICanMessageListener>& listener, listen_cb _hidl_cb) override;

    ICanController::Result up();
    bool down();

  protected:
    CanBus(const std::string& ifname);

    /**
     * Prepare the SocketCAN interface.
     *
     * After calling this method, mIfname network interface is available and ready to be brought up.
     */
    virtual ICanController::Result preUp();

    /**
     * Cleanup after bringing the interface down.
     *
     * This is a counterpart to preUp().
     */
    virtual bool postDown();

    /** Network interface name. */
    const std::string mIfname;

  private:
    struct CanMessageListener {
        sp<ICanMessageListener> callback;
        hidl_vec<CanMessageFilter> filter;
        wp<ICloseHandle> closeHandle;
    };
    void clearListeners();

    void onRead(const struct canfd_frame& frame, std::chrono::nanoseconds timestamp);
    void onError();

    std::mutex mListenersGuard;
    std::vector<CanMessageListener> mListeners GUARDED_BY(mListenersGuard);

    std::unique_ptr<CanSocket> mSocket;
    bool mWasUpInitially;

    /**
     * Guard for up flag is required to be held for entire time when the interface is being used
     * (i.e. message being sent), because we don't want the interface to be torn down while
     * executing that operation.
     */
    std::mutex mIsUpGuard;
    bool mIsUp GUARDED_BY(mIsUpGuard) = false;
};

}  // namespace implementation
}  // namespace V1_0
}  // namespace can
}  // namespace automotive
}  // namespace hardware
}  // namespace android
+57 −0
Original line number Original line 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.
 */

#include "CanBusNative.h"

#include <android-base/logging.h>
#include <libnetdevice/can.h>
#include <libnetdevice/libnetdevice.h>

namespace android {
namespace hardware {
namespace automotive {
namespace can {
namespace V1_0 {
namespace implementation {

CanBusNative::CanBusNative(const std::string& ifname, uint32_t baudrate)
    : CanBus(ifname), mBaudrate(baudrate) {}

ICanController::Result CanBusNative::preUp() {
    if (!netdevice::exists(mIfname)) {
        LOG(ERROR) << "Interface " << mIfname << " doesn't exist";
        return ICanController::Result::BAD_ADDRESS;
    }

    if (!netdevice::down(mIfname)) {
        LOG(ERROR) << "Can't bring " << mIfname << " down (to configure it)";
        return ICanController::Result::UNKNOWN_ERROR;
    }

    if (!netdevice::can::setBitrate(mIfname, mBaudrate)) {
        LOG(ERROR) << "Can't set bitrate " << mBaudrate << " for " << mIfname;
        return ICanController::Result::BAD_BAUDRATE;
    }

    return ICanController::Result::OK;
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace can
}  // namespace automotive
}  // namespace hardware
}  // namespace android
+43 −0
Original line number Original line 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.
 */

#pragma once

#include "CanBus.h"

namespace android {
namespace hardware {
namespace automotive {
namespace can {
namespace V1_0 {
namespace implementation {

struct CanBusNative : public CanBus {
    CanBusNative(const std::string& ifname, uint32_t baudrate);

  protected:
    virtual ICanController::Result preUp() override;

  private:
    const uint32_t mBaudrate;
};

}  // namespace implementation
}  // namespace V1_0
}  // namespace can
}  // namespace automotive
}  // namespace hardware
}  // namespace android
Loading