Merge "Move Tuner 1.1 new RecordFilterEvent fields into a separate struct"
diff --git a/automotive/can/1.0/default/CanBus.cpp b/automotive/can/1.0/default/CanBus.cpp
index 8b98e5e..2efe054 100644
--- a/automotive/can/1.0/default/CanBus.cpp
+++ b/automotive/can/1.0/default/CanBus.cpp
@@ -254,7 +254,7 @@
                                satisfiesFilterFlag(rule.extendedFormat, isExtendedId);
 
         if (rule.exclude) {
-            // Any excluded (blacklist) rule not being satisfied invalidates the whole filter set.
+            // Any exclude rule being satisfied invalidates the whole filter set.
             if (satisfied) return false;
         } else {
             anyNonExcludeRulePresent = true;
diff --git a/automotive/can/1.0/default/libnetdevice/can.cpp b/automotive/can/1.0/default/libnetdevice/can.cpp
index c6f1b04..083f4f0 100644
--- a/automotive/can/1.0/default/libnetdevice/can.cpp
+++ b/automotive/can/1.0/default/libnetdevice/can.cpp
@@ -20,8 +20,8 @@
 
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
-#include <libnl++/NetlinkRequest.h>
-#include <libnl++/NetlinkSocket.h>
+#include <libnl++/MessageFactory.h>
+#include <libnl++/Socket.h>
 
 #include <linux/can.h>
 #include <linux/can/error.h>
@@ -34,7 +34,7 @@
 static constexpr can_err_mask_t kErrMask = CAN_ERR_MASK;
 
 base::unique_fd socket(const std::string& ifname) {
-    struct sockaddr_can addr = {};
+    sockaddr_can addr = {};
     addr.can_family = AF_CAN;
     addr.can_ifindex = nametoindex(ifname);
     if (addr.can_ifindex == 0) {
@@ -58,7 +58,7 @@
         return {};
     }
 
-    if (0 != bind(sock.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr))) {
+    if (0 != bind(sock.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))) {
         LOG(ERROR) << "Can't bind to CAN interface " << ifname;
         return {};
     }
@@ -67,31 +67,30 @@
 }
 
 bool setBitrate(std::string ifname, uint32_t bitrate) {
-    struct can_bittiming bt = {};
+    can_bittiming bt = {};
     bt.bitrate = bitrate;
 
-    nl::NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST);
+    nl::MessageFactory<ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
 
-    const auto ifidx = nametoindex(ifname);
-    if (ifidx == 0) {
+    req->ifi_index = nametoindex(ifname);
+    if (req->ifi_index == 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 linkinfo = req.addNested(IFLA_LINKINFO);
+        req.add(IFLA_INFO_KIND, "can");
         {
-            auto infodata = req.nest(IFLA_INFO_DATA);
+            auto infodata = req.addNested(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);
+            req.add(IFLA_CAN_BITTIMING, bt);
         }
     }
 
-    nl::NetlinkSocket sock(NETLINK_ROUTE);
-    return sock.send(req) && sock.receiveAck();
+    nl::Socket sock(NETLINK_ROUTE);
+    return sock.send(req) && sock.receiveAck(req);
 }
 
 }  // namespace android::netdevice::can
diff --git a/automotive/can/1.0/default/libnetdevice/common.h b/automotive/can/1.0/default/libnetdevice/common.h
index 201909f..661e3f8 100644
--- a/automotive/can/1.0/default/libnetdevice/common.h
+++ b/automotive/can/1.0/default/libnetdevice/common.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <libnl++/nlbuf.h>
-
 #include <linux/can.h>
 #include <net/if.h>
 
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
index 9a26ff1..70cb688 100644
--- a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
@@ -20,6 +20,7 @@
 
 #include <array>
 #include <optional>
+#include <set>
 #include <string>
 
 namespace android::netdevice {
@@ -53,14 +54,34 @@
 std::optional<bool> isUp(std::string ifname);
 
 /**
- * Checks, if the network interface exists and is up.
- *
- * This is a convenience function to call both exists() and isUp().
- *
- * \param ifname Interface to check
- * \return true if the interface is up, false otherwise
+ * Interface condition to wait for.
  */
-bool existsAndIsUp(const std::string& ifname);
+enum class WaitCondition {
+    /**
+     * Interface is present (but not necessarily up).
+     */
+    PRESENT,
+
+    /**
+     * Interface is up.
+     */
+    PRESENT_AND_UP,
+
+    /**
+     * Interface is down or not present (disconnected) at all.
+     */
+    DOWN_OR_GONE,
+};
+
+/**
+ * Listens for interface changes until anticipated condition takes place.
+ *
+ * \param ifnames List of interfaces to watch for.
+ * \param cnd Awaited condition.
+ * \param allOf true if all interfaces need to satisfy the condition, false if only one satistying
+ *        interface should stop the wait.
+ */
+void waitFor(std::set<std::string> ifnames, WaitCondition cnd, bool allOf = true);
 
 /**
  * Brings network interface up.
diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
index aeb5005..4c5b309 100644
--- a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
+++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
@@ -20,13 +20,15 @@
 #include "ifreqs.h"
 
 #include <android-base/logging.h>
-#include <libnl++/NetlinkRequest.h>
-#include <libnl++/NetlinkSocket.h>
+#include <libnl++/MessageFactory.h>
+#include <libnl++/Socket.h>
 
 #include <linux/can.h>
 #include <linux/rtnetlink.h>
 #include <net/if.h>
 
+#include <sstream>
+
 namespace android::netdevice {
 
 void useSocketDomain(int domain) {
@@ -37,16 +39,6 @@
     return nametoindex(ifname) != 0;
 }
 
-std::optional<bool> isUp(std::string ifname) {
-    auto ifr = ifreqs::fromName(ifname);
-    if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return std::nullopt;
-    return ifr.ifr_flags & IFF_UP;
-}
-
-bool existsAndIsUp(const std::string& ifname) {
-    return exists(ifname) && isUp(ifname).value_or(false);
-}
-
 bool up(std::string ifname) {
     auto ifr = ifreqs::fromName(ifname);
     if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return false;
@@ -62,25 +54,25 @@
 }
 
 bool add(std::string dev, std::string type) {
-    nl::NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK,
-                                             NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
-    req.addattr(IFLA_IFNAME, dev);
+    nl::MessageFactory<ifinfomsg> req(RTM_NEWLINK,
+                                      NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
+    req.add(IFLA_IFNAME, dev);
 
     {
-        auto linkinfo = req.nest(IFLA_LINKINFO);
-        req.addattr(IFLA_INFO_KIND, type);
+        auto linkinfo = req.addNested(IFLA_LINKINFO);
+        req.add(IFLA_INFO_KIND, type);
     }
 
-    nl::NetlinkSocket sock(NETLINK_ROUTE);
-    return sock.send(req) && sock.receiveAck();
+    nl::Socket sock(NETLINK_ROUTE);
+    return sock.send(req) && sock.receiveAck(req);
 }
 
 bool del(std::string dev) {
-    nl::NetlinkRequest<struct ifinfomsg> req(RTM_DELLINK, NLM_F_REQUEST);
-    req.addattr(IFLA_IFNAME, dev);
+    nl::MessageFactory<ifinfomsg> req(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK);
+    req.add(IFLA_IFNAME, dev);
 
-    nl::NetlinkSocket sock(NETLINK_ROUTE);
-    return sock.send(req) && sock.receiveAck();
+    nl::Socket sock(NETLINK_ROUTE);
+    return sock.send(req) && sock.receiveAck(req);
 }
 
 std::optional<hwaddr_t> getHwAddr(const std::string& ifname) {
@@ -102,6 +94,97 @@
     return ifreqs::send(SIOCSIFHWADDR, ifr);
 }
 
+std::optional<bool> isUp(std::string ifname) {
+    auto ifr = ifreqs::fromName(ifname);
+    if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return std::nullopt;
+    return ifr.ifr_flags & IFF_UP;
+}
+
+struct WaitState {
+    bool present;
+    bool up;
+
+    bool satisfied(WaitCondition cnd) const {
+        switch (cnd) {
+            case WaitCondition::PRESENT:
+                if (present) return true;
+                break;
+            case WaitCondition::PRESENT_AND_UP:
+                if (present && up) return true;
+                break;
+            case WaitCondition::DOWN_OR_GONE:
+                if (!present || !up) return true;
+                break;
+        }
+        return false;
+    }
+};
+
+static std::string toString(WaitCondition cnd) {
+    switch (cnd) {
+        case WaitCondition::PRESENT:
+            return "become present";
+        case WaitCondition::PRESENT_AND_UP:
+            return "come up";
+        case WaitCondition::DOWN_OR_GONE:
+            return "go down";
+    }
+}
+
+static std::string toString(const std::set<std::string>& ifnames) {
+    std::stringstream ss;
+    std::copy(ifnames.begin(), ifnames.end(), std::ostream_iterator<std::string>(ss, ","));
+    auto str = ss.str();
+    str.pop_back();
+    return str;
+}
+
+void waitFor(std::set<std::string> ifnames, WaitCondition cnd, bool allOf) {
+    nl::Socket sock(NETLINK_ROUTE, 0, RTMGRP_LINK);
+
+    using StatesMap = std::map<std::string, WaitState>;
+    StatesMap states = {};
+    for (const auto ifname : ifnames) {
+        const auto present = exists(ifname);
+        const auto up = present && isUp(ifname).value_or(false);
+        states[ifname] = {present, up};
+    }
+
+    const auto mapConditionChecker = [cnd](const StatesMap::iterator::value_type& it) {
+        return it.second.satisfied(cnd);
+    };
+    const auto isFullySatisfied = [&states, allOf, mapConditionChecker]() {
+        if (allOf) {
+            return std::all_of(states.begin(), states.end(), mapConditionChecker);
+        } else {
+            return std::any_of(states.begin(), states.end(), mapConditionChecker);
+        }
+    };
+
+    if (isFullySatisfied()) return;
+
+    LOG(DEBUG) << "Waiting for " << (allOf ? "" : "any of ") << toString(ifnames) << " to "
+               << toString(cnd);
+    for (const auto rawMsg : sock) {
+        const auto msg = nl::Message<ifinfomsg>::parse(rawMsg, {RTM_NEWLINK, RTM_DELLINK});
+        if (!msg.has_value()) continue;
+
+        const auto ifname = msg->attributes.get<std::string>(IFLA_IFNAME);
+        if (ifnames.count(ifname) == 0) continue;
+
+        const bool present = (msg->header.nlmsg_type != RTM_DELLINK);
+        const bool up = present && (msg->data.ifi_flags & IFF_UP) != 0;
+        states[ifname] = {present, up};
+
+        if (isFullySatisfied()) {
+            LOG(DEBUG) << "Finished waiting for " << (allOf ? "" : "some of ") << toString(ifnames)
+                       << " to " << toString(cnd);
+            return;
+        }
+    }
+    LOG(FATAL) << "Can't read Netlink socket";
+}
+
 }  // namespace android::netdevice
 
 bool operator==(const android::netdevice::hwaddr_t lhs, const unsigned char rhs[ETH_ALEN]) {
diff --git a/automotive/can/1.0/default/libnetdevice/vlan.cpp b/automotive/can/1.0/default/libnetdevice/vlan.cpp
index e419154..ee02f7b 100644
--- a/automotive/can/1.0/default/libnetdevice/vlan.cpp
+++ b/automotive/can/1.0/default/libnetdevice/vlan.cpp
@@ -19,8 +19,8 @@
 #include "common.h"
 
 #include <android-base/logging.h>
-#include <libnl++/NetlinkRequest.h>
-#include <libnl++/NetlinkSocket.h>
+#include <libnl++/MessageFactory.h>
+#include <libnl++/Socket.h>
 
 #include <linux/rtnetlink.h>
 
@@ -33,23 +33,23 @@
         return false;
     }
 
-    nl::NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK,
-                                             NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
-    req.addattr(IFLA_IFNAME, vlan);
-    req.addattr<uint32_t>(IFLA_LINK, ethidx);
+    nl::MessageFactory<ifinfomsg> req(RTM_NEWLINK,
+                                      NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
+    req.add(IFLA_IFNAME, vlan);
+    req.add<uint32_t>(IFLA_LINK, ethidx);
 
     {
-        auto linkinfo = req.nest(IFLA_LINKINFO);
-        req.addattr(IFLA_INFO_KIND, "vlan");
+        auto linkinfo = req.addNested(IFLA_LINKINFO);
+        req.add(IFLA_INFO_KIND, "vlan");
 
         {
-            auto linkinfo = req.nest(IFLA_INFO_DATA);
-            req.addattr(IFLA_VLAN_ID, id);
+            auto linkinfo = req.addNested(IFLA_INFO_DATA);
+            req.add(IFLA_VLAN_ID, id);
         }
     }
 
-    nl::NetlinkSocket sock(NETLINK_ROUTE);
-    return sock.send(req) && sock.receiveAck();
+    nl::Socket sock(NETLINK_ROUTE);
+    return sock.send(req) && sock.receiveAck(req);
 }
 
 }  // namespace android::netdevice::vlan
diff --git a/automotive/can/1.0/default/libnl++/Android.bp b/automotive/can/1.0/default/libnl++/Android.bp
index cffefe7..4042b16 100644
--- a/automotive/can/1.0/default/libnl++/Android.bp
+++ b/automotive/can/1.0/default/libnl++/Android.bp
@@ -31,8 +31,9 @@
         "protocols/MessageDefinition.cpp",
         "protocols/NetlinkProtocol.cpp",
         "protocols/all.cpp",
-        "NetlinkRequest.cpp",
-        "NetlinkSocket.cpp",
+        "Attributes.cpp",
+        "MessageFactory.cpp",
+        "Socket.cpp",
         "common.cpp",
         "printer.cpp",
     ],
diff --git a/automotive/can/1.0/default/libnl++/Attributes.cpp b/automotive/can/1.0/default/libnl++/Attributes.cpp
new file mode 100644
index 0000000..620f57b
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/Attributes.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 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 <libnl++/Attributes.h>
+
+namespace android::nl {
+
+Attributes::Attributes() {}
+
+Attributes::Attributes(Buffer<nlattr> buffer) : Buffer<nlattr>(buffer) {}
+
+const Attributes::Index& Attributes::index() const {
+    if (mIndex.has_value()) return *mIndex;
+
+    mIndex = Index();
+    auto& index = *mIndex;
+
+    for (auto attr : static_cast<Buffer<nlattr>>(*this)) {
+        index.emplace(attr->nla_type, attr);
+    }
+
+    return index;
+}
+
+bool Attributes::contains(nlattrtype_t attrtype) const {
+    return index().count(attrtype) > 0;
+}
+
+/* Parser specializations for selected types (more to come if necessary). */
+
+template <>
+Attributes Attributes::parse(Buffer<nlattr> buf) {
+    return buf.data<nlattr>();
+}
+
+template <>
+std::string Attributes::parse(Buffer<nlattr> buf) {
+    const auto rawString = buf.data<char>().getRaw();
+    std::string str(rawString.ptr(), rawString.len());
+
+    str.erase(std::find(str.begin(), str.end(), '\0'), str.end());
+
+    return str;
+}
+
+template <typename T>
+static T parseUnsigned(Buffer<nlattr> buf) {
+    return buf.data<T>().copyFirst();
+}
+
+template <>
+uint8_t Attributes::parse(Buffer<nlattr> buf) {
+    return parseUnsigned<uint8_t>(buf);
+}
+
+template <>
+uint16_t Attributes::parse(Buffer<nlattr> buf) {
+    return parseUnsigned<uint16_t>(buf);
+}
+
+template <>
+uint32_t Attributes::parse(Buffer<nlattr> buf) {
+    return parseUnsigned<uint32_t>(buf);
+}
+
+template <>
+uint64_t Attributes::parse(Buffer<nlattr> buf) {
+    return parseUnsigned<uint64_t>(buf);
+}
+
+}  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/MessageFactory.cpp b/automotive/can/1.0/default/libnl++/MessageFactory.cpp
new file mode 100644
index 0000000..6f35897
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/MessageFactory.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <libnl++/MessageFactory.h>
+
+#include <android-base/logging.h>
+#include <libnl++/bits.h>
+
+namespace android::nl {
+
+static nlattr* tail(nlmsghdr* msg) {
+    return reinterpret_cast<nlattr*>(uintptr_t(msg) + impl::align(msg->nlmsg_len));
+}
+
+nlattr* MessageFactoryBase::add(nlmsghdr* msg, size_t maxLen, nlattrtype_t type, const void* data,
+                                size_t dataLen) {
+    const auto totalAttrLen = impl::space<nlattr>(dataLen);
+    const auto newLen = impl::align(msg->nlmsg_len) + totalAttrLen;
+    if (newLen > maxLen) {
+        LOG(ERROR) << "Can't add attribute of size " << dataLen  //
+                   << " - exceeded maxLen: " << newLen << " > " << maxLen;
+        return nullptr;
+    }
+
+    auto attr = tail(msg);
+    attr->nla_len = totalAttrLen;
+    attr->nla_type = type;
+    if (dataLen > 0) memcpy(impl::data<nlattr, void>(attr), data, dataLen);
+
+    msg->nlmsg_len = newLen;
+    return attr;
+}
+
+void MessageFactoryBase::closeNested(nlmsghdr* msg, nlattr* nested) {
+    if (nested == nullptr) return;
+    nested->nla_len = uintptr_t(tail(msg)) - uintptr_t(nested);
+}
+
+}  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/NetlinkRequest.cpp b/automotive/can/1.0/default/libnl++/NetlinkRequest.cpp
deleted file mode 100644
index b12489c..0000000
--- a/automotive/can/1.0/default/libnl++/NetlinkRequest.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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 <libnl++/NetlinkRequest.h>
-
-#include <android-base/logging.h>
-
-// for RTA_ macros missing from NLA_ definitions
-#include <linux/rtnetlink.h>
-
-namespace android::nl::impl {
-
-static struct nlattr* nlmsg_tail(struct nlmsghdr* n) {
-    return reinterpret_cast<struct nlattr*>(  //
-            reinterpret_cast<uintptr_t>(n) + NLMSG_ALIGN(n->nlmsg_len));
-}
-
-struct nlattr* addattr_l(struct nlmsghdr* n, size_t maxLen, nlattrtype_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->nla_len = RTA_SPACE(dataLen);
-    attr->nla_type = type;
-    if (dataLen > 0) memcpy(RTA_DATA(attr), data, dataLen);
-
-    n->nlmsg_len = newLen;
-    return attr;
-}
-
-struct nlattr* addattr_nest(struct nlmsghdr* n, size_t maxLen, nlattrtype_t type) {
-    return addattr_l(n, maxLen, type, nullptr, 0);
-}
-
-void addattr_nest_end(struct nlmsghdr* n, struct nlattr* nest) {
-    size_t nestLen = reinterpret_cast<uintptr_t>(nlmsg_tail(n)) - reinterpret_cast<uintptr_t>(nest);
-    nest->nla_len = nestLen;
-}
-
-}  // namespace android::nl::impl
diff --git a/automotive/can/1.0/default/libnl++/NetlinkSocket.cpp b/automotive/can/1.0/default/libnl++/NetlinkSocket.cpp
deleted file mode 100644
index 6f0f0c2..0000000
--- a/automotive/can/1.0/default/libnl++/NetlinkSocket.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * 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 <libnl++/NetlinkSocket.h>
-
-#include <libnl++/printer.h>
-
-#include <android-base/logging.h>
-
-namespace android::nl {
-
-/**
- * Print all outbound/inbound Netlink messages.
- */
-static constexpr bool kSuperVerbose = false;
-
-NetlinkSocket::NetlinkSocket(int protocol, unsigned int pid, uint32_t groups)
-    : mProtocol(protocol) {
-    mFd.reset(socket(AF_NETLINK, SOCK_RAW, protocol));
-    if (!mFd.ok()) {
-        PLOG(ERROR) << "Can't open Netlink socket";
-        mFailed = true;
-        return;
-    }
-
-    sockaddr_nl sa = {};
-    sa.nl_family = AF_NETLINK;
-    sa.nl_pid = pid;
-    sa.nl_groups = groups;
-
-    if (bind(mFd.get(), reinterpret_cast<sockaddr*>(&sa), sizeof(sa)) < 0) {
-        PLOG(ERROR) << "Can't bind Netlink socket";
-        mFd.reset();
-        mFailed = true;
-    }
-}
-
-bool NetlinkSocket::send(nlmsghdr* nlmsg, size_t totalLen) {
-    if constexpr (kSuperVerbose) {
-        nlmsg->nlmsg_seq = mSeq;
-        LOG(VERBOSE) << (mFailed ? "(not) " : "")
-                     << "sending Netlink message: " << toString({nlmsg, totalLen}, mProtocol);
-    }
-
-    if (mFailed) return false;
-
-    nlmsg->nlmsg_pid = 0;  // kernel
-    nlmsg->nlmsg_seq = mSeq++;
-    nlmsg->nlmsg_flags |= NLM_F_ACK;
-
-    iovec iov = {nlmsg, nlmsg->nlmsg_len};
-
-    sockaddr_nl sa = {};
-    sa.nl_family = AF_NETLINK;
-
-    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) {
-        PLOG(ERROR) << "Can't send Netlink message";
-        return false;
-    }
-    return true;
-}
-
-bool NetlinkSocket::send(const nlbuf<nlmsghdr>& msg, const sockaddr_nl& sa) {
-    if constexpr (kSuperVerbose) {
-        LOG(VERBOSE) << (mFailed ? "(not) " : "")
-                     << "sending Netlink message: " << toString(msg, mProtocol);
-    }
-
-    if (mFailed) return false;
-    const auto rawMsg = msg.getRaw();
-    const auto bytesSent = sendto(mFd.get(), rawMsg.ptr(), rawMsg.len(), 0,
-                                  reinterpret_cast<const sockaddr*>(&sa), sizeof(sa));
-    if (bytesSent < 0) {
-        PLOG(ERROR) << "Can't send Netlink message";
-        return false;
-    }
-    return true;
-}
-
-std::optional<nlbuf<nlmsghdr>> NetlinkSocket::receive(void* buf, size_t bufLen) {
-    sockaddr_nl sa = {};
-    return receive(buf, bufLen, sa);
-}
-
-std::optional<nlbuf<nlmsghdr>> NetlinkSocket::receive(void* buf, size_t bufLen, sockaddr_nl& sa) {
-    if (mFailed) return std::nullopt;
-
-    socklen_t saLen = sizeof(sa);
-    if (bufLen == 0) {
-        LOG(ERROR) << "Receive buffer has zero size!";
-        return std::nullopt;
-    }
-    const auto bytesReceived =
-            recvfrom(mFd.get(), buf, bufLen, MSG_TRUNC, reinterpret_cast<sockaddr*>(&sa), &saLen);
-    if (bytesReceived <= 0) {
-        PLOG(ERROR) << "Failed to receive Netlink message";
-        return std::nullopt;
-    } else if (unsigned(bytesReceived) > bufLen) {
-        PLOG(ERROR) << "Received data larger than the receive buffer! " << bytesReceived << " > "
-                    << bufLen;
-        return std::nullopt;
-    }
-
-    nlbuf<nlmsghdr> msg(reinterpret_cast<nlmsghdr*>(buf), bytesReceived);
-    if constexpr (kSuperVerbose) {
-        LOG(VERBOSE) << "received " << toString(msg, mProtocol);
-    }
-    return msg;
-}
-
-/* TODO(161389935): Migrate receiveAck to use nlmsg<> internally. Possibly reuse
- * NetlinkSocket::receive(). */
-bool NetlinkSocket::receiveAck() {
-    if (mFailed) return false;
-
-    char buf[8192];
-
-    sockaddr_nl sa;
-    iovec iov = {buf, sizeof(buf)};
-
-    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) {
-        PLOG(ERROR) << "Failed to receive Netlink message";
-        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<nlmsghdr*>(buf); NLMSG_OK(nlmsg, remainingLen);
-         nlmsg = NLMSG_NEXT(nlmsg, remainingLen)) {
-        if constexpr (kSuperVerbose) {
-            LOG(VERBOSE) << "received Netlink response: "
-                         << toString({nlmsg, nlmsg->nlmsg_len}, mProtocol);
-        }
-
-        // 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.
-        const auto nlerr = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(nlmsg));
-        if (nlerr->error != 0) {
-            LOG(ERROR) << "Received Netlink error message: " << strerror(-nlerr->error);
-            return false;
-        }
-        return true;
-    }
-    // Couldn't find any error/ack messages.
-    return false;
-}
-
-std::optional<unsigned int> NetlinkSocket::getSocketPid() {
-    sockaddr_nl sa = {};
-    socklen_t sasize = sizeof(sa);
-    if (getsockname(mFd.get(), reinterpret_cast<sockaddr*>(&sa), &sasize) < 0) {
-        PLOG(ERROR) << "Failed to getsockname() for netlink_fd!";
-        return std::nullopt;
-    }
-    return sa.nl_pid;
-}
-
-}  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/Socket.cpp b/automotive/can/1.0/default/libnl++/Socket.cpp
new file mode 100644
index 0000000..08683ca
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/Socket.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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 <libnl++/Socket.h>
+
+#include <libnl++/printer.h>
+
+#include <android-base/logging.h>
+
+namespace android::nl {
+
+/**
+ * Print all outbound/inbound Netlink messages.
+ */
+static constexpr bool kSuperVerbose = false;
+
+Socket::Socket(int protocol, unsigned pid, uint32_t groups) : mProtocol(protocol) {
+    mFd.reset(socket(AF_NETLINK, SOCK_RAW, protocol));
+    if (!mFd.ok()) {
+        PLOG(ERROR) << "Can't open Netlink socket";
+        mFailed = true;
+        return;
+    }
+
+    sockaddr_nl sa = {};
+    sa.nl_family = AF_NETLINK;
+    sa.nl_pid = pid;
+    sa.nl_groups = groups;
+
+    if (bind(mFd.get(), reinterpret_cast<sockaddr*>(&sa), sizeof(sa)) < 0) {
+        PLOG(ERROR) << "Can't bind Netlink socket";
+        mFd.reset();
+        mFailed = true;
+    }
+}
+
+bool Socket::send(const Buffer<nlmsghdr>& msg, const sockaddr_nl& sa) {
+    if constexpr (kSuperVerbose) {
+        LOG(VERBOSE) << (mFailed ? "(not) " : "") << "sending to " << sa.nl_pid << ": "
+                     << toString(msg, mProtocol);
+    }
+    if (mFailed) return false;
+
+    mSeq = msg->nlmsg_seq;
+    const auto rawMsg = msg.getRaw();
+    const auto bytesSent = sendto(mFd.get(), rawMsg.ptr(), rawMsg.len(), 0,
+                                  reinterpret_cast<const sockaddr*>(&sa), sizeof(sa));
+    if (bytesSent < 0) {
+        PLOG(ERROR) << "Can't send Netlink message";
+        return false;
+    } else if (size_t(bytesSent) != rawMsg.len()) {
+        LOG(ERROR) << "Can't send Netlink message: truncated message";
+        return false;
+    }
+    return true;
+}
+
+bool Socket::increaseReceiveBuffer(size_t maxSize) {
+    if (maxSize == 0) {
+        LOG(ERROR) << "Maximum receive size should not be zero";
+        return false;
+    }
+
+    if (mReceiveBuffer.size() < maxSize) mReceiveBuffer.resize(maxSize);
+    return true;
+}
+
+std::optional<Buffer<nlmsghdr>> Socket::receive(size_t maxSize) {
+    return receiveFrom(maxSize).first;
+}
+
+std::pair<std::optional<Buffer<nlmsghdr>>, sockaddr_nl> Socket::receiveFrom(size_t maxSize) {
+    if (mFailed) return {std::nullopt, {}};
+
+    if (!increaseReceiveBuffer(maxSize)) return {std::nullopt, {}};
+
+    sockaddr_nl sa = {};
+    socklen_t saLen = sizeof(sa);
+    const auto bytesReceived = recvfrom(mFd.get(), mReceiveBuffer.data(), maxSize, MSG_TRUNC,
+                                        reinterpret_cast<sockaddr*>(&sa), &saLen);
+
+    if (bytesReceived <= 0) {
+        PLOG(ERROR) << "Failed to receive Netlink message";
+        return {std::nullopt, {}};
+    } else if (size_t(bytesReceived) > maxSize) {
+        PLOG(ERROR) << "Received data larger than maximum receive size: "  //
+                    << bytesReceived << " > " << maxSize;
+        return {std::nullopt, {}};
+    }
+
+    Buffer<nlmsghdr> msg(reinterpret_cast<nlmsghdr*>(mReceiveBuffer.data()), bytesReceived);
+    if constexpr (kSuperVerbose) {
+        LOG(VERBOSE) << "received from " << sa.nl_pid << ": " << toString(msg, mProtocol);
+    }
+    return {msg, sa};
+}
+
+bool Socket::receiveAck(uint32_t seq) {
+    const auto nlerr = receive<nlmsgerr>({NLMSG_ERROR});
+    if (!nlerr.has_value()) return false;
+
+    if (nlerr->data.msg.nlmsg_seq != seq) {
+        LOG(ERROR) << "Received ACK for a different message (" << nlerr->data.msg.nlmsg_seq
+                   << ", expected " << seq << "). Multi-message tracking is not implemented.";
+        return false;
+    }
+
+    if (nlerr->data.error == 0) return true;
+
+    LOG(WARNING) << "Received Netlink error message: " << strerror(-nlerr->data.error);
+    return false;
+}
+
+std::optional<Buffer<nlmsghdr>> Socket::receive(const std::set<nlmsgtype_t>& msgtypes,
+                                                size_t maxSize) {
+    if (mFailed || !increaseReceiveBuffer(maxSize)) return std::nullopt;
+
+    for (const auto rawMsg : *this) {
+        if (msgtypes.count(rawMsg->nlmsg_type) == 0) {
+            LOG(WARNING) << "Received (and ignored) unexpected Netlink message of type "
+                         << rawMsg->nlmsg_type;
+            continue;
+        }
+
+        return rawMsg;
+    }
+
+    return std::nullopt;
+}
+
+std::optional<unsigned> Socket::getPid() {
+    if (mFailed) return std::nullopt;
+
+    sockaddr_nl sa = {};
+    socklen_t sasize = sizeof(sa);
+    if (getsockname(mFd.get(), reinterpret_cast<sockaddr*>(&sa), &sasize) < 0) {
+        PLOG(ERROR) << "Failed to get PID of Netlink socket";
+        return std::nullopt;
+    }
+    return sa.nl_pid;
+}
+
+Socket::receive_iterator::receive_iterator(Socket& socket, bool end)
+    : mSocket(socket), mIsEnd(end) {
+    if (!end) receive();
+}
+
+Socket::receive_iterator Socket::receive_iterator::operator++() {
+    CHECK(!mIsEnd) << "Trying to increment end iterator";
+    ++mCurrent;
+    if (mCurrent.isEnd()) receive();
+    return *this;
+}
+
+bool Socket::receive_iterator::operator==(const receive_iterator& other) const {
+    if (mIsEnd != other.mIsEnd) return false;
+    if (mIsEnd && other.mIsEnd) return true;
+    return mCurrent == other.mCurrent;
+}
+
+const Buffer<nlmsghdr>& Socket::receive_iterator::operator*() const {
+    CHECK(!mIsEnd) << "Trying to dereference end iterator";
+    return *mCurrent;
+}
+
+void Socket::receive_iterator::receive() {
+    CHECK(!mIsEnd) << "Trying to receive on end iterator";
+    CHECK(mCurrent.isEnd()) << "Trying to receive without draining previous read";
+
+    const auto buf = mSocket.receive();
+    if (buf.has_value()) {
+        mCurrent = buf->begin();
+    } else {
+        mIsEnd = true;
+    }
+}
+
+Socket::receive_iterator Socket::begin() {
+    return {*this, false};
+}
+
+Socket::receive_iterator Socket::end() {
+    return {*this, true};
+}
+
+}  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/common.cpp b/automotive/can/1.0/default/libnl++/common.cpp
index 7848646..23c2d94 100644
--- a/automotive/can/1.0/default/libnl++/common.cpp
+++ b/automotive/can/1.0/default/libnl++/common.cpp
@@ -32,16 +32,14 @@
     return 0;
 }
 
-std::string sanitize(std::string str) {
-    str.erase(std::find(str.begin(), str.end(), '\0'), str.end());
-
+std::string printableOnly(std::string str) {
     const auto isInvalid = [](char c) { return !isprint(c); };
     std::replace_if(str.begin(), str.end(), isInvalid, '?');
 
     return str;
 }
 
-uint16_t crc16(const nlbuf<uint8_t> data, uint16_t crc) {
+uint16_t crc16(const Buffer<uint8_t> data, uint16_t crc) {
     for (const auto byte : data.getRaw()) {
         crc ^= byte;
         for (unsigned i = 0; i < 8; i++) {
diff --git a/automotive/can/1.0/default/libnl++/common.h b/automotive/can/1.0/default/libnl++/common.h
index dc5777c..38263c5 100644
--- a/automotive/can/1.0/default/libnl++/common.h
+++ b/automotive/can/1.0/default/libnl++/common.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <libnl++/nlbuf.h>
+#include <libnl++/Buffer.h>
 
 #include <linux/can.h>
 #include <net/if.h>
@@ -37,11 +37,14 @@
 unsigned int nametoindex(const std::string& ifname);
 
 /**
- * Sanitize a string of unknown contents.
+ * Filter a string against non-printable characters.
  *
- * Trims the string to the first '\0' character and replaces all non-printable characters with '?'.
+ * Replaces all non-printable characters with '?'.
+ *
+ * \param str String to filter.
+ * \return Filtered string.
  */
-std::string sanitize(std::string str);
+std::string printableOnly(std::string str);
 
 /**
  * Calculates a (optionally running) CRC16 checksum.
@@ -54,6 +57,6 @@
  * \param crc Previous CRC16 value to continue calculating running checksum
  * \return CRC16 checksum
  */
-uint16_t crc16(const nlbuf<uint8_t> data, uint16_t crc = 0);
+uint16_t crc16(const Buffer<uint8_t> data, uint16_t crc = 0);
 
 }  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h b/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h
new file mode 100644
index 0000000..f16d997
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 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/logging.h>
+#include <libnl++/Buffer.h>
+#include <libnl++/types.h>
+#include <utils/Mutex.h>
+
+#include <map>
+
+namespace android::nl {
+
+/**
+ * Netlink attribute map.
+ *
+ * This is a C++-style, memory safe(r) implementation of linux/netlink.h macros accessing Netlink
+ * message attributes. The class doesn't own the underlying data, so the instance is valid as long
+ * as the source buffer is allocated and unmodified.
+ *
+ * WARNING: this class is NOT thread-safe (it's safe to be used in multithreaded application, but
+ * a single instance can only be used by a single thread - the one owning the underlying buffer).
+ */
+class Attributes : private Buffer<nlattr> {
+  public:
+    /**
+     * Constructs empty attribute map.
+     */
+    Attributes();
+
+    /**
+     * Construct attribute map from underlying buffer.
+     *
+     * \param buffer Source buffer pointing at the first attribute.
+     */
+    Attributes(Buffer<nlattr> buffer);
+
+    /**
+     * Checks, if the map contains given attribute type (key).
+     *
+     * \param attrtype Attribute type (such as IFLA_IFNAME).
+     * \return true if attribute is in the map, false otherwise.
+     */
+    bool contains(nlattrtype_t attrtype) const;
+
+    /**
+     * Fetches attribute of a given type by copying it.
+     *
+     * While this is quite efficient for simple types, fetching nested attribute creates a new copy
+     * of child attribute map. This may be costly if you calculate the index for child maps multiple
+     * times. Examples below.
+     *
+     * BAD:
+     * ```
+     * const auto flags = msg->attributes.
+     *     get<nl::Attributes>(IFLA_AF_SPEC).
+     *     get<nl::Attributes>(AF_INET6).  // IFLA_AF_SPEC index lazy-calculated
+     *     get<uint32_t>(IFLA_INET6_FLAGS);  // AF_INET6 index lazy-calculated
+     * const auto& cacheinfo = msg->attributes.
+     *     get<nl::Attributes>(IFLA_AF_SPEC).  // new instance of IFLA_AF_SPEC index
+     *     get<nl::Attributes>(AF_INET6).  // IFLA_AF_SPEC index calculated again
+     *     getStruct<ifla_cacheinfo>(IFLA_INET6_CACHEINFO);  // AF_INET6 calculated again
+     * ```
+     *
+     * GOOD:
+     * ```
+     * const auto inet6 = msg->attributes.
+     *     get<nl::Attributes>(IFLA_AF_SPEC).
+     *     get<nl::Attributes>(AF_INET6);
+     * const auto flags = inet6.get<uint32_t>(IFLA_INET6_FLAGS);  // AF_INET6 index lazy-calculated
+     * const auto& cache = inet6.getStruct<ifla_cacheinfo>(IFLA_INET6_CACHEINFO);  // index reused
+     * ```
+     *
+     * If the attribute doesn't exists, default value of a given type is returned and warning
+     * spawned into the log. To check for attribute existence, \see contains(nlattrtype_t).
+     *
+     * \param attrtype Attribute to fetch.
+     * \return Attribute value.
+     */
+    template <typename T>
+    T get(nlattrtype_t attrtype) const {
+        const auto& ind = index();
+        const auto it = ind.find(attrtype);
+        if (it == ind.end()) {
+            LOG(WARNING) << "Netlink attribute is missing: " << attrtype;
+            return T{};
+        }
+
+        return parse<T>(it->second);
+    }
+
+    /**
+     * Fetches a reference to a given attribute's data.
+     *
+     * This method is intended for arbitrary structures not specialized with get(nlattrtype_t)
+     * template and slightly more efficient for larger payloads due to not copying its data.
+     *
+     * If the attribute doesn't exists, a reference to empty value of a given type is returned and
+     * warning spawned into the log. To check for attribute existence, \see contains(nlattrtype_t).
+     *
+     * \param attrtype Attribute to fetch.
+     * \return Reference to the attribute's data.
+     */
+    template <typename T>
+    const T& getStruct(nlattrtype_t attrtype) const {
+        const auto& ind = index();
+        const auto it = ind.find(attrtype);
+        if (it == ind.end()) {
+            LOG(WARNING) << "Netlink attribute is missing: " << attrtype;
+            static const T empty = {};
+            return empty;
+        }
+
+        const auto& [ok, val] = it->second.data<T>().getFirst();
+        if (!ok) LOG(WARNING) << "Can't fetch structure of size " << sizeof(T);
+        return val;
+    }
+
+  private:
+    using Index = std::map<nlattrtype_t, Buffer<nlattr>>;
+
+    /**
+     * Attribute index.
+     *
+     * Since this field is not protected by mutex, the use of \see index() dependent methods
+     * (such as \see get(nlattrtype_t)) is not thread-safe. This is a compromise made based on the
+     * following assumptions:
+     *
+     * 1. Most (or even all) use-cases involve attribute parsing in the same thread as where the
+     *    buffer was allocated. This is partly forced by a dependence of nlmsg lifecycle on the
+     *    underlying data buffer.
+     *
+     * 2. Index calculation and access would come with performance penalty never justified in most
+     *    or all use cases (see the previous point). Since Index is not a trivially assignable data
+     *    structure, it's not possible to use it with atomic types only and avoid mutexes.
+     */
+    mutable std::optional<Index> mIndex;
+
+    /**
+     * Lazy-calculate and cache index.
+     *
+     * \return Attribute index.
+     */
+    const Index& index() const;
+
+    /**
+     * Parse attribute data into a specific type.
+     *
+     * \param buf Raw attribute data.
+     * \return Parsed data.
+     */
+    template <typename T>
+    static T parse(Buffer<nlattr> buf);
+};
+
+}  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/nlbuf.h b/automotive/can/1.0/default/libnl++/include/libnl++/Buffer.h
similarity index 71%
rename from automotive/can/1.0/default/libnl++/include/libnl++/nlbuf.h
rename to automotive/can/1.0/default/libnl++/include/libnl++/Buffer.h
index 4c0e581..bf83fbc 100644
--- a/automotive/can/1.0/default/libnl++/include/libnl++/nlbuf.h
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/Buffer.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android-base/logging.h>
+#include <libnl++/bits.h>
 
 #include <linux/netlink.h>
 
@@ -25,7 +26,7 @@
 namespace android::nl {
 
 /**
- * Buffer containing netlink structure (e.g. struct nlmsghdr, struct nlattr).
+ * Buffer wrapper containing netlink structure (e.g. nlmsghdr, nlattr).
  *
  * This is a C++-style, memory safe(r) and generic implementation of linux/netlink.h macros.
  *
@@ -33,7 +34,7 @@
  * not be trusted - the value may either be larger than the buffer message is allocated in or
  * smaller than the header itself (so it couldn't even fit itself).
  *
- * As a solution, nlbuf<> keeps track of two lengths (both attribute for header with payload):
+ * As a solution, Buffer<> keeps track of two lengths (both attribute for header with payload):
  * - buffer length - how much memory was allocated to a given structure
  * - declared length - what nlmsg_len or nla_len says how long the structure is
  *
@@ -42,24 +43,20 @@
  * this template attempts to protect against.
  */
 template <typename T>
-class nlbuf {
-    // The following definitions are C++ equivalents of NLMSG_* macros from linux/netlink.h
-
-    static constexpr size_t alignto = NLMSG_ALIGNTO;
-    static_assert(NLMSG_ALIGNTO == NLA_ALIGNTO);
-
-    static constexpr size_t align(size_t ptr) { return (ptr + alignto - 1) & ~(alignto - 1); }
-
-    static constexpr size_t hdrlen = align(sizeof(T));
-
+class Buffer {
   public:
     /**
-     * Constructor for nlbuf.
-     *
-     * \param data A pointer to the data the nlbuf wraps.
-     * \param bufferLen Length of buffer.
+     * Constructs empty buffer of size 0.
      */
-    nlbuf(const T* data, size_t bufferLen) : mData(data), mBufferEnd(pointerAdd(data, bufferLen)) {}
+    Buffer() : mData(nullptr), mBufferEnd(nullptr) {}
+
+    /**
+     * Buffer constructor.
+     *
+     * \param data A pointer to the data the Buffer wraps.
+     * \param bufLen Length of the buffer.
+     */
+    Buffer(const T* data, size_t bufLen) : mData(data), mBufferEnd(pointerAdd(data, bufLen)) {}
 
     const T* operator->() const {
         CHECK(firstOk()) << "buffer can't fit the first element's header";
@@ -68,8 +65,8 @@
 
     std::pair<bool, const T&> getFirst() const {
         if (!ok()) {
-            static const T dummy = {};
-            return {false, dummy};
+            static const T empty = {};
+            return {false, empty};
         }
         return {true, *mData};
     }
@@ -78,7 +75,8 @@
      * Copy the first element of the buffer.
      *
      * This is a memory-safe cast operation, useful for reading e.g. uint32_t values
-     * from 1-byte buffer.
+     * from 1-byte buffer. If the buffer is smaller than the copied type, the rest is
+     * padded with default constructor output (usually zeros).
      */
     T copyFirst() const {
         T val = {};
@@ -89,38 +87,38 @@
     bool firstOk() const { return sizeof(T) <= remainingLength(); }
 
     template <typename D>
-    const nlbuf<D> data(size_t offset = 0) const {
-        // Equivalent to NLMSG_DATA(hdr) + NLMSG_ALIGN(offset)
-        const D* dptr = reinterpret_cast<const D*>(uintptr_t(mData) + hdrlen + align(offset));
-        return {dptr, dataEnd()};
+    const Buffer<D> data(size_t offset = 0) const {
+        return {impl::data<const T, const D>(mData, offset), dataEnd()};
     }
 
     class iterator {
       public:
         iterator() : mCurrent(nullptr, size_t(0)) {
-            CHECK(!mCurrent.ok()) << "end() iterator should indicate it's beyond end";
+            CHECK(isEnd()) << "end() iterator should indicate it's beyond end";
         }
-        iterator(const nlbuf<T>& buf) : mCurrent(buf) {}
+        iterator(const Buffer<T>& buf) : mCurrent(buf) {}
 
         iterator operator++() {
             // mBufferEnd stays the same
             mCurrent.mData = reinterpret_cast<const T*>(  //
-                    uintptr_t(mCurrent.mData) + align(mCurrent.declaredLength()));
+                    uintptr_t(mCurrent.mData) + impl::align(mCurrent.declaredLength()));
 
             return *this;
         }
 
         bool operator==(const iterator& other) const {
             // all iterators beyond end are the same
-            if (!mCurrent.ok() && !other.mCurrent.ok()) return true;
+            if (isEnd() && other.isEnd()) return true;
 
             return uintptr_t(other.mCurrent.mData) == uintptr_t(mCurrent.mData);
         }
 
-        const nlbuf<T>& operator*() const { return mCurrent; }
+        const Buffer<T>& operator*() const { return mCurrent; }
+
+        bool isEnd() const { return !mCurrent.ok(); }
 
       protected:
-        nlbuf<T> mCurrent;
+        Buffer<T> mCurrent;
     };
     iterator begin() const { return {*this}; }
     iterator end() const { return {}; }
@@ -136,7 +134,7 @@
 
     class raw_view {
       public:
-        raw_view(const nlbuf<T>& buffer) : mBuffer(buffer) {}
+        raw_view(const Buffer<T>& buffer) : mBuffer(buffer) {}
         raw_iterator begin() const { return {mBuffer}; }
         raw_iterator end() const { return {}; }
 
@@ -144,7 +142,7 @@
         size_t len() const { return mBuffer.remainingLength(); }
 
       private:
-        const nlbuf<T> mBuffer;
+        const Buffer<T> mBuffer;
     };
 
     raw_view getRaw() const { return {*this}; }
@@ -153,7 +151,7 @@
     const T* mData;
     const void* mBufferEnd;
 
-    nlbuf(const T* data, const void* bufferEnd) : mData(data), mBufferEnd(bufferEnd) {}
+    Buffer(const T* data, const void* bufferEnd) : mData(data), mBufferEnd(bufferEnd) {}
 
     bool ok() const { return declaredLength() <= remainingLength(); }
 
@@ -186,16 +184,16 @@
     }
 
     template <typename D>
-    friend class nlbuf;  // calling private constructor of data buffers
+    friend class Buffer;  // calling private constructor of data buffers
 };
 
 template <>
-inline size_t nlbuf<nlmsghdr>::declaredLengthImpl() const {
+inline size_t Buffer<nlmsghdr>::declaredLengthImpl() const {
     return mData->nlmsg_len;
 }
 
 template <>
-inline size_t nlbuf<nlattr>::declaredLengthImpl() const {
+inline size_t Buffer<nlattr>::declaredLengthImpl() const {
     return mData->nla_len;
 }
 
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h b/automotive/can/1.0/default/libnl++/include/libnl++/Message.h
similarity index 77%
rename from automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h
rename to automotive/can/1.0/default/libnl++/include/libnl++/Message.h
index d6a797b..50b3c4b 100644
--- a/automotive/can/1.0/default/libnl++/include/libnl++/nlmsg.h
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/Message.h
@@ -16,7 +16,10 @@
 
 #pragma once
 
-#include <libnl++/nlbuf.h>
+#include <libnl++/Attributes.h>
+#include <libnl++/Buffer.h>
+
+#include <set>
 
 namespace android::nl {
 
@@ -26,17 +29,20 @@
  * This is a C++-style, memory safe(r) implementation of linux/netlink.h macros accessing Netlink
  * message contents. The class doesn't own the underlying data, so the instance is valid as long as
  * the source buffer is allocated and unmodified.
+ *
+ * WARNING: this class is NOT thread-safe (it's safe to be used in multithreaded application, but
+ * a single instance can only be used by a single thread - the one owning the underlying buffer).
  */
 template <typename T>
-class nlmsg {
+class Message {
   public:
     /**
-     * Validate buffer contents as a message carrying T data and create instance of nlmsg.
+     * Validate buffer contents as a message carrying T data and create instance of parsed message.
      *
      * \param buf Buffer containing the message.
      * \return Parsed message or nullopt, if the buffer data is invalid.
      */
-    static std::optional<nlmsg<T>> parse(nlbuf<nlmsghdr> buf) {
+    static std::optional<Message<T>> parse(Buffer<nlmsghdr> buf) {
         const auto& [nlOk, nlHeader] = buf.getFirst();
         if (!nlOk) return std::nullopt;
 
@@ -45,18 +51,19 @@
 
         const auto attributes = buf.data<nlattr>(sizeof(T));
 
-        return nlmsg<T>(nlHeader, dataHeader, attributes);
+        return Message<T>(nlHeader, dataHeader, attributes);
     }
 
     /**
-     * Validate buffer contents as a message of a given type and create instance of nlmsg.
+     * Validate buffer contents as a message of a given type and create instance of parsed message.
      *
      * \param buf Buffer containing the message.
      * \param msgtypes Acceptable message types (within a specific Netlink protocol)
      * \return Parsed message or nullopt, if the buffer data is invalid or message type
      *         doesn't match.
      */
-    static std::optional<nlmsg<T>> parse(nlbuf<nlmsghdr> buf, std::set<nlmsgtype_t> msgtypes) {
+    static std::optional<Message<T>> parse(Buffer<nlmsghdr> buf,
+                                           const std::set<nlmsgtype_t>& msgtypes) {
         const auto& [nlOk, nlHeader] = buf.getFirst();  // we're doing it twice, but it's fine
         if (!nlOk) return std::nullopt;
 
@@ -82,12 +89,12 @@
     /**
      * Netlink message attributes.
      */
-    const nlbuf<nlattr> attributes;
+    const Attributes attributes;
 
     const T* operator->() const { return &data; }
 
   private:
-    nlmsg(const nlmsghdr& nlHeader, const T& dataHeader, nlbuf<nlattr> attributes)
+    Message(const nlmsghdr& nlHeader, const T& dataHeader, Attributes attributes)
         : header(nlHeader), data(dataHeader), attributes(attributes) {}
 };
 
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/MessageFactory.h b/automotive/can/1.0/default/libnl++/include/libnl++/MessageFactory.h
new file mode 100644
index 0000000..c3d72c5
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/MessageFactory.h
@@ -0,0 +1,165 @@
+/*
+ * 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 <libnl++/Buffer.h>
+#include <libnl++/types.h>
+
+#include <linux/netlink.h>
+
+#include <string>
+
+namespace android::nl {
+
+class MessageFactoryBase {
+  protected:
+    static nlattr* add(nlmsghdr* msg, size_t maxLen, nlattrtype_t type, const void* data,
+                       size_t dataLen);
+    static void closeNested(nlmsghdr* msg, nlattr* nested);
+};
+
+/**
+ * Wrapper around NETLINK_ROUTE messages, to build them in C++ style.
+ *
+ * \param T Message payload type (such as ifinfomsg).
+ * \param BUFSIZE how much space to reserve for attributes.
+ */
+template <class T, unsigned int BUFSIZE = 128>
+class MessageFactory : private MessageFactoryBase {
+    struct alignas(NLMSG_ALIGNTO) Message {
+        nlmsghdr header;
+        T data;
+        uint8_t attributesBuffer[BUFSIZE];
+    };
+
+  public:
+    /**
+     * Create empty message.
+     *
+     * \param type Message type (such as RTM_NEWLINK).
+     * \param flags Message flags (such as NLM_F_REQUEST).
+     */
+    MessageFactory(nlmsgtype_t type, uint16_t flags)
+        : header(mMessage.header), data(mMessage.data) {
+        mMessage.header.nlmsg_len = offsetof(Message, attributesBuffer);
+        mMessage.header.nlmsg_type = type;
+        mMessage.header.nlmsg_flags = flags;
+    }
+
+    /**
+     * Netlink message header.
+     *
+     * This is a generic Netlink header containing information such as message flags.
+     */
+    nlmsghdr& header;
+
+    /**
+     * Netlink message data.
+     *
+     * This is a payload specific to a given message type.
+     */
+    T& data;
+
+    T* operator->() { return &mMessage.data; }
+
+    /**
+     * Build netlink message.
+     *
+     * In fact, this operation is almost a no-op, since the factory builds the message in a single
+     * buffer, using native data structures.
+     *
+     * A likely failure case is when the BUFSIZE template parameter is too small to acommodate
+     * added attributes. In such a case, please increase this parameter.
+     *
+     * \return Netlink message or std::nullopt in case of failure.
+     */
+    std::optional<Buffer<nlmsghdr>> build() const {
+        if (!mIsGood) return std::nullopt;
+        return {{&mMessage.header, mMessage.header.nlmsg_len}};
+    }
+
+    /**
+     * Adds an attribute of a trivially copyable type.
+     *
+     * Template specializations may extend this function for other types, such as std::string.
+     *
+     * If this method fails (i.e. due to insufficient space), a warning will be printed to the log
+     * and the message will be marked as bad, causing later \see build call to fail.
+     *
+     * \param type attribute type (such as IFLA_IFNAME)
+     * \param attr attribute data
+     */
+    template <class A>
+    void add(nlattrtype_t type, const A& attr) {
+        add(type, &attr, sizeof(attr));
+    }
+
+    template <>
+    void add(nlattrtype_t type, const std::string& s) {
+        add(type, s.c_str(), s.size() + 1);
+    }
+
+    /** Guard class to frame nested attributes. \see addNested(nlattrtype_t). */
+    class [[nodiscard]] NestedGuard {
+      public:
+        NestedGuard(MessageFactory & req, nlattrtype_t type) : mReq(req), mAttr(req.add(type)) {}
+        ~NestedGuard() { closeNested(&mReq.mMessage.header, mAttr); }
+
+      private:
+        MessageFactory& mReq;
+        nlattr* mAttr;
+
+        DISALLOW_COPY_AND_ASSIGN(NestedGuard);
+    };
+
+    /**
+     * Add nested attribute.
+     *
+     * The returned object is a guard for auto-nesting children inside the argument attribute.
+     * When the guard 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:
+     *    MessageFactory<ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST);
+     *    {
+     *        auto linkinfo = req.addNested(IFLA_LINKINFO);
+     *        req.add(IFLA_INFO_KIND, "can");
+     *        {
+     *            auto infodata = req.addNested(IFLA_INFO_DATA);
+     *            req.add(IFLA_CAN_BITTIMING, bitTimingStruct);
+     *        }
+     *    }
+     *    // use req
+     *
+     * \param type attribute type (such as IFLA_LINKINFO)
+     */
+    NestedGuard addNested(nlattrtype_t type) { return {*this, type}; }
+
+  private:
+    Message mMessage = {};
+    bool mIsGood = true;
+
+    nlattr* add(nlattrtype_t type, const void* data = nullptr, size_t len = 0) {
+        if (!mIsGood) return nullptr;
+        auto attr = MessageFactoryBase::add(&mMessage.header, sizeof(mMessage), type, data, len);
+        if (attr == nullptr) mIsGood = false;
+        return attr;
+    }
+};
+
+}  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/NetlinkRequest.h b/automotive/can/1.0/default/libnl++/include/libnl++/NetlinkRequest.h
deleted file mode 100644
index 29c3601..0000000
--- a/automotive/can/1.0/default/libnl++/include/libnl++/NetlinkRequest.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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 <libnl++/types.h>
-
-#include <linux/netlink.h>
-
-#include <string>
-
-namespace android::nl {
-
-/** Implementation details, do not use outside NetlinkRequest template. */
-namespace impl {
-
-struct nlattr* addattr_l(struct nlmsghdr* n, size_t maxLen, nlattrtype_t type, const void* data,
-                         size_t dataLen);
-struct nlattr* addattr_nest(struct nlmsghdr* n, size_t maxLen, nlattrtype_t type);
-void addattr_nest_end(struct nlmsghdr* n, struct nlattr* nest);
-
-}  // namespace impl
-
-// TODO(twasilczyk): rename to NetlinkMessage
-/**
- * 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 {
-    struct RequestData {
-        struct nlmsghdr nlmsg;
-        T data;
-        char buf[BUFSIZE];
-    };
-
-    static constexpr size_t totalLength = sizeof(RequestData);
-
-    /**
-     * 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;
-    }
-
-    /** \return 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(nlattrtype_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(nlattrtype_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, nlattrtype_t type) : mReq(req), mAttr(req.nestStart(type)) {}
-        ~Nest() { mReq.nestEnd(mAttr); }
-
-      private:
-        NetlinkRequest& mReq;
-        struct nlattr* 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;
-    RequestData mRequest = {};
-
-    struct nlattr* nestStart(nlattrtype_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 nlattr* nest) {
-        if (mIsGood && nest != nullptr) impl::addattr_nest_end(&mRequest.nlmsg, nest);
-    }
-};
-
-}  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/NetlinkSocket.h b/automotive/can/1.0/default/libnl++/include/libnl++/NetlinkSocket.h
deleted file mode 100644
index f2a38fb..0000000
--- a/automotive/can/1.0/default/libnl++/include/libnl++/NetlinkSocket.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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 <libnl++/NetlinkRequest.h>
-#include <libnl++/nlbuf.h>
-
-#include <linux/netlink.h>
-
-#include <optional>
-
-namespace android::nl {
-
-/**
- * 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 constructor.
-     *
-     * \param protocol the Netlink protocol to use.
-     * \param pid port id. Default value of 0 allows the kernel to assign us a unique pid. (NOTE:
-     * this is NOT the same as process id!)
-     * \param groups Netlink multicast groups to listen to. This is a 32-bit bitfield, where each
-     * bit is a different group. Default value of 0 means no groups are selected. See man netlink.7
-     * for more details.
-     */
-    NetlinkSocket(int protocol, unsigned int pid = 0, uint32_t groups = 0);
-
-    /**
-     * Send Netlink message to Kernel. The sequence number will be automatically incremented, and
-     * the NLM_F_ACK (request ACK) flag will be set.
-     *
-     * \param msg Message to send.
-     * \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(), req.totalLength);
-    }
-
-    /**
-     * Send Netlink message. The message will be sent as is, without any modification.
-     *
-     * \param msg Message to send.
-     * \param sa Destination address.
-     * \return true, if succeeded
-     */
-    bool send(const nlbuf<nlmsghdr>& msg, const sockaddr_nl& sa);
-
-    /**
-     * Receive Netlink data.
-     *
-     * \param buf buffer to hold message data.
-     * \param bufLen length of buf.
-     * \return nlbuf with message data, std::nullopt on error.
-     */
-    std::optional<nlbuf<nlmsghdr>> receive(void* buf, size_t bufLen);
-
-    /**
-     * Receive Netlink data with address info.
-     *
-     * \param buf buffer to hold message data.
-     * \param bufLen length of buf.
-     * \param sa Blank struct that recvfrom will populate with address info.
-     * \return nlbuf with message data, std::nullopt on error.
-     */
-    std::optional<nlbuf<nlmsghdr>> receive(void* buf, size_t bufLen, sockaddr_nl& sa);
-
-    /**
-     * Receive Netlink ACK message from Kernel.
-     *
-     * \return true if received ACK message, false in case of error
-     */
-    bool receiveAck();
-
-    /**
-     * Gets the PID assigned to mFd.
-     *
-     * \return pid that mSocket is bound to.
-     */
-    std::optional<unsigned int> getSocketPid();
-
-  private:
-    const int mProtocol;
-
-    uint32_t mSeq = 0;
-    base::unique_fd mFd;
-    bool mFailed = false;
-
-    bool send(nlmsghdr* msg, size_t totalLen);
-
-    DISALLOW_COPY_AND_ASSIGN(NetlinkSocket);
-};
-
-}  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/Socket.h b/automotive/can/1.0/default/libnl++/include/libnl++/Socket.h
new file mode 100644
index 0000000..c69523d
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/Socket.h
@@ -0,0 +1,226 @@
+/*
+ * 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 <libnl++/Buffer.h>
+#include <libnl++/Message.h>
+#include <libnl++/MessageFactory.h>
+
+#include <linux/netlink.h>
+
+#include <optional>
+#include <set>
+#include <vector>
+
+namespace android::nl {
+
+/**
+ * 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.
+ */
+class Socket {
+  public:
+    static constexpr size_t defaultReceiveSize = 8192;
+
+    /**
+     * Socket constructor.
+     *
+     * \param protocol the Netlink protocol to use.
+     * \param pid port id. Default value of 0 allows the kernel to assign us a unique pid.
+     *        (NOTE: this is NOT the same as process id).
+     * \param groups Netlink multicast groups to listen to. This is a 32-bit bitfield, where each
+     *        bit is a different group. Default value of 0 means no groups are selected.
+     *        See man netlink.7.
+     * for more details.
+     */
+    Socket(int protocol, unsigned pid = 0, uint32_t groups = 0);
+
+    /**
+     * Send Netlink message with incremented sequence number to the Kernel.
+     *
+     * \param msg Message to send. Its sequence number will be updated.
+     * \return true, if succeeded.
+     */
+    template <typename T, unsigned BUFSIZE>
+    bool send(MessageFactory<T, BUFSIZE>& req) {
+        sockaddr_nl sa = {};
+        sa.nl_family = AF_NETLINK;
+        sa.nl_pid = 0;  // Kernel
+        return send(req, sa);
+    }
+
+    /**
+     * Send Netlink message with incremented sequence number.
+     *
+     * \param msg Message to send. Its sequence number will be updated.
+     * \param sa Destination address.
+     * \return true, if succeeded.
+     */
+    template <typename T, unsigned BUFSIZE>
+    bool send(MessageFactory<T, BUFSIZE>& req, const sockaddr_nl& sa) {
+        req.header.nlmsg_seq = mSeq + 1;
+
+        const auto msg = req.build();
+        if (!msg.has_value()) return false;
+
+        return send(*msg, sa);
+    }
+
+    /**
+     * Send Netlink message.
+     *
+     * \param msg Message to send.
+     * \param sa Destination address.
+     * \return true, if succeeded.
+     */
+    bool send(const Buffer<nlmsghdr>& msg, const sockaddr_nl& sa);
+
+    /**
+     * Receive one or multiple Netlink messages.
+     *
+     * WARNING: the underlying buffer is owned by Socket class and the data is valid until the next
+     * call to the read function or until deallocation of Socket instance.
+     *
+     * \param maxSize Maximum total size of received messages
+     * \return Buffer view with message data, std::nullopt on error.
+     */
+    std::optional<Buffer<nlmsghdr>> receive(size_t maxSize = defaultReceiveSize);
+
+    /**
+     * Receive one or multiple Netlink messages and the sender process address.
+     *
+     * WARNING: the underlying buffer is owned by Socket class and the data is valid until the next
+     * call to the read function or until deallocation of Socket instance.
+     *
+     * \param maxSize Maximum total size of received messages.
+     * \return A pair (for use with structured binding) containing:
+     *         - buffer view with message data, std::nullopt on error;
+     *         - sender process address.
+     */
+    std::pair<std::optional<Buffer<nlmsghdr>>, sockaddr_nl> receiveFrom(
+            size_t maxSize = defaultReceiveSize);
+
+    /**
+     * Receive matching Netlink message of a given payload type.
+     *
+     * This method should be used if the caller expects exactly one incoming message of exactly
+     * given type (such as ACK). If there is a use case to handle multiple types of messages,
+     * please use receive(size_t) directly and iterate through potential multipart messages.
+     *
+     * If this method is used in such an environment, it will only return the first matching message
+     * from multipart packet and will issue warnings on messages that do not match.
+     *
+     * \param msgtypes Expected message types (such as NLMSG_ERROR).
+     * \param maxSize Maximum total size of received messages.
+     * \return Parsed message or std::nullopt in case of error.
+     */
+    template <typename T>
+    std::optional<Message<T>> receive(const std::set<nlmsgtype_t>& msgtypes,
+                                      size_t maxSize = defaultReceiveSize) {
+        const auto msg = receive(msgtypes, maxSize);
+        if (!msg.has_value()) return std::nullopt;
+
+        const auto parsed = Message<T>::parse(*msg);
+        if (!parsed.has_value()) {
+            LOG(WARNING) << "Received matching Netlink message, but couldn't parse it";
+            return std::nullopt;
+        }
+
+        return parsed;
+    }
+
+    /**
+     * Receive Netlink ACK message.
+     *
+     * \param req Message to match sequence number against.
+     * \return true if received ACK message, false in case of error.
+     */
+    template <typename T, unsigned BUFSIZE>
+    bool receiveAck(MessageFactory<T, BUFSIZE>& req) {
+        return receiveAck(req.header.nlmsg_seq);
+    }
+
+    /**
+     * Receive Netlink ACK message.
+     *
+     * \param seq Sequence number of message to ACK.
+     * \return true if received ACK message, false in case of error.
+     */
+    bool receiveAck(uint32_t seq);
+
+    /**
+     * Fetches the socket PID.
+     *
+     * \return PID that socket is bound to or std::nullopt.
+     */
+    std::optional<unsigned> getPid();
+
+    /**
+     * Live iterator continuously receiving messages from Netlink socket.
+     *
+     * Iteration ends when socket fails to receive a buffer.
+     *
+     * Example:
+     * ```
+     *     nl::Socket sock(NETLINK_ROUTE, 0, RTMGRP_LINK);
+     *     for (const auto rawMsg : sock) {
+     *         const auto msg = nl::Message<ifinfomsg>::parse(rawMsg, {RTM_NEWLINK, RTM_DELLINK});
+     *         if (!msg.has_value()) continue;
+     *
+     *         LOG(INFO) << msg->attributes.get<std::string>(IFLA_IFNAME)
+     *                   << " is " << ((msg->data.ifi_flags & IFF_UP) ? "up" : "down");
+     *     }
+     *     LOG(FATAL) << "Failed to read from Netlink socket";
+     * ```
+     */
+    class receive_iterator {
+      public:
+        receive_iterator(Socket& socket, bool end);
+
+        receive_iterator operator++();
+        bool operator==(const receive_iterator& other) const;
+        const Buffer<nlmsghdr>& operator*() const;
+
+      private:
+        Socket& mSocket;
+        bool mIsEnd;
+        Buffer<nlmsghdr>::iterator mCurrent;
+
+        void receive();
+    };
+    receive_iterator begin();
+    receive_iterator end();
+
+  private:
+    const int mProtocol;
+    base::unique_fd mFd;
+    std::vector<uint8_t> mReceiveBuffer;
+
+    bool mFailed = false;
+    uint32_t mSeq = 0;
+
+    bool increaseReceiveBuffer(size_t maxSize);
+    std::optional<Buffer<nlmsghdr>> receive(const std::set<nlmsgtype_t>& msgtypes, size_t maxSize);
+
+    DISALLOW_COPY_AND_ASSIGN(Socket);
+};
+
+}  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/bits.h b/automotive/can/1.0/default/libnl++/include/libnl++/bits.h
new file mode 100644
index 0000000..4c8f1aa
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/bits.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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 <linux/netlink.h>
+
+namespace android::nl::impl {
+
+// The following definitions are C++ equivalents of NLMSG_* macros from linux/netlink.h.
+
+/**
+ * Equivalent to NLMSG_ALIGNTO.
+ */
+constexpr size_t alignto = NLMSG_ALIGNTO;
+static_assert(NLMSG_ALIGNTO == NLA_ALIGNTO);
+
+/**
+ * Equivalent to NLMSG_ALIGN(len).
+ */
+constexpr size_t align(size_t len) {
+    return (len + alignto - 1) & ~(alignto - 1);
+}
+
+/**
+ * Equivalent to NLMSG_SPACE(len).
+ */
+template <typename H>
+constexpr size_t space(size_t len) {
+    return align(align(sizeof(H)) + len);
+}
+
+/**
+ * Equivalent to NLMSG_DATA(hdr) + NLMSG_ALIGN(offset).
+ */
+template <typename H, typename D>
+constexpr D* data(H* header, size_t offset = 0) {
+    return reinterpret_cast<D*>(uintptr_t(header) + space<H>(offset));
+}
+
+}  // namespace android::nl::impl
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/printer.h b/automotive/can/1.0/default/libnl++/include/libnl++/printer.h
index 7167965..3570918 100644
--- a/automotive/can/1.0/default/libnl++/include/libnl++/printer.h
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/printer.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <libnl++/nlbuf.h>
+#include <libnl++/Buffer.h>
 
 #include <linux/netlink.h>
 
@@ -32,6 +32,6 @@
  * \param printPayload True will stringify message data, false will only stringify the header(s).
  * \return Stringified message.
  */
-std::string toString(const nlbuf<nlmsghdr> hdr, int protocol, bool printPayload = false);
+std::string toString(const Buffer<nlmsghdr> hdr, int protocol, bool printPayload = true);
 
 }  // namespace android::nl
diff --git a/automotive/can/1.0/default/libnl++/printer.cpp b/automotive/can/1.0/default/libnl++/printer.cpp
index c32afb3..e6cada2 100644
--- a/automotive/can/1.0/default/libnl++/printer.cpp
+++ b/automotive/can/1.0/default/libnl++/printer.cpp
@@ -20,7 +20,7 @@
 #include "protocols/all.h"
 
 #include <android-base/logging.h>
-#include <libnl++/nlbuf.h>
+#include <libnl++/Buffer.h>
 
 #include <algorithm>
 #include <iomanip>
@@ -28,10 +28,10 @@
 
 namespace android::nl {
 
-static void flagsToStream(std::stringstream& ss, __u16 nlmsg_flags) {
+static void flagsToStream(std::stringstream& ss, __u16 nlmsg_flags, protocols::MessageGenre genre) {
     bool first = true;
     auto printFlag = [&ss, &first, &nlmsg_flags](__u16 flag, const std::string& name) {
-        if (!(nlmsg_flags & flag)) return;
+        if ((nlmsg_flags & flag) != flag) return;
         nlmsg_flags &= ~flag;
 
         if (first) {
@@ -42,6 +42,7 @@
 
         ss << name;
     };
+
     printFlag(NLM_F_REQUEST, "REQUEST");
     printFlag(NLM_F_MULTI, "MULTI");
     printFlag(NLM_F_ACK, "ACK");
@@ -49,11 +50,29 @@
     printFlag(NLM_F_DUMP_INTR, "DUMP_INTR");
     printFlag(NLM_F_DUMP_FILTERED, "DUMP_FILTERED");
 
-    // TODO(twasilczyk): print flags depending on request type
-    printFlag(NLM_F_ROOT, "ROOT-REPLACE");
-    printFlag(NLM_F_MATCH, "MATCH-EXCL");
-    printFlag(NLM_F_ATOMIC, "ATOMIC-CREATE");
-    printFlag(NLM_F_APPEND, "APPEND");
+    switch (genre) {
+        case protocols::MessageGenre::UNKNOWN:
+            break;
+        case protocols::MessageGenre::GET:
+            printFlag(NLM_F_DUMP, "DUMP");  // ROOT | MATCH
+            printFlag(NLM_F_ROOT, "ROOT");
+            printFlag(NLM_F_MATCH, "MATCH");
+            printFlag(NLM_F_ATOMIC, "ATOMIC");
+            break;
+        case protocols::MessageGenre::NEW:
+            printFlag(NLM_F_REPLACE, "REPLACE");
+            printFlag(NLM_F_EXCL, "EXCL");
+            printFlag(NLM_F_CREATE, "CREATE");
+            printFlag(NLM_F_APPEND, "APPEND");
+            break;
+        case protocols::MessageGenre::DELETE:
+            printFlag(NLM_F_NONREC, "NONREC");
+            break;
+        case protocols::MessageGenre::ACK:
+            printFlag(NLM_F_CAPPED, "CAPPED");
+            printFlag(NLM_F_ACK_TLVS, "ACK_TLVS");
+            break;
+    }
 
     if (nlmsg_flags != 0) {
         if (!first) ss << '|';
@@ -61,7 +80,7 @@
     }
 }
 
-static void toStream(std::stringstream& ss, const nlbuf<uint8_t> data) {
+static void toStream(std::stringstream& ss, const Buffer<uint8_t> data) {
     const auto rawData = data.getRaw();
     const auto dataLen = rawData.len();
     ss << std::hex;
@@ -77,7 +96,7 @@
     if (dataLen > 16) ss << std::endl;
 }
 
-static void toStream(std::stringstream& ss, const nlbuf<nlattr> attr,
+static void toStream(std::stringstream& ss, const Buffer<nlattr> attr,
                      const protocols::AttributeMap& attrMap) {
     using DataType = protocols::AttributeDefinition::DataType;
     const auto attrtype = attrMap[attr->nla_type];
@@ -100,7 +119,7 @@
         }
         case DataType::String: {
             const auto str = attr.data<char>().getRaw();
-            ss << '"' << sanitize({str.ptr(), str.len()}) << '"';
+            ss << '"' << printableOnly({str.ptr(), str.len()}) << '"';
             break;
         }
         case DataType::Uint:
@@ -115,7 +134,7 @@
     }
 }
 
-std::string toString(const nlbuf<nlmsghdr> hdr, int protocol, bool printPayload) {
+std::string toString(const Buffer<nlmsghdr> hdr, int protocol, bool printPayload) {
     if (!hdr.firstOk()) return "nlmsg{buffer overflow}";
 
     std::stringstream ss;
@@ -128,27 +147,26 @@
     }
     protocols::NetlinkProtocol& protocolDescr = *protocolMaybe;
 
-    auto msgDescMaybe = protocolDescr.getMessageDescriptor(hdr->nlmsg_type);
-    const auto msgTypeName = msgDescMaybe.has_value()
-                                     ? msgDescMaybe->get().getMessageName(hdr->nlmsg_type)
-                                     : std::to_string(hdr->nlmsg_type);
+    const auto msgDescMaybe = protocolDescr.getMessageDescriptor(hdr->nlmsg_type);
+    const auto msgDetails =
+            protocols::MessageDescriptor::getMessageDetails(msgDescMaybe, hdr->nlmsg_type);
 
     ss << "nlmsg{" << protocolDescr.getName() << " ";
 
     ss << "hdr={";
-    ss << "type=" << msgTypeName;
+    ss << "type=" << msgDetails.name;
     if (hdr->nlmsg_flags != 0) {
         ss << ", flags=";
-        flagsToStream(ss, hdr->nlmsg_flags);
+        flagsToStream(ss, hdr->nlmsg_flags, msgDetails.genre);
     }
     if (hdr->nlmsg_seq != 0) ss << ", seq=" << hdr->nlmsg_seq;
     if (hdr->nlmsg_pid != 0) ss << ", pid=" << hdr->nlmsg_pid;
     ss << ", len=" << hdr->nlmsg_len;
-
     ss << ", crc=" << std::hex << std::setw(4) << crc16(hdr.data<uint8_t>()) << std::dec;
-    ss << "} ";
+    ss << '}';
 
     if (!printPayload) return ss.str();
+    ss << ' ';
 
     if (!msgDescMaybe.has_value()) {
         toStream(ss, hdr.data<uint8_t>());
diff --git a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
index dc56643..c93d865 100644
--- a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
@@ -32,11 +32,12 @@
     return find(nla_type)->second;
 }
 
-MessageDescriptor::MessageDescriptor(const std::string& name, const MessageTypeMap&& messageTypes,
+MessageDescriptor::MessageDescriptor(const std::string& name,
+                                     const MessageDetailsMap&& messageDetails,
                                      const AttributeMap&& attrTypes, size_t contentsSize)
     : mName(name),
       mContentsSize(contentsSize),
-      mMessageTypes(messageTypes),
+      mMessageDetails(messageDetails),
       mAttributeMap(attrTypes) {}
 
 MessageDescriptor::~MessageDescriptor() {}
@@ -45,18 +46,25 @@
     return mContentsSize;
 }
 
-const MessageDescriptor::MessageTypeMap& MessageDescriptor::getMessageTypeMap() const {
-    return mMessageTypes;
+const MessageDescriptor::MessageDetailsMap& MessageDescriptor::getMessageDetailsMap() const {
+    return mMessageDetails;
 }
 
 const AttributeMap& MessageDescriptor::getAttributeMap() const {
     return mAttributeMap;
 }
 
-const std::string MessageDescriptor::getMessageName(nlmsgtype_t msgtype) const {
-    const auto it = mMessageTypes.find(msgtype);
-    if (it == mMessageTypes.end()) return "?";
+MessageDescriptor::MessageDetails MessageDescriptor::getMessageDetails(nlmsgtype_t msgtype) const {
+    const auto it = mMessageDetails.find(msgtype);
+    if (it == mMessageDetails.end()) return {std::to_string(msgtype), MessageGenre::UNKNOWN};
     return it->second;
 }
 
+MessageDescriptor::MessageDetails MessageDescriptor::getMessageDetails(
+        const std::optional<std::reference_wrapper<const MessageDescriptor>>& msgDescMaybe,
+        nlmsgtype_t msgtype) {
+    if (msgDescMaybe.has_value()) return msgDescMaybe->get().getMessageDetails(msgtype);
+    return {std::to_string(msgtype), protocols::MessageGenre::UNKNOWN};
+}
+
 }  // namespace android::nl::protocols
diff --git a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
index 046ef47..bd0e60f 100644
--- a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
+++ b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <libnl++/nlbuf.h>
+#include <libnl++/Buffer.h>
 #include <libnl++/types.h>
 
 #include <map>
@@ -60,7 +60,7 @@
         Uint,
         Struct,
     };
-    using ToStream = std::function<void(std::stringstream& ss, const nlbuf<nlattr> attr)>;
+    using ToStream = std::function<void(std::stringstream& ss, const Buffer<nlattr> attr)>;
 
     std::string name;
     DataType dataType = DataType::Raw;
@@ -68,30 +68,53 @@
 };
 
 /**
+ * General message type's kind.
+ *
+ * For example, RTM_NEWLINK is a NEW kind. For details, please see "Flags values"
+ * section in linux/netlink.h.
+ */
+enum class MessageGenre {
+    UNKNOWN,
+    GET,
+    NEW,
+    DELETE,
+    ACK,
+};
+
+/**
  * Message family descriptor.
  *
  * Describes the structure of all message types with the same header and attributes.
  */
 class MessageDescriptor {
-  protected:
-    typedef std::map<nlmsgtype_t, std::string> MessageTypeMap;
-
-    MessageDescriptor(const std::string& name, const MessageTypeMap&& messageTypes,
-                      const AttributeMap&& attrTypes, size_t contentsSize);
+  public:
+    struct MessageDetails {
+        std::string name;
+        MessageGenre genre;
+    };
+    typedef std::map<nlmsgtype_t, MessageDetails> MessageDetailsMap;
 
   public:
     virtual ~MessageDescriptor();
 
     size_t getContentsSize() const;
-    const MessageTypeMap& getMessageTypeMap() const;
+    const MessageDetailsMap& getMessageDetailsMap() const;
     const AttributeMap& getAttributeMap() const;
-    const std::string getMessageName(nlmsgtype_t msgtype) const;
-    virtual void dataToStream(std::stringstream& ss, const nlbuf<nlmsghdr> hdr) const = 0;
+    MessageDetails getMessageDetails(nlmsgtype_t msgtype) const;
+    virtual void dataToStream(std::stringstream& ss, const Buffer<nlmsghdr> hdr) const = 0;
+
+    static MessageDetails getMessageDetails(
+            const std::optional<std::reference_wrapper<const MessageDescriptor>>& msgDescMaybe,
+            nlmsgtype_t msgtype);
+
+  protected:
+    MessageDescriptor(const std::string& name, const MessageDetailsMap&& messageDetails,
+                      const AttributeMap&& attrTypes, size_t contentsSize);
 
   private:
     const std::string mName;
     const size_t mContentsSize;
-    const MessageTypeMap mMessageTypes;
+    const MessageDetailsMap mMessageDetails;
     const AttributeMap mAttributeMap;
 };
 
@@ -103,13 +126,13 @@
 template <typename T>
 class MessageDefinition : public MessageDescriptor {
   public:
-    MessageDefinition(
+    MessageDefinition(  //
             const std::string& name,
-            const std::initializer_list<MessageDescriptor::MessageTypeMap::value_type> messageTypes,
+            const std::initializer_list<MessageDescriptor::MessageDetailsMap::value_type> msgDet,
             const std::initializer_list<AttributeMap::value_type> attrTypes = {})
-        : MessageDescriptor(name, messageTypes, attrTypes, sizeof(T)) {}
+        : MessageDescriptor(name, msgDet, attrTypes, sizeof(T)) {}
 
-    void dataToStream(std::stringstream& ss, const nlbuf<nlmsghdr> hdr) const override {
+    void dataToStream(std::stringstream& ss, const Buffer<nlmsghdr> hdr) const override {
         const auto& [ok, msg] = hdr.data<T>().getFirst();
         if (!ok) {
             ss << "{incomplete payload}";
diff --git a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp
index 4b795d9..cd2e8c6 100644
--- a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp
@@ -18,7 +18,7 @@
 
 namespace android::nl::protocols {
 
-NetlinkProtocol::NetlinkProtocol(int protocol, const std::string name,
+NetlinkProtocol::NetlinkProtocol(int protocol, const std::string& name,
                                  const MessageDescriptorList&& messageDescrs)
     : mProtocol(protocol), mName(name), mMessageDescrs(toMap(messageDescrs, protocol)) {}
 
@@ -42,7 +42,7 @@
         const NetlinkProtocol::MessageDescriptorList& descrs, int protocol) {
     MessageDescriptorMap map;
     for (const auto& descr : descrs) {
-        for (const auto& [mtype, mname] : descr->getMessageTypeMap()) {
+        for (const auto& [mtype, mdet] : descr->getMessageDetailsMap()) {
             map.emplace(mtype, descr);
         }
     }
@@ -53,7 +53,7 @@
     };
 
     for (const auto& descr : baseDescriptors) {
-        for (const auto& [mtype, mname] : descr->getMessageTypeMap()) {
+        for (const auto& [mtype, mdet] : descr->getMessageDetailsMap()) {
             map.emplace(mtype, descr);
         }
     }
diff --git a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h
index 81a0a65..c969547 100644
--- a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h
+++ b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h
@@ -46,7 +46,7 @@
   protected:
     typedef std::vector<std::shared_ptr<const MessageDescriptor>> MessageDescriptorList;
 
-    NetlinkProtocol(int protocol, const std::string name,
+    NetlinkProtocol(int protocol, const std::string& name,
                     const MessageDescriptorList&& messageDescrs);
 
   private:
diff --git a/automotive/can/1.0/default/libnl++/protocols/common/Empty.cpp b/automotive/can/1.0/default/libnl++/protocols/common/Empty.cpp
index dbf10d4..8a672d3 100644
--- a/automotive/can/1.0/default/libnl++/protocols/common/Empty.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/common/Empty.cpp
@@ -20,9 +20,9 @@
 
 // clang-format off
 Empty::Empty() : MessageDefinition<char>("nlmsg", {
-    {NLMSG_NOOP, "NOOP"},
-    {NLMSG_DONE, "DONE"},
-    {NLMSG_OVERRUN, "OVERRUN"},
+    {NLMSG_NOOP, {"NOOP", MessageGenre::UNKNOWN}},
+    {NLMSG_DONE, {"DONE", MessageGenre::UNKNOWN}},
+    {NLMSG_OVERRUN, {"OVERRUN", MessageGenre::UNKNOWN}},
 }) {}
 // clang-format on
 
diff --git a/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp b/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp
index 1d6bd1c..44708a3 100644
--- a/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp
@@ -22,15 +22,21 @@
 
 namespace android::nl::protocols::base {
 
+using DataType = AttributeDefinition::DataType;
+
 // clang-format off
 Error::Error(int protocol) : MessageDefinition<nlmsgerr>("nlmsg", {
-    {NLMSG_ERROR, "ERROR"},
+    {NLMSG_ERROR, {"ERROR", MessageGenre::ACK}},
+}, {
+    {NLMSGERR_ATTR_MSG, {"MSG", DataType::String}},
+    {NLMSGERR_ATTR_OFFS, {"OFFS", DataType::Uint}},
+    {NLMSGERR_ATTR_COOKIE, {"COOKIE", DataType::Raw}},
 }), mProtocol(protocol) {}
 // clang-format on
 
 void Error::toStream(std::stringstream& ss, const nlmsgerr& data) const {
     ss << "nlmsgerr{error=" << data.error
-       << ", msg=" << toString({&data.msg, sizeof(data.msg)}, mProtocol) << "}";
+       << ", msg=" << toString({&data.msg, sizeof(data.msg)}, mProtocol, false) << "}";
 }
 
 }  // namespace android::nl::protocols::base
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp
index 5203368..134638e 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp
@@ -19,13 +19,14 @@
 namespace android::nl::protocols::generic {
 
 GenericMessageBase::GenericMessageBase(
-        nlmsgtype_t msgtype, std::string msgname,
+        nlmsgtype_t msgtype, const std::string&& msgname,
         const std::initializer_list<GenericCommandNameMap::value_type> commandNames,
         const std::initializer_list<AttributeMap::value_type> attrTypes)
-    : MessageDefinition<struct genlmsghdr>(msgname, {{msgtype, msgname}}, attrTypes),
+    : MessageDefinition<genlmsghdr>(msgname, {{msgtype, {msgname, MessageGenre::UNKNOWN}}},
+                                    attrTypes),
       mCommandNames(commandNames) {}
 
-void GenericMessageBase::toStream(std::stringstream& ss, const struct genlmsghdr& data) const {
+void GenericMessageBase::toStream(std::stringstream& ss, const genlmsghdr& data) const {
     ss << "genlmsghdr{";
     if (mCommandNames.count(data.cmd) == 0) {
         ss << "cmd=" << unsigned(data.cmd);
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.h b/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.h
index f3b0b31..443f10c 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.h
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.h
@@ -22,16 +22,16 @@
 
 namespace android::nl::protocols::generic {
 
-class GenericMessageBase : public MessageDefinition<struct genlmsghdr> {
+class GenericMessageBase : public MessageDefinition<genlmsghdr> {
   public:
     typedef std::map<uint8_t, std::string> GenericCommandNameMap;
 
     GenericMessageBase(
-            nlmsgtype_t msgtype, std::string msgname,
+            nlmsgtype_t msgtype, const std::string&& msgname,
             const std::initializer_list<GenericCommandNameMap::value_type> commandNames = {},
             const std::initializer_list<AttributeMap::value_type> attrTypes = {});
 
-    void toStream(std::stringstream& ss, const struct genlmsghdr& data) const override;
+    void toStream(std::stringstream& ss, const genlmsghdr& data) const override;
 
   private:
     const GenericCommandNameMap mCommandNames;
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp b/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
index 26d3e3e..7db487a 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
@@ -25,10 +25,10 @@
 using DataType = AttributeDefinition::DataType;
 
 // clang-format off
-Link::Link() : MessageDefinition<struct ifinfomsg>("link", {
-    {RTM_NEWLINK, "NEWLINK"},
-    {RTM_DELLINK, "DELLINK"},
-    {RTM_GETLINK, "GETLINK"},
+Link::Link() : MessageDefinition<ifinfomsg>("link", {
+    {RTM_NEWLINK, {"NEWLINK", MessageGenre::NEW}},
+    {RTM_DELLINK, {"DELLINK", MessageGenre::DELETE}},
+    {RTM_GETLINK, {"GETLINK", MessageGenre::GET}},
 }, {
     {IFLA_ADDRESS, {"ADDRESS"}},
     {IFLA_BROADCAST, {"BROADCAST"}},
@@ -107,7 +107,7 @@
 }) {}
 // clang-format off
 
-void Link::toStream(std::stringstream& ss, const struct ifinfomsg& data) const {
+void Link::toStream(std::stringstream& ss, const ifinfomsg& data) const {
     ss << "ifinfomsg{"
        << "family=" << unsigned(data.ifi_family) << ", type=" << data.ifi_type
        << ", index=" << data.ifi_index << ", flags=" << data.ifi_flags
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/Link.h b/automotive/can/1.0/default/libnl++/protocols/route/Link.h
index 4ea3eba..ecfefc9 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/Link.h
+++ b/automotive/can/1.0/default/libnl++/protocols/route/Link.h
@@ -22,10 +22,10 @@
 
 namespace android::nl::protocols::route {
 
-class Link : public MessageDefinition<struct ifinfomsg> {
+class Link : public MessageDefinition<ifinfomsg> {
   public:
     Link();
-    void toStream(std::stringstream& ss, const struct ifinfomsg& data) const override;
+    void toStream(std::stringstream& ss, const ifinfomsg& data) const override;
 };
 
 }  // namespace android::nl::protocols::route
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/structs.cpp b/automotive/can/1.0/default/libnl++/protocols/route/structs.cpp
index ea923bb..b62cec3 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/structs.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/route/structs.cpp
@@ -18,7 +18,7 @@
 
 namespace android::nl::protocols::route {
 
-void mapToStream(std::stringstream& ss, const nlbuf<nlattr> attr) {
+void mapToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
     const auto& [ok, data] = attr.data<rtnl_link_ifmap>().getFirst();
     if (!ok) {
         ss << "invalid structure";
@@ -33,7 +33,7 @@
        << unsigned(data.port) << '}';
 }
 
-void ifla_cacheinfoToStream(std::stringstream& ss, const nlbuf<nlattr> attr) {
+void ifla_cacheinfoToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
     const auto& [ok, data] = attr.data<ifla_cacheinfo>().getFirst();
     if (!ok) {
         ss << "invalid structure";
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/structs.h b/automotive/can/1.0/default/libnl++/protocols/route/structs.h
index 38776fa..b9d622a 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/structs.h
+++ b/automotive/can/1.0/default/libnl++/protocols/route/structs.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <libnl++/nlbuf.h>
+#include <libnl++/Buffer.h>
 
 #include <linux/rtnetlink.h>
 
@@ -25,13 +25,13 @@
 namespace android::nl::protocols::route {
 
 // rtnl_link_ifmap
-void mapToStream(std::stringstream& ss, const nlbuf<nlattr> attr);
+void mapToStream(std::stringstream& ss, const Buffer<nlattr> attr);
 
 // ifla_cacheinfo
-void ifla_cacheinfoToStream(std::stringstream& ss, const nlbuf<nlattr> attr);
+void ifla_cacheinfoToStream(std::stringstream& ss, const Buffer<nlattr> attr);
 
 template <typename T>
-void arrayToStream(std::stringstream& ss, const nlbuf<nlattr> attr) {
+void arrayToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
     ss << '{';
     for (const auto it : attr.data<T>().getRaw()) {
         ss << it << ',';
@@ -42,7 +42,7 @@
 
 // rtnl_link_stats or rtnl_link_stats64
 template <typename T>
-void statsToStream(std::stringstream& ss, const nlbuf<nlattr> attr) {
+void statsToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
     const auto& [ok, data] = attr.data<T>().getFirst();
     if (!ok) {
         ss << "invalid structure";
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
index 8dfd7d6..fc77579 100644
--- a/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
+++ b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
@@ -86,7 +86,7 @@
         EXPECT_EQ(ICanController::Result::OK, result);
 
         /* Not using ICanBus::getService here, since it ignores interfaces not in the manifest
-         * file -- this is a test, so we don't want to add dummy services to a device manifest. */
+         * file -- this is a test, so we don't want to add fake services to a device manifest. */
         auto manager = hidl::manager::V1_2::IServiceManager::getService();
         auto service = manager->get(ICanBus::descriptor, config.name);
         mBus = ICanBus::castFrom(service);
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
index b945978..294cd17 100644
--- a/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
+++ b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
@@ -119,7 +119,7 @@
 
 void CanControllerHalTest::assertRegistered(std::string srvname, bool expectRegistered) {
     /* Not using ICanBus::tryGetService here, since it ignores interfaces not in the manifest
-     * file -- this is a test, so we don't want to add dummy services to a device manifest. */
+     * file -- this is a test, so we don't want to add fake services to a device manifest. */
     auto manager = hidl::manager::V1_2::IServiceManager::getService();
     auto busService = manager->get(ICanBus::descriptor, srvname);
     ASSERT_EQ(expectRegistered, busService.withDefault(nullptr) != nullptr)
@@ -145,7 +145,7 @@
     assertRegistered(name, false);
 }
 
-TEST_P(CanControllerHalTest, DownDummy) {
+TEST_P(CanControllerHalTest, DownFake) {
     const auto result = mCanController->downInterface("imnotup");
     ASSERT_FALSE(result);
 }
diff --git a/automotive/evs/1.0/default/EvsCamera.cpp b/automotive/evs/1.0/default/EvsCamera.cpp
index e0782ec..0daea5a 100644
--- a/automotive/evs/1.0/default/EvsCamera.cpp
+++ b/automotive/evs/1.0/default/EvsCamera.cpp
@@ -49,7 +49,7 @@
 
     mDescription.cameraId = id;
 
-    // Set up dummy data for testing
+    // Set up mock data for testing
     if (mDescription.cameraId == kCameraName_Backup) {
         mWidth  = 640;          // full NTSC/VGA
         mHeight = 480;          // full NTSC/VGA
diff --git a/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp b/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp
index 7fe7a33..ad607d8 100644
--- a/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp
+++ b/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp
@@ -369,7 +369,7 @@
 TEST_P(EvsHidlTest, CameraStreamBuffering) {
     ALOGI("Starting CameraStreamBuffering test");
 
-    // Arbitrary constant (should be > 1 and less than crazy)
+    // Arbitrary constant (should be > 1 and not too big)
     static const unsigned int kBuffersToHold = 6;
 
     // Get the camera list
@@ -381,7 +381,7 @@
         sp<IEvsCamera> pCam = pEnumerator->openCamera(cam.cameraId);
         ASSERT_NE(pCam, nullptr);
 
-        // Ask for a crazy number of buffers in flight to ensure it errors correctly
+        // Ask for a very large number of buffers in flight to ensure it errors correctly
         Return<EvsResult> badResult = pCam->setMaxFramesInFlight(0xFFFFFFFF);
         EXPECT_EQ(EvsResult::BUFFER_NOT_AVAILABLE, badResult);
 
diff --git a/automotive/evs/1.1/default/ConfigManager.h b/automotive/evs/1.1/default/ConfigManager.h
index 870af1c..b0b2670 100644
--- a/automotive/evs/1.1/default/ConfigManager.h
+++ b/automotive/evs/1.1/default/ConfigManager.h
@@ -76,7 +76,7 @@
         }
 
         /*
-         * List of supported controls that the master client can program.
+         * List of supported controls that the primary client can program.
          * Paraemters are stored with its valid range
          */
         unordered_map<CameraParam,
diff --git a/automotive/evs/1.1/default/EvsEnumerator.cpp b/automotive/evs/1.1/default/EvsEnumerator.cpp
index 117ee7a..d066471 100644
--- a/automotive/evs/1.1/default/EvsEnumerator.cpp
+++ b/automotive/evs/1.1/default/EvsEnumerator.cpp
@@ -70,7 +70,7 @@
 
     // Add ultrasonics array desc.
     sUltrasonicsArrayRecordList.emplace_back(
-            EvsUltrasonicsArray::GetDummyArrayDesc("front_array"));
+            EvsUltrasonicsArray::GetMockArrayDesc("front_array"));
 }
 
 
diff --git a/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp b/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp
index bc69aa4..ebd47c6 100644
--- a/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp
+++ b/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp
@@ -45,7 +45,7 @@
 
 namespace {
 
-void fillDummyArrayDesc(UltrasonicsArrayDesc& arrayDesc) {
+void fillMockArrayDesc(UltrasonicsArrayDesc& arrayDesc) {
     arrayDesc.maxReadingsPerSensorCount = kMaxReadingsPerSensor;
     arrayDesc.maxReceiversCount = kMaxReceiversCount;
 
@@ -99,8 +99,8 @@
     }
 }
 
-// Fills dataFrameDesc with dummy data.
-bool fillDummyDataFrame(UltrasonicsDataFrameDesc& dataFrameDesc, sp<IMemory> pIMemory) {
+// Fills dataFrameDesc with mock data.
+bool fillMockDataFrame(UltrasonicsDataFrameDesc& dataFrameDesc, sp<IMemory> pIMemory) {
     dataFrameDesc.timestampNs = elapsedRealtimeNano();
 
     const std::vector<uint8_t> transmittersIdList = {0};
@@ -137,9 +137,9 @@
     : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED) {
     LOG(DEBUG) << "EvsUltrasonicsArray instantiated";
 
-    // Set up dummy data for description.
+    // Set up mock data for description.
     mArrayDesc.ultrasonicsArrayId = deviceName;
-    fillDummyArrayDesc(mArrayDesc);
+    fillMockArrayDesc(mArrayDesc);
 
     // Assign allocator.
     mShmemAllocator = IAllocator::getService("ashmem");
@@ -182,10 +182,10 @@
     mStreamState = DEAD;
 }
 
-UltrasonicsArrayDesc EvsUltrasonicsArray::GetDummyArrayDesc(const char* deviceName) {
+UltrasonicsArrayDesc EvsUltrasonicsArray::GetMockArrayDesc(const char* deviceName) {
     UltrasonicsArrayDesc ultrasonicsArrayDesc;
     ultrasonicsArrayDesc.ultrasonicsArrayId = deviceName;
-    fillDummyArrayDesc(ultrasonicsArrayDesc);
+    fillMockArrayDesc(ultrasonicsArrayDesc);
     return ultrasonicsArrayDesc;
 }
 
@@ -497,17 +497,17 @@
 
         if (timeForFrame) {
             // Assemble the buffer description we'll transmit below
-            UltrasonicsDataFrameDesc dummyDataFrameDesc;
-            dummyDataFrameDesc.dataFrameId = idx;
-            dummyDataFrameDesc.waveformsData = mDataFrames[idx].sharedMemory.hidlMemory;
+            UltrasonicsDataFrameDesc mockDataFrameDesc;
+            mockDataFrameDesc.dataFrameId = idx;
+            mockDataFrameDesc.waveformsData = mDataFrames[idx].sharedMemory.hidlMemory;
 
-            // Fill dummy waveform data.
-            fillDummyDataFrame(dummyDataFrameDesc, mDataFrames[idx].sharedMemory.pIMemory);
+            // Fill mock waveform data.
+            fillMockDataFrame(mockDataFrameDesc, mDataFrames[idx].sharedMemory.pIMemory);
 
             // Issue the (asynchronous) callback to the client -- can't be holding the lock
-            auto result = mStream->deliverDataFrame(dummyDataFrameDesc);
+            auto result = mStream->deliverDataFrame(mockDataFrameDesc);
             if (result.isOk()) {
-                LOG(DEBUG) << "Delivered data frame id: " << dummyDataFrameDesc.dataFrameId;
+                LOG(DEBUG) << "Delivered data frame id: " << mockDataFrameDesc.dataFrameId;
             } else {
                 // This can happen if the client dies and is likely unrecoverable.
                 // To avoid consuming resources generating failing calls, we stop sending
diff --git a/automotive/evs/1.1/default/EvsUltrasonicsArray.h b/automotive/evs/1.1/default/EvsUltrasonicsArray.h
index 7a41012..88aa600 100644
--- a/automotive/evs/1.1/default/EvsUltrasonicsArray.h
+++ b/automotive/evs/1.1/default/EvsUltrasonicsArray.h
@@ -58,7 +58,7 @@
     static sp<EvsUltrasonicsArray> Create(const char* deviceName);
 
     // Returns a ultrasonics array descriptor filled with sample data.
-    static UltrasonicsArrayDesc GetDummyArrayDesc(const char* id);
+    static UltrasonicsArrayDesc GetMockArrayDesc(const char* id);
 
     DISALLOW_COPY_AND_ASSIGN(EvsUltrasonicsArray);
     virtual ~EvsUltrasonicsArray() override;
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 4398fb1..638ecd5 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -500,7 +500,7 @@
 TEST_P(EvsHidlTest, CameraStreamBuffering) {
     LOG(INFO) << "Starting CameraStreamBuffering test";
 
-    // Arbitrary constant (should be > 1 and less than crazy)
+    // Arbitrary constant (should be > 1 and not too big)
     static const unsigned int kBuffersToHold = 6;
 
     // Get the camera list
@@ -527,7 +527,7 @@
         // Store a camera handle for a clean-up
         activeCameras.push_back(pCam);
 
-        // Ask for a crazy number of buffers in flight to ensure it errors correctly
+        // Ask for a very large number of buffers in flight to ensure it errors correctly
         Return<EvsResult> badResult = pCam->setMaxFramesInFlight(0xFFFFFFFF);
         EXPECT_EQ(EvsResult::BUFFER_NOT_AVAILABLE, badResult);
 
@@ -930,12 +930,12 @@
 
 
 /*
- * CameraMasterRelease
- * Verify that non-master client gets notified when the master client either
+ * CameraPrimaryClientRelease
+ * Verify that non-primary client gets notified when the primary client either
  * terminates or releases a role.
  */
-TEST_P(EvsHidlTest, CameraMasterRelease) {
-    LOG(INFO) << "Starting CameraMasterRelease test";
+TEST_P(EvsHidlTest, CameraPrimaryClientRelease) {
+    LOG(INFO) << "Starting CameraPrimaryClientRelease test";
 
     if (mIsHwModule) {
         // This test is not for HW module implementation.
@@ -961,57 +961,57 @@
         }
 
         // Create two camera clients.
-        sp<IEvsCamera_1_1> pCamMaster =
+        sp<IEvsCamera_1_1> pCamPrimary =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
-        ASSERT_NE(pCamMaster, nullptr);
+        ASSERT_NE(pCamPrimary, nullptr);
 
         // Store a camera handle for a clean-up
-        activeCameras.push_back(pCamMaster);
+        activeCameras.push_back(pCamPrimary);
 
-        sp<IEvsCamera_1_1> pCamNonMaster =
+        sp<IEvsCamera_1_1> pCamSecondary =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
-        ASSERT_NE(pCamNonMaster, nullptr);
+        ASSERT_NE(pCamSecondary, nullptr);
 
         // Store a camera handle for a clean-up
-        activeCameras.push_back(pCamNonMaster);
+        activeCameras.push_back(pCamSecondary);
 
         // Set up per-client frame receiver objects which will fire up its own thread
-        sp<FrameHandler> frameHandlerMaster =
-            new FrameHandler(pCamMaster, cam,
+        sp<FrameHandler> frameHandlerPrimary =
+            new FrameHandler(pCamPrimary, cam,
                              nullptr,
                              FrameHandler::eAutoReturn);
-        ASSERT_NE(frameHandlerMaster, nullptr);
-        sp<FrameHandler> frameHandlerNonMaster =
-            new FrameHandler(pCamNonMaster, cam,
+        ASSERT_NE(frameHandlerPrimary, nullptr);
+        sp<FrameHandler> frameHandlerSecondary =
+            new FrameHandler(pCamSecondary, cam,
                              nullptr,
                              FrameHandler::eAutoReturn);
-        ASSERT_NE(frameHandlerNonMaster, nullptr);
+        ASSERT_NE(frameHandlerSecondary, nullptr);
 
-        // Set one client as the master
-        EvsResult result = pCamMaster->setMaster();
+        // Set one client as the primary client
+        EvsResult result = pCamPrimary->setMaster();
         ASSERT_TRUE(result == EvsResult::OK);
 
-        // Try to set another client as the master.
-        result = pCamNonMaster->setMaster();
+        // Try to set another client as the primary client.
+        result = pCamSecondary->setMaster();
         ASSERT_TRUE(result == EvsResult::OWNERSHIP_LOST);
 
-        // Start the camera's video stream via a master client.
-        bool startResult = frameHandlerMaster->startStream();
+        // Start the camera's video stream via a primary client client.
+        bool startResult = frameHandlerPrimary->startStream();
         ASSERT_TRUE(startResult);
 
         // Ensure the stream starts
-        frameHandlerMaster->waitForFrameCount(1);
+        frameHandlerPrimary->waitForFrameCount(1);
 
         // Start the camera's video stream via another client
-        startResult = frameHandlerNonMaster->startStream();
+        startResult = frameHandlerSecondary->startStream();
         ASSERT_TRUE(startResult);
 
         // Ensure the stream starts
-        frameHandlerNonMaster->waitForFrameCount(1);
+        frameHandlerSecondary->waitForFrameCount(1);
 
-        // Non-master client expects to receive a master role relesed
+        // Non-primary client expects to receive a primary client role relesed
         // notification.
         EvsEventDesc aTargetEvent  = {};
         EvsEventDesc aNotification = {};
@@ -1020,14 +1020,14 @@
         std::mutex eventLock;
         std::condition_variable eventCond;
         std::thread listener = std::thread(
-            [&aNotification, &frameHandlerNonMaster, &listening, &eventCond]() {
+            [&aNotification, &frameHandlerSecondary, &listening, &eventCond]() {
                 // Notify that a listening thread is running.
                 listening = true;
                 eventCond.notify_all();
 
                 EvsEventDesc aTargetEvent;
                 aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
-                if (!frameHandlerNonMaster->waitForEvent(aTargetEvent, aNotification, true)) {
+                if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification, true)) {
                     LOG(WARNING) << "A timer is expired before a target event is fired.";
                 }
 
@@ -1043,8 +1043,8 @@
         }
         lock.unlock();
 
-        // Release a master role.
-        pCamMaster->unsetMaster();
+        // Release a primary client role.
+        pCamPrimary->unsetMaster();
 
         // Join a listening thread.
         if (listener.joinable()) {
@@ -1055,24 +1055,24 @@
         ASSERT_EQ(EvsEventType::MASTER_RELEASED,
                   static_cast<EvsEventType>(aNotification.aType));
 
-        // Non-master becomes a master.
-        result = pCamNonMaster->setMaster();
+        // Non-primary becomes a primary client.
+        result = pCamSecondary->setMaster();
         ASSERT_TRUE(result == EvsResult::OK);
 
-        // Previous master client fails to become a master.
-        result = pCamMaster->setMaster();
+        // Previous primary client fails to become a primary client.
+        result = pCamPrimary->setMaster();
         ASSERT_TRUE(result == EvsResult::OWNERSHIP_LOST);
 
         listening = false;
         listener = std::thread(
-            [&aNotification, &frameHandlerMaster, &listening, &eventCond]() {
+            [&aNotification, &frameHandlerPrimary, &listening, &eventCond]() {
                 // Notify that a listening thread is running.
                 listening = true;
                 eventCond.notify_all();
 
                 EvsEventDesc aTargetEvent;
                 aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
-                if (!frameHandlerMaster->waitForEvent(aTargetEvent, aNotification, true)) {
+                if (!frameHandlerPrimary->waitForEvent(aTargetEvent, aNotification, true)) {
                     LOG(WARNING) << "A timer is expired before a target event is fired.";
                 }
 
@@ -1087,8 +1087,8 @@
         }
         lock.unlock();
 
-        // Closing current master client.
-        frameHandlerNonMaster->shutdown();
+        // Closing current primary client.
+        frameHandlerSecondary->shutdown();
 
         // Join a listening thread.
         if (listener.joinable()) {
@@ -1100,11 +1100,11 @@
                   static_cast<EvsEventType>(aNotification.aType));
 
         // Closing streams.
-        frameHandlerMaster->shutdown();
+        frameHandlerPrimary->shutdown();
 
         // Explicitly release the camera
-        pEnumerator->closeCamera(pCamMaster);
-        pEnumerator->closeCamera(pCamNonMaster);
+        pEnumerator->closeCamera(pCamPrimary);
+        pEnumerator->closeCamera(pCamSecondary);
         activeCameras.clear();
     }
 }
@@ -1112,7 +1112,7 @@
 
 /*
  * MultiCameraParameter:
- * Verify that master and non-master clients behave as expected when they try to adjust
+ * Verify that primary and non-primary clients behave as expected when they try to adjust
  * camera parameters.
  */
 TEST_P(EvsHidlTest, MultiCameraParameter) {
@@ -1142,88 +1142,88 @@
         }
 
         // Create two camera clients.
-        sp<IEvsCamera_1_1> pCamMaster =
+        sp<IEvsCamera_1_1> pCamPrimary =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
-        ASSERT_NE(pCamMaster, nullptr);
+        ASSERT_NE(pCamPrimary, nullptr);
 
         // Store a camera handle for a clean-up
-        activeCameras.push_back(pCamMaster);
+        activeCameras.push_back(pCamPrimary);
 
-        sp<IEvsCamera_1_1> pCamNonMaster =
+        sp<IEvsCamera_1_1> pCamSecondary =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
-        ASSERT_NE(pCamNonMaster, nullptr);
+        ASSERT_NE(pCamSecondary, nullptr);
 
         // Store a camera handle for a clean-up
-        activeCameras.push_back(pCamNonMaster);
+        activeCameras.push_back(pCamSecondary);
 
         // Get the parameter list
-        std::vector<CameraParam> camMasterCmds, camNonMasterCmds;
-        pCamMaster->getParameterList([&camMasterCmds](hidl_vec<CameraParam> cmdList) {
-                camMasterCmds.reserve(cmdList.size());
+        std::vector<CameraParam> camPrimaryCmds, camSecondaryCmds;
+        pCamPrimary->getParameterList([&camPrimaryCmds](hidl_vec<CameraParam> cmdList) {
+                camPrimaryCmds.reserve(cmdList.size());
                 for (auto &&cmd : cmdList) {
-                    camMasterCmds.push_back(cmd);
+                    camPrimaryCmds.push_back(cmd);
                 }
             }
         );
 
-        pCamNonMaster->getParameterList([&camNonMasterCmds](hidl_vec<CameraParam> cmdList) {
-                camNonMasterCmds.reserve(cmdList.size());
+        pCamSecondary->getParameterList([&camSecondaryCmds](hidl_vec<CameraParam> cmdList) {
+                camSecondaryCmds.reserve(cmdList.size());
                 for (auto &&cmd : cmdList) {
-                    camNonMasterCmds.push_back(cmd);
+                    camSecondaryCmds.push_back(cmd);
                 }
             }
         );
 
-        if (camMasterCmds.size() < 1 ||
-            camNonMasterCmds.size() < 1) {
+        if (camPrimaryCmds.size() < 1 ||
+            camSecondaryCmds.size() < 1) {
             // Skip a camera device if it does not support any parameter.
             continue;
         }
 
         // Set up per-client frame receiver objects which will fire up its own thread
-        sp<FrameHandler> frameHandlerMaster =
-            new FrameHandler(pCamMaster, cam,
+        sp<FrameHandler> frameHandlerPrimary =
+            new FrameHandler(pCamPrimary, cam,
                              nullptr,
                              FrameHandler::eAutoReturn);
-        ASSERT_NE(frameHandlerMaster, nullptr);
-        sp<FrameHandler> frameHandlerNonMaster =
-            new FrameHandler(pCamNonMaster, cam,
+        ASSERT_NE(frameHandlerPrimary, nullptr);
+        sp<FrameHandler> frameHandlerSecondary =
+            new FrameHandler(pCamSecondary, cam,
                              nullptr,
                              FrameHandler::eAutoReturn);
-        ASSERT_NE(frameHandlerNonMaster, nullptr);
+        ASSERT_NE(frameHandlerSecondary, nullptr);
 
-        // Set one client as the master
-        EvsResult result = pCamMaster->setMaster();
+        // Set one client as the primary client.
+        EvsResult result = pCamPrimary->setMaster();
         ASSERT_EQ(EvsResult::OK, result);
 
-        // Try to set another client as the master.
-        result = pCamNonMaster->setMaster();
+        // Try to set another client as the primary client.
+        result = pCamSecondary->setMaster();
         ASSERT_EQ(EvsResult::OWNERSHIP_LOST, result);
 
-        // Start the camera's video stream via a master client.
-        bool startResult = frameHandlerMaster->startStream();
+        // Start the camera's video stream via a primary client client.
+        bool startResult = frameHandlerPrimary->startStream();
         ASSERT_TRUE(startResult);
 
         // Ensure the stream starts
-        frameHandlerMaster->waitForFrameCount(1);
+        frameHandlerPrimary->waitForFrameCount(1);
 
         // Start the camera's video stream via another client
-        startResult = frameHandlerNonMaster->startStream();
+        startResult = frameHandlerSecondary->startStream();
         ASSERT_TRUE(startResult);
 
         // Ensure the stream starts
-        frameHandlerNonMaster->waitForFrameCount(1);
+        frameHandlerSecondary->waitForFrameCount(1);
 
         int32_t val0 = 0;
         std::vector<int32_t> values;
         EvsEventDesc aNotification0 = {};
         EvsEventDesc aNotification1 = {};
-        for (auto &cmd : camMasterCmds) {
+        for (auto &cmd : camPrimaryCmds) {
             // Get a valid parameter value range
             int32_t minVal, maxVal, step;
-            pCamMaster->getIntParameterRange(
+            pCamPrimary->getIntParameterRange(
                 cmd,
                 [&minVal, &maxVal, &step](int32_t val0, int32_t val1, int32_t val2) {
                     minVal = val0;
@@ -1236,7 +1236,7 @@
             if (cmd == CameraParam::ABSOLUTE_FOCUS) {
                 // Try to turn off auto-focus
                 values.clear();
-                pCamMaster->setIntParameter(CameraParam::AUTO_FOCUS, 0,
+                pCamPrimary->setIntParameter(CameraParam::AUTO_FOCUS, 0,
                                    [&result, &values](auto status, auto effectiveValues) {
                                        result = status;
                                        if (status == EvsResult::OK) {
@@ -1261,7 +1261,7 @@
             std::condition_variable eventCond;
             std::thread listener0 = std::thread(
                 [cmd, val0,
-                 &aNotification0, &frameHandlerMaster, &listening0, &listening1, &eventCond]() {
+                 &aNotification0, &frameHandlerPrimary, &listening0, &listening1, &eventCond]() {
                     listening0 = true;
                     if (listening1) {
                         eventCond.notify_all();
@@ -1271,14 +1271,14 @@
                     aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
                     aTargetEvent.payload[0] = static_cast<uint32_t>(cmd);
                     aTargetEvent.payload[1] = val0;
-                    if (!frameHandlerMaster->waitForEvent(aTargetEvent, aNotification0)) {
+                    if (!frameHandlerPrimary->waitForEvent(aTargetEvent, aNotification0)) {
                         LOG(WARNING) << "A timer is expired before a target event is fired.";
                     }
                 }
             );
             std::thread listener1 = std::thread(
                 [cmd, val0,
-                 &aNotification1, &frameHandlerNonMaster, &listening0, &listening1, &eventCond]() {
+                 &aNotification1, &frameHandlerSecondary, &listening0, &listening1, &eventCond]() {
                     listening1 = true;
                     if (listening0) {
                         eventCond.notify_all();
@@ -1288,7 +1288,7 @@
                     aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
                     aTargetEvent.payload[0] = static_cast<uint32_t>(cmd);
                     aTargetEvent.payload[1] = val0;
-                    if (!frameHandlerNonMaster->waitForEvent(aTargetEvent, aNotification1)) {
+                    if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification1)) {
                         LOG(WARNING) << "A timer is expired before a target event is fired.";
                     }
                 }
@@ -1305,7 +1305,7 @@
 
             // Try to program a parameter
             values.clear();
-            pCamMaster->setIntParameter(cmd, val0,
+            pCamPrimary->setIntParameter(cmd, val0,
                                      [&result, &values](auto status, auto effectiveValues) {
                                          result = status;
                                          if (status == EvsResult::OK) {
@@ -1345,9 +1345,9 @@
             }
 
             // Clients expects to receive a parameter change notification
-            // whenever a master client adjusts it.
+            // whenever a primary client client adjusts it.
             values.clear();
-            pCamMaster->getIntParameter(cmd,
+            pCamPrimary->getIntParameter(cmd,
                                      [&result, &values](auto status, auto readValues) {
                                          result = status;
                                          if (status == EvsResult::OK) {
@@ -1362,9 +1362,9 @@
             }
         }
 
-        // Try to adjust a parameter via non-master client
+        // Try to adjust a parameter via non-primary client
         values.clear();
-        pCamNonMaster->setIntParameter(camNonMasterCmds[0], val0,
+        pCamSecondary->setIntParameter(camSecondaryCmds[0], val0,
                                     [&result, &values](auto status, auto effectiveValues) {
                                         result = status;
                                         if (status == EvsResult::OK) {
@@ -1375,21 +1375,21 @@
                                     });
         ASSERT_EQ(EvsResult::INVALID_ARG, result);
 
-        // Non-master client attemps to be a master
-        result = pCamNonMaster->setMaster();
+        // Non-primary client attempts to be a primary client
+        result = pCamSecondary->setMaster();
         ASSERT_EQ(EvsResult::OWNERSHIP_LOST, result);
 
-        // Master client retires from a master role
+        // Primary client retires from a primary client role
         bool listening = false;
         std::condition_variable eventCond;
         std::thread listener = std::thread(
-            [&aNotification0, &frameHandlerNonMaster, &listening, &eventCond]() {
+            [&aNotification0, &frameHandlerSecondary, &listening, &eventCond]() {
                 listening = true;
                 eventCond.notify_all();
 
                 EvsEventDesc aTargetEvent;
                 aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
-                if (!frameHandlerNonMaster->waitForEvent(aTargetEvent, aNotification0, true)) {
+                if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification0, true)) {
                     LOG(WARNING) << "A timer is expired before a target event is fired.";
                 }
             }
@@ -1403,7 +1403,7 @@
         }
         lock.unlock();
 
-        result = pCamMaster->unsetMaster();
+        result = pCamPrimary->unsetMaster();
         ASSERT_EQ(EvsResult::OK, result);
 
         if (listener.joinable()) {
@@ -1414,7 +1414,7 @@
 
         // Try to adjust a parameter after being retired
         values.clear();
-        pCamMaster->setIntParameter(camMasterCmds[0], val0,
+        pCamPrimary->setIntParameter(camPrimaryCmds[0], val0,
                                  [&result, &values](auto status, auto effectiveValues) {
                                      result = status;
                                      if (status == EvsResult::OK) {
@@ -1425,15 +1425,15 @@
                                  });
         ASSERT_EQ(EvsResult::INVALID_ARG, result);
 
-        // Non-master client becomes a master
-        result = pCamNonMaster->setMaster();
+        // Non-primary client becomes a primary client
+        result = pCamSecondary->setMaster();
         ASSERT_EQ(EvsResult::OK, result);
 
-        // Try to adjust a parameter via new master client
-        for (auto &cmd : camNonMasterCmds) {
+        // Try to adjust a parameter via new primary client
+        for (auto &cmd : camSecondaryCmds) {
             // Get a valid parameter value range
             int32_t minVal, maxVal, step;
-            pCamNonMaster->getIntParameterRange(
+            pCamSecondary->getIntParameterRange(
                 cmd,
                 [&minVal, &maxVal, &step](int32_t val0, int32_t val1, int32_t val2) {
                     minVal = val0;
@@ -1447,7 +1447,7 @@
             if (cmd == CameraParam::ABSOLUTE_FOCUS) {
                 // Try to turn off auto-focus
                 values.clear();
-                pCamNonMaster->setIntParameter(CameraParam::AUTO_FOCUS, 0,
+                pCamSecondary->setIntParameter(CameraParam::AUTO_FOCUS, 0,
                                    [&result, &values](auto status, auto effectiveValues) {
                                        result = status;
                                        if (status == EvsResult::OK) {
@@ -1471,7 +1471,7 @@
             bool listening1 = false;
             std::condition_variable eventCond;
             std::thread listener0 = std::thread(
-                [&cmd, &val0, &aNotification0, &frameHandlerMaster, &listening0, &listening1, &eventCond]() {
+                [&]() {
                     listening0 = true;
                     if (listening1) {
                         eventCond.notify_all();
@@ -1481,13 +1481,13 @@
                     aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
                     aTargetEvent.payload[0] = static_cast<uint32_t>(cmd);
                     aTargetEvent.payload[1] = val0;
-                    if (!frameHandlerMaster->waitForEvent(aTargetEvent, aNotification0)) {
+                    if (!frameHandlerPrimary->waitForEvent(aTargetEvent, aNotification0)) {
                         LOG(WARNING) << "A timer is expired before a target event is fired.";
                     }
                 }
             );
             std::thread listener1 = std::thread(
-                [&cmd, &val0, &aNotification1, &frameHandlerNonMaster, &listening0, &listening1, &eventCond]() {
+                [&]() {
                     listening1 = true;
                     if (listening0) {
                         eventCond.notify_all();
@@ -1497,7 +1497,7 @@
                     aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
                     aTargetEvent.payload[0] = static_cast<uint32_t>(cmd);
                     aTargetEvent.payload[1] = val0;
-                    if (!frameHandlerNonMaster->waitForEvent(aTargetEvent, aNotification1)) {
+                    if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification1)) {
                         LOG(WARNING) << "A timer is expired before a target event is fired.";
                     }
                 }
@@ -1514,7 +1514,7 @@
 
             // Try to program a parameter
             values.clear();
-            pCamNonMaster->setIntParameter(cmd, val0,
+            pCamSecondary->setIntParameter(cmd, val0,
                                         [&result, &values](auto status, auto effectiveValues) {
                                             result = status;
                                             if (status == EvsResult::OK) {
@@ -1526,9 +1526,9 @@
             ASSERT_EQ(EvsResult::OK, result);
 
             // Clients expects to receive a parameter change notification
-            // whenever a master client adjusts it.
+            // whenever a primary client client adjusts it.
             values.clear();
-            pCamNonMaster->getIntParameter(cmd,
+            pCamSecondary->getIntParameter(cmd,
                                         [&result, &values](auto status, auto readValues) {
                                             result = status;
                                             if (status == EvsResult::OK) {
@@ -1567,17 +1567,17 @@
             }
         }
 
-        // New master retires from a master role
-        result = pCamNonMaster->unsetMaster();
+        // New primary client retires from the role
+        result = pCamSecondary->unsetMaster();
         ASSERT_EQ(EvsResult::OK, result);
 
         // Shutdown
-        frameHandlerMaster->shutdown();
-        frameHandlerNonMaster->shutdown();
+        frameHandlerPrimary->shutdown();
+        frameHandlerSecondary->shutdown();
 
         // Explicitly release the camera
-        pEnumerator->closeCamera(pCamMaster);
-        pEnumerator->closeCamera(pCamNonMaster);
+        pEnumerator->closeCamera(pCamPrimary);
+        pEnumerator->closeCamera(pCamSecondary);
         activeCameras.clear();
     }
 }
@@ -1586,7 +1586,7 @@
 /*
  * HighPriorityCameraClient:
  * EVS client, which owns the display, is priortized and therefore can take over
- * a master role from other EVS clients without the display.
+ * a primary client role from other EVS clients without the display.
  */
 TEST_P(EvsHidlTest, HighPriorityCameraClient) {
     LOG(INFO) << "Starting HighPriorityCameraClient test";
@@ -1668,7 +1668,7 @@
         frameHandler0->waitForFrameCount(1);
         frameHandler1->waitForFrameCount(1);
 
-        // Client 1 becomes a master and programs a parameter.
+        // Client 1 becomes a primary client and programs a parameter.
         EvsResult result = EvsResult::OK;
         // Get a valid parameter value range
         int32_t minVal, maxVal, step;
@@ -1681,7 +1681,7 @@
             }
         );
 
-        // Client1 becomes a master
+        // Client1 becomes a primary client
         result = pCam1->setMaster();
         ASSERT_EQ(EvsResult::OK, result);
 
@@ -1820,7 +1820,7 @@
         }
         lock.unlock();
 
-        // Client 0 steals a master role
+        // Client 0 steals a primary client role
         ASSERT_EQ(EvsResult::OK, pCam0->forceMaster(pDisplay));
 
         // Join a listener
@@ -2241,7 +2241,7 @@
 TEST_P(EvsHidlTest, CameraStreamExternalBuffering) {
     LOG(INFO) << "Starting CameraStreamExternalBuffering test";
 
-    // Arbitrary constant (should be > 1 and less than crazy)
+    // Arbitrary constant (should be > 1 and not too big)
     static const unsigned int kBuffersToHold = 6;
 
     // Get the camera list
diff --git a/automotive/vehicle/2.0/utils/Android.bp b/automotive/vehicle/2.0/utils/Android.bp
new file mode 100644
index 0000000..e354634
--- /dev/null
+++ b/automotive/vehicle/2.0/utils/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 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.
+
+// User HAL helper library.
+cc_library_static {
+    name: "android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
+    defaults: ["vhal_v2_0_defaults"],
+    vendor: true,
+    host_supported: true,
+    srcs: [
+        "UserHalHelper.cpp",
+    ],
+    export_include_dirs: [
+        ".",
+    ],
+}
+
+cc_test {
+    name: "android.hardware.automotive.vehicle@2.0-utils-unit-tests",
+    defaults: ["vhal_v2_0_defaults"],
+    vendor: true,
+    srcs: [
+        "tests/UserHalHelper_test.cpp",
+    ],
+    static_libs: [
+        "android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
+        "libgmock",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/automotive/vehicle/2.0/utils/UserHalHelper.cpp b/automotive/vehicle/2.0/utils/UserHalHelper.cpp
new file mode 100644
index 0000000..33b3948
--- /dev/null
+++ b/automotive/vehicle/2.0/utils/UserHalHelper.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+#define LOG_TAG "UserHalHelper"
+
+#include "UserHalHelper.h"
+
+#include <log/log.h>
+#include <utils/SystemClock.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace user_hal_helper {
+namespace {
+
+using android::base::Error;
+using android::base::Result;
+
+static constexpr const char* kSeparator = "||";
+static const size_t kNumFieldsPerUserInfo = 2;
+static const size_t kNumFieldsPerSetAssociation = 2;
+
+template <typename T>
+Result<T> verifyAndCast(int32_t value) {
+    T castValue = static_cast<T>(value);
+    const auto iter = hidl_enum_range<T>();
+    if (castValue < *iter.begin() || castValue > *std::prev(iter.end())) {
+        return Error() << "Value " << value << " not in range [" << toString(*iter.begin()) << ", "
+                       << toString(*std::prev(iter.end())) << "]";
+    }
+    for (const auto& v : hidl_enum_range<T>()) {
+        if (castValue == v) {
+            return castValue;
+        }
+    }
+    return Error() << "Value " << value << " not in enum values";
+}
+
+Result<void> verifyPropValue(const VehiclePropValue& propValue, VehicleProperty vehicleProperty,
+                             size_t minInt32Values) {
+    auto prop = verifyAndCast<VehicleProperty>(propValue.prop);
+    if (!prop.ok()) {
+        return Error() << "Invalid vehicle property: " << prop.error();
+    }
+    if (*prop != vehicleProperty) {
+        return Error() << "Mismatching " << toString(vehicleProperty) << " request, received "
+                       << toString(*prop) << " property";
+    }
+    if (propValue.value.int32Values.size() < minInt32Values) {
+        return Error() << "Int32Values must have at least " << minInt32Values
+                       << " values, received " << propValue.value.int32Values.size();
+    }
+    return {};
+}
+
+Result<void> parseUserInfo(const hidl_vec<int32_t>& int32Values, size_t startPos,
+                           UserInfo* userInfo) {
+    if (int32Values.size() < startPos + kNumFieldsPerUserInfo) {
+        return Error() << "Int32Values must have at least " << startPos + 2 << " values, received "
+                       << int32Values.size();
+    }
+    userInfo->userId = int32Values[startPos];
+    auto userFlags = verifyAndCast<UserFlags>(int32Values[startPos + 1]);
+    if (!userFlags.ok()) {
+        return Error() << "Invalid user flags: " << userFlags.error();
+    }
+    userInfo->flags = *userFlags;
+    return {};
+}
+
+Result<void> parseUsersInfo(const hidl_vec<int32_t>& int32Values, size_t startPos,
+                            UsersInfo* usersInfo) {
+    if (int32Values.size() < startPos + 3) {
+        return Error() << "Int32Values must have at least " << startPos + 3 << " values, received "
+                       << int32Values.size();
+    }
+    auto ret = parseUserInfo(int32Values, startPos, &usersInfo->currentUser);
+    if (!ret.ok()) {
+        return ret;
+    }
+    usersInfo->numberUsers = int32Values[startPos + 2];
+    usersInfo->existingUsers.resize(usersInfo->numberUsers);
+    for (size_t i = 0; i < static_cast<size_t>(usersInfo->numberUsers); ++i) {
+        ret = parseUserInfo(int32Values, startPos + 3 + (kNumFieldsPerUserInfo * i),
+                            &usersInfo->existingUsers[i]);
+        if (!ret.ok()) {
+            return Error() << "Failed to parse existing user '" << i << "' info: " << ret.error();
+        }
+    }
+    return {};
+}
+
+Result<void> parseUserAssociationTypes(
+        const hidl_vec<int32_t>& int32Values, size_t startPos, size_t numberAssociationTypes,
+        hidl_vec<UserIdentificationAssociationType>* associationTypes) {
+    size_t minInt32Values = startPos + numberAssociationTypes;
+    if (int32Values.size() < minInt32Values) {
+        return Error() << "Int32Values must have at least " << minInt32Values
+                       << " values, received " << int32Values.size();
+    }
+    associationTypes->resize(numberAssociationTypes);
+    for (size_t i = 0; i < static_cast<size_t>(numberAssociationTypes); ++i) {
+        size_t pos = startPos + i;
+        auto type = verifyAndCast<UserIdentificationAssociationType>(int32Values[pos]);
+        if (!type.ok()) {
+            return Error() << "Invalid association type in query '" << i << "': " << type.error();
+        }
+        (*associationTypes)[i] = *type;
+    }
+    return {};
+}
+
+Result<void> parseUserAssociations(const hidl_vec<int32_t>& int32Values, size_t startPos,
+                                   size_t numberAssociations,
+                                   hidl_vec<UserIdentificationSetAssociation>* associations) {
+    size_t minInt32Values = startPos + (numberAssociations * kNumFieldsPerSetAssociation);
+    if (int32Values.size() < minInt32Values) {
+        return Error() << "Int32Values must have at least " << minInt32Values
+                       << " values, received " << int32Values.size();
+    }
+    associations->resize(numberAssociations);
+    for (size_t i = 0; i < static_cast<size_t>(numberAssociations); ++i) {
+        size_t pos = startPos + (kNumFieldsPerSetAssociation * i);
+        auto type = verifyAndCast<UserIdentificationAssociationType>(int32Values[pos]);
+        if (!type.ok()) {
+            return Error() << "Invalid association type in request '" << i << "': " << type.error();
+        }
+        (*associations)[i].type = *type;
+        auto value = verifyAndCast<UserIdentificationAssociationSetValue>(int32Values[pos + 1]);
+        if (!value.ok()) {
+            return Error() << "Invalid association set value in request '" << i
+                           << "': " << value.error();
+        }
+        (*associations)[i].value = *value;
+    }
+    return {};
+}
+
+}  // namespace
+
+Result<InitialUserInfoRequest> toInitialUserInfoRequest(const VehiclePropValue& propValue) {
+    auto ret = verifyPropValue(propValue, VehicleProperty::INITIAL_USER_INFO, 2);
+    if (!ret.ok()) {
+        return ret.error();
+    }
+    InitialUserInfoRequest request;
+    request.requestId = propValue.value.int32Values[0];
+    auto requestType = verifyAndCast<InitialUserInfoRequestType>(propValue.value.int32Values[1]);
+    if (!requestType.ok()) {
+        return Error() << "Invalid InitialUserInfoRequestType: " << requestType.error();
+    }
+    request.requestType = *requestType;
+    ret = parseUsersInfo(propValue.value.int32Values, 2, &request.usersInfo);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse users info: " << ret.error();
+    }
+    return request;
+}
+
+Result<SwitchUserRequest> toSwitchUserRequest(const VehiclePropValue& propValue) {
+    auto ret = verifyPropValue(propValue, VehicleProperty::SWITCH_USER, 2);
+    if (!ret.ok()) {
+        return ret.error();
+    }
+    SwitchUserRequest request;
+    auto messageType = verifyAndCast<SwitchUserMessageType>(propValue.value.int32Values[1]);
+    if (!messageType.ok()) {
+        return Error() << "Invalid SwitchUserMessageType: " << messageType.error();
+    }
+    if (*messageType != SwitchUserMessageType::LEGACY_ANDROID_SWITCH &&
+        *messageType != SwitchUserMessageType::ANDROID_SWITCH &&
+        *messageType != SwitchUserMessageType::ANDROID_POST_SWITCH) {
+        return Error() << "Invalid " << toString(*messageType) << " from Android System";
+    }
+    request.requestId = propValue.value.int32Values[0];
+    request.messageType = *messageType;
+    ret = parseUserInfo(propValue.value.int32Values, 2, &request.targetUser);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse target user info: " << ret.error();
+    }
+    ret = parseUsersInfo(propValue.value.int32Values, 4, &request.usersInfo);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse users info: " << ret.error();
+    }
+    return request;
+}
+
+Result<CreateUserRequest> toCreateUserRequest(const VehiclePropValue& propValue) {
+    auto ret = verifyPropValue(propValue, VehicleProperty::CREATE_USER, 1);
+    if (!ret.ok()) {
+        return ret.error();
+    }
+    CreateUserRequest request;
+    request.requestId = propValue.value.int32Values[0];
+    ret = parseUserInfo(propValue.value.int32Values, 1, &request.newUserInfo);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse new user info: " << ret.error();
+    }
+    request.newUserName = propValue.value.stringValue;
+    ret = parseUsersInfo(propValue.value.int32Values, 3, &request.usersInfo);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse users info: " << ret.error();
+    }
+    return request;
+}
+
+Result<RemoveUserRequest> toRemoveUserRequest(const VehiclePropValue& propValue) {
+    auto ret = verifyPropValue(propValue, VehicleProperty::REMOVE_USER, 1);
+    if (!ret.ok()) {
+        return ret.error();
+    }
+    RemoveUserRequest request;
+    request.requestId = propValue.value.int32Values[0];
+    ret = parseUserInfo(propValue.value.int32Values, 1, &request.removedUserInfo);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse removed user info: " << ret.error();
+    }
+    ret = parseUsersInfo(propValue.value.int32Values, 3, &request.usersInfo);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse users info: " << ret.error();
+    }
+    return request;
+}
+
+Result<UserIdentificationGetRequest> toUserIdentificationGetRequest(
+        const VehiclePropValue& propValue) {
+    auto ret = verifyPropValue(propValue, VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, 4);
+    if (!ret.ok()) {
+        return ret.error();
+    }
+    UserIdentificationGetRequest request;
+    request.requestId = propValue.value.int32Values[0];
+    ret = parseUserInfo(propValue.value.int32Values, 1, &request.userInfo);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse user info: " << ret.error();
+    }
+    request.numberAssociationTypes = propValue.value.int32Values[3];
+    ret = parseUserAssociationTypes(propValue.value.int32Values, 4, request.numberAssociationTypes,
+                                    &request.associationTypes);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse UserIdentificationAssociationType: " << ret.error();
+    }
+    return request;
+}
+
+Result<UserIdentificationSetRequest> toUserIdentificationSetRequest(
+        const VehiclePropValue& propValue) {
+    auto ret = verifyPropValue(propValue, VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, 4);
+    if (!ret.ok()) {
+        return ret.error();
+    }
+    UserIdentificationSetRequest request;
+    request.requestId = propValue.value.int32Values[0];
+    ret = parseUserInfo(propValue.value.int32Values, 1, &request.userInfo);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse user info: " << ret.error();
+    }
+    request.numberAssociations = propValue.value.int32Values[3];
+    ret = parseUserAssociations(propValue.value.int32Values, 4, request.numberAssociations,
+                                &request.associations);
+    if (!ret.ok()) {
+        return Error() << "Failed to parse UserIdentificationSetAssociation: " << ret.error();
+    }
+    return request;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserRequest& request) {
+    if (request.messageType != SwitchUserMessageType::VEHICLE_REQUEST) {
+        ALOGE("Invalid %s message type %s from HAL", toString(VehicleProperty::SWITCH_USER).c_str(),
+              toString(request.messageType).c_str());
+        return nullptr;
+    }
+    auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+    propValue->prop = static_cast<int32_t>(VehicleProperty::SWITCH_USER);
+    propValue->timestamp = elapsedRealtimeNano();
+    propValue->value.int32Values.resize(3);
+    propValue->value.int32Values[0] = static_cast<int32_t>(request.requestId);
+    propValue->value.int32Values[1] = static_cast<int32_t>(request.messageType);
+    propValue->value.int32Values[2] = static_cast<int32_t>(request.targetUser.userId);
+    return propValue;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const InitialUserInfoResponse& response) {
+    auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+    propValue->prop = static_cast<int32_t>(VehicleProperty::INITIAL_USER_INFO);
+    propValue->timestamp = elapsedRealtimeNano();
+    propValue->value.int32Values.resize(4);
+    propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId);
+    propValue->value.int32Values[1] = static_cast<int32_t>(response.action);
+    propValue->value.int32Values[2] = static_cast<int32_t>(response.userToSwitchOrCreate.userId);
+    propValue->value.int32Values[3] = static_cast<int32_t>(response.userToSwitchOrCreate.flags);
+    propValue->value.stringValue = std::string(response.userLocales) + std::string(kSeparator) +
+                                   std::string(response.userNameToCreate);
+    return propValue;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserResponse& response) {
+    auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+    propValue->prop = static_cast<int32_t>(VehicleProperty::SWITCH_USER);
+    propValue->timestamp = elapsedRealtimeNano();
+    propValue->value.int32Values.resize(3);
+    propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId);
+    propValue->value.int32Values[1] = static_cast<int32_t>(response.messageType);
+    propValue->value.int32Values[2] = static_cast<int32_t>(response.status);
+    if (response.status == SwitchUserStatus::FAILURE) {
+        propValue->value.stringValue = response.errorMessage;
+    }
+    return propValue;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const CreateUserResponse& response) {
+    auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+    propValue->prop = static_cast<int32_t>(VehicleProperty::CREATE_USER);
+    propValue->timestamp = elapsedRealtimeNano();
+    propValue->value.int32Values.resize(2);
+    propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId);
+    propValue->value.int32Values[1] = static_cast<int32_t>(response.status);
+    if (response.status == CreateUserStatus::FAILURE) {
+        propValue->value.stringValue = response.errorMessage;
+    }
+    return propValue;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const UserIdentificationResponse& response) {
+    auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+    propValue->prop = static_cast<int32_t>(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
+    propValue->timestamp = elapsedRealtimeNano();
+    propValue->value.int32Values.resize(2 + (response.numberAssociation * 2));
+    propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId);
+    propValue->value.int32Values[1] = static_cast<int32_t>(response.numberAssociation);
+    for (size_t i = 0; i < static_cast<size_t>(response.numberAssociation); ++i) {
+        size_t int32ValuesPos = 2 + (2 * i);
+        propValue->value.int32Values[int32ValuesPos] =
+                static_cast<int32_t>(response.associations[i].type);
+        propValue->value.int32Values[int32ValuesPos + 1] =
+                static_cast<int32_t>(response.associations[i].value);
+    }
+    if (!response.errorMessage.empty()) {
+        propValue->value.stringValue = response.errorMessage;
+    }
+    return propValue;
+}
+
+}  // namespace user_hal_helper
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/utils/UserHalHelper.h b/automotive/vehicle/2.0/utils/UserHalHelper.h
new file mode 100644
index 0000000..bee34cf
--- /dev/null
+++ b/automotive/vehicle/2.0/utils/UserHalHelper.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_UserHalHelper_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_UserHalHelper_H_
+
+#include <android-base/result.h>
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+
+#include <functional>
+#include <memory>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace user_hal_helper {
+
+// Below functions parse VehiclePropValues to the respective User HAL request structs. On success,
+// these functions return the User HAL struct. Otherwise, they return the error.
+android::base::Result<InitialUserInfoRequest> toInitialUserInfoRequest(
+        const VehiclePropValue& propValue);
+android::base::Result<SwitchUserRequest> toSwitchUserRequest(const VehiclePropValue& propValue);
+android::base::Result<CreateUserRequest> toCreateUserRequest(const VehiclePropValue& propValue);
+android::base::Result<RemoveUserRequest> toRemoveUserRequest(const VehiclePropValue& propValue);
+android::base::Result<UserIdentificationGetRequest> toUserIdentificationGetRequest(
+        const VehiclePropValue& propValue);
+android::base::Result<UserIdentificationSetRequest> toUserIdentificationSetRequest(
+        const VehiclePropValue& propValue);
+
+// Below functions convert the User HAL structs to VehiclePropValues. On success, these functions
+// return the pointer to VehiclePropValue. Otherwise, they return nullptr.
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserRequest& request);
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const InitialUserInfoResponse& response);
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserResponse& response);
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const CreateUserResponse& response);
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const UserIdentificationResponse& response);
+
+}  // namespace user_hal_helper
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_UserHalHelper_H_
diff --git a/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp b/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
new file mode 100644
index 0000000..7da87a2
--- /dev/null
+++ b/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2020 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 "UserHalHelper.h"
+
+#include <gtest/gtest.h>
+
+#include <cstdint>
+
+#include "gmock/gmock.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace user_hal_helper {
+
+namespace {
+
+using testing::Eq;
+using testing::Gt;
+using testing::IsNull;
+using testing::NotNull;
+using testing::Pointee;
+
+constexpr int32_t INITIAL_USER_INFO = static_cast<int32_t>(VehicleProperty::INITIAL_USER_INFO);
+constexpr int32_t SWITCH_USER = static_cast<int32_t>(VehicleProperty::SWITCH_USER);
+constexpr int32_t CREATE_USER = static_cast<int32_t>(VehicleProperty::CREATE_USER);
+constexpr int32_t REMOVE_USER = static_cast<int32_t>(VehicleProperty::REMOVE_USER);
+constexpr int32_t USER_IDENTIFICATION_ASSOCIATION =
+        static_cast<int32_t>(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
+
+constexpr int32_t FIRST_BOOT_AFTER_OTA =
+        static_cast<int32_t>(InitialUserInfoRequestType::FIRST_BOOT_AFTER_OTA);
+constexpr int32_t LEGACY_ANDROID_SWITCH =
+        static_cast<int32_t>(SwitchUserMessageType::LEGACY_ANDROID_SWITCH);
+constexpr int32_t VEHICLE_REQUEST = static_cast<int32_t>(SwitchUserMessageType::VEHICLE_REQUEST);
+
+constexpr int32_t GUEST_USER = static_cast<int32_t>(UserFlags::GUEST);
+constexpr int32_t NONE_USER = static_cast<int32_t>(UserFlags::NONE);
+constexpr int32_t SYSTEM_USER = static_cast<int32_t>(UserFlags::SYSTEM);
+
+constexpr int32_t USER_ID_ASSOC_KEY_FOB =
+        static_cast<int32_t>(UserIdentificationAssociationType::KEY_FOB);
+constexpr int32_t USER_ID_ASSOC_CUSTOM_1 =
+        static_cast<int32_t>(UserIdentificationAssociationType::CUSTOM_1);
+
+constexpr int32_t USER_ID_ASSOC_SET_CURRENT_USER =
+        static_cast<int32_t>(UserIdentificationAssociationSetValue::ASSOCIATE_CURRENT_USER);
+constexpr int32_t USER_ID_ASSOC_UNSET_CURRENT_USER =
+        static_cast<int32_t>(UserIdentificationAssociationSetValue::DISASSOCIATE_CURRENT_USER);
+
+constexpr int32_t USER_ID_ASSOC_CURRENT_USER =
+        static_cast<int32_t>(UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER);
+constexpr int32_t USER_ID_ASSOC_NO_USER =
+        static_cast<int32_t>(UserIdentificationAssociationValue::NOT_ASSOCIATED_ANY_USER);
+
+}  // namespace
+
+TEST(UserHalHelperTest, TestToInitialUserInfoRequest) {
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, SYSTEM_USER,
+                                      10, NONE_USER}},
+    };
+    InitialUserInfoRequest expected{
+            .requestId = 23,
+            .requestType = InitialUserInfoRequestType::FIRST_BOOT_AFTER_OTA,
+            .usersInfo = {{10, UserFlags::NONE},
+                          2,
+                          {{0, UserFlags::SYSTEM}, {10, UserFlags::NONE}}},
+    };
+
+    auto actual = toInitialUserInfoRequest(propValue);
+
+    ASSERT_TRUE(actual.ok()) << actual.error().message();
+    EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithMismatchingPropType) {
+    VehiclePropValue propValue{
+            .prop = INT32_MAX,
+            .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, SYSTEM_USER,
+                                      10, NONE_USER}},
+    };
+
+    auto actual = toInitialUserInfoRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithInvalidRequestType) {
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, INT32_MAX, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+                                      NONE_USER}},
+    };
+
+    auto actual = toInitialUserInfoRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on invalid request type";
+}
+
+TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithInvalidUserFlag) {
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, SYSTEM_USER,
+                                      10, INT32_MAX}},
+    };
+
+    auto actual = toInitialUserInfoRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on invalid user flags";
+}
+
+TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithIncompleteUsersInfo) {
+    VehiclePropValue propValueMissingSecondUserInfo{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0,
+                                      SYSTEM_USER /*Missing 2nd UserInfo*/}},
+    };
+
+    auto actual = toInitialUserInfoRequest(propValueMissingSecondUserInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing second user info";
+
+    VehiclePropValue propValueMissingUsersInfo{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, /*Missing UsersInfo*/}},
+    };
+
+    actual = toInitialUserInfoRequest(propValueMissingUsersInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing users info";
+}
+
+TEST(UserHalHelperTest, TestToSwitchUserRequest) {
+    VehiclePropValue propValue{
+            .prop = SWITCH_USER,
+            .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, 0, SYSTEM_USER, 10, NONE_USER, 2,
+                                      0, SYSTEM_USER, 10, NONE_USER}},
+    };
+    SwitchUserRequest expected{
+            .requestId = 23,
+            .messageType = SwitchUserMessageType::LEGACY_ANDROID_SWITCH,
+            .targetUser = {0, UserFlags::SYSTEM},
+            .usersInfo = {{10, UserFlags::NONE},
+                          2,
+                          {{0, UserFlags::SYSTEM}, {10, UserFlags::NONE}}},
+    };
+
+    auto actual = toSwitchUserRequest(propValue);
+
+    ASSERT_TRUE(actual.ok()) << actual.error().message();
+    EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToSwitchUserRequestWithMismatchingPropType) {
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, 0, SYSTEM_USER, 10, NONE_USER, 2,
+                                      0, SYSTEM_USER, 10, NONE_USER}},
+    };
+
+    auto actual = toSwitchUserRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToSwitchUserRequestWithInvalidMessageType) {
+    VehiclePropValue propValueIncompatibleMessageType{
+            .prop = SWITCH_USER,
+            .value = {.int32Values = {23, VEHICLE_REQUEST, 0, SYSTEM_USER, 10, NONE_USER, 2, 0,
+                                      SYSTEM_USER, 10, NONE_USER}},
+    };
+
+    auto actual = toSwitchUserRequest(propValueIncompatibleMessageType);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on incompatible message type";
+
+    VehiclePropValue propValueInvalidMessageType{
+            .prop = SWITCH_USER,
+            .value = {.int32Values = {23, INT32_MAX, 0, SYSTEM_USER, 10, NONE_USER, 2, 0,
+                                      SYSTEM_USER, 10, NONE_USER}},
+    };
+
+    actual = toSwitchUserRequest(propValueInvalidMessageType);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on invalid message type";
+}
+
+TEST(UserHalHelperTest, TestFailsToSwitchUserRequestWithIncompleteUsersInfo) {
+    VehiclePropValue propValueMissingSecondUserInfo{
+            .prop = SWITCH_USER,
+            .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, 0, SYSTEM_USER, 10, NONE_USER, 2,
+                                      0, SYSTEM_USER,
+                                      /*Missing 2nd UserInfo*/}},
+    };
+
+    auto actual = toSwitchUserRequest(propValueMissingSecondUserInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing second user info";
+
+    VehiclePropValue propValueMissingUsersInfo{
+            .prop = SWITCH_USER,
+            .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, 0, SYSTEM_USER,
+                                      /*Missing UsersInfo*/}},
+    };
+
+    actual = toSwitchUserRequest(propValueMissingUsersInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing users info";
+
+    VehiclePropValue propValueMissingTargetUser{
+            .prop = SWITCH_USER,
+            .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, /*Missing target UserInfo*/}},
+    };
+
+    actual = toSwitchUserRequest(propValueMissingTargetUser);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing target user info";
+}
+
+TEST(UserHalHelperTest, TestToCreateUserRequest) {
+    VehiclePropValue propValue{
+            .prop = CREATE_USER,
+            .value = {.int32Values = {23, 11, GUEST_USER, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+                                      NONE_USER},
+                      .stringValue = "Guest11"},
+    };
+    CreateUserRequest expected{
+            .requestId = 23,
+            .newUserInfo = {11, UserFlags::GUEST},
+            .newUserName = "Guest11",
+            .usersInfo = {{10, UserFlags::NONE},
+                          2,
+                          {{0, UserFlags::SYSTEM}, {10, UserFlags::NONE}}},
+    };
+
+    auto actual = toCreateUserRequest(propValue);
+
+    ASSERT_TRUE(actual.ok()) << actual.error().message();
+    EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToCreateUserRequestWithMismatchingPropType) {
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, 11, GUEST_USER, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+                                      NONE_USER},
+                      .stringValue = "Guest11"},
+    };
+
+    auto actual = toCreateUserRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToCreateUserRequestWithIncompleteUsersInfo) {
+    VehiclePropValue propValueMissingSecondUserInfo{
+            .prop = CREATE_USER,
+            .value = {.int32Values = {23, 11, GUEST_USER, 10, NONE_USER, 2, 0,
+                                      SYSTEM_USER /*Missing 2nd UserInfo*/},
+                      .stringValue = "Guest11"},
+    };
+
+    auto actual = toCreateUserRequest(propValueMissingSecondUserInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing second user info";
+
+    VehiclePropValue propValueMissingUsersInfo{
+            .prop = CREATE_USER,
+            .value = {.int32Values = {23, 11, GUEST_USER, /*Missing UsersInfo*/},
+                      .stringValue = "Guest11"},
+    };
+
+    actual = toCreateUserRequest(propValueMissingUsersInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing users info";
+
+    VehiclePropValue propValueMissingCreateUserInfo{
+            .prop = CREATE_USER,
+            .value = {.int32Values = {23, /*Missing create UserInfo*/}, .stringValue = "Guest11"},
+    };
+
+    actual = toCreateUserRequest(propValueMissingCreateUserInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing create user info";
+}
+
+TEST(UserHalHelperTest, TestToRemoveUserRequest) {
+    VehiclePropValue propValue{
+            .prop = REMOVE_USER,
+            .value = {.int32Values = {23, 10, NONE_USER, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+                                      NONE_USER}},
+    };
+    RemoveUserRequest expected{
+            .requestId = 23,
+            .removedUserInfo = {10, UserFlags::NONE},
+            .usersInfo = {{10, UserFlags::NONE},
+                          2,
+                          {{0, UserFlags::SYSTEM}, {10, UserFlags::NONE}}},
+    };
+
+    auto actual = toRemoveUserRequest(propValue);
+
+    ASSERT_TRUE(actual.ok()) << actual.error().message();
+    EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToRemoveUserRequestWithMismatchingPropType) {
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, 10, NONE_USER, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+                                      NONE_USER}},
+    };
+
+    auto actual = toRemoveUserRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToRemoveUserRequestWithIncompleteUsersInfo) {
+    VehiclePropValue propValueMissingSecondUserInfo{
+            .prop = REMOVE_USER,
+            .value = {.int32Values = {23, 10, NONE_USER, 10, NONE_USER, 2, 0,
+                                      SYSTEM_USER /*Missing 2nd UserInfo*/}},
+    };
+
+    auto actual = toRemoveUserRequest(propValueMissingSecondUserInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing second user info";
+
+    VehiclePropValue propValueMissingUsersInfo{
+            .prop = REMOVE_USER,
+            .value = {.int32Values = {23, 10, NONE_USER, /*Missing UsersInfo*/}},
+    };
+
+    actual = toRemoveUserRequest(propValueMissingUsersInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing users info";
+
+    VehiclePropValue propValueMissingRemoveUserInfo{
+            .prop = REMOVE_USER,
+            .value = {.int32Values = {23, /*Missing remove UserInfo*/}},
+    };
+
+    actual = toRemoveUserRequest(propValueMissingRemoveUserInfo);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing remove user info";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequest) {
+    VehiclePropValue propValue{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+                                      USER_ID_ASSOC_CUSTOM_1}},
+    };
+    UserIdentificationGetRequest expected{
+            .requestId = 23,
+            .userInfo = {10, UserFlags::NONE},
+            .numberAssociationTypes = 2,
+            .associationTypes = {UserIdentificationAssociationType::KEY_FOB,
+                                 UserIdentificationAssociationType::CUSTOM_1},
+    };
+
+    auto actual = toUserIdentificationGetRequest(propValue);
+
+    ASSERT_TRUE(actual.ok()) << actual.error().message();
+    EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequestWithMismatchingPropType) {
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+                                      USER_ID_ASSOC_CUSTOM_1}},
+    };
+
+    auto actual = toUserIdentificationGetRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequestWithInvalidAssociationTypes) {
+    VehiclePropValue propValue{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 10, NONE_USER, 1, INT32_MAX}},
+    };
+
+    auto actual = toUserIdentificationGetRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on invalid association type";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequestWithIncompleteAssociationTypes) {
+    VehiclePropValue propValueMissingSecondAssociationType{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 10, NONE_USER, 2,
+                                      USER_ID_ASSOC_KEY_FOB /*Missing 2nd association type*/}},
+    };
+
+    auto actual = toUserIdentificationGetRequest(propValueMissingSecondAssociationType);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing second association type";
+
+    VehiclePropValue propValueMissingNumberAssociationTypes{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 10, NONE_USER, /*Missing number association types*/}},
+    };
+
+    actual = toUserIdentificationGetRequest(propValueMissingNumberAssociationTypes);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing number association types";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequestWithMissingUserInfo) {
+    VehiclePropValue propValue{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, /*Missing user info*/}},
+    };
+
+    auto actual = toUserIdentificationGetRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing UserInfo";
+}
+
+TEST(UserHalHelperTest, TestToUserIdentificationSetRequest) {
+    VehiclePropValue propValue{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+                                      USER_ID_ASSOC_SET_CURRENT_USER, USER_ID_ASSOC_CUSTOM_1,
+                                      USER_ID_ASSOC_UNSET_CURRENT_USER}},
+    };
+    UserIdentificationSetRequest expected{
+            .requestId = 23,
+            .userInfo = {10, UserFlags::NONE},
+            .numberAssociations = 2,
+            .associations = {{UserIdentificationAssociationType::KEY_FOB,
+                              UserIdentificationAssociationSetValue::ASSOCIATE_CURRENT_USER},
+                             {UserIdentificationAssociationType::CUSTOM_1,
+                              UserIdentificationAssociationSetValue::DISASSOCIATE_CURRENT_USER}},
+    };
+
+    auto actual = toUserIdentificationSetRequest(propValue);
+
+    ASSERT_TRUE(actual.ok()) << actual.error().message();
+    EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationSetRequestWithMismatchingPropType) {
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+                                      USER_ID_ASSOC_SET_CURRENT_USER, USER_ID_ASSOC_CUSTOM_1,
+                                      USER_ID_ASSOC_UNSET_CURRENT_USER}},
+    };
+
+    auto actual = toUserIdentificationSetRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationSetRequestWithInvalidAssociations) {
+    VehiclePropValue propValueInvalidAssociationType{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 10, NONE_USER, 1, INT32_MAX,
+                                      USER_ID_ASSOC_SET_CURRENT_USER}},
+    };
+
+    auto actual = toUserIdentificationSetRequest(propValueInvalidAssociationType);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on invalid association type";
+
+    VehiclePropValue propValueInvalidAssociationValue{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 10, NONE_USER, USER_ID_ASSOC_KEY_FOB, INT32_MAX}},
+    };
+
+    actual = toUserIdentificationSetRequest(propValueInvalidAssociationValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing number association types";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationSetRequestWithIncompleteAssociations) {
+    VehiclePropValue propValueMissingSecondAssociationType{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+                                      USER_ID_ASSOC_SET_CURRENT_USER,
+                                      /*Missing 2nd association*/}},
+    };
+
+    auto actual = toUserIdentificationSetRequest(propValueMissingSecondAssociationType);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing second association type";
+
+    VehiclePropValue propValueMissingNumberAssociationTypes{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 10, NONE_USER, /*Missing number associations*/}},
+    };
+
+    actual = toUserIdentificationSetRequest(propValueMissingNumberAssociationTypes);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing number association types";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationSetRequestWithMissingUserInfo) {
+    VehiclePropValue propValue{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, /*Missing user info*/}},
+    };
+
+    auto actual = toUserIdentificationSetRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on missing UserInfo";
+}
+
+TEST(UserHalHelperTest, TestSwitchUserRequestToVehiclePropValue) {
+    SwitchUserRequest request{
+            .requestId = 23,
+            .messageType = SwitchUserMessageType::VEHICLE_REQUEST,
+            .targetUser = {11, UserFlags::GUEST},
+    };
+    VehiclePropValue expected{
+            .prop = SWITCH_USER,
+            .value = {.int32Values = {23,
+                                      static_cast<int32_t>(SwitchUserMessageType::VEHICLE_REQUEST),
+                                      11}},
+    };
+
+    auto actual = toVehiclePropValue(request);
+
+    ASSERT_THAT(actual, NotNull());
+    EXPECT_THAT(actual->timestamp, Gt(0));
+    // Don't rely on real timestamp in tests as the expected and actual objects won't have the same
+    // timestamps. Thus remove the timestamps before comparing them.
+    actual->timestamp = 0;
+    EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+TEST(UserHalHelperTest, TestFailsSwitchUserRequestToVehiclePropValueWithIncompatibleMessageType) {
+    SwitchUserRequest request{
+            .requestId = 23,
+            .messageType = SwitchUserMessageType::VEHICLE_RESPONSE,
+            .targetUser = {11, UserFlags::GUEST},
+    };
+
+    auto actual = toVehiclePropValue(request);
+
+    EXPECT_THAT(actual, IsNull());
+}
+
+TEST(UserHalHelperTest, TestInitialUserInfoResponseToVehiclePropValue) {
+    InitialUserInfoResponse response{
+            .requestId = 23,
+            .action = InitialUserInfoResponseAction::CREATE,
+            .userToSwitchOrCreate = {11, UserFlags::GUEST},
+            .userLocales = "en-US,pt-BR",
+            .userNameToCreate = "Owner",
+    };
+    VehiclePropValue expected{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23,
+                                      static_cast<int32_t>(InitialUserInfoResponseAction::CREATE),
+                                      11, GUEST_USER},
+                      .stringValue = "en-US,pt-BR||Owner"},
+    };
+
+    auto actual = toVehiclePropValue(response);
+
+    ASSERT_THAT(actual, NotNull());
+    EXPECT_THAT(actual->timestamp, Gt(0));
+    actual->timestamp = 0;
+    EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+TEST(UserHalHelperTest, TestSwitchUserResponseToVehiclePropValue) {
+    SwitchUserResponse response{
+            .requestId = 23,
+            .messageType = SwitchUserMessageType::VEHICLE_RESPONSE,
+            .status = SwitchUserStatus::FAILURE,
+            .errorMessage = "random error",
+    };
+    VehiclePropValue expected{
+            .prop = SWITCH_USER,
+            .value = {.int32Values = {23,
+                                      static_cast<int32_t>(SwitchUserMessageType::VEHICLE_RESPONSE),
+                                      static_cast<int32_t>(SwitchUserStatus::FAILURE)},
+                      .stringValue = "random error"},
+    };
+
+    auto actual = toVehiclePropValue(response);
+
+    ASSERT_THAT(actual, NotNull());
+    EXPECT_THAT(actual->timestamp, Gt(0));
+    actual->timestamp = 0;
+    EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+TEST(UserHalHelperTest, TestCreateUserResponseToVehiclePropValue) {
+    CreateUserResponse response{
+            .requestId = 23,
+            .status = CreateUserStatus::FAILURE,
+            .errorMessage = "random error",
+    };
+    VehiclePropValue expected{
+            .prop = CREATE_USER,
+            .value = {.int32Values = {23, static_cast<int32_t>(CreateUserStatus::FAILURE)},
+                      .stringValue = "random error"},
+    };
+
+    auto actual = toVehiclePropValue(response);
+
+    ASSERT_THAT(actual, NotNull());
+    EXPECT_THAT(actual->timestamp, Gt(0));
+    actual->timestamp = 0;
+    EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+TEST(UserHalHelperTest, TestUserIdentificationResponseToVehiclePropValue) {
+    UserIdentificationResponse response{
+            .requestId = 23,
+            .numberAssociation = 2,
+            .associations = {{UserIdentificationAssociationType::KEY_FOB,
+                              UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER},
+                             {UserIdentificationAssociationType::CUSTOM_1,
+                              UserIdentificationAssociationValue::NOT_ASSOCIATED_ANY_USER}},
+            .errorMessage = "random error",
+    };
+    VehiclePropValue expected{
+            .prop = USER_IDENTIFICATION_ASSOCIATION,
+            .value = {.int32Values = {23, 2, USER_ID_ASSOC_KEY_FOB, USER_ID_ASSOC_CURRENT_USER,
+                                      USER_ID_ASSOC_CUSTOM_1, USER_ID_ASSOC_NO_USER},
+                      .stringValue = "random error"},
+    };
+
+    auto actual = toVehiclePropValue(response);
+
+    ASSERT_THAT(actual, NotNull());
+    EXPECT_THAT(actual->timestamp, Gt(0));
+    actual->timestamp = 0;
+    EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+}  // namespace user_hal_helper
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 384edf3..832c419 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -4453,9 +4453,12 @@
                             nullptr};
         } else {
             allocateGraphicBuffer(testStream.width, testStream.height,
-                    android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
-                        halStreamConfig.streams[0].consumerUsage),
-                    halStreamConfig.streams[0].overrideFormat, &buffer_handle);
+                                  /* We don't look at halStreamConfig.streams[0].consumerUsage
+                                   * since that is 0 for output streams
+                                   */
+                                  android_convertGralloc1To0Usage(
+                                          halStreamConfig.streams[0].producerUsage, bufferUsage),
+                                  halStreamConfig.streams[0].overrideFormat, &buffer_handle);
             outputBuffer = {halStreamConfig.streams[0].id,
                             bufferId,
                             buffer_handle,
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 9463924..44fbc64 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -537,7 +537,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.wifi.hostapd</name>
-        <version>1.0-2</version>
+        <version>1.0-3</version>
         <interface>
             <name>IHostapd</name>
             <instance>default</instance>
@@ -545,7 +545,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.wifi.supplicant</name>
-        <version>1.0-3</version>
+        <version>1.0-4</version>
         <interface>
             <name>ISupplicant</name>
             <instance>default</instance>
diff --git a/gnss/common/utils/default/include/v2_1/GnssTemplate.h b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
index d16a67b..1fe6c3e 100644
--- a/gnss/common/utils/default/include/v2_1/GnssTemplate.h
+++ b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/hardware/gnss/2.1/IGnss.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
@@ -158,14 +159,16 @@
 template <class T_IGnss>
 std::unique_ptr<V2_0::GnssLocation> GnssTemplate<T_IGnss>::getLocationFromHW() {
     char inputBuffer[INPUT_BUFFER_SIZE];
-    mHardwareModeOn = false;
     if (mGnssFd == -1) {
         mGnssFd = open(GNSS_PATH, O_RDWR | O_NONBLOCK);
     }
+
     if (mGnssFd == -1) {
+        ALOGW("Failed to open /dev/gnss0 errno: %d", errno);
         return nullptr;
     }
-    // Send control message to device
+    // Indicates it is a hardwareMode, don't report the default location.
+    mHardwareModeOn = true;
     int bytes_write = write(mGnssFd, CMD_GET_LOCATION, strlen(CMD_GET_LOCATION));
     if (bytes_write <= 0) {
         return nullptr;
@@ -179,8 +182,7 @@
     int bytes_read = -1;
     std::string inputStr = "";
     int epoll_ret = epoll_wait(epoll_fd, events, 1, mMinIntervalMs);
-    // Indicates it is a hardwareMode, don't need to wait outside.
-    mHardwareModeOn = true;
+
     if (epoll_ret == -1) {
         return nullptr;
     }
@@ -206,10 +208,12 @@
         while (mIsActive == true) {
             auto svStatus = filterBlacklistedSatellitesV2_1(Utils::getMockSvInfoListV2_1());
             this->reportSvStatus(svStatus);
-
             auto currentLocation = getLocationFromHW();
-            if (currentLocation != nullptr) {
-                this->reportLocation(*currentLocation);
+            if (mHardwareModeOn) {
+                if (currentLocation != nullptr) {
+                    // Only report location if the return from hardware is valid
+                    this->reportLocation(*currentLocation);
+                }
             } else {
                 if (sGnssCallback_2_1 != nullptr || sGnssCallback_2_0 != nullptr) {
                     const auto location = Utils::getMockLocationV2_0();
@@ -218,13 +222,8 @@
                     const auto location = Utils::getMockLocationV1_0();
                     this->reportLocation(location);
                 }
-
-                // Only need do the sleep in the static location mode, which mocks the "wait
-                // for" hardware behavior.
-                if (!mHardwareModeOn) {
-                    std::this_thread::sleep_for(std::chrono::milliseconds(mMinIntervalMs));
-                }
             }
+            std::this_thread::sleep_for(std::chrono::milliseconds(mMinIntervalMs));
         }
     });
     return true;
diff --git a/radio/1.5/.hidl_for_system_ext b/radio/1.5/.hidl_for_system_ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/radio/1.5/.hidl_for_system_ext
diff --git a/radio/1.5/Android.bp b/radio/1.5/Android.bp
index 0542924..74de0fd 100644
--- a/radio/1.5/Android.bp
+++ b/radio/1.5/Android.bp
@@ -19,4 +19,5 @@
         "android.hidl.safe_union@1.0",
     ],
     gen_java: true,
+    system_ext_specific: true,
 }
diff --git a/radio/config/1.1/.hidl_for_system_ext b/radio/config/1.1/.hidl_for_system_ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/radio/config/1.1/.hidl_for_system_ext
diff --git a/radio/config/1.1/Android.bp b/radio/config/1.1/Android.bp
index a5c3114..69d9a83 100644
--- a/radio/config/1.1/Android.bp
+++ b/radio/config/1.1/Android.bp
@@ -15,4 +15,5 @@
         "android.hidl.base@1.0",
     ],
     gen_java: true,
+    system_ext_specific: true,
 }
diff --git a/radio/config/1.2/.hidl_for_system_ext b/radio/config/1.2/.hidl_for_system_ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/radio/config/1.2/.hidl_for_system_ext
diff --git a/radio/config/1.2/Android.bp b/radio/config/1.2/Android.bp
index 39d55ff..f751868 100644
--- a/radio/config/1.2/Android.bp
+++ b/radio/config/1.2/Android.bp
@@ -15,4 +15,5 @@
         "android.hidl.base@1.0",
     ],
     gen_java: true,
+    system_ext_specific: true,
 }
diff --git a/vibrator/bench/Android.bp b/vibrator/bench/Android.bp
new file mode 100644
index 0000000..c7f824d
--- /dev/null
+++ b/vibrator/bench/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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_benchmark {
+    name: "VibratorHalIntegrationBenchmark",
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "benchmark.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.vibrator-cpp",
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vibrator@1.2",
+        "android.hardware.vibrator@1.3",
+        "libbinder",
+        "libhardware",
+        "libhidlbase",
+        "libutils",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/vibrator/bench/benchmark.cpp b/vibrator/bench/benchmark.cpp
new file mode 100644
index 0000000..e19dc6f
--- /dev/null
+++ b/vibrator/bench/benchmark.cpp
@@ -0,0 +1,577 @@
+/*
+ * 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 "benchmark/benchmark.h"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+
+using ::android::enum_range;
+using ::android::sp;
+using ::android::hardware::hidl_enum_range;
+using ::android::hardware::Return;
+using ::android::hardware::details::hidl_enum_values;
+using ::benchmark::Counter;
+using ::benchmark::Fixture;
+using ::benchmark::kMicrosecond;
+using ::benchmark::State;
+using ::benchmark::internal::Benchmark;
+using ::std::chrono::duration;
+using ::std::chrono::duration_cast;
+using ::std::chrono::high_resolution_clock;
+
+namespace Aidl = ::android::hardware::vibrator;
+namespace V1_0 = ::android::hardware::vibrator::V1_0;
+namespace V1_1 = ::android::hardware::vibrator::V1_1;
+namespace V1_2 = ::android::hardware::vibrator::V1_2;
+namespace V1_3 = ::android::hardware::vibrator::V1_3;
+
+template <typename I>
+class BaseBench : public Fixture {
+  public:
+    void TearDown(State& /*state*/) override {
+        if (!mVibrator) {
+            return;
+        }
+        mVibrator->off();
+    }
+
+    static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
+
+    static void DefaultArgs(Benchmark* /*b*/) { /* none */
+    }
+
+  protected:
+    auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
+
+  protected:
+    sp<I> mVibrator;
+};
+
+template <typename I>
+class VibratorBench : public BaseBench<I> {
+  public:
+    void SetUp(State& /*state*/) override { this->mVibrator = I::getService(); }
+};
+
+enum class EmptyEnum : uint32_t;
+template <>
+inline constexpr std::array<EmptyEnum, 0> hidl_enum_values<EmptyEnum> = {};
+
+template <typename T, typename U>
+std::set<T> difference(const hidl_enum_range<T>& t, const hidl_enum_range<U>& u) {
+    class Compare {
+      public:
+        bool operator()(const T& a, const U& b) { return a < static_cast<T>(b); }
+        bool operator()(const U& a, const T& b) { return static_cast<T>(a) < b; }
+    };
+    std::set<T> ret;
+
+    std::set_difference(t.begin(), t.end(), u.begin(), u.end(),
+                        std::insert_iterator<decltype(ret)>(ret, ret.begin()), Compare());
+
+    return ret;
+}
+
+template <typename I, typename E1, typename E2 = EmptyEnum>
+class VibratorEffectsBench : public VibratorBench<I> {
+  public:
+    using Effect = E1;
+    using EffectStrength = V1_0::EffectStrength;
+    using Status = V1_0::Status;
+
+  public:
+    static void DefaultArgs(Benchmark* b) {
+        b->ArgNames({"Effect", "Strength"});
+        for (const auto& effect : difference(hidl_enum_range<E1>(), hidl_enum_range<E2>())) {
+            for (const auto& strength : hidl_enum_range<EffectStrength>()) {
+                b->Args({static_cast<long>(effect), static_cast<long>(strength)});
+            }
+        }
+    }
+
+    void performBench(State* state, Return<void> (I::*performApi)(Effect, EffectStrength,
+                                                                  typename I::perform_cb)) {
+        auto effect = getEffect(*state);
+        auto strength = getStrength(*state);
+        bool supported = true;
+
+        (*this->mVibrator.*performApi)(effect, strength, [&](Status status, uint32_t /*lengthMs*/) {
+            if (status == Status::UNSUPPORTED_OPERATION) {
+                supported = false;
+            }
+        });
+
+        if (!supported) {
+            return;
+        }
+
+        for (auto _ : *state) {
+            state->ResumeTiming();
+            (*this->mVibrator.*performApi)(effect, strength,
+                                           [](Status /*status*/, uint32_t /*lengthMs*/) {});
+            state->PauseTiming();
+            this->mVibrator->off();
+        }
+    }
+
+  protected:
+    auto getEffect(const State& state) const {
+        return static_cast<Effect>(this->getOtherArg(state, 0));
+    }
+
+    auto getStrength(const State& state) const {
+        return static_cast<EffectStrength>(this->getOtherArg(state, 1));
+    }
+};
+
+#define BENCHMARK_WRAPPER(fixt, test, code) \
+    BENCHMARK_DEFINE_F(fixt, test)          \
+    /* NOLINTNEXTLINE */                    \
+    (State & state) {                       \
+        if (!mVibrator) {                   \
+            return;                         \
+        }                                   \
+                                            \
+        code                                \
+    }                                       \
+    BENCHMARK_REGISTER_F(fixt, test)->Apply(fixt::DefaultConfig)->Apply(fixt::DefaultArgs)
+
+using VibratorBench_V1_0 = VibratorBench<V1_0::IVibrator>;
+
+BENCHMARK_WRAPPER(VibratorBench_V1_0, on, {
+    uint32_t ms = UINT32_MAX;
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mVibrator->on(ms);
+        state.PauseTiming();
+        mVibrator->off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_V1_0, off, {
+    uint32_t ms = UINT32_MAX;
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        mVibrator->on(ms);
+        state.ResumeTiming();
+        mVibrator->off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_V1_0, supportsAmplitudeControl, {
+    for (auto _ : state) {
+        mVibrator->supportsAmplitudeControl();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_V1_0, setAmplitude, {
+    uint8_t amplitude = UINT8_MAX;
+
+    if (!mVibrator->supportsAmplitudeControl()) {
+        return;
+    }
+
+    mVibrator->on(UINT32_MAX);
+
+    for (auto _ : state) {
+        mVibrator->setAmplitude(amplitude);
+    }
+
+    mVibrator->off();
+});
+
+using VibratorEffectsBench_V1_0 = VibratorEffectsBench<V1_0::IVibrator, V1_0::Effect>;
+
+BENCHMARK_WRAPPER(VibratorEffectsBench_V1_0, perform,
+                  { performBench(&state, &V1_0::IVibrator::perform); });
+
+using VibratorEffectsBench_V1_1 =
+        VibratorEffectsBench<V1_1::IVibrator, V1_1::Effect_1_1, V1_0::Effect>;
+
+BENCHMARK_WRAPPER(VibratorEffectsBench_V1_1, perform_1_1,
+                  { performBench(&state, &V1_1::IVibrator::perform_1_1); });
+
+using VibratorEffectsBench_V1_2 =
+        VibratorEffectsBench<V1_2::IVibrator, V1_2::Effect, V1_1::Effect_1_1>;
+
+BENCHMARK_WRAPPER(VibratorEffectsBench_V1_2, perform_1_2,
+                  { performBench(&state, &V1_2::IVibrator::perform_1_2); });
+
+using VibratorBench_V1_3 = VibratorBench<V1_3::IVibrator>;
+
+BENCHMARK_WRAPPER(VibratorBench_V1_3, supportsExternalControl, {
+    for (auto _ : state) {
+        mVibrator->supportsExternalControl();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_V1_3, setExternalControl, {
+    bool enable = true;
+
+    if (!mVibrator->supportsExternalControl()) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mVibrator->setExternalControl(enable);
+        state.PauseTiming();
+        mVibrator->setExternalControl(false);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_V1_3, supportsExternalAmplitudeControl, {
+    if (!mVibrator->supportsExternalControl()) {
+        return;
+    }
+
+    mVibrator->setExternalControl(true);
+
+    for (auto _ : state) {
+        mVibrator->supportsAmplitudeControl();
+    }
+
+    mVibrator->setExternalControl(false);
+});
+
+BENCHMARK_WRAPPER(VibratorBench_V1_3, setExternalAmplitude, {
+    uint8_t amplitude = UINT8_MAX;
+
+    if (!mVibrator->supportsExternalControl()) {
+        return;
+    }
+
+    mVibrator->setExternalControl(true);
+
+    if (!mVibrator->supportsAmplitudeControl()) {
+        return;
+    }
+
+    for (auto _ : state) {
+        mVibrator->setAmplitude(amplitude);
+    }
+
+    mVibrator->setExternalControl(false);
+});
+
+using VibratorEffectsBench_V1_3 = VibratorEffectsBench<V1_3::IVibrator, V1_3::Effect, V1_2::Effect>;
+
+BENCHMARK_WRAPPER(VibratorEffectsBench_V1_3, perform_1_3,
+                  { performBench(&state, &V1_3::IVibrator::perform_1_3); });
+
+class VibratorBench_Aidl : public BaseBench<Aidl::IVibrator> {
+  public:
+    void SetUp(State& /*state*/) override {
+        this->mVibrator = android::waitForVintfService<Aidl::IVibrator>();
+    }
+};
+
+class HalCallback : public Aidl::BnVibratorCallback {
+  public:
+    HalCallback() = default;
+    ~HalCallback() = default;
+
+    android::binder::Status onComplete() override { return android::binder::Status::ok(); }
+};
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, on, {
+    int32_t capabilities = 0;
+    mVibrator->getCapabilities(&capabilities);
+
+    int32_t ms = INT32_MAX;
+    auto cb = (capabilities & Aidl::IVibrator::CAP_ON_CALLBACK) ? new HalCallback() : nullptr;
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mVibrator->on(ms, cb);
+        state.PauseTiming();
+        mVibrator->off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, off, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        mVibrator->on(INT32_MAX, nullptr);
+        state.ResumeTiming();
+        mVibrator->off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, getCapabilities, {
+    int32_t capabilities = 0;
+
+    for (auto _ : state) {
+        mVibrator->getCapabilities(&capabilities);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, setAmplitude, {
+    int32_t capabilities = 0;
+    mVibrator->getCapabilities(&capabilities);
+    if ((capabilities & Aidl::IVibrator::CAP_AMPLITUDE_CONTROL) == 0) {
+        return;
+    }
+
+    float amplitude = 1.0f;
+    mVibrator->on(INT32_MAX, nullptr);
+
+    for (auto _ : state) {
+        mVibrator->setAmplitude(amplitude);
+    }
+
+    mVibrator->off();
+});
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, setExternalControl, {
+    int32_t capabilities = 0;
+    mVibrator->getCapabilities(&capabilities);
+    if ((capabilities & Aidl::IVibrator::CAP_EXTERNAL_CONTROL) == 0) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mVibrator->setExternalControl(true);
+        state.PauseTiming();
+        mVibrator->setExternalControl(false);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, setExternalAmplitude, {
+    int32_t capabilities = 0;
+    mVibrator->getCapabilities(&capabilities);
+    if ((capabilities & Aidl::IVibrator::CAP_EXTERNAL_CONTROL) == 0 ||
+        (capabilities & Aidl::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL) == 0) {
+        return;
+    }
+
+    float amplitude = 1.0f;
+    mVibrator->setExternalControl(true);
+
+    for (auto _ : state) {
+        mVibrator->setAmplitude(amplitude);
+    }
+
+    mVibrator->setExternalControl(false);
+});
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, getSupportedEffects, {
+    std::vector<Aidl::Effect> supportedEffects;
+
+    for (auto _ : state) {
+        mVibrator->getSupportedEffects(&supportedEffects);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, getSupportedAlwaysOnEffects, {
+    std::vector<Aidl::Effect> supportedEffects;
+
+    for (auto _ : state) {
+        mVibrator->getSupportedAlwaysOnEffects(&supportedEffects);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, getSupportedPrimitives, {
+    std::vector<Aidl::CompositePrimitive> supportedPrimitives;
+
+    for (auto _ : state) {
+        mVibrator->getSupportedPrimitives(&supportedPrimitives);
+    }
+});
+
+class VibratorEffectsBench_Aidl : public VibratorBench_Aidl {
+  public:
+    static void DefaultArgs(Benchmark* b) {
+        b->ArgNames({"Effect", "Strength"});
+        for (const auto& effect : enum_range<Aidl::Effect>()) {
+            for (const auto& strength : enum_range<Aidl::EffectStrength>()) {
+                b->Args({static_cast<long>(effect), static_cast<long>(strength)});
+            }
+        }
+    }
+
+  protected:
+    auto getEffect(const State& state) const {
+        return static_cast<Aidl::Effect>(this->getOtherArg(state, 0));
+    }
+
+    auto getStrength(const State& state) const {
+        return static_cast<Aidl::EffectStrength>(this->getOtherArg(state, 1));
+    }
+};
+
+BENCHMARK_WRAPPER(VibratorEffectsBench_Aidl, alwaysOnEnable, {
+    int32_t capabilities = 0;
+    mVibrator->getCapabilities(&capabilities);
+    if ((capabilities & Aidl::IVibrator::CAP_ALWAYS_ON_CONTROL) == 0) {
+        return;
+    }
+
+    int32_t id = 1;
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+
+    std::vector<Aidl::Effect> supported;
+    mVibrator->getSupportedAlwaysOnEffects(&supported);
+    if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mVibrator->alwaysOnEnable(id, effect, strength);
+        state.PauseTiming();
+        mVibrator->alwaysOnDisable(id);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench_Aidl, alwaysOnDisable, {
+    int32_t capabilities = 0;
+    mVibrator->getCapabilities(&capabilities);
+    if ((capabilities & Aidl::IVibrator::CAP_ALWAYS_ON_CONTROL) == 0) {
+        return;
+    }
+
+    int32_t id = 1;
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+
+    std::vector<Aidl::Effect> supported;
+    mVibrator->getSupportedAlwaysOnEffects(&supported);
+    if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        mVibrator->alwaysOnEnable(id, effect, strength);
+        state.ResumeTiming();
+        mVibrator->alwaysOnDisable(id);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench_Aidl, perform, {
+    int32_t capabilities = 0;
+    mVibrator->getCapabilities(&capabilities);
+
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+    auto cb = (capabilities & Aidl::IVibrator::CAP_PERFORM_CALLBACK) ? new HalCallback() : nullptr;
+    int32_t lengthMs = 0;
+
+    std::vector<Aidl::Effect> supported;
+    mVibrator->getSupportedEffects(&supported);
+    if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mVibrator->perform(effect, strength, cb, &lengthMs);
+        state.PauseTiming();
+        mVibrator->off();
+    }
+});
+
+class VibratorPrimitivesBench_Aidl : public VibratorBench_Aidl {
+  public:
+    static void DefaultArgs(Benchmark* b) {
+        b->ArgNames({"Primitive"});
+        for (const auto& primitive : enum_range<Aidl::CompositePrimitive>()) {
+            b->Args({static_cast<long>(primitive)});
+        }
+    }
+
+  protected:
+    auto getPrimitive(const State& state) const {
+        return static_cast<Aidl::CompositePrimitive>(this->getOtherArg(state, 0));
+    }
+};
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, getCompositionDelayMax, {
+    int32_t ms = 0;
+
+    for (auto _ : state) {
+        mVibrator->getCompositionDelayMax(&ms);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench_Aidl, getCompositionSizeMax, {
+    int32_t size = 0;
+
+    for (auto _ : state) {
+        mVibrator->getCompositionSizeMax(&size);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorPrimitivesBench_Aidl, getPrimitiveDuration, {
+    int32_t capabilities = 0;
+    mVibrator->getCapabilities(&capabilities);
+    if ((capabilities & Aidl::IVibrator::CAP_COMPOSE_EFFECTS) == 0) {
+        return;
+    }
+
+    auto primitive = getPrimitive(state);
+    int32_t ms = 0;
+
+    std::vector<Aidl::CompositePrimitive> supported;
+    mVibrator->getSupportedPrimitives(&supported);
+    if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
+        return;
+    }
+
+    for (auto _ : state) {
+        mVibrator->getPrimitiveDuration(primitive, &ms);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorPrimitivesBench_Aidl, compose, {
+    int32_t capabilities = 0;
+    mVibrator->getCapabilities(&capabilities);
+    if ((capabilities & Aidl::IVibrator::CAP_COMPOSE_EFFECTS) == 0) {
+        return;
+    }
+
+    Aidl::CompositeEffect effect;
+    effect.primitive = getPrimitive(state);
+    effect.scale = 1.0f;
+    effect.delayMs = 0;
+
+    std::vector<Aidl::CompositePrimitive> supported;
+    mVibrator->getSupportedPrimitives(&supported);
+    if (std::find(supported.begin(), supported.end(), effect.primitive) == supported.end()) {
+        return;
+    }
+
+    auto cb = new HalCallback();
+    std::vector<Aidl::CompositeEffect> effects;
+    effects.push_back(effect);
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mVibrator->compose(effects, cb);
+        state.PauseTiming();
+        mVibrator->off();
+    }
+});
+
+BENCHMARK_MAIN();
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp
index 3ddb2b3..75d6252 100644
--- a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp
@@ -17,7 +17,6 @@
 #include <android-base/logging.h>
 
 #include <android/hidl/manager/1.0/IServiceManager.h>
-#include <android/hidl/manager/1.0/IServiceNotification.h>
 #include <hidl/HidlTransportSupport.h>
 
 #include <wifi_system/hostapd_manager.h>
@@ -39,7 +38,6 @@
 using ::android::hardware::wifi::hostapd::V1_0::IHostapd;
 using ::android::hardware::wifi::V1_0::ChipModeId;
 using ::android::hardware::wifi::V1_0::IWifiChip;
-using ::android::hidl::manager::V1_0::IServiceNotification;
 using ::android::wifi_system::HostapdManager;
 using ::android::wifi_system::SupplicantManager;
 
@@ -68,55 +66,6 @@
 }
 }  // namespace
 
-// Utility class to wait for wpa_hostapd's HIDL service registration.
-class ServiceNotificationListener : public IServiceNotification {
-   public:
-    Return<void> onRegistration(const hidl_string& fully_qualified_name,
-                                const hidl_string& instance_name,
-                                bool pre_existing) override {
-        if (pre_existing) {
-            return Void();
-        }
-        std::unique_lock<std::mutex> lock(mutex_);
-        registered_.push_back(std::string(fully_qualified_name.c_str()) + "/" +
-                              instance_name.c_str());
-        lock.unlock();
-        condition_.notify_one();
-        return Void();
-    }
-
-    bool registerForHidlServiceNotifications(const std::string& instance_name) {
-        if (!IHostapd::registerForNotifications(instance_name, this)) {
-            return false;
-        }
-        configureRpcThreadpool(2, false);
-        return true;
-    }
-
-    bool waitForHidlService(uint32_t timeout_in_millis,
-                            const std::string& instance_name) {
-        std::unique_lock<std::mutex> lock(mutex_);
-        condition_.wait_for(lock, std::chrono::milliseconds(timeout_in_millis),
-                            [&]() { return registered_.size() >= 1; });
-        if (registered_.size() != 1) {
-            return false;
-        }
-        std::string expected_registered =
-            std::string(IHostapd::descriptor) + "/" + instance_name;
-        if (registered_[0] != expected_registered) {
-            LOG(ERROR) << "Expected: " << expected_registered
-                       << ", Got: " << registered_[0];
-            return false;
-        }
-        return true;
-    }
-
-   private:
-    std::vector<std::string> registered_{};
-    std::mutex mutex_;
-    std::condition_variable condition_;
-};
-
 void stopSupplicantIfNeeded(const std::string& instance_name) {
     SupplicantManager supplicant_manager;
     if (supplicant_manager.IsSupplicantRunning()) {
@@ -139,16 +88,11 @@
     const std::string& hostapd_instance_name) {
     initilializeDriverAndFirmware(wifi_instance_name);
 
-    android::sp<ServiceNotificationListener> notification_listener =
-        new ServiceNotificationListener();
-    ASSERT_TRUE(notification_listener->registerForHidlServiceNotifications(
-        hostapd_instance_name));
-
     HostapdManager hostapd_manager;
     ASSERT_TRUE(hostapd_manager.StartHostapd());
 
-    ASSERT_TRUE(
-        notification_listener->waitForHidlService(500, hostapd_instance_name));
+    // Wait for hostapd service to come up.
+    IHostapd::getService(hostapd_instance_name);
 }
 
 bool is_1_1(const sp<IHostapd>& hostapd) {
diff --git a/wifi/hostapd/1.1/vts/functional/Android.bp b/wifi/hostapd/1.1/vts/functional/Android.bp
index 291eceb..61a8dfd 100644
--- a/wifi/hostapd/1.1/vts/functional/Android.bp
+++ b/wifi/hostapd/1.1/vts/functional/Android.bp
@@ -25,11 +25,15 @@
         "VtsHalWifiHostapdV1_0TargetTestUtil",
         "android.hardware.wifi.hostapd@1.0",
         "android.hardware.wifi.hostapd@1.1",
+        "android.hardware.wifi.hostapd@1.2",
+        "android.hardware.wifi.hostapd@1.3",
         "android.hardware.wifi@1.0",
         "libgmock",
         "libwifi-system",
         "libwifi-system-iface",
     ],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
-
diff --git a/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp b/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp
index 345cf31..0162a99 100644
--- a/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp
+++ b/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp
@@ -23,6 +23,7 @@
 
 #include <android/hardware/wifi/1.0/IWifi.h>
 #include <android/hardware/wifi/hostapd/1.1/IHostapd.h>
+#include <android/hardware/wifi/hostapd/1.3/IHostapd.h>
 
 #include "hostapd_hidl_call_util.h"
 #include "hostapd_hidl_test_utils.h"
@@ -43,6 +44,7 @@
 constexpr char kNwPassphrase[] = "test12345";
 constexpr int kIfaceChannel = 6;
 constexpr int kIfaceInvalidChannel = 567;
+
 }  // namespace
 
 class HostapdHidlTest
@@ -170,10 +172,17 @@
     }
 };
 
+bool is_1_3(const sp<IHostapd>& hostapd) {
+    sp<::android::hardware::wifi::hostapd::V1_3::IHostapd> hostapd_1_3 =
+        ::android::hardware::wifi::hostapd::V1_3::IHostapd::castFrom(hostapd);
+    return hostapd_1_3.get() != nullptr;
+}
+
 /*
  * RegisterCallback
  */
 TEST_P(HostapdHidlTest, registerCallback) {
+    if (is_1_3(hostapd_)) GTEST_SKIP() << "Ignore since current HIDL over 1.3";
     hostapd_->registerCallback(
         new IfaceCallback(), [](const HostapdStatus& status) {
             EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
diff --git a/wifi/hostapd/1.3/Android.bp b/wifi/hostapd/1.3/Android.bp
new file mode 100644
index 0000000..aef3267
--- /dev/null
+++ b/wifi/hostapd/1.3/Android.bp
@@ -0,0 +1,22 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.wifi.hostapd@1.3",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+        "IHostapd.hal",
+        "IHostapdCallback.hal",
+    ],
+    interfaces: [
+        "android.hardware.wifi.hostapd@1.0",
+        "android.hardware.wifi.hostapd@1.1",
+        "android.hardware.wifi.hostapd@1.2",
+        "android.hardware.wifi.supplicant@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+    apex_available: [
+        "com.android.wifi",
+    ],
+}
diff --git a/wifi/hostapd/1.3/IHostapd.hal b/wifi/hostapd/1.3/IHostapd.hal
new file mode 100644
index 0000000..7ac20e4
--- /dev/null
+++ b/wifi/hostapd/1.3/IHostapd.hal
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.hostapd@1.3;
+
+import @1.2::IHostapd;
+import @1.2::HostapdStatus;
+
+import IHostapdCallback;
+/**
+ * Top-level object for managing SoftAPs.
+ */
+interface IHostapd extends @1.2::IHostapd {
+    /**
+     * Register for callbacks from the hostapd service.
+     *
+     * These callbacks are invoked for global events that are not specific
+     * to any interface or network. Registration of multiple callback
+     * objects is supported. These objects must be deleted when the corresponding
+     * client process is dead.
+     *
+     * @param callback An instance of the |IHostapdCallback| HIDL interface
+     *     object.
+     * @return status Status of the operation.
+     *     Possible status codes:
+     *     |HostapdStatusCode.SUCCESS|,
+     *     |HostapdStatusCode.FAILURE_UNKNOWN|
+     */
+    registerCallback_1_3(IHostapdCallback callback)
+        generates (HostapdStatus status);
+};
diff --git a/wifi/hostapd/1.3/IHostapdCallback.hal b/wifi/hostapd/1.3/IHostapdCallback.hal
new file mode 100644
index 0000000..5202178
--- /dev/null
+++ b/wifi/hostapd/1.3/IHostapdCallback.hal
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.hostapd@1.3;
+
+import @1.1::IHostapdCallback;
+import Generation;
+
+/**
+ * Top-level callback object for managing SoftAPs.
+ */
+interface IHostapdCallback extends @1.1::IHostapdCallback {
+    oneway onInterfaceInfoChanged(string ifaceName, Generation generation);
+};
diff --git a/wifi/hostapd/1.3/types.hal b/wifi/hostapd/1.3/types.hal
new file mode 100644
index 0000000..ce092c0
--- /dev/null
+++ b/wifi/hostapd/1.3/types.hal
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.hostapd@1.3;
+
+/**
+ * The wifi generation which AP reside on.
+ * It depends on hw mode and HT/VHT capabilities in hostapd.
+ *
+ * WIFI_STANDARD_LEGACY = (hw_mode is HOSTAPD_MODE_IEEE80211B) or
+ *                        (hw_mode is HOSTAPD_MODE_IEEE80211G and HT is 0).
+ * WIFI_STANDARD_11N = [hw_mode is HOSTAPD_MODE_IEEE80211G and (HT is 1 or HT40 is 1)] or
+ *                     [hw_mode is HOSTAPD_MODE_IEEE80211A and VHT is 0].
+ * WIFI_STANDARD_11AC = hw_mode is HOSTAPD_MODE_IEEE80211A and VHT is 1.
+ * WIFI_STANDARD_11AX = hw_mode is HOSTAPD_MODE_IEEE80211AX.
+ */
+enum Generation : uint32_t {
+    WIFI_STANDARD_UNKNOWN = -1,
+    WIFI_STANDARD_LEGACY = 0,
+    WIFI_STANDARD_11N = 1,
+    WIFI_STANDARD_11AC = 2,
+    WIFI_STANDARD_11AX = 3,
+};
+
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test_utils.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test_utils.cpp
index addf066..5e7a371 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test_utils.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test_utils.cpp
@@ -18,7 +18,6 @@
 #include <cutils/properties.h>
 
 #include <android/hidl/manager/1.0/IServiceManager.h>
-#include <android/hidl/manager/1.0/IServiceNotification.h>
 #include <hidl/HidlTransportSupport.h>
 
 #include <wifi_system/interface_tool.h>
@@ -45,7 +44,6 @@
 using ::android::hardware::wifi::supplicant::V1_0::IfaceType;
 using ::android::hardware::wifi::supplicant::V1_0::SupplicantStatus;
 using ::android::hardware::wifi::supplicant::V1_0::SupplicantStatusCode;
-using ::android::hidl::manager::V1_0::IServiceNotification;
 using ::android::wifi_system::InterfaceTool;
 using ::android::wifi_system::SupplicantManager;
 
@@ -120,55 +118,6 @@
 }
 }  // namespace
 
-// Utility class to wait for wpa_supplicant's HIDL service registration.
-class ServiceNotificationListener : public IServiceNotification {
-   public:
-    Return<void> onRegistration(const hidl_string& fully_qualified_name,
-                                const hidl_string& instance_name,
-                                bool pre_existing) override {
-        if (pre_existing) {
-            return Void();
-        }
-        std::unique_lock<std::mutex> lock(mutex_);
-        registered_.push_back(std::string(fully_qualified_name.c_str()) + "/" +
-                              instance_name.c_str());
-        lock.unlock();
-        condition_.notify_one();
-        return Void();
-    }
-
-    bool registerForHidlServiceNotifications(const std::string& instance_name) {
-        if (!ISupplicant::registerForNotifications(instance_name, this)) {
-            return false;
-        }
-        configureRpcThreadpool(2, false);
-        return true;
-    }
-
-    bool waitForHidlService(uint32_t timeout_in_millis,
-                            const std::string& instance_name) {
-        std::unique_lock<std::mutex> lock(mutex_);
-        condition_.wait_for(lock, std::chrono::milliseconds(timeout_in_millis),
-                            [&]() { return registered_.size() >= 1; });
-        if (registered_.size() != 1) {
-            return false;
-        }
-        std::string exptected_registered =
-            std::string(ISupplicant::descriptor) + "/" + instance_name;
-        if (registered_[0] != exptected_registered) {
-            LOG(ERROR) << "Expected: " << exptected_registered
-                       << ", Got: " << registered_[0];
-            return false;
-        }
-        return true;
-    }
-
-   private:
-    std::vector<std::string> registered_{};
-    std::mutex mutex_;
-    std::condition_variable condition_;
-};
-
 void stopSupplicant() { stopSupplicant(""); }
 
 void stopSupplicant(const std::string& wifi_instance_name) {
@@ -184,17 +133,12 @@
     const std::string& supplicant_instance_name) {
     initilializeDriverAndFirmware(wifi_instance_name);
 
-    android::sp<ServiceNotificationListener> notification_listener =
-        new ServiceNotificationListener();
-    ASSERT_TRUE(notification_listener->registerForHidlServiceNotifications(
-        supplicant_instance_name));
-
     SupplicantManager supplicant_manager;
     ASSERT_TRUE(supplicant_manager.StartSupplicant());
     ASSERT_TRUE(supplicant_manager.IsSupplicantRunning());
 
-    ASSERT_TRUE(notification_listener->waitForHidlService(
-        500, supplicant_instance_name));
+    // Wait for supplicant service to come up.
+    ISupplicant::getService(supplicant_instance_name);
 }
 
 bool is_1_1(const sp<ISupplicant>& supplicant) {
diff --git a/wifi/supplicant/1.4/Android.bp b/wifi/supplicant/1.4/Android.bp
new file mode 100644
index 0000000..ff2a8bc
--- /dev/null
+++ b/wifi/supplicant/1.4/Android.bp
@@ -0,0 +1,21 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.wifi.supplicant@1.4",
+    root: "android.hardware",
+    srcs: [
+        "ISupplicant.hal",
+    ],
+    interfaces: [
+        "android.hardware.wifi.supplicant@1.0",
+        "android.hardware.wifi.supplicant@1.1",
+        "android.hardware.wifi.supplicant@1.2",
+        "android.hardware.wifi.supplicant@1.3",
+        "android.hardware.wifi@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+    apex_available: [
+        "com.android.wifi",
+    ],
+}
diff --git a/wifi/supplicant/1.4/ISupplicant.hal b/wifi/supplicant/1.4/ISupplicant.hal
new file mode 100644
index 0000000..9cb88ba
--- /dev/null
+++ b/wifi/supplicant/1.4/ISupplicant.hal
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.supplicant@1.4;
+
+import @1.3::ISupplicant;
+
+/**
+ * Interface exposed by the supplicant HIDL service registered
+ * with the hardware service manager.
+ * This is the root level object for any the supplicant interactions.
+ * To use 1.4 features you must cast specific interfaces returned from the
+ * 1.1 HAL. For example V1_1::ISupplicant::addIface() adds V1_0::ISupplicantIface,
+ * which can be cast to V1_4::ISupplicantStaIface.
+ */
+interface ISupplicant extends @1.3::ISupplicant {};