Default implementation for CAN bus HAL
This implementation supports SocketCAN interfaces.
Bug: 135918744
Test: VTS (separate new change)
Change-Id: I12b93e37fa64e341bee2c64eaf130b39977fcef5
diff --git a/automotive/can/1.0/default/Android.bp b/automotive/can/1.0/default/Android.bp
new file mode 100644
index 0000000..0a4afd6
--- /dev/null
+++ b/automotive/can/1.0/default/Android.bp
@@ -0,0 +1,55 @@
+//
+// 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",
+ ],
+}
diff --git a/automotive/can/1.0/default/CanBus.cpp b/automotive/can/1.0/default/CanBus.cpp
new file mode 100644
index 0000000..65da291
--- /dev/null
+++ b/automotive/can/1.0/default/CanBus.cpp
@@ -0,0 +1,249 @@
+/*
+ * 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
diff --git a/automotive/can/1.0/default/CanBus.h b/automotive/can/1.0/default/CanBus.h
new file mode 100644
index 0000000..81a83c8
--- /dev/null
+++ b/automotive/can/1.0/default/CanBus.h
@@ -0,0 +1,97 @@
+/*
+ * 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
diff --git a/automotive/can/1.0/default/CanBusNative.cpp b/automotive/can/1.0/default/CanBusNative.cpp
new file mode 100644
index 0000000..365b749
--- /dev/null
+++ b/automotive/can/1.0/default/CanBusNative.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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
diff --git a/automotive/can/1.0/default/CanBusNative.h b/automotive/can/1.0/default/CanBusNative.h
new file mode 100644
index 0000000..126f1cb
--- /dev/null
+++ b/automotive/can/1.0/default/CanBusNative.h
@@ -0,0 +1,43 @@
+/*
+ * 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
diff --git a/automotive/can/1.0/default/CanBusVirtual.cpp b/automotive/can/1.0/default/CanBusVirtual.cpp
new file mode 100644
index 0000000..cc59fa9
--- /dev/null
+++ b/automotive/can/1.0/default/CanBusVirtual.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "CanBusVirtual.h"
+
+#include <android-base/logging.h>
+#include <libnetdevice/libnetdevice.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+CanBusVirtual::CanBusVirtual(const std::string& ifname) : CanBus(ifname) {}
+
+ICanController::Result CanBusVirtual::preUp() {
+ if (netdevice::exists(mIfname)) return ICanController::Result::OK;
+
+ LOG(DEBUG) << "Virtual interface " << mIfname << " doesn't exist, creating...";
+ mWasCreated = true;
+ if (!netdevice::add(mIfname, "vcan")) {
+ LOG(ERROR) << "Can't create vcan interface " << mIfname;
+ return ICanController::Result::UNKNOWN_ERROR;
+ }
+
+ return ICanController::Result::OK;
+}
+
+bool CanBusVirtual::postDown() {
+ if (mWasCreated) {
+ mWasCreated = false;
+ if (!netdevice::del(mIfname)) {
+ LOG(ERROR) << "Couldn't remove vcan interface " << mIfname;
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanBusVirtual.h b/automotive/can/1.0/default/CanBusVirtual.h
new file mode 100644
index 0000000..c2d5794
--- /dev/null
+++ b/automotive/can/1.0/default/CanBusVirtual.h
@@ -0,0 +1,44 @@
+/*
+ * 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 CanBusVirtual : public CanBus {
+ CanBusVirtual(const std::string& ifname);
+
+ protected:
+ virtual ICanController::Result preUp() override;
+ virtual bool postDown() override;
+
+ private:
+ bool mWasCreated = false;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanController.cpp b/automotive/can/1.0/default/CanController.cpp
new file mode 100644
index 0000000..20adbe1
--- /dev/null
+++ b/automotive/can/1.0/default/CanController.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 "CanController.h"
+
+#include "CanBusNative.h"
+#include "CanBusVirtual.h"
+
+#include <android-base/logging.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+
+#include <regex>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+using IfaceIdDisc = ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator;
+
+Return<void> CanController::getSupportedInterfaceTypes(getSupportedInterfaceTypes_cb _hidl_cb) {
+ _hidl_cb({
+ ICanController::InterfaceType::VIRTUAL,
+ ICanController::InterfaceType::SOCKETCAN,
+ });
+ return {};
+}
+
+static bool isValidName(const std::string& name) {
+ static const std::regex nameRE("^[a-zA-Z0-9_]{1,32}$");
+ return std::regex_match(name, nameRE);
+}
+
+Return<ICanController::Result> CanController::upInterface(
+ const ICanController::BusConfiguration& config) {
+ LOG(VERBOSE) << "Attempting to bring interface up: " << toString(config);
+
+ std::lock_guard<std::mutex> lck(mCanBusesGuard);
+
+ if (!isValidName(config.name)) {
+ LOG(ERROR) << "Bus name " << config.name << " is invalid";
+ return ICanController::Result::UNKNOWN_ERROR;
+ }
+
+ if (mCanBuses.find(config.name) != mCanBuses.end()) {
+ LOG(ERROR) << "Bus " << config.name << " is already up";
+ return ICanController::Result::INVALID_STATE;
+ }
+
+ sp<CanBus> busService;
+
+ if (config.iftype == ICanController::InterfaceType::SOCKETCAN) {
+ // TODO(b/135918744): support serialno
+ if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
+ busService = new CanBusNative(config.interfaceId.address(), config.baudrate);
+ } else {
+ return ICanController::Result::BAD_ADDRESS;
+ }
+ } else if (config.iftype == ICanController::InterfaceType::VIRTUAL) {
+ if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
+ busService = new CanBusVirtual(config.interfaceId.address());
+ } else {
+ return ICanController::Result::BAD_ADDRESS;
+ }
+ } else {
+ return ICanController::Result::NOT_SUPPORTED;
+ }
+
+ const auto result = busService->up();
+ if (result != ICanController::Result::OK) return result;
+
+ if (busService->registerAsService(config.name) != OK) {
+ LOG(ERROR) << "Failed to register ICanBus/" << config.name;
+ if (!busService->down()) {
+ LOG(WARNING) << "Failed to bring down CAN bus that failed to register";
+ }
+ return ICanController::Result::UNKNOWN_ERROR;
+ }
+
+ mCanBuses[config.name] = busService;
+
+ return ICanController::Result::OK;
+}
+
+Return<bool> CanController::downInterface(const hidl_string& name) {
+ LOG(VERBOSE) << "Attempting to bring interface down: " << name;
+
+ std::lock_guard<std::mutex> lck(mCanBusesGuard);
+
+ auto busEntry = mCanBuses.extract(name);
+ if (!busEntry) {
+ LOG(WARNING) << "Interface " << name << " is not up";
+ return false;
+ }
+
+ auto success = true;
+
+ auto manager = hidl::manager::V1_2::IServiceManager::getService();
+ if (!manager || !manager->tryUnregister(ICanBus::descriptor, name, busEntry.mapped())) {
+ LOG(ERROR) << "Couldn't unregister " << name;
+ // don't return yet, let's try to do best-effort cleanup
+ success = false;
+ }
+
+ if (!busEntry.mapped()->down()) {
+ LOG(ERROR) << "Couldn't bring " << name << " down";
+ success = false;
+ }
+
+ return success;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanController.h b/automotive/can/1.0/default/CanController.h
new file mode 100644
index 0000000..0674d0e
--- /dev/null
+++ b/automotive/can/1.0/default/CanController.h
@@ -0,0 +1,47 @@
+/*
+ * 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"
+
+#include <android/hardware/automotive/can/1.0/ICanController.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+struct CanController : public ICanController {
+ Return<void> getSupportedInterfaceTypes(getSupportedInterfaceTypes_cb _hidl_cb) override;
+
+ Return<ICanController::Result> upInterface(
+ const ICanController::BusConfiguration& config) override;
+ Return<bool> downInterface(const hidl_string& name) override;
+
+ private:
+ std::mutex mCanBusesGuard;
+ std::map<std::string, sp<CanBus>> mCanBuses GUARDED_BY(mCanBusesGuard);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanSocket.cpp b/automotive/can/1.0/default/CanSocket.cpp
new file mode 100644
index 0000000..4d86de6
--- /dev/null
+++ b/automotive/can/1.0/default/CanSocket.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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 "CanSocket.h"
+
+#include <android-base/logging.h>
+#include <libnetdevice/can.h>
+#include <libnetdevice/libnetdevice.h>
+#include <linux/can.h>
+#include <utils/SystemClock.h>
+
+#include <chrono>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+using namespace std::chrono_literals;
+
+/**
+ * How frequently the read thread checks whether the interface was asked to be down.
+ *
+ * Note: This does *not* affect read timing or bandwidth, just CPU load vs time to
+ * down the interface.
+ */
+static constexpr auto kReadPooling = 100ms;
+
+std::unique_ptr<CanSocket> CanSocket::open(const std::string& ifname, ReadCallback rdcb,
+ ErrorCallback errcb) {
+ auto sock = netdevice::can::socket(ifname);
+ if (!sock.ok()) {
+ LOG(ERROR) << "Can't open CAN socket on " << ifname;
+ return nullptr;
+ }
+
+ // Can't use std::make_unique due to private CanSocket constructor.
+ return std::unique_ptr<CanSocket>(new CanSocket(std::move(sock), rdcb, errcb));
+}
+
+CanSocket::CanSocket(base::unique_fd socket, ReadCallback rdcb, ErrorCallback errcb)
+ : mReadCallback(rdcb),
+ mErrorCallback(errcb),
+ mSocket(std::move(socket)),
+ mReaderThread(&CanSocket::readerThread, this) {}
+
+CanSocket::~CanSocket() {
+ mStopReaderThread = true;
+ mReaderThread.join();
+}
+
+bool CanSocket::send(const struct canfd_frame& frame) {
+ const auto res = write(mSocket.get(), &frame, CAN_MTU);
+ if (res < 0) {
+ LOG(DEBUG) << "CanSocket send failed: " << errno;
+ return false;
+ }
+ if (res != CAN_MTU) {
+ LOG(DEBUG) << "CanSocket sent wrong number of bytes: " << res;
+ return false;
+ }
+ return true;
+}
+
+static struct timeval toTimeval(std::chrono::microseconds t) {
+ struct timeval tv;
+ tv.tv_sec = t / 1s;
+ tv.tv_usec = (t % 1s) / 1us;
+ return tv;
+}
+
+static int selectRead(const base::unique_fd& fd, std::chrono::microseconds timeout) {
+ auto timeouttv = toTimeval(timeout);
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(fd.get(), &readfds);
+ return select(fd.get() + 1, &readfds, nullptr, nullptr, &timeouttv);
+}
+
+void CanSocket::readerThread() {
+ LOG(VERBOSE) << "Reader thread started";
+
+ while (!mStopReaderThread) {
+ /* The ideal would be to have a blocking read(3) call and interrupt it with shutdown(3).
+ * This is unfortunately not supported for SocketCAN, so we need to rely on select(3).
+ */
+ const auto sel = selectRead(mSocket, kReadPooling);
+ if (sel == 0) continue; // timeout
+ if (sel == -1) {
+ LOG(ERROR) << "Select failed: " << errno;
+ break;
+ }
+
+ struct canfd_frame frame;
+ const auto nbytes = read(mSocket.get(), &frame, CAN_MTU);
+
+ /* We could use SIOCGSTAMP to get a precise UNIX timestamp for a given packet, but what
+ * we really need is a time since boot. There is no direct way to convert between these
+ * clocks. We could implement a class to calculate the difference between the clocks
+ * (querying both several times and picking the smallest difference); apply the difference
+ * to a SIOCGSTAMP returned value; re-synchronize if the elapsed time is too much in the
+ * past (indicating the UNIX timestamp might have been adjusted).
+ *
+ * Apart from the added complexity, it's possible the added calculations and system calls
+ * would add so much time to the processing pipeline so the precision of the reported time
+ * was buried under the subsystem latency. Let's just use a local time since boot here and
+ * leave precise hardware timestamps for custom proprietary implementations (if needed).
+ */
+ const std::chrono::nanoseconds ts(elapsedRealtimeNano());
+
+ if (nbytes != CAN_MTU) {
+ if (nbytes >= 0) {
+ LOG(ERROR) << "Failed to read CAN packet, got " << nbytes << " bytes";
+ break;
+ }
+ if (errno == EAGAIN) continue;
+
+ LOG(ERROR) << "Failed to read CAN packet: " << errno;
+ break;
+ }
+
+ mReadCallback(frame, ts);
+ }
+
+ if (!mStopReaderThread) mErrorCallback();
+
+ LOG(VERBOSE) << "Reader thread stopped";
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanSocket.h b/automotive/can/1.0/default/CanSocket.h
new file mode 100644
index 0000000..cd7162d
--- /dev/null
+++ b/automotive/can/1.0/default/CanSocket.h
@@ -0,0 +1,80 @@
+/*
+ * 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 <android-base/macros.h>
+#include <android-base/unique_fd.h>
+#include <linux/can.h>
+
+#include <atomic>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * Wrapper around SocketCAN socket.
+ */
+struct CanSocket {
+ using ReadCallback = std::function<void(const struct canfd_frame&, std::chrono::nanoseconds)>;
+ using ErrorCallback = std::function<void()>;
+
+ /**
+ * Open and bind SocketCAN socket.
+ *
+ * \param ifname SocketCAN network interface name (such as can0)
+ * \param rdcb Callback on received messages
+ * \param errcb Callback on socket failure
+ * \return Socket instance, or nullptr if it wasn't possible to open one
+ */
+ static std::unique_ptr<CanSocket> open(const std::string& ifname, ReadCallback rdcb,
+ ErrorCallback errcb);
+ virtual ~CanSocket();
+
+ /**
+ * Send CAN frame.
+ *
+ * \param frame Frame to send
+ * \return true in case of success, false otherwise
+ */
+ bool send(const struct canfd_frame& frame);
+
+ private:
+ CanSocket(base::unique_fd socket, ReadCallback rdcb, ErrorCallback errcb);
+ void readerThread();
+
+ ReadCallback mReadCallback;
+ ErrorCallback mErrorCallback;
+
+ const base::unique_fd mSocket;
+ std::thread mReaderThread;
+ std::atomic<bool> mStopReaderThread = false;
+
+ DISALLOW_COPY_AND_ASSIGN(CanSocket);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CloseHandle.cpp b/automotive/can/1.0/default/CloseHandle.cpp
new file mode 100644
index 0000000..13693d2
--- /dev/null
+++ b/automotive/can/1.0/default/CloseHandle.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "CloseHandle.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+CloseHandle::CloseHandle(Callback callback) : mCallback(callback) {}
+
+CloseHandle::~CloseHandle() {
+ close();
+}
+
+Return<void> CloseHandle::close() {
+ const auto wasClosed = mIsClosed.exchange(true);
+ if (wasClosed) return {};
+
+ mCallback();
+ return {};
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CloseHandle.h b/automotive/can/1.0/default/CloseHandle.h
new file mode 100644
index 0000000..94972e0
--- /dev/null
+++ b/automotive/can/1.0/default/CloseHandle.h
@@ -0,0 +1,59 @@
+/*
+ * 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 <android-base/macros.h>
+#include <android/hardware/automotive/can/1.0/ICloseHandle.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * Generic ICloseHandle implementation ignoring double-close events.
+ */
+struct CloseHandle : public ICloseHandle {
+ using Callback = std::function<void()>;
+
+ /**
+ * Create a handle with a callback.
+ *
+ * The callback is guaranteed to be called exactly once.
+ *
+ * \param callback Called on the first close() call, or on destruction of the handle
+ */
+ CloseHandle(Callback callback);
+ virtual ~CloseHandle();
+
+ Return<void> close() override;
+
+ private:
+ Callback mCallback;
+ std::atomic<bool> mIsClosed = false;
+
+ DISALLOW_COPY_AND_ASSIGN(CloseHandle);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/android.hardware.automotive.can@1.0-service.rc b/automotive/can/1.0/default/android.hardware.automotive.can@1.0-service.rc
new file mode 100644
index 0000000..a629bda
--- /dev/null
+++ b/automotive/can/1.0/default/android.hardware.automotive.can@1.0-service.rc
@@ -0,0 +1,5 @@
+service can-hal-1.0-service /vendor/bin/hw/android.hardware.automotive.can@1.0-service
+ class hal
+ capabilities NET_ADMIN
+ user vehicle_network
+ group system inet
diff --git a/automotive/can/1.0/default/libnetdevice/Android.bp b/automotive/can/1.0/default/libnetdevice/Android.bp
new file mode 100644
index 0000000..31e5ad0
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/Android.bp
@@ -0,0 +1,30 @@
+//
+// 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_library_static {
+ name: "android.hardware.automotive.can@libnetdevice",
+ defaults: ["android.hardware.automotive.can@defaults"],
+ vendor_available: true,
+ relative_install_path: "hw",
+ srcs: [
+ "NetlinkRequest.cpp",
+ "NetlinkSocket.cpp",
+ "can.cpp",
+ "common.cpp",
+ "libnetdevice.cpp",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/automotive/can/1.0/default/libnetdevice/NetlinkRequest.cpp b/automotive/can/1.0/default/libnetdevice/NetlinkRequest.cpp
new file mode 100644
index 0000000..9845bc7
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/NetlinkRequest.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "NetlinkRequest.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace netdevice {
+namespace impl {
+
+static struct rtattr* nlmsg_tail(struct nlmsghdr* n) {
+ return reinterpret_cast<struct rtattr*>( //
+ reinterpret_cast<uintptr_t>(n) + NLMSG_ALIGN(n->nlmsg_len));
+}
+
+struct rtattr* addattr_l(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type, const void* data,
+ size_t dataLen) {
+ size_t newLen = NLMSG_ALIGN(n->nlmsg_len) + RTA_SPACE(dataLen);
+ if (newLen > maxLen) {
+ LOG(ERROR) << "addattr_l failed - exceeded maxLen: " << newLen << " > " << maxLen;
+ return nullptr;
+ }
+
+ auto attr = nlmsg_tail(n);
+ attr->rta_len = RTA_SPACE(dataLen);
+ attr->rta_type = type;
+ if (dataLen > 0) memcpy(RTA_DATA(attr), data, dataLen);
+
+ n->nlmsg_len = newLen;
+ return attr;
+}
+
+struct rtattr* addattr_nest(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type) {
+ return addattr_l(n, maxLen, type, nullptr, 0);
+}
+
+void addattr_nest_end(struct nlmsghdr* n, struct rtattr* nest) {
+ size_t nestLen = reinterpret_cast<uintptr_t>(nlmsg_tail(n)) - reinterpret_cast<uintptr_t>(nest);
+ nest->rta_len = nestLen;
+}
+
+} // namespace impl
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/NetlinkRequest.h b/automotive/can/1.0/default/libnetdevice/NetlinkRequest.h
new file mode 100644
index 0000000..21202e3
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/NetlinkRequest.h
@@ -0,0 +1,159 @@
+/*
+ * 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 <android-base/macros.h>
+#include <linux/rtnetlink.h>
+
+#include <string>
+
+namespace android {
+namespace netdevice {
+
+typedef unsigned short rtattrtype_t; // as in rtnetlink.h
+typedef __u16 nlmsgtype_t; // as in netlink.h
+
+/**
+ * Implementation details, do not use outside NetlinkRequest template.
+ */
+namespace impl {
+
+struct rtattr* addattr_l(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type, const void* data,
+ size_t dataLen);
+struct rtattr* addattr_nest(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type);
+void addattr_nest_end(struct nlmsghdr* n, struct rtattr* nest);
+
+} // namespace impl
+
+/**
+ * Wrapper around NETLINK_ROUTE messages, to build them in C++ style.
+ *
+ * \param T specific message header (such as struct ifinfomsg)
+ * \param BUFSIZE how much space to reserve for payload (not counting the header size)
+ */
+template <class T, unsigned int BUFSIZE = 128>
+struct NetlinkRequest {
+ /**
+ * Create empty message.
+ *
+ * \param type Message type (such as RTM_NEWLINK)
+ * \param flags Message flags (such as NLM_F_REQUEST)
+ */
+ NetlinkRequest(nlmsgtype_t type, uint16_t flags) {
+ mRequest.nlmsg.nlmsg_len = NLMSG_LENGTH(sizeof(mRequest.data));
+ mRequest.nlmsg.nlmsg_type = type;
+ mRequest.nlmsg.nlmsg_flags = flags;
+ }
+
+ /** Returns pointer to raw netlink message header. */
+ struct nlmsghdr* header() {
+ return &mRequest.nlmsg;
+ }
+ /** Reference to message-specific header. */
+ T& data() { return mRequest.data; }
+
+ /**
+ * Adds an attribute of a simple type.
+ *
+ * If this method fails (i.e. due to insufficient space), the message will be marked
+ * as bad (\see isGood).
+ *
+ * \param type attribute type (such as IFLA_IFNAME)
+ * \param attr attribute data
+ */
+ template <class A>
+ void addattr(rtattrtype_t type, const A& attr) {
+ if (!mIsGood) return;
+ auto ap = impl::addattr_l(&mRequest.nlmsg, sizeof(mRequest), type, &attr, sizeof(attr));
+ if (ap == nullptr) mIsGood = false;
+ }
+
+ template <>
+ void addattr(rtattrtype_t type, const std::string& s) {
+ if (!mIsGood) return;
+ auto ap = impl::addattr_l(&mRequest.nlmsg, sizeof(mRequest), type, s.c_str(), s.size() + 1);
+ if (ap == nullptr) mIsGood = false;
+ }
+
+ /**
+ * Guard class to frame nested attributes. See nest(int).
+ */
+ struct Nest {
+ Nest(NetlinkRequest& req, rtattrtype_t type) : mReq(req), mAttr(req.nestStart(type)) {}
+ ~Nest() { mReq.nestEnd(mAttr); }
+
+ private:
+ NetlinkRequest& mReq;
+ struct rtattr* mAttr;
+
+ DISALLOW_COPY_AND_ASSIGN(Nest);
+ };
+
+ /**
+ * Add nested attribute.
+ *
+ * The returned object is a guard for auto-nesting children inside the argument attribute.
+ * When the Nest object goes out of scope, the nesting attribute is closed.
+ *
+ * Example usage nesting IFLA_CAN_BITTIMING inside IFLA_INFO_DATA, which is nested
+ * inside IFLA_LINKINFO:
+ * NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST);
+ * {
+ * auto linkinfo = req.nest(IFLA_LINKINFO);
+ * req.addattr(IFLA_INFO_KIND, "can");
+ * {
+ * auto infodata = req.nest(IFLA_INFO_DATA);
+ * req.addattr(IFLA_CAN_BITTIMING, bitTimingStruct);
+ * }
+ * }
+ * // use req
+ *
+ * \param type attribute type (such as IFLA_LINKINFO)
+ */
+ Nest nest(int type) { return Nest(*this, type); }
+
+ /**
+ * Indicates, whether the message is in a good state.
+ *
+ * The bad state is usually a result of payload buffer being too small.
+ * You can modify BUFSIZE template parameter to fix this.
+ */
+ bool isGood() const { return mIsGood; }
+
+ private:
+ bool mIsGood = true;
+
+ struct {
+ struct nlmsghdr nlmsg;
+ T data;
+ char buf[BUFSIZE];
+ } mRequest = {};
+
+ struct rtattr* nestStart(rtattrtype_t type) {
+ if (!mIsGood) return nullptr;
+ auto attr = impl::addattr_nest(&mRequest.nlmsg, sizeof(mRequest), type);
+ if (attr == nullptr) mIsGood = false;
+ return attr;
+ }
+
+ void nestEnd(struct rtattr* nest) {
+ if (mIsGood && nest != nullptr) impl::addattr_nest_end(&mRequest.nlmsg, nest);
+ }
+};
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp
new file mode 100644
index 0000000..0514764
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 "NetlinkSocket.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace netdevice {
+
+NetlinkSocket::NetlinkSocket(int protocol) {
+ mFd.reset(socket(AF_NETLINK, SOCK_RAW, protocol));
+ if (!mFd.ok()) {
+ LOG(ERROR) << "Can't open Netlink socket: " << errno;
+ mFailed = true;
+ return;
+ }
+
+ struct sockaddr_nl sa = {};
+ sa.nl_family = AF_NETLINK;
+
+ if (bind(mFd.get(), reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa)) < 0) {
+ LOG(ERROR) << "Can't bind Netlink socket: " << errno;
+ mFd.reset();
+ mFailed = true;
+ }
+}
+
+bool NetlinkSocket::send(struct nlmsghdr* nlmsg) {
+ if (mFailed) return false;
+
+ nlmsg->nlmsg_pid = 0; // kernel
+ nlmsg->nlmsg_seq = mSeq++;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ struct iovec iov = {nlmsg, nlmsg->nlmsg_len};
+
+ struct sockaddr_nl sa = {};
+ sa.nl_family = AF_NETLINK;
+
+ struct msghdr msg = {};
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (sendmsg(mFd.get(), &msg, 0) < 0) {
+ LOG(ERROR) << "Can't send Netlink message: " << errno;
+ return false;
+ }
+ return true;
+}
+
+bool NetlinkSocket::receiveAck() {
+ if (mFailed) return false;
+
+ char buf[8192];
+
+ struct sockaddr_nl sa;
+ struct iovec iov = {buf, sizeof(buf)};
+
+ struct msghdr msg = {};
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ const ssize_t status = recvmsg(mFd.get(), &msg, 0);
+ if (status < 0) {
+ LOG(ERROR) << "Failed to receive Netlink message: " << errno;
+ return false;
+ }
+ size_t remainingLen = status;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ LOG(ERROR) << "Failed to receive Netlink message: truncated";
+ return false;
+ }
+
+ for (auto nlmsg = reinterpret_cast<struct nlmsghdr*>(buf); NLMSG_OK(nlmsg, remainingLen);
+ nlmsg = NLMSG_NEXT(nlmsg, remainingLen)) {
+ // We're looking for error/ack message only, ignoring others.
+ if (nlmsg->nlmsg_type != NLMSG_ERROR) {
+ LOG(WARNING) << "Received unexpected Netlink message (ignored): " << nlmsg->nlmsg_type;
+ continue;
+ }
+
+ // Found error/ack message, return status.
+ auto nlerr = reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(nlmsg));
+ if (nlerr->error != 0) {
+ LOG(ERROR) << "Received Netlink error message: " << nlerr->error;
+ return false;
+ }
+ return true;
+ }
+ // Couldn't find any error/ack messages.
+ return false;
+}
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/NetlinkSocket.h b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.h
new file mode 100644
index 0000000..81d6224
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.h
@@ -0,0 +1,68 @@
+/*
+ * 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 "NetlinkRequest.h"
+
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+#include <linux/netlink.h>
+
+namespace android {
+namespace netdevice {
+
+/**
+ * A wrapper around AF_NETLINK sockets.
+ *
+ * This class is not thread safe to use a single instance between multiple threads, but it's fine to
+ * use multiple instances over multiple threads.
+ */
+struct NetlinkSocket {
+ NetlinkSocket(int protocol);
+
+ /**
+ * Send Netlink message to Kernel.
+ *
+ * @param msg Message to send, nlmsg_seq will be set to next sequence number
+ * @return true, if succeeded
+ */
+ template <class T, unsigned int BUFSIZE>
+ bool send(NetlinkRequest<T, BUFSIZE>& req) {
+ if (!req.isGood()) return false;
+ return send(req.header());
+ }
+
+ /**
+ * Receive Netlink ACK message from Kernel.
+ *
+ * @return true if received ACK message, false in case of error
+ */
+ bool receiveAck();
+
+ private:
+ uint32_t mSeq = 0;
+ base::unique_fd mFd;
+ bool mFailed = false;
+
+ bool send(struct nlmsghdr* msg);
+
+ DISALLOW_COPY_AND_ASSIGN(NetlinkSocket);
+};
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/can.cpp b/automotive/can/1.0/default/libnetdevice/can.cpp
new file mode 100644
index 0000000..87617dd
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/can.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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 <libnetdevice/libnetdevice.h>
+
+#include "NetlinkRequest.h"
+#include "NetlinkSocket.h"
+#include "common.h"
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include <linux/can.h>
+#include <linux/can/netlink.h>
+
+namespace android {
+namespace netdevice {
+namespace can {
+
+base::unique_fd socket(const std::string& ifname) {
+ struct sockaddr_can addr = {};
+ addr.can_family = AF_CAN;
+ addr.can_ifindex = nametoindex(ifname);
+ if (addr.can_ifindex == 0) {
+ LOG(ERROR) << "Interface " << ifname << " doesn't exists";
+ return {};
+ }
+
+ base::unique_fd sock(::socket(PF_CAN, SOCK_RAW, CAN_RAW));
+ if (!sock.ok()) {
+ LOG(ERROR) << "Failed to create CAN socket";
+ return {};
+ }
+
+ if (0 != fcntl(sock.get(), F_SETFL, O_RDWR | O_NONBLOCK)) {
+ LOG(ERROR) << "Couldn't put CAN socket in non-blocking mode";
+ return {};
+ }
+
+ if (0 != bind(sock.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr))) {
+ LOG(ERROR) << "Can't bind to CAN interface " << ifname;
+ return {};
+ }
+
+ return sock;
+}
+
+bool setBitrate(std::string ifname, uint32_t bitrate) {
+ struct can_bittiming bt = {};
+ bt.bitrate = bitrate;
+
+ NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST);
+
+ const auto ifidx = nametoindex(ifname);
+ if (ifidx == 0) {
+ LOG(ERROR) << "Can't find interface " << ifname;
+ return false;
+ }
+ req.data().ifi_index = ifidx;
+
+ {
+ auto linkinfo = req.nest(IFLA_LINKINFO);
+ req.addattr(IFLA_INFO_KIND, "can");
+ {
+ auto infodata = req.nest(IFLA_INFO_DATA);
+ /* For CAN FD, it would require to add IFLA_CAN_DATA_BITTIMING
+ * and IFLA_CAN_CTRLMODE as well. */
+ req.addattr(IFLA_CAN_BITTIMING, bt);
+ }
+ }
+
+ NetlinkSocket sock(NETLINK_ROUTE);
+ return sock.send(req) && sock.receiveAck();
+}
+
+} // namespace can
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/common.cpp b/automotive/can/1.0/default/libnetdevice/common.cpp
new file mode 100644
index 0000000..3deac3e
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/common.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "common.h"
+
+#include <android-base/logging.h>
+
+#include <net/if.h>
+
+namespace android {
+namespace netdevice {
+
+unsigned int nametoindex(const std::string& ifname) {
+ const auto ifidx = if_nametoindex(ifname.c_str());
+ if (ifidx != 0) return ifidx;
+
+ const auto err = errno;
+ if (err != ENODEV) {
+ LOG(ERROR) << "if_nametoindex(" << ifname << ") failed: " << err;
+ }
+ return 0;
+}
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/common.h b/automotive/can/1.0/default/libnetdevice/common.h
new file mode 100644
index 0000000..9bdff4d
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/common.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <string>
+
+namespace android {
+namespace netdevice {
+
+/**
+ * Returns the index of a given network interface.
+ *
+ * If the syscall to check the index fails with other error than ENODEV, it gets logged and the
+ * return value indicates the interface doesn't exists.
+ *
+ * \param ifname Interface to check
+ * \return Interface index, or 0 if the interface doesn't exist
+ */
+unsigned int nametoindex(const std::string& ifname);
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/can.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/can.h
new file mode 100644
index 0000000..ec3f962
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/can.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <android-base/unique_fd.h>
+
+#include <string>
+
+namespace android {
+namespace netdevice {
+namespace can {
+
+/**
+ * Opens and binds SocketCAN socket.
+ *
+ * @param ifname Interface to open a socket against
+ * @return Socket's FD or -1 in case of failure
+ */
+base::unique_fd socket(const std::string& ifname);
+
+/**
+ * Sets CAN interface bitrate.
+ */
+bool setBitrate(std::string ifname, uint32_t bitrate);
+
+} // namespace can
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
new file mode 100644
index 0000000..33d5de5
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
@@ -0,0 +1,75 @@
+/*
+ * 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 <optional>
+#include <string>
+
+namespace android {
+namespace netdevice {
+
+/**
+ * Checks, if the network interface exists.
+ *
+ * @param ifname Interface to check
+ * @return true if it exists, false otherwise
+ */
+bool exists(std::string ifname);
+
+/**
+ * Checks if network interface is up.
+ *
+ * @param ifname Interface to check
+ * @return true/false if the check succeeded, nullopt otherwise
+ */
+std::optional<bool> isUp(std::string ifname);
+
+/**
+ * Brings network interface up.
+ *
+ * @param ifname Interface to bring up
+ * @return true in case of success, false otherwise
+ */
+bool up(std::string ifname);
+
+/**
+ * Brings network interface down.
+ *
+ * @param ifname Interface to bring down
+ * @return true in case of success, false otherwise
+ */
+bool down(std::string ifname);
+
+/**
+ * Adds virtual link.
+ *
+ * @param dev the name of the new virtual device
+ * @param type the type of the new device
+ * @return true in case of success, false otherwise
+ */
+bool add(std::string dev, std::string type);
+
+/**
+ * Deletes virtual link.
+ *
+ * @param dev the name of the device to remove
+ * @return true in case of success, false otherwise
+ */
+bool del(std::string dev);
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
new file mode 100644
index 0000000..fc2b193
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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 <libnetdevice/libnetdevice.h>
+
+#include "NetlinkRequest.h"
+#include "NetlinkSocket.h"
+#include "common.h"
+
+#include <android-base/logging.h>
+
+#include <linux/can.h>
+#include <net/if.h>
+
+namespace android {
+namespace netdevice {
+
+bool exists(std::string ifname) {
+ return nametoindex(ifname) != 0;
+}
+
+static bool sendIfreq(unsigned long request, struct ifreq& ifr) {
+ /* For general interfaces it would be socket(AF_INET, SOCK_DGRAM, 0),
+ * but SEPolicy forces us to limit our flexibility here. */
+ base::unique_fd sock(socket(PF_CAN, SOCK_RAW, CAN_RAW));
+ if (!sock.ok()) {
+ LOG(ERROR) << "Can't create socket";
+ return false;
+ }
+
+ if (ioctl(sock.get(), request, &ifr) < 0) {
+ LOG(ERROR) << "ioctl(" << std::hex << request << std::dec << ") failed: " << errno;
+ return false;
+ }
+
+ return true;
+}
+
+static struct ifreq ifreqFromName(const std::string& ifname) {
+ struct ifreq ifr = {};
+ strlcpy(ifr.ifr_name, ifname.c_str(), IF_NAMESIZE);
+ return ifr;
+}
+
+std::optional<bool> isUp(std::string ifname) {
+ struct ifreq ifr = ifreqFromName(ifname);
+ if (!sendIfreq(SIOCGIFFLAGS, ifr)) return std::nullopt;
+ return ifr.ifr_flags & IFF_UP;
+}
+
+bool up(std::string ifname) {
+ struct ifreq ifr = ifreqFromName(ifname);
+ if (!sendIfreq(SIOCGIFFLAGS, ifr)) return false;
+ ifr.ifr_flags |= IFF_UP;
+ return sendIfreq(SIOCSIFFLAGS, ifr);
+}
+
+bool down(std::string ifname) {
+ struct ifreq ifr = ifreqFromName(ifname);
+ if (!sendIfreq(SIOCGIFFLAGS, ifr)) return false;
+ ifr.ifr_flags &= ~IFF_UP;
+ return sendIfreq(SIOCSIFFLAGS, ifr);
+}
+
+bool add(std::string dev, std::string type) {
+ NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
+ req.addattr(IFLA_IFNAME, dev);
+
+ {
+ auto linkinfo = req.nest(IFLA_LINKINFO);
+ req.addattr(IFLA_INFO_KIND, type);
+ }
+
+ NetlinkSocket sock(NETLINK_ROUTE);
+ return sock.send(req) && sock.receiveAck();
+}
+
+bool del(std::string dev) {
+ NetlinkRequest<struct ifinfomsg> req(RTM_DELLINK, NLM_F_REQUEST);
+ req.addattr(IFLA_IFNAME, dev);
+
+ NetlinkSocket sock(NETLINK_ROUTE);
+ return sock.send(req) && sock.receiveAck();
+}
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/service.cpp b/automotive/can/1.0/default/service.cpp
new file mode 100644
index 0000000..7ef44fc
--- /dev/null
+++ b/automotive/can/1.0/default/service.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 "CanController.h"
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+static void canControllerService() {
+ base::SetDefaultTag("CanController");
+ base::SetMinimumLogSeverity(android::base::VERBOSE);
+ configureRpcThreadpool(16, true);
+ LOG(DEBUG) << "CAN controller service starting...";
+
+ CanController canController;
+ if (canController.registerAsService("socketcan") != OK) {
+ LOG(FATAL) << "Failed to register CAN controller";
+ return;
+ }
+
+ LOG(INFO) << "CAN controller service ready";
+ joinRpcThreadpool();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+int main() {
+ ::android::hardware::automotive::can::V1_0::implementation::canControllerService();
+ return 1; // canBusService (joinRpcThreadpool) shouldn't exit
+}