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/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