Merge "audio HAL: add output flag indicating support for gapless transitions"
diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp
index c7bfe08..a809a92 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/vts/functional/Android.bp
@@ -22,6 +22,8 @@
         "libxml2",
     ],
     shared_libs: [
+        "audioclient-types-aidl-unstable-cpp",
+        "libaudioclient_aidl_conversion",
         "libbinder",
         "libfmq",
     ],
diff --git a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.aidl b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.aidl
index fc35fa9..98fa4e8 100644
--- a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.aidl
+++ b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.aidl
@@ -27,7 +27,7 @@
     GAIN_TRANSIENT = 2,
     GAIN_TRANSIENT_MAY_DUCK = 3,
     GAIN_TRANSIENT_EXCLUSIVE = 4,
-    LOSS = -1, // -1 * GAIN,
-    LOSS_TRANSIENT = -2, // -1 * GAIN_TRANSIENT,
-    LOSS_TRANSIENT_CAN_DUCK = -3, // -1 * GAIN_TRANSIENT_MAY_DUCK,
+    LOSS = -1 * GAIN,
+    LOSS_TRANSIENT = -1 * GAIN_TRANSIENT,
+    LOSS_TRANSIENT_CAN_DUCK = -1 * GAIN_TRANSIENT_MAY_DUCK,
 }
\ No newline at end of file
diff --git a/automotive/can/1.0/default/libnl++/Android.bp b/automotive/can/1.0/default/libnl++/Android.bp
index 4042b16..90e1002 100644
--- a/automotive/can/1.0/default/libnl++/Android.bp
+++ b/automotive/can/1.0/default/libnl++/Android.bp
@@ -25,6 +25,7 @@
         "protocols/generic/Generic.cpp",
         "protocols/generic/GenericMessageBase.cpp",
         "protocols/generic/Unknown.cpp",
+        "protocols/generic/families/Nl80211.cpp",
         "protocols/route/Link.cpp",
         "protocols/route/Route.cpp",
         "protocols/route/structs.cpp",
diff --git a/automotive/can/1.0/default/libnl++/printer.cpp b/automotive/can/1.0/default/libnl++/printer.cpp
index e6cada2..f08897e 100644
--- a/automotive/can/1.0/default/libnl++/printer.cpp
+++ b/automotive/can/1.0/default/libnl++/printer.cpp
@@ -51,24 +51,24 @@
     printFlag(NLM_F_DUMP_FILTERED, "DUMP_FILTERED");
 
     switch (genre) {
-        case protocols::MessageGenre::UNKNOWN:
+        case protocols::MessageGenre::Unknown:
             break;
-        case protocols::MessageGenre::GET:
+        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:
+        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:
+        case protocols::MessageGenre::Delete:
             printFlag(NLM_F_NONREC, "NONREC");
             break;
-        case protocols::MessageGenre::ACK:
+        case protocols::MessageGenre::Ack:
             printFlag(NLM_F_CAPPED, "CAPPED");
             printFlag(NLM_F_ACK_TLVS, "ACK_TLVS");
             break;
@@ -99,11 +99,25 @@
 static void toStream(std::stringstream& ss, const Buffer<nlattr> attr,
                      const protocols::AttributeMap& attrMap) {
     using DataType = protocols::AttributeDefinition::DataType;
+    using Flags = protocols::AttributeDefinition::Flags;
     const auto attrtype = attrMap[attr->nla_type];
 
-    ss << attrtype.name << ": ";
+    ss << attrtype.name;
+
+    if (attrtype.dataType == DataType::Flag && attr.data<uint8_t>().getRaw().len() == 0) return;
+    ss << ": ";
+
+    if (attrtype.flags == Flags::Verbose) {
+        const auto raw = attr.data<uint8_t>();
+        ss << "{len=" << raw.getRaw().len();
+        ss << ", crc=" << std::hex << std::setw(4) << crc16(raw) << std::dec;
+        ss << "}";
+        return;
+    }
+
     switch (attrtype.dataType) {
         case DataType::Raw:
+        case DataType::Flag:
             toStream(ss, attr.data<uint8_t>());
             break;
         case DataType::Nested: {
@@ -117,13 +131,19 @@
             ss << '}';
             break;
         }
+        case DataType::StringNul:
         case DataType::String: {
             const auto str = attr.data<char>().getRaw();
-            ss << '"' << printableOnly({str.ptr(), str.len()}) << '"';
+            auto len = str.len();
+            if (attrtype.dataType == DataType::StringNul && len > 0 && str.ptr()[len - 1] == '\0') {
+                len--;
+            }
+
+            ss << '"' << printableOnly({str.ptr(), len}) << '"';
             break;
         }
         case DataType::Uint:
-            ss << attr.data<uint32_t>().copyFirst();
+            ss << attr.data<uint64_t>().copyFirst();
             break;
         case DataType::Struct: {
             const auto structToStream =
@@ -147,10 +167,12 @@
     }
     protocols::NetlinkProtocol& protocolDescr = *protocolMaybe;
 
-    const auto msgDescMaybe = protocolDescr.getMessageDescriptor(hdr->nlmsg_type);
+    auto msgDescMaybe = protocolDescr.getMessageDescriptor(hdr->nlmsg_type);
     const auto msgDetails =
             protocols::MessageDescriptor::getMessageDetails(msgDescMaybe, hdr->nlmsg_type);
 
+    if (msgDescMaybe.has_value()) msgDescMaybe->get().track(hdr);
+
     ss << "nlmsg{" << protocolDescr.getName() << " ";
 
     ss << "hdr={";
diff --git a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
index c93d865..aaf24a5 100644
--- a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
@@ -56,15 +56,17 @@
 
 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};
+    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,
+        const std::optional<std::reference_wrapper<MessageDescriptor>>& msgDescMaybe,
         nlmsgtype_t msgtype) {
     if (msgDescMaybe.has_value()) return msgDescMaybe->get().getMessageDetails(msgtype);
-    return {std::to_string(msgtype), protocols::MessageGenre::UNKNOWN};
+    return {std::to_string(msgtype), protocols::MessageGenre::Unknown};
 }
 
+void MessageDescriptor::track(const Buffer<nlmsghdr> /* hdr */) {}
+
 }  // 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 bd0e60f..8bed5e7 100644
--- a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
+++ b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
@@ -54,17 +54,64 @@
  */
 struct AttributeDefinition {
     enum class DataType : uint8_t {
+        /**
+         * Binary blob (or attribute of unknown type).
+         */
         Raw,
+
+        /**
+         * Nested attribute (with or without NLA_F_NESTED).
+         */
         Nested,
+
+        /**
+         * Non-null terminated string.
+         *
+         * The length of the string is determined by the size of an attribute.
+         */
         String,
+
+        /**
+         * Null terminated string.
+         */
+        StringNul,
+
+        /**
+         * Unsigned integer of size 8, 16, 32 or 64 bits.
+         */
         Uint,
+
+        /**
+         * Structure which printer is defined in ops ToStream variant.
+         */
         Struct,
+
+        /**
+         * Flag attribute.
+         *
+         * The attribute doesn't have any contents. The flag is set when the attribute is present,
+         * it's not when it's absent from attribute list.
+         */
+        Flag,
+    };
+    enum class Flags : uint8_t {
+        Verbose = (1 << 0),
     };
     using ToStream = std::function<void(std::stringstream& ss, const Buffer<nlattr> attr)>;
 
     std::string name;
     DataType dataType = DataType::Raw;
     std::variant<AttributeMap, ToStream> ops = AttributeMap{};
+
+    /**
+     * Attribute flags.
+     *
+     * It's not really a bitmask flag set (since you are not supposed to compare enum class by
+     * bitmask), but std::set<Flags> bumps compile time from 16s to 3m. Let's leave it as-is for
+     * now and revisit if we get some flags that can be used in pairs. When it happens, review all
+     * uses of the flags field to include the "&" operator and not "==".
+     */
+    Flags flags = {};
 };
 
 /**
@@ -74,11 +121,11 @@
  * section in linux/netlink.h.
  */
 enum class MessageGenre {
-    UNKNOWN,
-    GET,
-    NEW,
-    DELETE,
-    ACK,
+    Unknown,
+    Get,
+    New,
+    Delete,
+    Ack,
 };
 
 /**
@@ -103,8 +150,15 @@
     MessageDetails getMessageDetails(nlmsgtype_t msgtype) const;
     virtual void dataToStream(std::stringstream& ss, const Buffer<nlmsghdr> hdr) const = 0;
 
+    /**
+     * Message tracking for stateful protocols (such as NETLINK_GENERIC).
+     *
+     * \param hdr Message to track
+     */
+    virtual void track(const Buffer<nlmsghdr> hdr);
+
     static MessageDetails getMessageDetails(
-            const std::optional<std::reference_wrapper<const MessageDescriptor>>& msgDescMaybe,
+            const std::optional<std::reference_wrapper<MessageDescriptor>>& msgDescMaybe,
             nlmsgtype_t msgtype);
 
   protected:
diff --git a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp
index cd2e8c6..16208ab 100644
--- a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp
@@ -32,7 +32,7 @@
     return mName;
 }
 
-const std::optional<std::reference_wrapper<const MessageDescriptor>>
+const std::optional<std::reference_wrapper<MessageDescriptor>>
 NetlinkProtocol::getMessageDescriptor(nlmsgtype_t nlmsg_type) {
     if (mMessageDescrs.count(nlmsg_type) == 0) return std::nullopt;
     return *mMessageDescrs.find(nlmsg_type)->second;
@@ -41,7 +41,7 @@
 NetlinkProtocol::MessageDescriptorMap NetlinkProtocol::toMap(
         const NetlinkProtocol::MessageDescriptorList& descrs, int protocol) {
     MessageDescriptorMap map;
-    for (const auto& descr : descrs) {
+    for (auto& descr : descrs) {
         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 c969547..b173b91 100644
--- a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h
+++ b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h
@@ -40,17 +40,17 @@
 
     const std::string& getName() const;
 
-    virtual const std::optional<std::reference_wrapper<const MessageDescriptor>>
-    getMessageDescriptor(nlmsgtype_t nlmsg_type);
+    virtual const std::optional<std::reference_wrapper<MessageDescriptor>> getMessageDescriptor(
+            nlmsgtype_t nlmsg_type);
 
   protected:
-    typedef std::vector<std::shared_ptr<const MessageDescriptor>> MessageDescriptorList;
+    typedef std::vector<std::shared_ptr<MessageDescriptor>> MessageDescriptorList;
 
     NetlinkProtocol(int protocol, const std::string& name,
                     const MessageDescriptorList&& messageDescrs);
 
   private:
-    typedef std::map<nlmsgtype_t, std::shared_ptr<const MessageDescriptor>> MessageDescriptorMap;
+    typedef std::map<nlmsgtype_t, std::shared_ptr<MessageDescriptor>> MessageDescriptorMap;
 
     const int mProtocol;
     const std::string mName;
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 8a672d3..4b509c9 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", MessageGenre::UNKNOWN}},
-    {NLMSG_DONE, {"DONE", MessageGenre::UNKNOWN}},
-    {NLMSG_OVERRUN, {"OVERRUN", MessageGenre::UNKNOWN}},
+    {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 44708a3..77451ed 100644
--- a/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp
@@ -20,13 +20,15 @@
 
 #include <libnl++/printer.h>
 
+#include <map>
+
 namespace android::nl::protocols::base {
 
 using DataType = AttributeDefinition::DataType;
 
 // clang-format off
 Error::Error(int protocol) : MessageDefinition<nlmsgerr>("nlmsg", {
-    {NLMSG_ERROR, {"ERROR", MessageGenre::ACK}},
+    {NLMSG_ERROR, {"ERROR", MessageGenre::Ack}},
 }, {
     {NLMSGERR_ATTR_MSG, {"MSG", DataType::String}},
     {NLMSGERR_ATTR_OFFS, {"OFFS", DataType::Uint}},
@@ -34,9 +36,156 @@
 }), mProtocol(protocol) {}
 // clang-format on
 
+std::map<int, std::string> errnoNames{
+        {EPERM, "EPERM"},                      // Operation not permitted
+        {ENOENT, "ENOENT"},                    // No such file or directory
+        {ESRCH, "ESRCH"},                      // No such process
+        {EINTR, "EINTR"},                      // Interrupted system call
+        {EIO, "EIO"},                          // I/O error
+        {ENXIO, "ENXIO"},                      // No such device or address
+        {E2BIG, "E2BIG"},                      // Argument list too long
+        {ENOEXEC, "ENOEXEC"},                  // Exec format error
+        {EBADF, "EBADF"},                      // Bad file number
+        {ECHILD, "ECHILD"},                    // No child processes
+        {EAGAIN, "EAGAIN"},                    // Try again
+        {ENOMEM, "ENOMEM"},                    // Out of memory
+        {EACCES, "EACCES"},                    // Permission denied
+        {EFAULT, "EFAULT"},                    // Bad address
+        {ENOTBLK, "ENOTBLK"},                  // Block device required
+        {EBUSY, "EBUSY"},                      // Device or resource busy
+        {EEXIST, "EEXIST"},                    // File exists
+        {EXDEV, "EXDEV"},                      // Cross-device link
+        {ENODEV, "ENODEV"},                    // No such device
+        {ENOTDIR, "ENOTDIR"},                  // Not a directory
+        {EISDIR, "EISDIR"},                    // Is a directory
+        {EINVAL, "EINVAL"},                    // Invalid argument
+        {ENFILE, "ENFILE"},                    // File table overflow
+        {EMFILE, "EMFILE"},                    // Too many open files
+        {ENOTTY, "ENOTTY"},                    // Not a typewriter
+        {ETXTBSY, "ETXTBSY"},                  // Text file busy
+        {EFBIG, "EFBIG"},                      // File too large
+        {ENOSPC, "ENOSPC"},                    // No space left on device
+        {ESPIPE, "ESPIPE"},                    // Illegal seek
+        {EROFS, "EROFS"},                      // Read-only file system
+        {EMLINK, "EMLINK"},                    // Too many links
+        {EPIPE, "EPIPE"},                      // Broken pipe
+        {EDOM, "EDOM"},                        // Math argument out of domain of func
+        {ERANGE, "ERANGE"},                    // Math result not representable
+        {EDEADLK, "EDEADLK"},                  // Resource deadlock would occur
+        {ENAMETOOLONG, "ENAMETOOLONG"},        // File name too long
+        {ENOLCK, "ENOLCK"},                    // No record locks available
+        {ENOSYS, "ENOSYS"},                    // Invalid system call number
+        {ENOTEMPTY, "ENOTEMPTY"},              // Directory not empty
+        {ELOOP, "ELOOP"},                      // Too many symbolic links encountered
+        {ENOMSG, "ENOMSG"},                    // No message of desired type
+        {EIDRM, "EIDRM"},                      // Identifier removed
+        {ECHRNG, "ECHRNG"},                    // Channel number out of range
+        {EL2NSYNC, "EL2NSYNC"},                // Level 2 not synchronized
+        {EL3HLT, "EL3HLT"},                    // Level 3 halted
+        {EL3RST, "EL3RST"},                    // Level 3 reset
+        {ELNRNG, "ELNRNG"},                    // Link number out of range
+        {EUNATCH, "EUNATCH"},                  // Protocol driver not attached
+        {ENOCSI, "ENOCSI"},                    // No CSI structure available
+        {EL2HLT, "EL2HLT"},                    // Level 2 halted
+        {EBADE, "EBADE"},                      // Invalid exchange
+        {EBADR, "EBADR"},                      // Invalid request descriptor
+        {EXFULL, "EXFULL"},                    // Exchange full
+        {ENOANO, "ENOANO"},                    // No anode
+        {EBADRQC, "EBADRQC"},                  // Invalid request code
+        {EBADSLT, "EBADSLT"},                  // Invalid slot
+        {EBFONT, "EBFONT"},                    // Bad font file format
+        {ENOSTR, "ENOSTR"},                    // Device not a stream
+        {ENODATA, "ENODATA"},                  // No data available
+        {ETIME, "ETIME"},                      // Timer expired
+        {ENOSR, "ENOSR"},                      // Out of streams resources
+        {ENONET, "ENONET"},                    // Machine is not on the network
+        {ENOPKG, "ENOPKG"},                    // Package not installed
+        {EREMOTE, "EREMOTE"},                  // Object is remote
+        {ENOLINK, "ENOLINK"},                  // Link has been severed
+        {EADV, "EADV"},                        // Advertise error
+        {ESRMNT, "ESRMNT"},                    // Srmount error
+        {ECOMM, "ECOMM"},                      // Communication error on send
+        {EPROTO, "EPROTO"},                    // Protocol error
+        {EMULTIHOP, "EMULTIHOP"},              // Multihop attempted
+        {EDOTDOT, "EDOTDOT"},                  // RFS specific error
+        {EBADMSG, "EBADMSG"},                  // Not a data message
+        {EOVERFLOW, "EOVERFLOW"},              // Value too large for defined data type
+        {ENOTUNIQ, "ENOTUNIQ"},                // Name not unique on network
+        {EBADFD, "EBADFD"},                    // File descriptor in bad state
+        {EREMCHG, "EREMCHG"},                  // Remote address changed
+        {ELIBACC, "ELIBACC"},                  // Can not access a needed shared library
+        {ELIBBAD, "ELIBBAD"},                  // Accessing a corrupted shared library
+        {ELIBSCN, "ELIBSCN"},                  // .lib section in a.out corrupted
+        {ELIBMAX, "ELIBMAX"},                  // Attempting to link in too many shared libraries
+        {ELIBEXEC, "ELIBEXEC"},                // Cannot exec a shared library directly
+        {EILSEQ, "EILSEQ"},                    // Illegal byte sequence
+        {ERESTART, "ERESTART"},                // Interrupted system call should be restarted
+        {ESTRPIPE, "ESTRPIPE"},                // Streams pipe error
+        {EUSERS, "EUSERS"},                    // Too many users
+        {ENOTSOCK, "ENOTSOCK"},                // Socket operation on non-socket
+        {EDESTADDRREQ, "EDESTADDRREQ"},        // Destination address required
+        {EMSGSIZE, "EMSGSIZE"},                // Message too long
+        {EPROTOTYPE, "EPROTOTYPE"},            // Protocol wrong type for socket
+        {ENOPROTOOPT, "ENOPROTOOPT"},          // Protocol not available
+        {EPROTONOSUPPORT, "EPROTONOSUPPORT"},  // Protocol not supported
+        {ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT"},  // Socket type not supported
+        {EOPNOTSUPP, "EOPNOTSUPP"},            // Operation not supported on transport endpoint
+        {EPFNOSUPPORT, "EPFNOSUPPORT"},        // Protocol family not supported
+        {EAFNOSUPPORT, "EAFNOSUPPORT"},        // Address family not supported by protocol
+        {EADDRINUSE, "EADDRINUSE"},            // Address already in use
+        {EADDRNOTAVAIL, "EADDRNOTAVAIL"},      // Cannot assign requested address
+        {ENETDOWN, "ENETDOWN"},                // Network is down
+        {ENETUNREACH, "ENETUNREACH"},          // Network is unreachable
+        {ENETRESET, "ENETRESET"},              // Network dropped connection because of reset
+        {ECONNABORTED, "ECONNABORTED"},        // Software caused connection abort
+        {ECONNRESET, "ECONNRESET"},            // Connection reset by peer
+        {ENOBUFS, "ENOBUFS"},                  // No buffer space available
+        {EISCONN, "EISCONN"},                  // Transport endpoint is already connected
+        {ENOTCONN, "ENOTCONN"},                // Transport endpoint is not connected
+        {ESHUTDOWN, "ESHUTDOWN"},              // Cannot send after transport endpoint shutdown
+        {ETOOMANYREFS, "ETOOMANYREFS"},        // Too many references: cannot splice
+        {ETIMEDOUT, "ETIMEDOUT"},              // Connection timed out
+        {ECONNREFUSED, "ECONNREFUSED"},        // Connection refused
+        {EHOSTDOWN, "EHOSTDOWN"},              // Host is down
+        {EHOSTUNREACH, "EHOSTUNREACH"},        // No route to host
+        {EALREADY, "EALREADY"},                // Operation already in progress
+        {EINPROGRESS, "EINPROGRESS"},          // Operation now in progress
+        {ESTALE, "ESTALE"},                    // Stale file handle
+        {EUCLEAN, "EUCLEAN"},                  // Structure needs cleaning
+        {ENOTNAM, "ENOTNAM"},                  // Not a XENIX named type file
+        {ENAVAIL, "ENAVAIL"},                  // No XENIX semaphores available
+        {EISNAM, "EISNAM"},                    // Is a named type file
+        {EREMOTEIO, "EREMOTEIO"},              // Remote I/O error
+        {EDQUOT, "EDQUOT"},                    // Quota exceeded
+        {ENOMEDIUM, "ENOMEDIUM"},              // No medium found
+        {EMEDIUMTYPE, "EMEDIUMTYPE"},          // Wrong medium type
+        {ECANCELED, "ECANCELED"},              // Operation Canceled
+        {ENOKEY, "ENOKEY"},                    // Required key not available
+        {EKEYEXPIRED, "EKEYEXPIRED"},          // Key has expired
+        {EKEYREVOKED, "EKEYREVOKED"},          // Key has been revoked
+        {EKEYREJECTED, "EKEYREJECTED"},        // Key was rejected by service
+        {EOWNERDEAD, "EOWNERDEAD"},            // Owner died
+        {ENOTRECOVERABLE, "ENOTRECOVERABLE"},  // State not recoverable
+        {ERFKILL, "ERFKILL"},                  // Operation not possible due to RF-kill
+        {EHWPOISON, "EHWPOISON"},              // Memory page has hardware error
+
+        // Duplicates: EWOULDBLOCK (Operation would block), EDEADLOCK
+};
+
 void Error::toStream(std::stringstream& ss, const nlmsgerr& data) const {
-    ss << "nlmsgerr{error=" << data.error
-       << ", msg=" << toString({&data.msg, sizeof(data.msg)}, mProtocol, false) << "}";
+    ss << "nlmsgerr{";
+    if (data.error == 0) {
+        ss << "ACK";
+    } else {
+        ss << "error=";
+        const auto nameIt = errnoNames.find(-data.error);
+        if (nameIt == errnoNames.end()) {
+            ss << data.error;
+        } else {
+            ss << nameIt->second;
+        }
+    }
+    ss << ", 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/Ctrl.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.cpp
index a3c6736..1e1ad12 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.cpp
@@ -16,12 +16,19 @@
 
 #include "Ctrl.h"
 
+#include "families/Nl80211.h"
+
+#include <libnl++/Message.h>
+
 namespace android::nl::protocols::generic {
 
 using DataType = AttributeDefinition::DataType;
+using Flags = AttributeDefinition::Flags;
 
 // clang-format off
-Ctrl::Ctrl() : GenericMessageBase(GENL_ID_CTRL, "ID_CTRL", {
+Ctrl::Ctrl(Generic::FamilyRegister& familyRegister)
+    : GenericMessageBase(GENL_ID_CTRL, "ID_CTRL",
+{
     {CTRL_CMD_NEWFAMILY, "NEWFAMILY"},
     {CTRL_CMD_DELFAMILY, "DELFAMILY"},
     {CTRL_CMD_GETFAMILY, "GETFAMILY"},
@@ -33,7 +40,7 @@
     {CTRL_CMD_GETMCAST_GRP, "GETMCAST_GRP"},
 }, {
     {CTRL_ATTR_FAMILY_ID, {"FAMILY_ID", DataType::Uint}},
-    {CTRL_ATTR_FAMILY_NAME, {"FAMILY_NAME", DataType::String}},
+    {CTRL_ATTR_FAMILY_NAME, {"FAMILY_NAME", DataType::StringNul}},
     {CTRL_ATTR_VERSION, {"VERSION", DataType::Uint}},
     {CTRL_ATTR_HDRSIZE, {"HDRSIZE", DataType::Uint}},
     {CTRL_ATTR_MAXATTR, {"MAXATTR", DataType::Uint}},
@@ -42,14 +49,31 @@
             {CTRL_ATTR_OP_ID, {"ID", DataType::Uint}},
             {CTRL_ATTR_OP_FLAGS, {"FLAGS", DataType::Uint}},
         }}},
-    }}},
+    }, Flags::Verbose}},
     {CTRL_ATTR_MCAST_GROUPS, {"MCAST_GROUPS", DataType::Nested, AttributeMap{
         {std::nullopt, {"GRP", DataType::Nested, AttributeMap{
-            {CTRL_ATTR_MCAST_GRP_NAME, {"NAME", DataType::String}},
+            {CTRL_ATTR_MCAST_GRP_NAME, {"NAME", DataType::StringNul}},
             {CTRL_ATTR_MCAST_GRP_ID, {"ID", DataType::Uint}},
         }}},
     }}},
-}) {}
+}), mFamilyRegister(familyRegister) {}
 // clang-format on
 
+void Ctrl::track(const Buffer<nlmsghdr> hdr) {
+    const auto msgMaybe = Message<genlmsghdr>::parse(hdr, {GENL_ID_CTRL});
+    if (!msgMaybe.has_value()) return;
+    const auto msg = *msgMaybe;
+
+    if (msg->cmd != CTRL_CMD_NEWFAMILY) return;
+    const auto familyId = msg.attributes.get<uint16_t>(CTRL_ATTR_FAMILY_ID);
+    const auto familyName = msg.attributes.get<std::string>(CTRL_ATTR_FAMILY_NAME);
+
+    /* For now, we support just a single family. But if you add more, please define proper
+     * abstraction and not hardcode every name and class here.
+     */
+    if (familyName == "nl80211") {
+        mFamilyRegister[familyId] = std::make_shared<families::Nl80211>(familyId);
+    }
+}
+
 }  // namespace android::nl::protocols::generic
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.h b/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.h
index 6af87a8..b13df02 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.h
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.h
@@ -16,13 +16,19 @@
 
 #pragma once
 
+#include "Generic.h"
 #include "GenericMessageBase.h"
 
 namespace android::nl::protocols::generic {
 
 class Ctrl : public GenericMessageBase {
   public:
-    Ctrl();
+    Ctrl(Generic::FamilyRegister& familyRegister);
+
+    void track(const Buffer<nlmsghdr> hdr) override;
+
+  private:
+    Generic::FamilyRegister& mFamilyRegister;
 };
 
 }  // namespace android::nl::protocols::generic
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/Generic.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/Generic.cpp
index 1a24914..5e34a1f 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/Generic.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/Generic.cpp
@@ -21,11 +21,12 @@
 
 namespace android::nl::protocols::generic {
 
-Generic::Generic() : NetlinkProtocol(NETLINK_GENERIC, "GENERIC", {std::make_shared<Ctrl>()}) {}
+Generic::Generic()
+    : NetlinkProtocol(NETLINK_GENERIC, "GENERIC", {std::make_shared<Ctrl>(mFamilyRegister)}) {}
 
-const std::optional<std::reference_wrapper<const MessageDescriptor>> Generic::getMessageDescriptor(
+const std::optional<std::reference_wrapper<MessageDescriptor>> Generic::getMessageDescriptor(
         nlmsgtype_t nlmsg_type) {
-    const auto desc = NetlinkProtocol::getMessageDescriptor(nlmsg_type);
+    auto desc = NetlinkProtocol::getMessageDescriptor(nlmsg_type);
     if (desc.has_value()) return desc;
 
     auto it = mFamilyRegister.find(nlmsg_type);
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/Generic.h b/automotive/can/1.0/default/libnl++/protocols/generic/Generic.h
index 593c92d..2cdd584 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/Generic.h
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/Generic.h
@@ -25,13 +25,15 @@
  */
 class Generic : public NetlinkProtocol {
   public:
+    typedef std::map<nlmsgtype_t, std::shared_ptr<MessageDescriptor>> FamilyRegister;
+
     Generic();
 
-    const std::optional<std::reference_wrapper<const MessageDescriptor>> getMessageDescriptor(
+    const std::optional<std::reference_wrapper<MessageDescriptor>> getMessageDescriptor(
             nlmsgtype_t nlmsg_type);
 
   private:
-    std::map<nlmsgtype_t, std::shared_ptr<const MessageDescriptor>> mFamilyRegister;
+    FamilyRegister mFamilyRegister;
 };
 
 }  // namespace android::nl::protocols::generic
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 134638e..b7b811b 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp
@@ -22,16 +22,27 @@
         nlmsgtype_t msgtype, const std::string&& msgname,
         const std::initializer_list<GenericCommandNameMap::value_type> commandNames,
         const std::initializer_list<AttributeMap::value_type> attrTypes)
-    : MessageDefinition<genlmsghdr>(msgname, {{msgtype, {msgname, MessageGenre::UNKNOWN}}},
+    : MessageDefinition<genlmsghdr>(msgname, {{msgtype, {msgname, MessageGenre::Unknown}}},
                                     attrTypes),
       mCommandNames(commandNames) {}
 
 void GenericMessageBase::toStream(std::stringstream& ss, const genlmsghdr& data) const {
+    const auto commandNameIt = mCommandNames.find(data.cmd);
+    const auto commandName = (commandNameIt == mCommandNames.end())
+                                     ? std::nullopt
+                                     : std::optional<std::string>(commandNameIt->second);
+
+    if (commandName.has_value() && data.version == 1 && data.reserved == 0) {
+        // short version
+        ss << *commandName;
+        return;
+    }
+
     ss << "genlmsghdr{";
-    if (mCommandNames.count(data.cmd) == 0) {
+    if (commandName.has_value()) {
         ss << "cmd=" << unsigned(data.cmd);
     } else {
-        ss << "cmd=" << mCommandNames.find(data.cmd)->second;
+        ss << "cmd=" << *commandName;
     }
     ss << ", version=" << unsigned(data.version);
     if (data.reserved != 0) ss << ", reserved=" << data.reserved;
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.cpp
new file mode 100644
index 0000000..23ec66f
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.cpp
@@ -0,0 +1,1009 @@
+/*
+ * 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 "Nl80211.h"
+
+#include "../../structs.h"
+#include "common.h"
+
+#include <linux/nl80211.h>
+
+#include <iomanip>
+
+namespace android::nl::protocols::generic::families {
+
+/**
+ * Reduce verbosity of printed Information Elements.
+ */
+static constexpr bool kCompactIE = true;
+
+enum {
+    // broken compatibility in Aug 2020
+    NL80211_ATTR_CNTDWN_OFFS_BEACON = NL80211_ATTR_CSA_C_OFF_BEACON,
+    NL80211_ATTR_CNTDWN_OFFS_PRESP = NL80211_ATTR_CSA_C_OFF_PRESP,
+
+    // new fields not available in current Android
+    NL80211_ATTR_FILS_DISCOVERY = NL80211_ATTR_HE_6GHZ_CAPABILITY + 1,
+    NL80211_ATTR_UNSOL_BCAST_PROBE_RESP,
+    NL80211_ATTR_S1G_CAPABILITY,
+    NL80211_ATTR_S1G_CAPABILITY_MASK,
+
+    NL80211_FREQUENCY_ATTR_1MHZ = NL80211_FREQUENCY_ATTR_OFFSET + 1,
+    NL80211_FREQUENCY_ATTR_2MHZ,
+    NL80211_FREQUENCY_ATTR_4MHZ,
+    NL80211_FREQUENCY_ATTR_8MHZ,
+    NL80211_FREQUENCY_ATTR_16MHZ,
+};
+
+enum ieee80211_eid {
+    WLAN_EID_SSID = 0,
+};
+
+using DataType = AttributeDefinition::DataType;
+using Flags = AttributeDefinition::Flags;
+
+static void informationElementsToStream(std::stringstream& ss, const Buffer<nlattr> attr);
+static void nl80211_pattern_supportToStream(std::stringstream& ss, const Buffer<nlattr> attr);
+
+static const AttributeMap iftypes{
+        {NL80211_IFTYPE_UNSPECIFIED, {"UNSPECIFIED", DataType::Flag}},
+        {NL80211_IFTYPE_ADHOC, {"ADHOC", DataType::Flag}},
+        {NL80211_IFTYPE_STATION, {"STATION", DataType::Flag}},
+        {NL80211_IFTYPE_AP, {"AP", DataType::Flag}},
+        {NL80211_IFTYPE_AP_VLAN, {"AP_VLAN", DataType::Flag}},
+        {NL80211_IFTYPE_WDS, {"WDS", DataType::Flag}},
+        {NL80211_IFTYPE_MONITOR, {"MONITOR", DataType::Flag}},
+        {NL80211_IFTYPE_MESH_POINT, {"MESH_POINT", DataType::Flag}},
+        {NL80211_IFTYPE_P2P_CLIENT, {"P2P_CLIENT", DataType::Flag}},
+        {NL80211_IFTYPE_P2P_GO, {"P2P_GO", DataType::Flag}},
+        {NL80211_IFTYPE_P2P_DEVICE, {"P2P_DEVICE", DataType::Flag}},
+        {NL80211_IFTYPE_OCB, {"OCB", DataType::Flag}},
+        {NL80211_IFTYPE_NAN, {"NAN", DataType::Flag}},
+};
+
+// clang-format off
+Nl80211::Nl80211(nlmsgtype_t familyId) : GenericMessageBase(familyId, "nl80211", {
+    /* Script to generate the (initial) top-level list from linux/nl80211.h:
+     * sed -e 's/^  NL80211_CMD_\(.*\),$/    {NL80211_CMD_\1, "\1"},/g'
+     */
+    {NL80211_CMD_UNSPEC, "UNSPEC"},
+
+    {NL80211_CMD_GET_WIPHY, "GET_WIPHY"},
+    {NL80211_CMD_SET_WIPHY, "SET_WIPHY"},
+    {NL80211_CMD_NEW_WIPHY, "NEW_WIPHY"},
+    {NL80211_CMD_DEL_WIPHY, "DEL_WIPHY"},
+
+    {NL80211_CMD_GET_INTERFACE, "GET_INTERFACE"},
+    {NL80211_CMD_SET_INTERFACE, "SET_INTERFACE"},
+    {NL80211_CMD_NEW_INTERFACE, "NEW_INTERFACE"},
+    {NL80211_CMD_DEL_INTERFACE, "DEL_INTERFACE"},
+
+    {NL80211_CMD_GET_KEY, "GET_KEY"},
+    {NL80211_CMD_SET_KEY, "SET_KEY"},
+    {NL80211_CMD_NEW_KEY, "NEW_KEY"},
+    {NL80211_CMD_DEL_KEY, "DEL_KEY"},
+
+    {NL80211_CMD_GET_BEACON, "GET_BEACON"},
+    {NL80211_CMD_SET_BEACON, "SET_BEACON"},
+    {NL80211_CMD_START_AP, "START_AP"},
+    {NL80211_CMD_STOP_AP, "STOP_AP"},
+
+    {NL80211_CMD_GET_STATION, "GET_STATION"},
+    {NL80211_CMD_SET_STATION, "SET_STATION"},
+    {NL80211_CMD_NEW_STATION, "NEW_STATION"},
+    {NL80211_CMD_DEL_STATION, "DEL_STATION"},
+
+    {NL80211_CMD_GET_MPATH, "GET_MPATH"},
+    {NL80211_CMD_SET_MPATH, "SET_MPATH"},
+    {NL80211_CMD_NEW_MPATH, "NEW_MPATH"},
+    {NL80211_CMD_DEL_MPATH, "DEL_MPATH"},
+
+    {NL80211_CMD_SET_BSS, "SET_BSS"},
+
+    {NL80211_CMD_SET_REG, "SET_REG"},
+    {NL80211_CMD_REQ_SET_REG, "REQ_SET_REG"},
+
+    {NL80211_CMD_GET_MESH_CONFIG, "GET_MESH_CONFIG"},
+    {NL80211_CMD_SET_MESH_CONFIG, "SET_MESH_CONFIG"},
+
+    {NL80211_CMD_SET_MGMT_EXTRA_IE, "SET_MGMT_EXTRA_IE"},
+
+    {NL80211_CMD_GET_REG, "GET_REG"},
+
+    {NL80211_CMD_GET_SCAN, "GET_SCAN"},
+    {NL80211_CMD_TRIGGER_SCAN, "TRIGGER_SCAN"},
+    {NL80211_CMD_NEW_SCAN_RESULTS, "NEW_SCAN_RESULTS"},
+    {NL80211_CMD_SCAN_ABORTED, "SCAN_ABORTED"},
+
+    {NL80211_CMD_REG_CHANGE, "REG_CHANGE"},
+
+    {NL80211_CMD_AUTHENTICATE, "AUTHENTICATE"},
+    {NL80211_CMD_ASSOCIATE, "ASSOCIATE"},
+    {NL80211_CMD_DEAUTHENTICATE, "DEAUTHENTICATE"},
+    {NL80211_CMD_DISASSOCIATE, "DISASSOCIATE"},
+
+    {NL80211_CMD_MICHAEL_MIC_FAILURE, "MICHAEL_MIC_FAILURE"},
+
+    {NL80211_CMD_REG_BEACON_HINT, "REG_BEACON_HINT"},
+
+    {NL80211_CMD_JOIN_IBSS, "JOIN_IBSS"},
+    {NL80211_CMD_LEAVE_IBSS, "LEAVE_IBSS"},
+
+    {NL80211_CMD_TESTMODE, "TESTMODE"},
+
+    {NL80211_CMD_CONNECT, "CONNECT"},
+    {NL80211_CMD_ROAM, "ROAM"},
+    {NL80211_CMD_DISCONNECT, "DISCONNECT"},
+
+    {NL80211_CMD_SET_WIPHY_NETNS, "SET_WIPHY_NETNS"},
+
+    {NL80211_CMD_GET_SURVEY, "GET_SURVEY"},
+    {NL80211_CMD_NEW_SURVEY_RESULTS, "NEW_SURVEY_RESULTS"},
+
+    {NL80211_CMD_SET_PMKSA, "SET_PMKSA"},
+    {NL80211_CMD_DEL_PMKSA, "DEL_PMKSA"},
+    {NL80211_CMD_FLUSH_PMKSA, "FLUSH_PMKSA"},
+
+    {NL80211_CMD_REMAIN_ON_CHANNEL, "REMAIN_ON_CHANNEL"},
+    {NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, "CANCEL_REMAIN_ON_CHANNEL"},
+
+    {NL80211_CMD_SET_TX_BITRATE_MASK, "SET_TX_BITRATE_MASK"},
+
+    {NL80211_CMD_REGISTER_FRAME, "REGISTER_FRAME"},
+    {NL80211_CMD_FRAME, "FRAME"},
+    {NL80211_CMD_FRAME_TX_STATUS, "FRAME_TX_STATUS"},
+
+    {NL80211_CMD_SET_POWER_SAVE, "SET_POWER_SAVE"},
+    {NL80211_CMD_GET_POWER_SAVE, "GET_POWER_SAVE"},
+
+    {NL80211_CMD_SET_CQM, "SET_CQM"},
+    {NL80211_CMD_NOTIFY_CQM, "NOTIFY_CQM"},
+
+    {NL80211_CMD_SET_CHANNEL, "SET_CHANNEL"},
+    {NL80211_CMD_SET_WDS_PEER, "SET_WDS_PEER"},
+
+    {NL80211_CMD_FRAME_WAIT_CANCEL, "FRAME_WAIT_CANCEL"},
+
+    {NL80211_CMD_JOIN_MESH, "JOIN_MESH"},
+    {NL80211_CMD_LEAVE_MESH, "LEAVE_MESH"},
+
+    {NL80211_CMD_UNPROT_DEAUTHENTICATE, "UNPROT_DEAUTHENTICATE"},
+    {NL80211_CMD_UNPROT_DISASSOCIATE, "UNPROT_DISASSOCIATE"},
+
+    {NL80211_CMD_NEW_PEER_CANDIDATE, "NEW_PEER_CANDIDATE"},
+
+    {NL80211_CMD_GET_WOWLAN, "GET_WOWLAN"},
+    {NL80211_CMD_SET_WOWLAN, "SET_WOWLAN"},
+
+    {NL80211_CMD_START_SCHED_SCAN, "START_SCHED_SCAN"},
+    {NL80211_CMD_STOP_SCHED_SCAN, "STOP_SCHED_SCAN"},
+    {NL80211_CMD_SCHED_SCAN_RESULTS, "SCHED_SCAN_RESULTS"},
+    {NL80211_CMD_SCHED_SCAN_STOPPED, "SCHED_SCAN_STOPPED"},
+
+    {NL80211_CMD_SET_REKEY_OFFLOAD, "SET_REKEY_OFFLOAD"},
+
+    {NL80211_CMD_PMKSA_CANDIDATE, "PMKSA_CANDIDATE"},
+
+    {NL80211_CMD_TDLS_OPER, "TDLS_OPER"},
+    {NL80211_CMD_TDLS_MGMT, "TDLS_MGMT"},
+
+    {NL80211_CMD_UNEXPECTED_FRAME, "UNEXPECTED_FRAME"},
+
+    {NL80211_CMD_PROBE_CLIENT, "PROBE_CLIENT"},
+
+    {NL80211_CMD_REGISTER_BEACONS, "REGISTER_BEACONS"},
+
+    {NL80211_CMD_UNEXPECTED_4ADDR_FRAME, "UNEXPECTED_4ADDR_FRAME"},
+
+    {NL80211_CMD_SET_NOACK_MAP, "SET_NOACK_MAP"},
+
+    {NL80211_CMD_CH_SWITCH_NOTIFY, "CH_SWITCH_NOTIFY"},
+
+    {NL80211_CMD_START_P2P_DEVICE, "START_P2P_DEVICE"},
+    {NL80211_CMD_STOP_P2P_DEVICE, "STOP_P2P_DEVICE"},
+
+    {NL80211_CMD_CONN_FAILED, "CONN_FAILED"},
+
+    {NL80211_CMD_SET_MCAST_RATE, "SET_MCAST_RATE"},
+
+    {NL80211_CMD_SET_MAC_ACL, "SET_MAC_ACL"},
+
+    {NL80211_CMD_RADAR_DETECT, "RADAR_DETECT"},
+
+    {NL80211_CMD_GET_PROTOCOL_FEATURES, "GET_PROTOCOL_FEATURES"},
+
+    {NL80211_CMD_UPDATE_FT_IES, "UPDATE_FT_IES"},
+    {NL80211_CMD_FT_EVENT, "FT_EVENT"},
+
+    {NL80211_CMD_CRIT_PROTOCOL_START, "CRIT_PROTOCOL_START"},
+    {NL80211_CMD_CRIT_PROTOCOL_STOP, "CRIT_PROTOCOL_STOP"},
+
+    {NL80211_CMD_GET_COALESCE, "GET_COALESCE"},
+    {NL80211_CMD_SET_COALESCE, "SET_COALESCE"},
+
+    {NL80211_CMD_CHANNEL_SWITCH, "CHANNEL_SWITCH"},
+
+    {NL80211_CMD_VENDOR, "VENDOR"},
+
+    {NL80211_CMD_SET_QOS_MAP, "SET_QOS_MAP"},
+
+    {NL80211_CMD_ADD_TX_TS, "ADD_TX_TS"},
+    {NL80211_CMD_DEL_TX_TS, "DEL_TX_TS"},
+
+    {NL80211_CMD_GET_MPP, "GET_MPP"},
+
+    {NL80211_CMD_JOIN_OCB, "JOIN_OCB"},
+    {NL80211_CMD_LEAVE_OCB, "LEAVE_OCB"},
+
+    {NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, "CH_SWITCH_STARTED_NOTIFY"},
+
+    {NL80211_CMD_TDLS_CHANNEL_SWITCH, "TDLS_CHANNEL_SWITCH"},
+    {NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, "TDLS_CANCEL_CHANNEL_SWITCH"},
+
+    {NL80211_CMD_WIPHY_REG_CHANGE, "WIPHY_REG_CHANGE"},
+
+    {NL80211_CMD_ABORT_SCAN, "ABORT_SCAN"},
+
+    {NL80211_CMD_START_NAN, "START_NAN"},
+    {NL80211_CMD_STOP_NAN, "STOP_NAN"},
+    {NL80211_CMD_ADD_NAN_FUNCTION, "ADD_NAN_FUNCTION"},
+    {NL80211_CMD_DEL_NAN_FUNCTION, "DEL_NAN_FUNCTION"},
+    {NL80211_CMD_CHANGE_NAN_CONFIG, "CHANGE_NAN_CONFIG"},
+    {NL80211_CMD_NAN_MATCH, "NAN_MATCH"},
+
+    {NL80211_CMD_SET_MULTICAST_TO_UNICAST, "SET_MULTICAST_TO_UNICAST"},
+
+    {NL80211_CMD_UPDATE_CONNECT_PARAMS, "UPDATE_CONNECT_PARAMS"},
+
+    {NL80211_CMD_SET_PMK, "SET_PMK"},
+    {NL80211_CMD_DEL_PMK, "DEL_PMK"},
+
+    {NL80211_CMD_PORT_AUTHORIZED, "PORT_AUTHORIZED"},
+
+    {NL80211_CMD_RELOAD_REGDB, "RELOAD_REGDB"},
+
+    {NL80211_CMD_EXTERNAL_AUTH, "EXTERNAL_AUTH"},
+
+    {NL80211_CMD_STA_OPMODE_CHANGED, "STA_OPMODE_CHANGED"},
+
+    {NL80211_CMD_CONTROL_PORT_FRAME, "CONTROL_PORT_FRAME"},
+
+    {NL80211_CMD_GET_FTM_RESPONDER_STATS, "GET_FTM_RESPONDER_STATS"},
+
+    {NL80211_CMD_PEER_MEASUREMENT_START, "PEER_MEASUREMENT_START"},
+    {NL80211_CMD_PEER_MEASUREMENT_RESULT, "PEER_MEASUREMENT_RESULT"},
+    {NL80211_CMD_PEER_MEASUREMENT_COMPLETE, "PEER_MEASUREMENT_COMPLETE"},
+
+    {NL80211_CMD_NOTIFY_RADAR, "NOTIFY_RADAR"},
+
+    {NL80211_CMD_UPDATE_OWE_INFO, "UPDATE_OWE_INFO"},
+
+    {NL80211_CMD_PROBE_MESH_LINK, "PROBE_MESH_LINK"},
+
+    {NL80211_CMD_SET_TID_CONFIG, "SET_TID_CONFIG"},
+
+    {NL80211_CMD_UNPROT_BEACON, "UNPROT_BEACON"},
+
+    {NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS, "CONTROL_PORT_FRAME_TX_STATUS"},
+}, {
+    /* Script to generate the (initial) top-level list from linux/nl80211.h:
+     * sed -e 's/^\tNL80211_ATTR_\(.*\),$/    {NL80211_ATTR_\1, {"\1"}},/g'
+     */
+    {NL80211_ATTR_UNSPEC, {"UNSPEC"}},
+
+    {NL80211_ATTR_WIPHY, {"WIPHY", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_NAME, {"WIPHY_NAME", DataType::StringNul}},
+
+    {NL80211_ATTR_IFINDEX, {"IFINDEX", DataType::Uint}},
+    {NL80211_ATTR_IFNAME, {"IFNAME", DataType::StringNul}},
+    {NL80211_ATTR_IFTYPE, {"IFTYPE", DataType::Uint}},
+
+    {NL80211_ATTR_MAC, {"MAC", DataType::Raw}},
+
+    {NL80211_ATTR_KEY_DATA, {"KEY_DATA"}},
+    {NL80211_ATTR_KEY_IDX, {"KEY_IDX"}},
+    {NL80211_ATTR_KEY_CIPHER, {"KEY_CIPHER"}},
+    {NL80211_ATTR_KEY_SEQ, {"KEY_SEQ"}},
+    {NL80211_ATTR_KEY_DEFAULT, {"KEY_DEFAULT"}},
+
+    {NL80211_ATTR_BEACON_INTERVAL, {"BEACON_INTERVAL"}},
+    {NL80211_ATTR_DTIM_PERIOD, {"DTIM_PERIOD"}},
+    {NL80211_ATTR_BEACON_HEAD, {"BEACON_HEAD"}},
+    {NL80211_ATTR_BEACON_TAIL, {"BEACON_TAIL"}},
+
+    {NL80211_ATTR_STA_AID, {"STA_AID"}},
+    {NL80211_ATTR_STA_FLAGS, {"STA_FLAGS"}},
+    {NL80211_ATTR_STA_LISTEN_INTERVAL, {"STA_LISTEN_INTERVAL"}},
+    {NL80211_ATTR_STA_SUPPORTED_RATES, {"STA_SUPPORTED_RATES"}},
+    {NL80211_ATTR_STA_VLAN, {"STA_VLAN"}},
+    {NL80211_ATTR_STA_INFO, {"STA_INFO"}},
+
+    {NL80211_ATTR_WIPHY_BANDS, {"WIPHY_BANDS", DataType::Nested, AttributeMap{
+        {std::nullopt, {"BAND", DataType::Nested, AttributeMap{
+            {NL80211_BAND_ATTR_FREQS, {"FREQS", DataType::Nested, AttributeMap{
+                {std::nullopt, {"FQ", DataType::Nested, AttributeMap{
+                    {NL80211_FREQUENCY_ATTR_FREQ, {"FREQ", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_DISABLED, {"DISABLED", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_IR, {"NO_IR", DataType::Flag}},
+                    {__NL80211_FREQUENCY_ATTR_NO_IBSS, {"_NO_IBSS", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_RADAR, {"RADAR", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_MAX_TX_POWER, {"MAX_TX_POWER", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_DFS_STATE, {"DFS_STATE", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_DFS_TIME, {"DFS_TIME", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, {"NO_HT40_MINUS", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, {"NO_HT40_PLUS", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_80MHZ, {"NO_80MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_160MHZ, {"NO_160MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, {"DFS_CAC_TIME", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_INDOOR_ONLY, {"INDOOR_ONLY", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_IR_CONCURRENT, {"IR_CONCURRENT", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_20MHZ, {"NO_20MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_10MHZ, {"NO_10MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_WMM, {"WMM"}},
+                    {NL80211_FREQUENCY_ATTR_NO_HE, {"NO_HE", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_OFFSET, {"OFFSET", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_1MHZ, {"1MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_2MHZ, {"2MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_4MHZ, {"4MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_8MHZ, {"8MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_16MHZ, {"16MHZ", DataType::Flag}},
+                }}},
+            }, Flags::Verbose}},
+            {NL80211_BAND_ATTR_RATES, {"RATES", DataType::Nested, AttributeMap{
+                {std::nullopt, {"RATE", DataType::Nested, AttributeMap{
+                    {NL80211_BITRATE_ATTR_RATE, {"RATE", DataType::Uint}},
+                    {NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE,
+                            {"2GHZ_SHORTPREAMBLE", DataType::Flag}},
+                }}},
+            }}},
+
+            {NL80211_BAND_ATTR_HT_MCS_SET, {"HT_MCS_SET"}},  // struct ieee80211_mcs_info
+            {NL80211_BAND_ATTR_HT_CAPA, {"HT_CAPA", DataType::Uint}},
+            {NL80211_BAND_ATTR_HT_AMPDU_FACTOR, {"HT_AMPDU_FACTOR", DataType::Uint}},
+            {NL80211_BAND_ATTR_HT_AMPDU_DENSITY, {"HT_AMPDU_DENSITY", DataType::Uint}},
+
+            {NL80211_BAND_ATTR_VHT_MCS_SET, {"VHT_MCS_SET"}},  // struct ieee80211_vht_mcs_info
+            {NL80211_BAND_ATTR_VHT_CAPA, {"VHT_CAPA", DataType::Uint}},
+            {NL80211_BAND_ATTR_IFTYPE_DATA, {"IFTYPE_DATA"}},
+
+            {NL80211_BAND_ATTR_EDMG_CHANNELS, {"EDMG_CHANNELS"}},
+            {NL80211_BAND_ATTR_EDMG_BW_CONFIG, {"EDMG_BW_CONFIG"}},
+        }}},
+    }, Flags::Verbose}},
+
+    {NL80211_ATTR_MNTR_FLAGS, {"MNTR_FLAGS"}},
+
+    {NL80211_ATTR_MESH_ID, {"MESH_ID"}},
+    {NL80211_ATTR_STA_PLINK_ACTION, {"STA_PLINK_ACTION"}},
+    {NL80211_ATTR_MPATH_NEXT_HOP, {"MPATH_NEXT_HOP"}},
+    {NL80211_ATTR_MPATH_INFO, {"MPATH_INFO"}},
+
+    {NL80211_ATTR_BSS_CTS_PROT, {"BSS_CTS_PROT"}},
+    {NL80211_ATTR_BSS_SHORT_PREAMBLE, {"BSS_SHORT_PREAMBLE"}},
+    {NL80211_ATTR_BSS_SHORT_SLOT_TIME, {"BSS_SHORT_SLOT_TIME"}},
+
+    {NL80211_ATTR_HT_CAPABILITY, {"HT_CAPABILITY"}},
+
+    {NL80211_ATTR_SUPPORTED_IFTYPES, {"SUPPORTED_IFTYPES", DataType::Nested, iftypes}},
+
+    {NL80211_ATTR_REG_ALPHA2, {"REG_ALPHA2"}},
+    {NL80211_ATTR_REG_RULES, {"REG_RULES"}},
+
+    {NL80211_ATTR_MESH_CONFIG, {"MESH_CONFIG"}},
+
+    {NL80211_ATTR_BSS_BASIC_RATES, {"BSS_BASIC_RATES"}},
+
+    {NL80211_ATTR_WIPHY_TXQ_PARAMS, {"WIPHY_TXQ_PARAMS"}},
+    {NL80211_ATTR_WIPHY_FREQ, {"WIPHY_FREQ"}},
+    {NL80211_ATTR_WIPHY_CHANNEL_TYPE, {"WIPHY_CHANNEL_TYPE"}},
+
+    {NL80211_ATTR_KEY_DEFAULT_MGMT, {"KEY_DEFAULT_MGMT"}},
+
+    {NL80211_ATTR_MGMT_SUBTYPE, {"MGMT_SUBTYPE"}},
+    {NL80211_ATTR_IE, {"IE"}},
+
+    {NL80211_ATTR_MAX_NUM_SCAN_SSIDS, {"MAX_NUM_SCAN_SSIDS", DataType::Uint}},
+
+    {NL80211_ATTR_SCAN_FREQUENCIES, {"SCAN_FREQUENCIES", DataType::Nested, AttributeMap{
+        {std::nullopt, {"FQ", DataType::Uint}},
+    }, Flags::Verbose}},
+    {NL80211_ATTR_SCAN_SSIDS, {"SCAN_SSIDS", DataType::Nested, AttributeMap{
+        {std::nullopt, {"SSID", DataType::String}},
+    }}},
+    {NL80211_ATTR_GENERATION, {"GENERATION", DataType::Uint}},
+    {NL80211_ATTR_BSS, {"BSS", DataType::Nested, AttributeMap{
+        {NL80211_BSS_BSSID, {"BSSID", DataType::Raw}},
+        {NL80211_BSS_FREQUENCY, {"FREQUENCY", DataType::Uint}},
+        {NL80211_BSS_TSF, {"TSF", DataType::Uint}},
+        {NL80211_BSS_BEACON_INTERVAL, {"BEACON_INTERVAL", DataType::Uint}},
+        {NL80211_BSS_CAPABILITY, {"CAPABILITY", DataType::Uint}},
+        {NL80211_BSS_INFORMATION_ELEMENTS, {"INFORMATION_ELEMENTS",
+                DataType::Struct, informationElementsToStream}},
+        {NL80211_BSS_SIGNAL_MBM, {"SIGNAL_MBM", DataType::Uint}},
+        {NL80211_BSS_SIGNAL_UNSPEC, {"SIGNAL_UNSPEC", DataType::Uint}},
+        {NL80211_BSS_STATUS, {"STATUS", DataType::Uint}},  // enum nl80211_bss_status
+        {NL80211_BSS_SEEN_MS_AGO, {"SEEN_MS_AGO", DataType::Uint}},
+        {NL80211_BSS_BEACON_IES, {"BEACON_IES", DataType::Struct, informationElementsToStream}},
+        {NL80211_BSS_CHAN_WIDTH, {"CHAN_WIDTH", DataType::Uint}},
+        {NL80211_BSS_BEACON_TSF, {"BEACON_TSF", DataType::Uint}},
+        {NL80211_BSS_PRESP_DATA, {"PRESP_DATA", DataType::Flag}},
+        {NL80211_BSS_LAST_SEEN_BOOTTIME, {"LAST_SEEN_BOOTTIME", DataType::Uint}},
+        {NL80211_BSS_PAD, {"PAD"}},
+        {NL80211_BSS_PARENT_TSF, {"PARENT_TSF"}},
+        {NL80211_BSS_PARENT_BSSID, {"PARENT_BSSID"}},
+        {NL80211_BSS_CHAIN_SIGNAL, {"CHAIN_SIGNAL", DataType::Nested, AttributeMap{
+            {std::nullopt, {"SIG", DataType::Uint}},
+        }}},
+        {NL80211_BSS_FREQUENCY_OFFSET, {"FREQUENCY_OFFSET"}},
+    }}},
+
+    {NL80211_ATTR_REG_INITIATOR, {"REG_INITIATOR"}},
+    {NL80211_ATTR_REG_TYPE, {"REG_TYPE"}},
+
+    {NL80211_ATTR_SUPPORTED_COMMANDS, {"SUPPORTED_COMMANDS", DataType::Nested,AttributeMap{
+        {std::nullopt, {"CMD", DataType::Uint}}, // enum nl80211_commands
+    }}},
+
+    {NL80211_ATTR_FRAME, {"FRAME"}},
+    {NL80211_ATTR_SSID, {"SSID"}},
+    {NL80211_ATTR_AUTH_TYPE, {"AUTH_TYPE"}},
+    {NL80211_ATTR_REASON_CODE, {"REASON_CODE"}},
+
+    {NL80211_ATTR_KEY_TYPE, {"KEY_TYPE"}},
+
+    {NL80211_ATTR_MAX_SCAN_IE_LEN, {"MAX_SCAN_IE_LEN", DataType::Uint}},
+    {NL80211_ATTR_CIPHER_SUITES, {"CIPHER_SUITES", DataType::Struct, arrayToStream<int32_t>}},
+
+    {NL80211_ATTR_FREQ_BEFORE, {"FREQ_BEFORE"}},
+    {NL80211_ATTR_FREQ_AFTER, {"FREQ_AFTER"}},
+
+    {NL80211_ATTR_FREQ_FIXED, {"FREQ_FIXED"}},
+
+    {NL80211_ATTR_WIPHY_RETRY_SHORT, {"WIPHY_RETRY_SHORT", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_RETRY_LONG, {"WIPHY_RETRY_LONG", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_FRAG_THRESHOLD, {"WIPHY_FRAG_THRESHOLD", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_RTS_THRESHOLD, {"WIPHY_RTS_THRESHOLD", DataType::Uint}},
+
+    {NL80211_ATTR_TIMED_OUT, {"TIMED_OUT"}},
+
+    {NL80211_ATTR_USE_MFP, {"USE_MFP"}},
+
+    {NL80211_ATTR_STA_FLAGS2, {"STA_FLAGS2"}},
+
+    {NL80211_ATTR_CONTROL_PORT, {"CONTROL_PORT"}},
+
+    {NL80211_ATTR_TESTDATA, {"TESTDATA"}},
+
+    {NL80211_ATTR_PRIVACY, {"PRIVACY"}},
+
+    {NL80211_ATTR_DISCONNECTED_BY_AP, {"DISCONNECTED_BY_AP"}},
+    {NL80211_ATTR_STATUS_CODE, {"STATUS_CODE"}},
+
+    {NL80211_ATTR_CIPHER_SUITES_PAIRWISE, {"CIPHER_SUITES_PAIRWISE"}},
+    {NL80211_ATTR_CIPHER_SUITE_GROUP, {"CIPHER_SUITE_GROUP"}},
+    {NL80211_ATTR_WPA_VERSIONS, {"WPA_VERSIONS"}},
+    {NL80211_ATTR_AKM_SUITES, {"AKM_SUITES"}},
+
+    {NL80211_ATTR_REQ_IE, {"REQ_IE"}},
+    {NL80211_ATTR_RESP_IE, {"RESP_IE"}},
+
+    {NL80211_ATTR_PREV_BSSID, {"PREV_BSSID"}},
+
+    {NL80211_ATTR_KEY, {"KEY"}},
+    {NL80211_ATTR_KEYS, {"KEYS"}},
+
+    {NL80211_ATTR_PID, {"PID"}},
+
+    {NL80211_ATTR_4ADDR, {"4ADDR"}},
+
+    {NL80211_ATTR_SURVEY_INFO, {"SURVEY_INFO"}},
+
+    {NL80211_ATTR_PMKID, {"PMKID"}},
+    {NL80211_ATTR_MAX_NUM_PMKIDS, {"MAX_NUM_PMKIDS", DataType::Uint}},
+
+    {NL80211_ATTR_DURATION, {"DURATION"}},
+
+    {NL80211_ATTR_COOKIE, {"COOKIE"}},
+
+    {NL80211_ATTR_WIPHY_COVERAGE_CLASS, {"WIPHY_COVERAGE_CLASS", DataType::Uint}},
+
+    {NL80211_ATTR_TX_RATES, {"TX_RATES"}},
+
+    {NL80211_ATTR_FRAME_MATCH, {"FRAME_MATCH"}},
+
+    {NL80211_ATTR_ACK, {"ACK"}},
+
+    {NL80211_ATTR_PS_STATE, {"PS_STATE"}},
+
+    {NL80211_ATTR_CQM, {"CQM"}},
+
+    {NL80211_ATTR_LOCAL_STATE_CHANGE, {"LOCAL_STATE_CHANGE"}},
+
+    {NL80211_ATTR_AP_ISOLATE, {"AP_ISOLATE"}},
+
+    {NL80211_ATTR_WIPHY_TX_POWER_SETTING, {"WIPHY_TX_POWER_SETTING"}},
+    {NL80211_ATTR_WIPHY_TX_POWER_LEVEL, {"WIPHY_TX_POWER_LEVEL"}},
+
+    {NL80211_ATTR_TX_FRAME_TYPES, {"TX_FRAME_TYPES", DataType::Nested, AttributeMap{
+        {std::nullopt, {"TFT", DataType::Nested, AttributeMap{
+            {NL80211_ATTR_FRAME_TYPE, {"FRAME_TYPE", DataType::Uint}},
+        }}},
+    }, Flags::Verbose}},
+    {NL80211_ATTR_RX_FRAME_TYPES, {"RX_FRAME_TYPES", DataType::Nested, AttributeMap{
+        {std::nullopt, {"RFT", DataType::Nested, AttributeMap{
+            {NL80211_ATTR_FRAME_TYPE, {"FRAME_TYPE", DataType::Uint}},
+        }}},
+    }, Flags::Verbose}},
+
+    {NL80211_ATTR_FRAME_TYPE, {"FRAME_TYPE", DataType::Uint}},
+
+    {NL80211_ATTR_CONTROL_PORT_ETHERTYPE, {"CONTROL_PORT_ETHERTYPE"}},
+    {NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, {"CONTROL_PORT_NO_ENCRYPT"}},
+
+    {NL80211_ATTR_SUPPORT_IBSS_RSN, {"SUPPORT_IBSS_RSN"}},
+
+    {NL80211_ATTR_WIPHY_ANTENNA_TX, {"WIPHY_ANTENNA_TX"}},
+    {NL80211_ATTR_WIPHY_ANTENNA_RX, {"WIPHY_ANTENNA_RX"}},
+
+    {NL80211_ATTR_MCAST_RATE, {"MCAST_RATE"}},
+
+    {NL80211_ATTR_OFFCHANNEL_TX_OK, {"OFFCHANNEL_TX_OK", DataType::Flag}},
+
+    {NL80211_ATTR_BSS_HT_OPMODE, {"BSS_HT_OPMODE"}},
+
+    {NL80211_ATTR_KEY_DEFAULT_TYPES, {"KEY_DEFAULT_TYPES"}},
+
+    {NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+            {"MAX_REMAIN_ON_CHANNEL_DURATION", DataType::Uint}},
+
+    {NL80211_ATTR_MESH_SETUP, {"MESH_SETUP"}},
+
+    {NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, {"WIPHY_ANTENNA_AVAIL_TX", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, {"WIPHY_ANTENNA_AVAIL_RX", DataType::Uint}},
+
+    {NL80211_ATTR_SUPPORT_MESH_AUTH, {"SUPPORT_MESH_AUTH"}},
+    {NL80211_ATTR_STA_PLINK_STATE, {"STA_PLINK_STATE"}},
+
+    {NL80211_ATTR_WOWLAN_TRIGGERS, {"WOWLAN_TRIGGERS"}},
+    {NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED,
+            {"WOWLAN_TRIGGERS_SUPPORTED", DataType::Nested, AttributeMap{
+        {NL80211_WOWLAN_TRIG_ANY, {"ANY", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_DISCONNECT, {"DISCONNECT", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_MAGIC_PKT, {"MAGIC_PKT", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_PKT_PATTERN,
+                {"PKT_PATTERN", DataType::Struct, nl80211_pattern_supportToStream}},
+        {NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, {"GTK_REKEY_SUPPORTED", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, {"GTK_REKEY_FAILURE", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, {"EAP_IDENT_REQUEST", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, {"4WAY_HANDSHAKE", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_RFKILL_RELEASE, {"RFKILL_RELEASE", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_TCP_CONNECTION, {"TCP_CONNECTION", DataType::Nested, AttributeMap{
+            {NL80211_WOWLAN_TCP_SRC_IPV4, {"SRC_IPV4"}},
+            {NL80211_WOWLAN_TCP_DST_IPV4, {"DST_IPV4"}},
+            {NL80211_WOWLAN_TCP_DST_MAC, {"DST_MAC"}},
+            {NL80211_WOWLAN_TCP_SRC_PORT, {"SRC_PORT", DataType::Uint}},
+            {NL80211_WOWLAN_TCP_DST_PORT, {"DST_PORT", DataType::Uint}},
+            {NL80211_WOWLAN_TCP_DATA_PAYLOAD, {"DATA_PAYLOAD"}},
+            {NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, {"DATA_PAYLOAD_SEQ"}},
+            {NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, {"DATA_PAYLOAD_TOKEN"}},
+            {NL80211_WOWLAN_TCP_DATA_INTERVAL, {"DATA_INTERVAL", DataType::Uint}},
+            {NL80211_WOWLAN_TCP_WAKE_PAYLOAD, {"WAKE_PAYLOAD"}},
+            {NL80211_WOWLAN_TCP_WAKE_MASK, {"WAKE_MASK"}},
+        }}},
+        {NL80211_WOWLAN_TRIG_NET_DETECT, {"NET_DETECT", DataType::Uint}},
+
+        /* Not in WOWLAN_TRIGGERS_SUPPORTED:
+         * - NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211
+         * - NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN
+         * - NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023
+         * - NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN
+         * - NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH
+         * - NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST
+         * - NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS
+         * - NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS
+         */
+    }}},
+
+    {NL80211_ATTR_SCHED_SCAN_INTERVAL, {"SCHED_SCAN_INTERVAL"}},
+
+    {NL80211_ATTR_INTERFACE_COMBINATIONS, {"INTERFACE_COMBINATIONS", DataType::Nested, AttributeMap{
+        {std::nullopt, {"IC", DataType::Nested, AttributeMap{
+            {NL80211_IFACE_COMB_UNSPEC, {"UNSPEC"}},
+            {NL80211_IFACE_COMB_LIMITS, {"LIMITS", DataType::Nested, AttributeMap{
+                {std::nullopt, {"LT", DataType::Nested, AttributeMap{
+                    {NL80211_IFACE_LIMIT_UNSPEC, {"UNSPEC"}},
+                    {NL80211_IFACE_LIMIT_MAX, {"MAX", DataType::Uint}},
+                    {NL80211_IFACE_LIMIT_TYPES, {"TYPES", DataType::Nested, iftypes}},
+                }}},
+            }}},
+            {NL80211_IFACE_COMB_MAXNUM, {"MAXNUM", DataType::Uint}},
+            {NL80211_IFACE_COMB_STA_AP_BI_MATCH, {"STA_AP_BI_MATCH", DataType::Flag}},
+            {NL80211_IFACE_COMB_NUM_CHANNELS, {"NUM_CHANNELS", DataType::Uint}},
+            {NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, {"RADAR_DETECT_WIDTHS", DataType::Uint}},
+            {NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, {"RADAR_DETECT_REGIONS", DataType::Uint}},
+            {NL80211_IFACE_COMB_BI_MIN_GCD, {"BI_MIN_GCD"}},
+        }}},
+    }, Flags::Verbose}},
+    {NL80211_ATTR_SOFTWARE_IFTYPES, {"SOFTWARE_IFTYPES", DataType::Nested, iftypes}},
+
+    {NL80211_ATTR_REKEY_DATA, {"REKEY_DATA"}},
+
+    {NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, {"MAX_NUM_SCHED_SCAN_SSIDS", DataType::Uint}},
+    {NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, {"MAX_SCHED_SCAN_IE_LEN", DataType::Uint}},
+
+    {NL80211_ATTR_SCAN_SUPP_RATES, {"SCAN_SUPP_RATES"}},
+
+    {NL80211_ATTR_HIDDEN_SSID, {"HIDDEN_SSID"}},
+
+    {NL80211_ATTR_IE_PROBE_RESP, {"IE_PROBE_RESP"}},
+    {NL80211_ATTR_IE_ASSOC_RESP, {"IE_ASSOC_RESP"}},
+
+    {NL80211_ATTR_STA_WME, {"STA_WME"}},
+    {NL80211_ATTR_SUPPORT_AP_UAPSD, {"SUPPORT_AP_UAPSD"}},
+
+    {NL80211_ATTR_ROAM_SUPPORT, {"ROAM_SUPPORT", DataType::Flag}},
+
+    {NL80211_ATTR_SCHED_SCAN_MATCH, {"SCHED_SCAN_MATCH"}},
+    {NL80211_ATTR_MAX_MATCH_SETS, {"MAX_MATCH_SETS", DataType::Uint}},
+
+    {NL80211_ATTR_PMKSA_CANDIDATE, {"PMKSA_CANDIDATE"}},
+
+    {NL80211_ATTR_TX_NO_CCK_RATE, {"TX_NO_CCK_RATE"}},
+
+    {NL80211_ATTR_TDLS_ACTION, {"TDLS_ACTION"}},
+    {NL80211_ATTR_TDLS_DIALOG_TOKEN, {"TDLS_DIALOG_TOKEN"}},
+    {NL80211_ATTR_TDLS_OPERATION, {"TDLS_OPERATION"}},
+    {NL80211_ATTR_TDLS_SUPPORT, {"TDLS_SUPPORT", DataType::Flag}},
+    {NL80211_ATTR_TDLS_EXTERNAL_SETUP, {"TDLS_EXTERNAL_SETUP", DataType::Flag}},
+
+    {NL80211_ATTR_DEVICE_AP_SME, {"DEVICE_AP_SME", DataType::Uint}},
+
+    {NL80211_ATTR_DONT_WAIT_FOR_ACK, {"DONT_WAIT_FOR_ACK"}},
+
+    {NL80211_ATTR_FEATURE_FLAGS, {"FEATURE_FLAGS", DataType::Uint}},
+
+    {NL80211_ATTR_PROBE_RESP_OFFLOAD, {"PROBE_RESP_OFFLOAD", DataType::Uint}},
+
+    {NL80211_ATTR_PROBE_RESP, {"PROBE_RESP"}},
+
+    {NL80211_ATTR_DFS_REGION, {"DFS_REGION"}},
+
+    {NL80211_ATTR_DISABLE_HT, {"DISABLE_HT"}},
+    {NL80211_ATTR_HT_CAPABILITY_MASK, {"HT_CAPABILITY_MASK"}},
+
+    {NL80211_ATTR_NOACK_MAP, {"NOACK_MAP"}},
+
+    {NL80211_ATTR_INACTIVITY_TIMEOUT, {"INACTIVITY_TIMEOUT"}},
+
+    {NL80211_ATTR_RX_SIGNAL_DBM, {"RX_SIGNAL_DBM"}},
+
+    {NL80211_ATTR_BG_SCAN_PERIOD, {"BG_SCAN_PERIOD"}},
+
+    {NL80211_ATTR_WDEV, {"WDEV", DataType::Uint}},
+
+    {NL80211_ATTR_USER_REG_HINT_TYPE, {"USER_REG_HINT_TYPE"}},
+
+    {NL80211_ATTR_CONN_FAILED_REASON, {"CONN_FAILED_REASON"}},
+
+    {NL80211_ATTR_AUTH_DATA, {"AUTH_DATA"}},
+
+    {NL80211_ATTR_VHT_CAPABILITY, {"VHT_CAPABILITY"}},
+
+    {NL80211_ATTR_SCAN_FLAGS, {"SCAN_FLAGS", DataType::Uint}},
+
+    {NL80211_ATTR_CHANNEL_WIDTH, {"CHANNEL_WIDTH"}},
+    {NL80211_ATTR_CENTER_FREQ1, {"CENTER_FREQ1"}},
+    {NL80211_ATTR_CENTER_FREQ2, {"CENTER_FREQ2"}},
+
+    {NL80211_ATTR_P2P_CTWINDOW, {"P2P_CTWINDOW"}},
+    {NL80211_ATTR_P2P_OPPPS, {"P2P_OPPPS"}},
+
+    {NL80211_ATTR_LOCAL_MESH_POWER_MODE, {"LOCAL_MESH_POWER_MODE"}},
+
+    {NL80211_ATTR_ACL_POLICY, {"ACL_POLICY"}},
+
+    {NL80211_ATTR_MAC_ADDRS, {"MAC_ADDRS"}},
+
+    {NL80211_ATTR_MAC_ACL_MAX, {"MAC_ACL_MAX", DataType::Uint}},
+
+    {NL80211_ATTR_RADAR_EVENT, {"RADAR_EVENT"}},
+
+    {NL80211_ATTR_EXT_CAPA, {"EXT_CAPA"}},
+    {NL80211_ATTR_EXT_CAPA_MASK, {"EXT_CAPA_MASK"}},
+
+    {NL80211_ATTR_STA_CAPABILITY, {"STA_CAPABILITY"}},
+    {NL80211_ATTR_STA_EXT_CAPABILITY, {"STA_EXT_CAPABILITY"}},
+
+    {NL80211_ATTR_PROTOCOL_FEATURES, {"PROTOCOL_FEATURES", DataType::Uint}},
+    {NL80211_ATTR_SPLIT_WIPHY_DUMP, {"SPLIT_WIPHY_DUMP", DataType::Flag}},
+
+    {NL80211_ATTR_DISABLE_VHT, {"DISABLE_VHT", DataType::Flag}},
+    {NL80211_ATTR_VHT_CAPABILITY_MASK, {"VHT_CAPABILITY_MASK"}},
+
+    {NL80211_ATTR_MDID, {"MDID"}},
+    {NL80211_ATTR_IE_RIC, {"IE_RIC"}},
+
+    {NL80211_ATTR_CRIT_PROT_ID, {"CRIT_PROT_ID"}},
+    {NL80211_ATTR_MAX_CRIT_PROT_DURATION, {"MAX_CRIT_PROT_DURATION"}},
+
+    {NL80211_ATTR_PEER_AID, {"PEER_AID"}},
+
+    {NL80211_ATTR_COALESCE_RULE, {"COALESCE_RULE"}},
+
+    {NL80211_ATTR_CH_SWITCH_COUNT, {"CH_SWITCH_COUNT"}},
+    {NL80211_ATTR_CH_SWITCH_BLOCK_TX, {"CH_SWITCH_BLOCK_TX"}},
+    {NL80211_ATTR_CSA_IES, {"CSA_IES"}},
+    {NL80211_ATTR_CNTDWN_OFFS_BEACON, {"CNTDWN_OFFS_BEACON"}},
+    {NL80211_ATTR_CNTDWN_OFFS_PRESP, {"CNTDWN_OFFS_PRESP"}},
+
+    {NL80211_ATTR_RXMGMT_FLAGS, {"RXMGMT_FLAGS"}},
+
+    {NL80211_ATTR_STA_SUPPORTED_CHANNELS, {"STA_SUPPORTED_CHANNELS"}},
+
+    {NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, {"STA_SUPPORTED_OPER_CLASSES"}},
+
+    {NL80211_ATTR_HANDLE_DFS, {"HANDLE_DFS"}},
+
+    {NL80211_ATTR_SUPPORT_5_MHZ, {"SUPPORT_5_MHZ"}},
+    {NL80211_ATTR_SUPPORT_10_MHZ, {"SUPPORT_10_MHZ"}},
+
+    {NL80211_ATTR_OPMODE_NOTIF, {"OPMODE_NOTIF"}},
+
+    {NL80211_ATTR_VENDOR_ID, {"VENDOR_ID"}},
+    {NL80211_ATTR_VENDOR_SUBCMD, {"VENDOR_SUBCMD"}},
+    {NL80211_ATTR_VENDOR_DATA, {"VENDOR_DATA", DataType::Raw, AttributeMap{}, Flags::Verbose}},
+    {NL80211_ATTR_VENDOR_EVENTS, {"VENDOR_EVENTS", DataType::Nested, AttributeMap{},
+            Flags::Verbose}},
+
+    {NL80211_ATTR_QOS_MAP, {"QOS_MAP"}},
+
+    {NL80211_ATTR_MAC_HINT, {"MAC_HINT"}},
+    {NL80211_ATTR_WIPHY_FREQ_HINT, {"WIPHY_FREQ_HINT"}},
+
+    {NL80211_ATTR_MAX_AP_ASSOC_STA, {"MAX_AP_ASSOC_STA"}},
+
+    {NL80211_ATTR_TDLS_PEER_CAPABILITY, {"TDLS_PEER_CAPABILITY"}},
+
+    {NL80211_ATTR_SOCKET_OWNER, {"SOCKET_OWNER"}},
+
+    {NL80211_ATTR_CSA_C_OFFSETS_TX, {"CSA_C_OFFSETS_TX"}},
+    {NL80211_ATTR_MAX_CSA_COUNTERS, {"MAX_CSA_COUNTERS"}},
+
+    {NL80211_ATTR_TDLS_INITIATOR, {"TDLS_INITIATOR"}},
+
+    {NL80211_ATTR_USE_RRM, {"USE_RRM"}},
+
+    {NL80211_ATTR_WIPHY_DYN_ACK, {"WIPHY_DYN_ACK"}},
+
+    {NL80211_ATTR_TSID, {"TSID"}},
+    {NL80211_ATTR_USER_PRIO, {"USER_PRIO"}},
+    {NL80211_ATTR_ADMITTED_TIME, {"ADMITTED_TIME"}},
+
+    {NL80211_ATTR_SMPS_MODE, {"SMPS_MODE"}},
+
+    {NL80211_ATTR_OPER_CLASS, {"OPER_CLASS"}},
+
+    {NL80211_ATTR_MAC_MASK, {"MAC_MASK"}},
+
+    {NL80211_ATTR_WIPHY_SELF_MANAGED_REG, {"WIPHY_SELF_MANAGED_REG"}},
+
+    {NL80211_ATTR_EXT_FEATURES, {"EXT_FEATURES"}},
+
+    {NL80211_ATTR_SURVEY_RADIO_STATS, {"SURVEY_RADIO_STATS"}},
+
+    {NL80211_ATTR_NETNS_FD, {"NETNS_FD"}},
+
+    {NL80211_ATTR_SCHED_SCAN_DELAY, {"SCHED_SCAN_DELAY"}},
+
+    {NL80211_ATTR_REG_INDOOR, {"REG_INDOOR"}},
+
+    {NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS, {"MAX_NUM_SCHED_SCAN_PLANS", DataType::Uint}},
+    {NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL, {"MAX_SCAN_PLAN_INTERVAL", DataType::Uint}},
+    {NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS, {"MAX_SCAN_PLAN_ITERATIONS", DataType::Uint}},
+    {NL80211_ATTR_SCHED_SCAN_PLANS, {"SCHED_SCAN_PLANS"}},
+
+    {NL80211_ATTR_PBSS, {"PBSS"}},
+
+    {NL80211_ATTR_BSS_SELECT, {"BSS_SELECT"}},
+
+    {NL80211_ATTR_STA_SUPPORT_P2P_PS, {"STA_SUPPORT_P2P_PS"}},
+
+    {NL80211_ATTR_PAD, {"PAD"}},
+
+    {NL80211_ATTR_IFTYPE_EXT_CAPA, {"IFTYPE_EXT_CAPA"}},
+
+    {NL80211_ATTR_MU_MIMO_GROUP_DATA, {"MU_MIMO_GROUP_DATA"}},
+    {NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, {"MU_MIMO_FOLLOW_MAC_ADDR"}},
+
+    {NL80211_ATTR_SCAN_START_TIME_TSF, {"SCAN_START_TIME_TSF"}},
+    {NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, {"SCAN_START_TIME_TSF_BSSID"}},
+    {NL80211_ATTR_MEASUREMENT_DURATION, {"MEASUREMENT_DURATION"}},
+    {NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY, {"MEASUREMENT_DURATION_MANDATORY"}},
+
+    {NL80211_ATTR_MESH_PEER_AID, {"MESH_PEER_AID"}},
+
+    {NL80211_ATTR_NAN_MASTER_PREF, {"NAN_MASTER_PREF"}},
+    {NL80211_ATTR_BANDS, {"BANDS"}},
+    {NL80211_ATTR_NAN_FUNC, {"NAN_FUNC"}},
+    {NL80211_ATTR_NAN_MATCH, {"NAN_MATCH"}},
+
+    {NL80211_ATTR_FILS_KEK, {"FILS_KEK"}},
+    {NL80211_ATTR_FILS_NONCES, {"FILS_NONCES"}},
+
+    {NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED, {"MULTICAST_TO_UNICAST_ENABLED"}},
+
+    {NL80211_ATTR_BSSID, {"BSSID"}},
+
+    {NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, {"SCHED_SCAN_RELATIVE_RSSI"}},
+    {NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, {"SCHED_SCAN_RSSI_ADJUST"}},
+
+    {NL80211_ATTR_TIMEOUT_REASON, {"TIMEOUT_REASON"}},
+
+    {NL80211_ATTR_FILS_ERP_USERNAME, {"FILS_ERP_USERNAME"}},
+    {NL80211_ATTR_FILS_ERP_REALM, {"FILS_ERP_REALM"}},
+    {NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, {"FILS_ERP_NEXT_SEQ_NUM"}},
+    {NL80211_ATTR_FILS_ERP_RRK, {"FILS_ERP_RRK"}},
+    {NL80211_ATTR_FILS_CACHE_ID, {"FILS_CACHE_ID"}},
+
+    {NL80211_ATTR_PMK, {"PMK"}},
+
+    {NL80211_ATTR_SCHED_SCAN_MULTI, {"SCHED_SCAN_MULTI"}},
+    {NL80211_ATTR_SCHED_SCAN_MAX_REQS, {"SCHED_SCAN_MAX_REQS"}},
+
+    {NL80211_ATTR_WANT_1X_4WAY_HS, {"WANT_1X_4WAY_HS"}},
+    {NL80211_ATTR_PMKR0_NAME, {"PMKR0_NAME"}},
+    {NL80211_ATTR_PORT_AUTHORIZED, {"PORT_AUTHORIZED"}},
+
+    {NL80211_ATTR_EXTERNAL_AUTH_ACTION, {"EXTERNAL_AUTH_ACTION"}},
+    {NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, {"EXTERNAL_AUTH_SUPPORT"}},
+
+    {NL80211_ATTR_NSS, {"NSS"}},
+    {NL80211_ATTR_ACK_SIGNAL, {"ACK_SIGNAL"}},
+
+    {NL80211_ATTR_CONTROL_PORT_OVER_NL80211, {"CONTROL_PORT_OVER_NL80211"}},
+
+    {NL80211_ATTR_TXQ_STATS, {"TXQ_STATS"}},
+    {NL80211_ATTR_TXQ_LIMIT, {"TXQ_LIMIT"}},
+    {NL80211_ATTR_TXQ_MEMORY_LIMIT, {"TXQ_MEMORY_LIMIT"}},
+    {NL80211_ATTR_TXQ_QUANTUM, {"TXQ_QUANTUM"}},
+
+    {NL80211_ATTR_HE_CAPABILITY, {"HE_CAPABILITY"}},
+
+    {NL80211_ATTR_FTM_RESPONDER, {"FTM_RESPONDER"}},
+
+    {NL80211_ATTR_FTM_RESPONDER_STATS, {"FTM_RESPONDER_STATS"}},
+
+    {NL80211_ATTR_TIMEOUT, {"TIMEOUT"}},
+
+    {NL80211_ATTR_PEER_MEASUREMENTS, {"PEER_MEASUREMENTS"}},
+
+    {NL80211_ATTR_AIRTIME_WEIGHT, {"AIRTIME_WEIGHT"}},
+    {NL80211_ATTR_STA_TX_POWER_SETTING, {"STA_TX_POWER_SETTING"}},
+    {NL80211_ATTR_STA_TX_POWER, {"STA_TX_POWER"}},
+
+    {NL80211_ATTR_SAE_PASSWORD, {"SAE_PASSWORD"}},
+
+    {NL80211_ATTR_TWT_RESPONDER, {"TWT_RESPONDER"}},
+
+    {NL80211_ATTR_HE_OBSS_PD, {"HE_OBSS_PD"}},
+
+    {NL80211_ATTR_WIPHY_EDMG_CHANNELS, {"WIPHY_EDMG_CHANNELS"}},
+    {NL80211_ATTR_WIPHY_EDMG_BW_CONFIG, {"WIPHY_EDMG_BW_CONFIG"}},
+
+    {NL80211_ATTR_VLAN_ID, {"VLAN_ID"}},
+
+    {NL80211_ATTR_HE_BSS_COLOR, {"HE_BSS_COLOR"}},
+
+    {NL80211_ATTR_IFTYPE_AKM_SUITES, {"IFTYPE_AKM_SUITES"}},
+
+    {NL80211_ATTR_TID_CONFIG, {"TID_CONFIG"}},
+
+    {NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, {"CONTROL_PORT_NO_PREAUTH"}},
+
+    {NL80211_ATTR_PMK_LIFETIME, {"PMK_LIFETIME"}},
+    {NL80211_ATTR_PMK_REAUTH_THRESHOLD, {"PMK_REAUTH_THRESHOLD"}},
+
+    {NL80211_ATTR_RECEIVE_MULTICAST, {"RECEIVE_MULTICAST"}},
+    {NL80211_ATTR_WIPHY_FREQ_OFFSET, {"WIPHY_FREQ_OFFSET"}},
+    {NL80211_ATTR_CENTER_FREQ1_OFFSET, {"CENTER_FREQ1_OFFSET"}},
+    {NL80211_ATTR_SCAN_FREQ_KHZ, {"SCAN_FREQ_KHZ"}},
+
+    {NL80211_ATTR_HE_6GHZ_CAPABILITY, {"HE_6GHZ_CAPABILITY"}},
+
+    {NL80211_ATTR_FILS_DISCOVERY, {"FILS_DISCOVERY"}},
+
+    {NL80211_ATTR_UNSOL_BCAST_PROBE_RESP, {"UNSOL_BCAST_PROBE_RESP"}},
+
+    {NL80211_ATTR_S1G_CAPABILITY, {"S1G_CAPABILITY"}},
+    {NL80211_ATTR_S1G_CAPABILITY_MASK, {"S1G_CAPABILITY_MASK"}},
+}) {}
+// clang-format on
+
+static void informationElementsToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
+    struct IEHeader {
+        uint8_t elementId;
+        uint8_t length;
+    } __attribute__((packed));
+    static_assert(sizeof(IEHeader) == 2);
+
+    const auto alldata = attr.data<uint8_t>();
+    const auto bytes = alldata.getRaw();
+
+    ss << '{';
+
+    if constexpr (kCompactIE) {
+        ss << "len=" << bytes.len() << ", ";
+        ss << "crc=" << std::hex << std::setw(4) << crc16(alldata) << std::dec << ", ";
+    }
+
+    bool first = true;
+    auto printComma = [&first, &ss]() {
+        // put separator at every but first entry
+        if (!first) ss << ", ";
+        first = false;
+    };
+
+    for (size_t offset = 0; offset < bytes.len();) {
+        const auto ptr = bytes.ptr() + offset;
+        const auto remainingLen = bytes.len() - offset;
+
+        // can we fit one more header?
+        if (sizeof(IEHeader) > remainingLen) break;
+        IEHeader ieHeader;
+        memcpy(&ieHeader, ptr, sizeof(IEHeader));
+        if (sizeof(IEHeader) + ieHeader.length > remainingLen) {
+            printComma();
+            ss << "ERR";
+            break;
+        }
+        offset += sizeof(IEHeader) + ieHeader.length;
+
+        const Buffer<uint8_t> data(ptr + sizeof(IEHeader), ieHeader.length);
+
+        if (ieHeader.elementId == WLAN_EID_SSID) {
+            printComma();
+
+            const auto str = data.getRaw();
+            const std::string ssid(reinterpret_cast<const char*>(str.ptr()), str.len());
+            ss << "SSID=\"" << printableOnly(ssid) << '"';
+
+            continue;
+        }
+
+        if constexpr (kCompactIE) continue;
+
+        // print entry ID:LENGTH/CRC16
+        printComma();
+        ss << (int)ieHeader.elementId << ':' << (int)ieHeader.length << '/';
+        ss << std::hex << std::setw(4) << crc16(data) << std::dec;
+    }
+    ss << '}';
+}
+
+static void nl80211_pattern_supportToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
+    const auto& [ok, data] = attr.data<nl80211_pattern_support>().getFirst();
+    if (!ok) {
+        ss << "invalid structure";
+        return;
+    }
+    ss << '{'                          //
+       << data.max_patterns << ','     //
+       << data.min_pattern_len << ','  //
+       << data.max_pattern_len << ','  //
+       << data.max_pkt_offset << '}';
+}
+
+}  // namespace android::nl::protocols::generic::families
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.h b/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.h
new file mode 100644
index 0000000..8a9608c
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.h
@@ -0,0 +1,28 @@
+/*
+ * 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 "../GenericMessageBase.h"
+
+namespace android::nl::protocols::generic::families {
+
+class Nl80211 : public GenericMessageBase {
+  public:
+    Nl80211(nlmsgtype_t familyId);
+};
+
+}  // namespace android::nl::protocols::generic::families
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 7db487a..9cc05da 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
@@ -16,6 +16,7 @@
 
 #include "Link.h"
 
+#include "../structs.h"
 #include "structs.h"
 
 #include <net/if.h>
@@ -26,9 +27,9 @@
 
 // clang-format off
 Link::Link() : MessageDefinition<ifinfomsg>("link", {
-    {RTM_NEWLINK, {"NEWLINK", MessageGenre::NEW}},
-    {RTM_DELLINK, {"DELLINK", MessageGenre::DELETE}},
-    {RTM_GETLINK, {"GETLINK", MessageGenre::GET}},
+    {RTM_NEWLINK, {"NEWLINK", MessageGenre::New}},
+    {RTM_DELLINK, {"DELLINK", MessageGenre::Delete}},
+    {RTM_GETLINK, {"GETLINK", MessageGenre::Get}},
 }, {
     {IFLA_ADDRESS, {"ADDRESS"}},
     {IFLA_BROADCAST, {"BROADCAST"}},
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 b9d622a..fea2ce1 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/structs.h
+++ b/automotive/can/1.0/default/libnl++/protocols/route/structs.h
@@ -30,16 +30,6 @@
 // ifla_cacheinfo
 void ifla_cacheinfoToStream(std::stringstream& ss, const Buffer<nlattr> attr);
 
-template <typename T>
-void arrayToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
-    ss << '{';
-    for (const auto it : attr.data<T>().getRaw()) {
-        ss << it << ',';
-    }
-    ss.seekp(-1, std::ios_base::cur);
-    ss << '}';
-}
-
 // rtnl_link_stats or rtnl_link_stats64
 template <typename T>
 void statsToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
diff --git a/automotive/can/1.0/default/libnl++/protocols/structs.h b/automotive/can/1.0/default/libnl++/protocols/structs.h
new file mode 100644
index 0000000..44c17b8
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/structs.h
@@ -0,0 +1,33 @@
+/*
+ * 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 <sstream>
+
+namespace android::nl::protocols {
+
+template <typename T>
+void arrayToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
+    ss << '{';
+    for (const auto it : attr.data<T>().getRaw()) {
+        ss << it << ',';
+    }
+    ss.seekp(-1, std::ios_base::cur);
+    ss << '}';
+}
+
+}  // namespace android::nl::protocols
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 8d1693a..bbb48e1 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -88,6 +88,7 @@
     whole_static_libs: [
         "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
         "android.hardware.automotive.vehicle@2.0-manager-lib",
+        "libqemu_pipe",
     ],
     shared_libs: [
         "libbase",
@@ -95,7 +96,6 @@
         "libprotobuf-cpp-lite",
     ],
     static_libs: [
-        "libqemu_pipe",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
     ],
 }
@@ -210,6 +210,5 @@
         "android.hardware.automotive.vehicle@2.0-manager-lib",
         "android.hardware.automotive.vehicle@2.0-default-impl-lib",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
-        "libqemu_pipe",
     ],
 }
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 5ecce46..8ef2b60 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -308,6 +308,19 @@
 
         {.config =
                  {
+                         .prop = toInt(VehicleProperty::SEAT_OCCUPANCY),
+                         .access = VehiclePropertyAccess::READ,
+                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                         .areaConfigs = {VehicleAreaConfig{.areaId = (SEAT_1_LEFT)},
+                                         VehicleAreaConfig{.areaId = (SEAT_1_RIGHT)}},
+                 },
+         .initialAreaValues = {{SEAT_1_LEFT,
+                                {.int32Values = {(int)VehicleSeatOccupancyState::VACANT}}},
+                               {SEAT_1_RIGHT,
+                                {.int32Values = {(int)VehicleSeatOccupancyState::VACANT}}}}},
+
+        {.config =
+                 {
                          .prop = toInt(VehicleProperty::INFO_DRIVER_SEAT),
                          .access = VehiclePropertyAccess::READ,
                          .changeMode = VehiclePropertyChangeMode::STATIC,
diff --git a/biometrics/face/1.1/default/BiometricsFace.cpp b/biometrics/face/1.1/default/BiometricsFace.cpp
index 2143880..57b3a92 100644
--- a/biometrics/face/1.1/default/BiometricsFace.cpp
+++ b/biometrics/face/1.1/default/BiometricsFace.cpp
@@ -24,8 +24,8 @@
 constexpr uint64_t kDeviceId = 123;
 // Arbitrary value.
 constexpr uint64_t kAuthenticatorId = 987;
-// Arbitrary value.
-constexpr uint64_t kLockoutDuration = 555;
+// Not locked out.
+constexpr uint64_t kLockoutDuration = 0;
 
 BiometricsFace::BiometricsFace() : mRandom(std::mt19937::default_seed) {}
 
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl
index 88c066c..a05fad9 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl
@@ -42,4 +42,8 @@
   SENSOR_DIRTY = 21,
   VENDOR = 22,
   FIRST_FRAME_RECEIVED = 23,
+  DARK_GLASSES_DETECTED = 24,
+  FACE_COVERING_DETECTED = 25,
+  EYES_NOT_VISIBLE = 26,
+  MOUTH_NOT_VISIBLE = 27,
 }
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl
index 5897cdc..56a600f 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl
@@ -218,6 +218,30 @@
     /**
      * The first frame from the camera has been received.
      */
-    FIRST_FRAME_RECEIVED = 23
+    FIRST_FRAME_RECEIVED = 23,
+
+    /**
+     * Dark glasses detected. This can be useful for providing relevant feedback to the user and
+     * enabling an alternative authentication logic if the implementation supports it.
+     */
+    DARK_GLASSES_DETECTED = 24,
+
+    /**
+     * A face mask or face covering detected. This can be useful for providing relevant feedback to
+     * the user and enabling an alternative authentication logic if the implementation supports it.
+     */
+    FACE_COVERING_DETECTED = 25,
+
+    /**
+     * Either one or both eyes are not visible in the frame. Prefer to use DARK_GLASSES_DETECTED if
+     * the eyes are not visible due to dark glasses.
+     */
+    EYES_NOT_VISIBLE = 26,
+
+    /**
+     * The mouth is not visible in the frame. Prefer to use MASK_DETECTED if the mouth is not
+     * visible due to a mask.
+     */
+    MOUTH_NOT_VISIBLE = 27,
 }
 
diff --git a/biometrics/fingerprint/2.1/default/README.md b/biometrics/fingerprint/2.1/default/README.md
new file mode 100644
index 0000000..c41664e
--- /dev/null
+++ b/biometrics/fingerprint/2.1/default/README.md
@@ -0,0 +1,6 @@
+## Default IBiometricsFingerprint@2.1 HAL ##
+---
+
+## Overview: ##
+
+Provides a default implementation that loads pre-HIDL HALs and exposes it to the framework.
\ No newline at end of file
diff --git a/biometrics/fingerprint/2.2/default/Android.bp b/biometrics/fingerprint/2.2/default/Android.bp
new file mode 100644
index 0000000..8931308
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/Android.bp
@@ -0,0 +1,22 @@
+cc_binary {
+    name: "android.hardware.biometrics.fingerprint@2.2-service.example",
+    defaults: ["hidl_defaults"],
+    init_rc: ["android.hardware.biometrics.fingerprint@2.2-service.rc"],
+    vintf_fragments: ["android.hardware.biometrics.fingerprint@2.2-service.xml"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "BiometricsFingerprint.cpp",
+        "service.cpp",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libhidlbase",
+        "libhardware",
+        "libutils",
+        "android.hardware.biometrics.fingerprint@2.2",
+    ],
+
+}
diff --git a/biometrics/fingerprint/2.2/default/BiometricsFingerprint.cpp b/biometrics/fingerprint/2.2/default/BiometricsFingerprint.cpp
new file mode 100644
index 0000000..b07a17c
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/BiometricsFingerprint.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 "android.hardware.biometrics.fingerprint@2.2-service"
+#define LOG_VERBOSE "android.hardware.biometrics.fingerprint@2.2-service"
+
+#include <hardware/hw_auth_token.h>
+
+#include <android/log.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
+#include "BiometricsFingerprint.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+namespace android {
+namespace hardware {
+namespace biometrics {
+namespace fingerprint {
+namespace V2_2 {
+namespace implementation {
+
+using RequestStatus = android::hardware::biometrics::fingerprint::V2_1::RequestStatus;
+using FingerprintError = android::hardware::biometrics::fingerprint::V2_1::FingerprintError;
+
+constexpr uint64_t kDeviceId = 1;
+
+BiometricsFingerprint::BiometricsFingerprint() {
+
+}
+
+BiometricsFingerprint::~BiometricsFingerprint() {
+
+}
+
+Return<uint64_t> BiometricsFingerprint::setNotify(
+        const sp<IBiometricsFingerprintClientCallback>& clientCallback) {
+    mClientCallback = clientCallback;
+    return kDeviceId;
+}
+
+Return<uint64_t> BiometricsFingerprint::preEnroll()  {
+    // On a real implementation, this must be generated and stored in the TEE or its equivalent.
+    return rand();
+}
+
+Return<RequestStatus> BiometricsFingerprint::enroll(const hidl_array<uint8_t, 69>&  /* hat */,
+        uint32_t /* gid */, uint32_t /* timeoutSec */) {
+    // On a real implementation, the HAT must be checked in the TEE or its equivalent.
+    mClientCallback->onError(kDeviceId, FingerprintError::ERROR_UNABLE_TO_PROCESS,
+            0 /* vendorCode */);
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::postEnroll() {
+    return RequestStatus::SYS_OK;
+}
+
+Return<uint64_t> BiometricsFingerprint::getAuthenticatorId() {
+    return 1;
+}
+
+Return<RequestStatus> BiometricsFingerprint::cancel() {
+    mClientCallback->onError(kDeviceId, FingerprintError::ERROR_CANCELED, 0 /* vendorCode */);
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::enumerate()  {
+    mClientCallback->onEnumerate(kDeviceId, 0 /* fingerId */, 0 /* groupId */,
+            0 /* remaining */);
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::remove(uint32_t gid, uint32_t fid) {
+    mClientCallback->onRemoved(kDeviceId, fid, gid, 0 /* remaining */);
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::setActiveGroup(uint32_t /* gid */,
+        const hidl_string& storePath) {
+    // Return invalid for paths that the HAL is unable to write to.
+    std::string path = storePath.c_str();
+    if (path.compare("") == 0 || path.compare("/") == 0) {
+        return RequestStatus::SYS_EINVAL;
+    }
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::authenticate(uint64_t /* operationId */,
+        uint32_t /* gid */) {
+    return RequestStatus::SYS_OK;
+}
+
+} // namespace implementation
+}  // namespace V2_2
+}  // namespace fingerprint
+}  // namespace biometrics
+}  // namespace hardware
+}  // namespace android
diff --git a/biometrics/fingerprint/2.2/default/BiometricsFingerprint.h b/biometrics/fingerprint/2.2/default/BiometricsFingerprint.h
new file mode 100644
index 0000000..a6861b3
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/BiometricsFingerprint.h
@@ -0,0 +1,73 @@
+/*
+ * 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_BIOMETRICS_FINGERPRINT_V2_2_BIOMETRICSFINGERPRINT_H
+#define ANDROID_HARDWARE_BIOMETRICS_FINGERPRINT_V2_2_BIOMETRICSFINGERPRINT_H
+
+#include <log/log.h>
+#include <android/log.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <android/hardware/biometrics/fingerprint/2.2/IBiometricsFingerprint.h>
+
+namespace android {
+namespace hardware {
+namespace biometrics {
+namespace fingerprint {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint;
+using ::android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprintClientCallback;
+using ::android::hardware::biometrics::fingerprint::V2_1::RequestStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+
+struct BiometricsFingerprint : public IBiometricsFingerprint {
+public:
+    BiometricsFingerprint();
+    ~BiometricsFingerprint();
+
+    // Methods from ::android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint follow.
+    Return<uint64_t> setNotify(const sp<IBiometricsFingerprintClientCallback>& clientCallback) override;
+    Return<uint64_t> preEnroll() override;
+    Return<RequestStatus> enroll(const hidl_array<uint8_t, 69>& hat, uint32_t gid, uint32_t timeoutSec) override;
+    Return<RequestStatus> postEnroll() override;
+    Return<uint64_t> getAuthenticatorId() override;
+    Return<RequestStatus> cancel() override;
+    Return<RequestStatus> enumerate() override;
+    Return<RequestStatus> remove(uint32_t gid, uint32_t fid) override;
+    Return<RequestStatus> setActiveGroup(uint32_t gid, const hidl_string& storePath) override;
+    Return<RequestStatus> authenticate(uint64_t operationId, uint32_t gid) override;
+
+private:
+    sp<IBiometricsFingerprintClientCallback> mClientCallback;
+
+};
+
+}  // namespace implementation
+}  // namespace V2_2
+}  // namespace fingerprint
+}  // namespace biometrics
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_BIOMETRICS_FINGERPRINT_V2_2_BIOMETRICSFINGERPRINT_H
diff --git a/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.rc b/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.rc
new file mode 100644
index 0000000..1b406b0
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.rc
@@ -0,0 +1,4 @@
+service vendor.fps_hal /vendor/bin/hw/android.hardware.biometrics.fingerprint@2.2-service.example
+    class hal
+    user nobody
+    group nobody
diff --git a/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.xml b/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.xml
new file mode 100644
index 0000000..5e69a1e
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.biometrics.fingerprint</name>
+        <transport>hwbinder</transport>
+        <version>2.2</version>
+        <interface>
+            <name>IBiometricsFingerprint</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/biometrics/fingerprint/2.2/default/service.cpp b/biometrics/fingerprint/2.2/default/service.cpp
new file mode 100644
index 0000000..5bc69a0
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/service.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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 "android.hardware.biometrics.fingerprint@2.2-service"
+
+#include <android/log.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <android/hardware/biometrics/fingerprint/2.2/IBiometricsFingerprint.h>
+#include <android/hardware/biometrics/fingerprint/2.2/types.h>
+#include "BiometricsFingerprint.h"
+
+using android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint;
+using android::hardware::biometrics::fingerprint::V2_2::implementation::BiometricsFingerprint;
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::sp;
+
+int main() {
+    android::sp<IBiometricsFingerprint> bio = new BiometricsFingerprint();
+
+    configureRpcThreadpool(1, true /*callerWillJoin*/);
+
+    if (::android::OK != bio->registerAsService()) {
+        return 1;
+    }
+
+    joinRpcThreadpool();
+
+    return 0; // should never get here
+}
diff --git a/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h b/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
index 5468658..ac17d6d 100644
--- a/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
+++ b/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
@@ -32,6 +32,7 @@
   unsigned int GetNumberSlots();
   unsigned int GetCurrentSlot();
   bool MarkBootSuccessful();
+  unsigned int GetActiveBootSlot();
   bool SetActiveBootSlot(unsigned int slot);
   bool SetSlotAsUnbootable(unsigned int slot);
   bool SetSlotBootable(unsigned int slot);
diff --git a/boot/1.1/default/boot_control/libboot_control.cpp b/boot/1.1/default/boot_control/libboot_control.cpp
index 2c6ccaf..9387c32 100644
--- a/boot/1.1/default/boot_control/libboot_control.cpp
+++ b/boot/1.1/default/boot_control/libboot_control.cpp
@@ -261,6 +261,24 @@
   return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
 }
 
+unsigned int BootControl::GetActiveBootSlot() {
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
+
+  // Use the current slot by default.
+  unsigned int active_boot_slot = current_slot_;
+  unsigned int max_priority = bootctrl.slot_info[current_slot_].priority;
+  // Find the slot with the highest priority.
+  for (unsigned int i = 0; i < num_slots_; ++i) {
+    if (bootctrl.slot_info[i].priority > max_priority) {
+      max_priority = bootctrl.slot_info[i].priority;
+      active_boot_slot = i;
+    }
+  }
+
+  return active_boot_slot;
+}
+
 bool BootControl::SetActiveBootSlot(unsigned int slot) {
   if (slot >= kMaxNumSlots || slot >= num_slots_) {
     // Invalid slot number.
diff --git a/boot/1.2/Android.bp b/boot/1.2/Android.bp
new file mode 100644
index 0000000..e51c5cd
--- /dev/null
+++ b/boot/1.2/Android.bp
@@ -0,0 +1,15 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.boot@1.2",
+    root: "android.hardware",
+    srcs: [
+        "IBootControl.hal",
+    ],
+    interfaces: [
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/boot/1.2/IBootControl.hal b/boot/1.2/IBootControl.hal
new file mode 100644
index 0000000..bb0ad13
--- /dev/null
+++ b/boot/1.2/IBootControl.hal
@@ -0,0 +1,35 @@
+/*
+ * 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.boot@1.2;
+
+import @1.0::IBootControl;
+import @1.0::Slot;
+import @1.1::IBootControl;
+
+interface IBootControl extends @1.1::IBootControl {
+
+  /**
+   * Returns the active slot to boot into on the next boot. If
+   * setActiveBootSlot() has been called, the getter function should return the
+   * same slot as the one provided in the last setActiveBootSlot() call.
+   * The returned value is always guaranteed to be strictly less than the
+   * value returned by getNumberSlots. Slots start at 0 and finish at
+   * getNumberSlots() - 1. For instance, a system with A/B must return 0 or 1.
+   */
+   getActiveBootSlot() generates (Slot slot);
+};
+
diff --git a/boot/1.2/default/Android.bp b/boot/1.2/default/Android.bp
new file mode 100644
index 0000000..c097667
--- /dev/null
+++ b/boot/1.2/default/Android.bp
@@ -0,0 +1,50 @@
+cc_library_shared {
+    name: "android.hardware.boot@1.2-impl",
+    stem: "android.hardware.boot@1.0-impl-1.2",
+    defaults: [
+        "hidl_defaults",
+        "libboot_control_defaults",
+    ],
+    relative_install_path: "hw",
+    vendor: true,
+    recovery_available: true,
+    srcs: ["BootControl.cpp"],
+
+    shared_libs: [
+        "liblog",
+        "libhidlbase",
+        "libhardware",
+        "libutils",
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hardware.boot@1.2",
+    ],
+    static_libs: [
+        "libboot_control",
+        "libfstab",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.boot@1.2-service",
+    defaults: ["hidl_defaults"],
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["android.hardware.boot@1.2-service.rc"],
+    srcs: ["service.cpp"],
+
+    vintf_fragments: [
+        "android.hardware.boot@1.2.xml",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libhardware",
+        "libhidlbase",
+        "libutils",
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hardware.boot@1.2",
+    ],
+
+}
diff --git a/boot/1.2/default/BootControl.cpp b/boot/1.2/default/BootControl.cpp
new file mode 100644
index 0000000..c0bf02f
--- /dev/null
+++ b/boot/1.2/default/BootControl.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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 "android.hardware.boot@1.2-impl"
+
+#include <memory>
+
+#include <log/log.h>
+
+#include "BootControl.h"
+
+namespace android {
+namespace hardware {
+namespace boot {
+namespace V1_2 {
+namespace implementation {
+
+using ::android::hardware::boot::V1_0::CommandResult;
+
+bool BootControl::Init() {
+    return impl_.Init();
+}
+
+// Methods from ::android::hardware::boot::V1_0::IBootControl.
+Return<uint32_t> BootControl::getNumberSlots() {
+    return impl_.GetNumberSlots();
+}
+
+Return<uint32_t> BootControl::getCurrentSlot() {
+    return impl_.GetCurrentSlot();
+}
+
+Return<void> BootControl::markBootSuccessful(markBootSuccessful_cb _hidl_cb) {
+    struct CommandResult cr;
+    if (impl_.MarkBootSuccessful()) {
+        cr.success = true;
+        cr.errMsg = "Success";
+    } else {
+        cr.success = false;
+        cr.errMsg = "Operation failed";
+    }
+    _hidl_cb(cr);
+    return Void();
+}
+
+Return<void> BootControl::setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) {
+    struct CommandResult cr;
+    if (impl_.SetActiveBootSlot(slot)) {
+        cr.success = true;
+        cr.errMsg = "Success";
+    } else {
+        cr.success = false;
+        cr.errMsg = "Operation failed";
+    }
+    _hidl_cb(cr);
+    return Void();
+}
+
+Return<void> BootControl::setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) {
+    struct CommandResult cr;
+    if (impl_.SetSlotAsUnbootable(slot)) {
+        cr.success = true;
+        cr.errMsg = "Success";
+    } else {
+        cr.success = false;
+        cr.errMsg = "Operation failed";
+    }
+    _hidl_cb(cr);
+    return Void();
+}
+
+Return<BoolResult> BootControl::isSlotBootable(uint32_t slot) {
+    if (!impl_.IsValidSlot(slot)) {
+        return BoolResult::INVALID_SLOT;
+    }
+    return impl_.IsSlotBootable(slot) ? BoolResult::TRUE : BoolResult::FALSE;
+}
+
+Return<BoolResult> BootControl::isSlotMarkedSuccessful(uint32_t slot) {
+    if (!impl_.IsValidSlot(slot)) {
+        return BoolResult::INVALID_SLOT;
+    }
+    return impl_.IsSlotMarkedSuccessful(slot) ? BoolResult::TRUE : BoolResult::FALSE;
+}
+
+Return<void> BootControl::getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) {
+    hidl_string ans;
+    const char* suffix = impl_.GetSuffix(slot);
+    if (suffix) {
+        ans = suffix;
+    }
+    _hidl_cb(ans);
+    return Void();
+}
+
+// Methods from ::android::hardware::boot::V1_1::IBootControl.
+Return<bool> BootControl::setSnapshotMergeStatus(MergeStatus status) {
+    return impl_.SetSnapshotMergeStatus(status);
+}
+
+Return<MergeStatus> BootControl::getSnapshotMergeStatus() {
+    return impl_.GetSnapshotMergeStatus();
+}
+
+// Methods from ::android::hardware::boot::V1_2::IBootControl.
+Return<uint32_t> BootControl::getActiveBootSlot() {
+    return impl_.GetActiveBootSlot();
+}
+
+IBootControl* HIDL_FETCH_IBootControl(const char* /* hal */) {
+    auto module = std::make_unique<BootControl>();
+    if (!module->Init()) {
+        ALOGE("Could not initialize BootControl module");
+        return nullptr;
+    }
+    return module.release();
+}
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace boot
+}  // namespace hardware
+}  // namespace android
diff --git a/boot/1.2/default/BootControl.h b/boot/1.2/default/BootControl.h
new file mode 100644
index 0000000..5791699
--- /dev/null
+++ b/boot/1.2/default/BootControl.h
@@ -0,0 +1,66 @@
+/*
+ * 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/hardware/boot/1.2/IBootControl.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <libboot_control/libboot_control.h>
+
+namespace android {
+namespace hardware {
+namespace boot {
+namespace V1_2 {
+namespace implementation {
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::boot::V1_0::BoolResult;
+using ::android::hardware::boot::V1_1::MergeStatus;
+using ::android::hardware::boot::V1_2::IBootControl;
+
+class BootControl : public IBootControl {
+  public:
+    bool Init();
+
+    // Methods from ::android::hardware::boot::V1_0::IBootControl.
+    Return<uint32_t> getNumberSlots() override;
+    Return<uint32_t> getCurrentSlot() override;
+    Return<void> markBootSuccessful(markBootSuccessful_cb _hidl_cb) override;
+    Return<void> setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) override;
+    Return<void> setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) override;
+    Return<BoolResult> isSlotBootable(uint32_t slot) override;
+    Return<BoolResult> isSlotMarkedSuccessful(uint32_t slot) override;
+    Return<void> getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) override;
+
+    // Methods from ::android::hardware::boot::V1_1::IBootControl.
+    Return<bool> setSnapshotMergeStatus(MergeStatus status) override;
+    Return<MergeStatus> getSnapshotMergeStatus() override;
+
+    // Methods from ::android::hardware::boot::V1_2::IBootControl.
+    Return<uint32_t> getActiveBootSlot() override;
+
+  private:
+    android::bootable::BootControl impl_;
+};
+
+extern "C" IBootControl* HIDL_FETCH_IBootControl(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace boot
+}  // namespace hardware
+}  // namespace android
diff --git a/boot/1.2/default/android.hardware.boot@1.2-service.rc b/boot/1.2/default/android.hardware.boot@1.2-service.rc
new file mode 100644
index 0000000..14926c0
--- /dev/null
+++ b/boot/1.2/default/android.hardware.boot@1.2-service.rc
@@ -0,0 +1,7 @@
+service vendor.boot-hal-1-2 /vendor/bin/hw/android.hardware.boot@1.2-service
+    interface android.hardware.boot@1.0::IBootControl default
+    interface android.hardware.boot@1.1::IBootControl default
+    interface android.hardware.boot@1.2::IBootControl default
+    class early_hal
+    user root
+    group root
diff --git a/boot/1.2/default/android.hardware.boot@1.2.xml b/boot/1.2/default/android.hardware.boot@1.2.xml
new file mode 100644
index 0000000..ba91e8f
--- /dev/null
+++ b/boot/1.2/default/android.hardware.boot@1.2.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.boot</name>
+        <transport>hwbinder</transport>
+        <fqname>@1.2::IBootControl/default</fqname>
+    </hal>
+</manifest>
diff --git a/boot/1.2/default/service.cpp b/boot/1.2/default/service.cpp
new file mode 100644
index 0000000..3053957
--- /dev/null
+++ b/boot/1.2/default/service.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 "android.hardware.boot@1.2-service"
+
+#include <android/hardware/boot/1.2/IBootControl.h>
+#include <hidl/LegacySupport.h>
+
+using android::hardware::defaultPassthroughServiceImplementation;
+using IBootControl_V1_0 = android::hardware::boot::V1_0::IBootControl;
+using IBootControl_V1_2 = android::hardware::boot::V1_2::IBootControl;
+
+int main(int /* argc */, char* /* argv */[]) {
+    return defaultPassthroughServiceImplementation<IBootControl_V1_0, IBootControl_V1_2>();
+}
diff --git a/boot/1.2/vts/functional/Android.bp b/boot/1.2/vts/functional/Android.bp
new file mode 100644
index 0000000..a7f5ccb
--- /dev/null
+++ b/boot/1.2/vts/functional/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "VtsHalBootV1_2TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalBootV1_2TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hardware.boot@1.2",
+        "libgmock",
+    ],
+    test_suites: [
+        "device-tests",
+        "vts",
+    ],
+}
diff --git a/boot/1.2/vts/functional/VtsHalBootV1_2TargetTest.cpp b/boot/1.2/vts/functional/VtsHalBootV1_2TargetTest.cpp
new file mode 100644
index 0000000..9df23c3
--- /dev/null
+++ b/boot/1.2/vts/functional/VtsHalBootV1_2TargetTest.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "boot_hidl_hal_test"
+
+#include <android-base/logging.h>
+#include <android/hardware/boot/1.2/IBootControl.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <unistd.h>
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::boot::V1_0::CommandResult;
+using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::boot::V1_2::IBootControl;
+
+class BootHidlTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        boot = IBootControl::getService(GetParam());
+        ASSERT_NE(boot, nullptr);
+
+        LOG(INFO) << "Test is remote " << boot->isRemote();
+    }
+
+    sp<IBootControl> boot;
+};
+
+auto generate_callback(CommandResult* dest) {
+    return [=](CommandResult cr) { *dest = cr; };
+}
+
+TEST_P(BootHidlTest, GetActiveBootSlot) {
+    Slot curSlot = boot->getCurrentSlot();
+    Slot otherSlot = curSlot ? 0 : 1;
+
+    // Set the active slot, then check if the getter returns the correct slot.
+    CommandResult cr;
+    Return<void> result = boot->setActiveBootSlot(otherSlot, generate_callback(&cr));
+    EXPECT_TRUE(result.isOk());
+    Slot activeSlot = boot->getActiveBootSlot();
+    EXPECT_EQ(otherSlot, activeSlot);
+
+    result = boot->setActiveBootSlot(curSlot, generate_callback(&cr));
+    EXPECT_TRUE(result.isOk());
+    activeSlot = boot->getActiveBootSlot();
+    EXPECT_EQ(curSlot, activeSlot);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BootHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, BootHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBootControl::descriptor)),
+        android::hardware::PrintInstanceNameToString);
diff --git a/camera/common/1.0/default/CameraModule.cpp b/camera/common/1.0/default/CameraModule.cpp
index 86f26e4..27e74f1 100644
--- a/camera/common/1.0/default/CameraModule.cpp
+++ b/camera/common/1.0/default/CameraModule.cpp
@@ -529,24 +529,29 @@
 }
 
 void CameraModule::removeCamera(int cameraId) {
-    std::unordered_set<std::string> physicalIds;
-    camera_metadata_t *metadata = const_cast<camera_metadata_t*>(
-            mCameraInfoMap.valueFor(cameraId).static_camera_characteristics);
-    common::V1_0::helper::CameraMetadata hidlMetadata(metadata);
+    // Skip HAL1 devices which isn't cached in mCameraInfoMap and don't advertise
+    // static_camera_characteristics
+    if (getDeviceVersion(cameraId) >= CAMERA_DEVICE_API_VERSION_3_0) {
+        std::unordered_set<std::string> physicalIds;
+        camera_metadata_t *metadata = const_cast<camera_metadata_t*>(
+                mCameraInfoMap.valueFor(cameraId).static_camera_characteristics);
+        common::V1_0::helper::CameraMetadata hidlMetadata(metadata);
 
-    if (isLogicalMultiCamera(hidlMetadata, &physicalIds)) {
-        for (const auto& id : physicalIds) {
-            int idInt = std::stoi(id);
-            if (mPhysicalCameraInfoMap.indexOfKey(idInt) >= 0) {
-                free_camera_metadata(mPhysicalCameraInfoMap[idInt]);
-                mPhysicalCameraInfoMap.removeItem(idInt);
-            } else {
-                ALOGE("%s: Cannot find corresponding static metadata for physical id %s",
-                        __FUNCTION__, id.c_str());
+        if (isLogicalMultiCamera(hidlMetadata, &physicalIds)) {
+            for (const auto& id : physicalIds) {
+                int idInt = std::stoi(id);
+                if (mPhysicalCameraInfoMap.indexOfKey(idInt) >= 0) {
+                    free_camera_metadata(mPhysicalCameraInfoMap[idInt]);
+                    mPhysicalCameraInfoMap.removeItem(idInt);
+                } else {
+                    ALOGE("%s: Cannot find corresponding static metadata for physical id %s",
+                            __FUNCTION__, id.c_str());
+                }
             }
         }
+        free_camera_metadata(metadata);
     }
-    free_camera_metadata(metadata);
+
     mCameraInfoMap.removeItem(cameraId);
     mDeviceVersionMap.removeItem(cameraId);
 }
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 74af9e3..72321e2 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -134,7 +134,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.boot</name>
-        <version>1.1</version>
+        <version>1.2</version>
         <interface>
             <name>IBootControl</name>
             <instance>default</instance>
diff --git a/current.txt b/current.txt
index 7aa9593..8623fc0 100644
--- a/current.txt
+++ b/current.txt
@@ -772,12 +772,15 @@
 2c331a9605f3a08d9c1e0a36169ca57758bc43c11a78ef3f3730509885e52c15 android.hardware.graphics.composer@2.4::IComposerClient
 3da3ce039247872d95c6bd48621dbfdfa1c2d2a91a90f257862f87ee2bc46300 android.hardware.health@2.1::types
 9679f27a42f75781c8993ef163ed92808a1928de186639834841d0b8e326e63d android.hardware.gatekeeper@1.0::IGatekeeper
+9c4eb603d7b9ad675a14edb6180681c5a78da5c6bdc7755853912c974a21f7e5 android.hardware.gnss@1.0::IAGnssCallback
 40456eb90ea88b62d18ad3fbf1da8917981cd55ac04ce69c8e058d49ff5beff4 android.hardware.keymaster@3.0::IKeymasterDevice
 6017b4f2481feb0fffceae81c62bc372c898998b2d8fe69fbd39859d3a315e5e android.hardware.keymaster@4.0::IKeymasterDevice
 dabe23dde7c9e3ad65c61def7392f186d7efe7f4216f9b6f9cf0863745b1a9f4 android.hardware.keymaster@4.1::IKeymasterDevice
 cd84ab19c590e0e73dd2307b591a3093ee18147ef95e6d5418644463a6620076 android.hardware.neuralnetworks@1.2::IDevice
 9625e85f56515ad2cf87b6a1847906db669f746ea4ab02cd3d4ca25abc9b0109 android.hardware.neuralnetworks@1.2::types
 9e758e208d14f7256e0885d6d8ad0b61121b21d8c313864f981727ae55bffd16 android.hardware.neuralnetworks@1.3::types
+e8c86c69c438da8d1549856c1bb3e2d1b8da52722f8235ff49a30f2cce91742c android.hardware.soundtrigger@2.1::ISoundTriggerHwCallback
+b9fbb6e2e061ed0960939d48b785e9700210add1f13ed32ecd688d0f1ca20ef7 android.hardware.renderscript@1.0::types
 0f53d70e1eadf8d987766db4bf6ae2048004682168f4cab118da576787def3fa android.hardware.radio@1.0::types
 38d65fb20c60a5b823298560fc0825457ecdc49603a4b4e94bf81511790737da android.hardware.radio@1.4::types
 954c334efd80e8869b66d1ce5fe2755712d96ba4b3c38d415739c330af5fb4cb android.hardware.radio@1.5::types
diff --git a/gnss/1.0/IAGnssCallback.hal b/gnss/1.0/IAGnssCallback.hal
index 81f1689..11a6a5d 100644
--- a/gnss/1.0/IAGnssCallback.hal
+++ b/gnss/1.0/IAGnssCallback.hal
@@ -42,7 +42,6 @@
     /**
      * Represents the status of AGNSS augmented to support IPv4.
      */
-    @export(name="", value_prefix="GPS_")
     struct AGnssStatusIpV4 {
         AGnssType type;
         AGnssStatusValue status;
diff --git a/gnss/1.1/default/Android.bp b/gnss/1.1/default/Android.bp
index 9c498d5..ef43a34 100644
--- a/gnss/1.1/default/Android.bp
+++ b/gnss/1.1/default/Android.bp
@@ -18,6 +18,7 @@
         "android.hardware.gnss@2.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@1.0",
+        "android.hardware.gnss-ndk_platform",
     ],
     static_libs: [
         "android.hardware.gnss@common-default-lib",
diff --git a/gnss/2.0/default/Android.bp b/gnss/2.0/default/Android.bp
index 37de55d..7da462f 100644
--- a/gnss/2.0/default/Android.bp
+++ b/gnss/2.0/default/Android.bp
@@ -29,7 +29,7 @@
         "GnssMeasurement.cpp",
         "GnssMeasurementCorrections.cpp",
         "GnssVisibilityControl.cpp",
-        "service.cpp"
+        "service.cpp",
     ],
     shared_libs: [
         "libhidlbase",
@@ -39,8 +39,9 @@
         "android.hardware.gnss.visibility_control@1.0",
         "android.hardware.gnss@2.1",
         "android.hardware.gnss@2.0",
-        "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
+        "android.hardware.gnss@1.0",
+        "android.hardware.gnss-ndk_platform",
     ],
     static_libs: [
         "android.hardware.gnss@common-default-lib",
diff --git a/gnss/2.1/default/Android.bp b/gnss/2.1/default/Android.bp
index 7739f90..63e5013 100644
--- a/gnss/2.1/default/Android.bp
+++ b/gnss/2.1/default/Android.bp
@@ -34,6 +34,7 @@
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
+        "android.hardware.gnss-ndk_platform",
     ],
     static_libs: [
         "android.hardware.gnss@common-default-lib",
diff --git a/gnss/2.1/vts/functional/gnss_hal_test.h b/gnss/2.1/vts/functional/gnss_hal_test.h
index 7950670..2bcecf4 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test.h
+++ b/gnss/2.1/vts/functional/gnss_hal_test.h
@@ -19,10 +19,9 @@
 #include <android/hardware/gnss/2.1/IGnss.h>
 #include "v2_1/gnss_hal_test_template.h"
 
-using android::hardware::gnss::V2_1::IGnss;
-
 // The main test class for GNSS HAL.
-class GnssHalTest : public GnssHalTestTemplate<IGnss> {
+class GnssHalTest : public android::hardware::gnss::common::GnssHalTestTemplate<
+                            android::hardware::gnss::V2_1::IGnss> {
   public:
     /**
      * IsGnssHalVersion_2_1:
diff --git a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
index 7afd49c..deb80e8 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
@@ -27,6 +27,9 @@
 
 using android::hardware::gnss::common::Utils;
 
+using android::hardware::gnss::V2_1::IGnssAntennaInfo;
+using android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
+
 using IGnssMeasurement_2_1 = android::hardware::gnss::V2_1::IGnssMeasurement;
 using IGnssMeasurement_2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
 using IGnssMeasurement_1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
diff --git a/gnss/3.0/default/Android.bp b/gnss/3.0/default/Android.bp
index 2b33b32..bb3c467 100644
--- a/gnss/3.0/default/Android.bp
+++ b/gnss/3.0/default/Android.bp
@@ -36,6 +36,7 @@
         "android.hardware.gnss@3.0",
         "android.hardware.gnss.measurement_corrections@1.1",
         "android.hardware.gnss.measurement_corrections@1.0",
+        "android.hardware.gnss-ndk_platform",
     ],
     static_libs: [
         "android.hardware.gnss@common-default-lib",
diff --git a/gnss/3.0/vts/functional/gnss_hal_test.h b/gnss/3.0/vts/functional/gnss_hal_test.h
index 387214e..be6d38c 100644
--- a/gnss/3.0/vts/functional/gnss_hal_test.h
+++ b/gnss/3.0/vts/functional/gnss_hal_test.h
@@ -19,7 +19,6 @@
 #include <android/hardware/gnss/3.0/IGnss.h>
 #include "v2_1/gnss_hal_test_template.h"
 
-using android::hardware::gnss::V3_0::IGnss;
-
 // The main test class for GNSS HAL.
-class GnssHalTest : public GnssHalTestTemplate<IGnss> {};
\ No newline at end of file
+class GnssHalTest : public android::hardware::gnss::common::GnssHalTestTemplate<
+                            android::hardware::gnss::V3_0::IGnss> {};
\ No newline at end of file
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl
index 354c953..a0e8de4 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl
@@ -21,4 +21,6 @@
   int flags;
   long timestampNs;
   double timeUncertaintyNs;
+  const int HAS_TIMESTAMP_NS = 1;
+  const int HAS_TIME_UNCERTAINTY_NS = 2;
 }
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl
new file mode 100644
index 0000000..42b940e
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl
@@ -0,0 +1,39 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable GnssClock {
+  int gnssClockFlags;
+  int leapSecond;
+  long timeNs;
+  double timeUncertaintyNs;
+  long fullBiasNs;
+  double biasNs;
+  double biasUncertaintyNs;
+  double driftNsps;
+  double driftUncertaintyNsps;
+  int hwClockDiscontinuityCount;
+  android.hardware.gnss.GnssSignalType referenceSignalTypeForIsb;
+  const int HAS_LEAP_SECOND = 1;
+  const int HAS_TIME_UNCERTAINTY = 2;
+  const int HAS_FULL_BIAS = 4;
+  const int HAS_BIAS = 8;
+  const int HAS_BIAS_UNCERTAINTY = 16;
+  const int HAS_DRIFT = 32;
+  const int HAS_DRIFT_UNCERTAINTY = 64;
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl
new file mode 100644
index 0000000..7ffabd2
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable GnssData {
+  android.hardware.gnss.GnssMeasurement[] measurements;
+  android.hardware.gnss.GnssClock clock;
+  android.hardware.gnss.ElapsedRealtime elapsedRealtime;
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl
new file mode 100644
index 0000000..7d15855
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl
@@ -0,0 +1,79 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable GnssMeasurement {
+  int flags;
+  int svid;
+  android.hardware.gnss.GnssSignalType signalType;
+  double timeOffsetNs;
+  int state;
+  long receivedSvTimeInNs;
+  long receivedSvTimeUncertaintyInNs;
+  double antennaCN0DbHz;
+  double basebandCN0DbHz;
+  double pseudorangeRateMps;
+  double pseudorangeRateUncertaintyMps;
+  int accumulatedDeltaRangeState;
+  double accumulatedDeltaRangeM;
+  double accumulatedDeltaRangeUncertaintyM;
+  float carrierFrequencyHz;
+  long carrierCycles;
+  double carrierPhase;
+  double carrierPhaseUncertainty;
+  android.hardware.gnss.GnssMultipathIndicator multipathIndicator;
+  double snrDb;
+  double agcLevelDb;
+  double fullInterSignalBiasNs;
+  double fullInterSignalBiasUncertaintyNs;
+  double satelliteInterSignalBiasNs;
+  double satelliteInterSignalBiasUncertaintyNs;
+  const int HAS_SNR = 1;
+  const int HAS_CARRIER_FREQUENCY = 512;
+  const int HAS_CARRIER_CYCLES = 1024;
+  const int HAS_CARRIER_PHASE = 2048;
+  const int HAS_CARRIER_PHASE_UNCERTAINTY = 4096;
+  const int HAS_AUTOMATIC_GAIN_CONTROL = 8192;
+  const int HAS_FULL_ISB = 65536;
+  const int HAS_FULL_ISB_UNCERTAINTY = 131072;
+  const int HAS_SATELLITE_ISB = 262144;
+  const int HAS_SATELLITE_ISB_UNCERTAINTY = 524288;
+  const int STATE_UNKNOWN = 0;
+  const int STATE_CODE_LOCK = 1;
+  const int STATE_BIT_SYNC = 2;
+  const int STATE_SUBFRAME_SYNC = 4;
+  const int STATE_TOW_DECODED = 8;
+  const int STATE_MSEC_AMBIGUOUS = 16;
+  const int STATE_SYMBOL_SYNC = 32;
+  const int STATE_GLO_STRING_SYNC = 64;
+  const int STATE_GLO_TOD_DECODED = 128;
+  const int STATE_BDS_D2_BIT_SYNC = 256;
+  const int STATE_BDS_D2_SUBFRAME_SYNC = 512;
+  const int STATE_GAL_E1BC_CODE_LOCK = 1024;
+  const int STATE_GAL_E1C_2ND_CODE_LOCK = 2048;
+  const int STATE_GAL_E1B_PAGE_SYNC = 4096;
+  const int STATE_SBAS_SYNC = 8192;
+  const int STATE_TOW_KNOWN = 16384;
+  const int STATE_GLO_TOD_KNOWN = 32768;
+  const int STATE_2ND_CODE_LOCK = 65536;
+  const int ADR_STATE_UNKNOWN = 0;
+  const int ADR_STATE_VALID = 1;
+  const int ADR_STATE_RESET = 2;
+  const int ADR_STATE_CYCLE_SLIP = 4;
+  const int ADR_STATE_HALF_CYCLE_RESOLVED = 8;
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl
new file mode 100644
index 0000000..75ca3af
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@Backing(type="int") @VintfStability
+enum GnssMultipathIndicator {
+  UNKNOWN = 0,
+  PRESENT = 1,
+  NOT_PRESENT = 2,
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl
new file mode 100644
index 0000000..3e66c4a
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl
@@ -0,0 +1,40 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable GnssSignalType {
+  android.hardware.gnss.GnssConstellationType constellation;
+  double carrierFrequencyHz;
+  String codeType;
+  const String CODE_TYPE_A = "A";
+  const String CODE_TYPE_B = "B";
+  const String CODE_TYPE_C = "C";
+  const String CODE_TYPE_D = "D";
+  const String CODE_TYPE_I = "I";
+  const String CODE_TYPE_L = "L";
+  const String CODE_TYPE_M = "M";
+  const String CODE_TYPE_N = "N";
+  const String CODE_TYPE_P = "P";
+  const String CODE_TYPE_Q = "Q";
+  const String CODE_TYPE_S = "S";
+  const String CODE_TYPE_W = "W";
+  const String CODE_TYPE_X = "X";
+  const String CODE_TYPE_Y = "Y";
+  const String CODE_TYPE_Z = "Z";
+  const String CODE_TYPE_UNKNOWN = "UNKNOWN";
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
index e1a4b9e..10ac150 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
@@ -22,8 +22,7 @@
   void close();
   android.hardware.gnss.IGnssPsds getExtensionPsds();
   android.hardware.gnss.IGnssConfiguration getExtensionGnssConfiguration();
+  android.hardware.gnss.IGnssMeasurementInterface getExtensionGnssMeasurement();
   android.hardware.gnss.IGnssPowerIndication getExtensionGnssPowerIndication();
   const int ERROR_INVALID_ARGUMENT = 1;
-  const int ELAPSED_REALTIME_HAS_TIMESTAMP_NS = 1;
-  const int ELAPSED_REALTIME_HAS_TIME_UNCERTAINTY_NS = 2;
 }
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl
new file mode 100644
index 0000000..e05e9b9
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+interface IGnssMeasurementCallback {
+  void gnssMeasurementCb(in android.hardware.gnss.GnssData data);
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl
new file mode 100644
index 0000000..9576205
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+interface IGnssMeasurementInterface {
+  void setCallback(in android.hardware.gnss.IGnssMeasurementCallback callback, in boolean enableFullTracking);
+  void close();
+}
diff --git a/gnss/aidl/android/hardware/gnss/ElapsedRealtime.aidl b/gnss/aidl/android/hardware/gnss/ElapsedRealtime.aidl
index fae14f8..67d090e 100644
--- a/gnss/aidl/android/hardware/gnss/ElapsedRealtime.aidl
+++ b/gnss/aidl/android/hardware/gnss/ElapsedRealtime.aidl
@@ -22,10 +22,18 @@
 @VintfStability
 parcelable ElapsedRealtime {
 
+    /** Bit mask indicating a valid timestampNs is stored in the ElapsedRealtime parcelable. */
+    const int HAS_TIMESTAMP_NS = 1 << 0;
+
+    /**
+     * Bit mask indicating a valid timeUncertaintyNs is stored in the ElapsedRealtime parcelable.
+     */
+    const int HAS_TIME_UNCERTAINTY_NS = 1 << 1;
+
     /**
      * A bit field of flags indicating the validity of each field in this data structure.
      *
-     * The bit masks are defined in IGnss interface and prefixed with ELAPSED_REALTIME_HAS_.
+     * The bit masks are the constants with prefix HAS_.
      *
      * Fields may have invalid information in them, if not marked as valid by the corresponding bit
      * in flags.
diff --git a/gnss/aidl/android/hardware/gnss/GnssClock.aidl b/gnss/aidl/android/hardware/gnss/GnssClock.aidl
new file mode 100644
index 0000000..f416e08
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssClock.aidl
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssSignalType;
+
+/**
+ * Represents an estimate of the GNSS clock time.
+ */
+@VintfStability
+parcelable GnssClock {
+    /** Bit mask indicating a valid 'leap second' is stored in the GnssClock. */
+    const int HAS_LEAP_SECOND        = 1 << 0;
+    /** Bit mask indicating a valid 'time uncertainty' is stored in the GnssClock. */
+    const int HAS_TIME_UNCERTAINTY   = 1 << 1;
+    /** Bit mask indicating a valid 'full bias' is stored in the GnssClock. */
+    const int HAS_FULL_BIAS          = 1 << 2;
+    /** Bit mask indicating a valid 'bias' is stored in the GnssClock. */
+    const int HAS_BIAS               = 1 << 3;
+    /** Bit mask indicating a valid 'bias uncertainty' is stored in the GnssClock. */
+    const int HAS_BIAS_UNCERTAINTY   = 1 << 4;
+    /** Bit mask indicating a valid 'drift' is stored in the GnssClock. */
+    const int HAS_DRIFT              = 1 << 5;
+    /** Bit mask indicating a valid 'drift uncertainty' is stored in the GnssClock. */
+    const int HAS_DRIFT_UNCERTAINTY  = 1 << 6;
+
+    /**
+     * A bitfield of flags indicating the validity of the fields in this data
+     * structure.
+     *
+     * The bit masks are the constants with perfix HAS_.
+     *
+     * Fields for which there is no corresponding flag must be filled in
+     * with a valid value.  For convenience, these are marked as mandatory.
+     *
+     * Others fields may have invalid information in them, if not marked as
+     * valid by the corresponding bit in gnssClockFlags.
+     */
+    int gnssClockFlags;
+
+    /**
+     * Leap second data.
+     * The sign of the value is defined by the following equation:
+     *      utcTimeNs = timeNs - (fullBiasNs + biasNs) - leapSecond *
+     *      1,000,000,000
+     *
+     * If this data is available, gnssClockFlags must contain
+     * HAS_LEAP_SECOND.
+     */
+    int leapSecond;
+
+    /**
+     * The GNSS receiver internal clock value. This is the local hardware clock
+     * value.
+     *
+     * For local hardware clock, this value is expected to be monotonically
+     * increasing while the hardware clock remains powered on. (For the case of a
+     * HW clock that is not continuously on, see the
+     * hwClockDiscontinuityCount field). The receiver's estimate of GNSS time
+     * can be derived by subtracting the sum of fullBiasNs and biasNs (when
+     * available) from this value.
+     *
+     * This GNSS time must be the best estimate of current GNSS time
+     * that GNSS receiver can achieve.
+     *
+     * Sub-nanosecond accuracy can be provided by means of the 'biasNs' field.
+     * The value contains the timeUncertaintyNs in it.
+     *
+     * This value is mandatory.
+     */
+    long timeNs;
+
+    /**
+     * 1-Sigma uncertainty associated with the clock's time in nanoseconds.
+     * The uncertainty is represented as an absolute (single sided) value.
+     *
+     * If the data is available, gnssClockFlags must contain
+     * HAS_TIME_UNCERTAINTY. Ths value is ideally zero, as the time
+     * 'latched' by timeNs is defined as the reference clock vs. which all
+     * other times (and corresponding uncertainties) are measured.
+     */
+    double timeUncertaintyNs;
+
+    /**
+     * The difference between hardware clock ('time' field) inside GNSS receiver
+     * and the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
+     *
+     * The sign of the value is defined by the following equation:
+     *      local estimate of GPS time = timeNs - (fullBiasNs + biasNs)
+     *
+     * If receiver has computed time for a non-GPS constellation, the time offset of
+     * that constellation versus GPS time must be applied to fill this value.
+     *
+     * The error estimate for the sum of this and the biasNs is the biasUncertaintyNs.
+     *
+     * If the data is available gnssClockFlags must contain HAS_FULL_BIAS.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    long fullBiasNs;
+
+    /**
+     * Sub-nanosecond bias - used with fullBiasNS, see fullBiasNs for details.
+     *
+     * The error estimate for the sum of this and the fullBiasNs is the
+     * biasUncertaintyNs.
+     *
+     * If the data is available gnssClockFlags must contain HAS_BIAS.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    double biasNs;
+
+    /**
+     * 1-Sigma uncertainty associated with the local estimate of GNSS time (clock
+     * bias) in nanoseconds. The uncertainty is represented as an absolute
+     * (single sided) value.
+     *
+     * The caller is responsible for using this uncertainty (it can be very
+     * large before the GPS time has been fully resolved.)
+     *
+     * If the data is available gnssClockFlags must contain HAS_BIAS_UNCERTAINTY.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    double biasUncertaintyNs;
+
+    /**
+     * The clock's drift in nanoseconds (per second).
+     *
+     * A positive value means that the frequency is higher than the nominal
+     * frequency, and that the (fullBiasNs + biasNs) is growing more positive
+     * over time.
+     *
+     * If the data is available gnssClockFlags must contain HAS_DRIFT.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    double driftNsps;
+
+    /**
+     * 1-Sigma uncertainty associated with the clock's drift in nanoseconds (per
+     * second).
+     * The uncertainty is represented as an absolute (single sided) value.
+     *
+     * If the data is available gnssClockFlags must contain HAS_DRIFT_UNCERTAINTY.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    double driftUncertaintyNsps;
+
+    /**
+     * This field must be incremented, when there are discontinuities in the
+     * hardware clock.
+     *
+     * A "discontinuity" is meant to cover the case of a switch from one source
+     * of clock to another.  A single free-running crystal oscillator (XO)
+     * will generally not have any discontinuities, and this can be set and
+     * left at 0.
+     *
+     * If, however, the timeNs value (HW clock) is derived from a composite of
+     * sources, that is not as smooth as a typical XO, or is otherwise stopped &
+     * restarted, then this value shall be incremented each time a discontinuity
+     * occurs.  (E.g. this value can start at zero at device boot-up and
+     * increment each time there is a change in clock continuity. In the
+     * unlikely event that this value reaches full scale, rollover (not
+     * clamping) is required, such that this value continues to change, during
+     * subsequent discontinuity events.)
+     *
+     * While this number stays the same, between GnssClock reports, it can be
+     * safely assumed that the timeNs value has been running continuously, e.g.
+     * derived from a single, high quality clock (XO like, or better, that is
+     * typically used during continuous GNSS signal sampling.)
+     *
+     * It is expected, esp. during periods where there are few GNSS signals
+     * available, that the HW clock be discontinuity-free as long as possible,
+     * as this avoids the need to use (waste) a GNSS measurement to fully
+     * re-solve for the GNSS clock bias and drift, when using the accompanying
+     * measurements, from consecutive GnssData reports.
+     *
+     * This value is mandatory.
+     */
+    int hwClockDiscontinuityCount;
+
+    /**
+     * Reference GNSS signal type for inter-signal bias.
+     */
+    GnssSignalType referenceSignalTypeForIsb;
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssData.aidl b/gnss/aidl/android/hardware/gnss/GnssData.aidl
new file mode 100644
index 0000000..ed30c98
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssData.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.ElapsedRealtime;
+import android.hardware.gnss.GnssClock;
+import android.hardware.gnss.GnssMeasurement;
+
+/**
+ * Represents a reading of GNSS measurements. For devices launched in Android Q or newer, it is
+ * mandatory that these be provided, on request, when the GNSS receiver is searching/tracking
+ * signals.
+ *
+ * - Reporting of GNSS constellation measurements is mandatory.
+ * - Reporting of all tracked constellations are encouraged.
+ */
+@VintfStability
+parcelable GnssData {
+    /** The array of measurements. */
+    GnssMeasurement[] measurements;
+
+    /** The GNSS clock time reading. */
+    GnssClock clock;
+
+    /**
+     * Timing information of the GNSS data synchronized with SystemClock.elapsedRealtimeNanos()
+     * clock.
+     */
+    ElapsedRealtime elapsedRealtime;
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl b/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
new file mode 100644
index 0000000..fae862b
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
@@ -0,0 +1,634 @@
+/*
+ * 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.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssConstellationType;
+import android.hardware.gnss.GnssSignalType;
+import android.hardware.gnss.GnssMultipathIndicator;
+
+/**
+ * Represents a GNSS Measurement, it contains raw and computed information.
+ *
+ * All signal measurement information (e.g. svTime, pseudorangeRate, multipathIndicator) reported in
+ * this struct must be based on GNSS signal measurements only. You must not synthesize measurements
+ * by calculating or reporting expected measurements based on known or estimated position, velocity,
+ * or time.
+ */
+@VintfStability
+parcelable GnssMeasurement {
+    /** Bit mask indicating a valid 'snr' is stored in the GnssMeasurement. */
+    const int HAS_SNR                        = 1 << 0;
+    /** Bit mask indicating a valid 'carrier frequency' is stored in the GnssMeasurement. */
+    const int HAS_CARRIER_FREQUENCY          = 1 << 9;
+    /** Bit mask indicating a valid 'carrier cycles' is stored in the GnssMeasurement. */
+    const int HAS_CARRIER_CYCLES             = 1 << 10;
+    /** Bit mask indicating a valid 'carrier phase' is stored in the GnssMeasurement. */
+    const int HAS_CARRIER_PHASE              = 1 << 11;
+    /** Bit mask indicating a valid 'carrier phase uncertainty' is stored in the GnssMeasurement. */
+    const int HAS_CARRIER_PHASE_UNCERTAINTY  = 1 << 12;
+    /** Bit mask indicating a valid automatic gain control is stored in the GnssMeasurement. */
+    const int HAS_AUTOMATIC_GAIN_CONTROL     = 1 << 13;
+    /** Bit mask indicating a valid full inter-signal bias is stored in the GnssMeasurement. */
+    const int HAS_FULL_ISB                   = 1 << 16;
+    /**
+     * Bit mask indicating a valid full inter-signal bias uncertainty is stored in the
+     * GnssMeasurement.
+     */
+    const int HAS_FULL_ISB_UNCERTAINTY       = 1 << 17;
+    /**
+     * Bit mask indicating a valid satellite inter-signal bias is stored in the GnssMeasurement.
+     */
+    const int HAS_SATELLITE_ISB              = 1 << 18;
+    /**
+     * Bit mask indicating a valid satellite inter-signal bias uncertainty is stored in the
+     * GnssMeasurement.
+     */
+    const int HAS_SATELLITE_ISB_UNCERTAINTY  = 1 << 19;
+
+    /**
+     * A bitfield of flags indicating the validity of the fields in this GnssMeasurement. The bit
+     * masks are defined in the constants with prefix HAS_*
+     *
+     * Fields for which there is no corresponding flag must be filled in with a valid value.  For
+     * convenience, these are marked as mandatory.
+     *
+     * Others fields may have invalid information in them, if not marked as valid by the
+     * corresponding bit in flags.
+     */
+    int flags;
+
+    /**
+     * Satellite vehicle ID number, as defined in GnssSvInfo::svid
+     *
+     * This value is mandatory.
+     */
+    int svid;
+
+    /**
+     * Defines the constellation of the given SV.
+     *
+     * This value is mandatory.
+     */
+    GnssSignalType signalType;
+
+    /**
+     * Time offset at which the measurement was taken in nanoseconds.
+     * The reference receiver's time is specified by GnssData::clock::timeNs.
+     *
+     * The sign of timeOffsetNs is given by the following equation:
+     *      measurement time = GnssClock::timeNs + timeOffsetNs
+     *
+     * It provides an individual time-stamp for the measurement, and allows
+     * sub-nanosecond accuracy. It may be zero if all measurements are
+     * aligned to a common time.
+     *
+     * This value is mandatory.
+     */
+    double timeOffsetNs;
+
+    /**
+     * Flags indicating the GNSS measurement state.
+     *
+     * The expected behavior here is for GNSS HAL to set all the flags that apply. For example, if
+     * the state for a satellite is only C/A code locked and bit synchronized, and there is still
+     * millisecond ambiguity, the state must be set as:
+     *
+     * STATE_CODE_LOCK | STATE_BIT_SYNC |  STATE_MSEC_AMBIGUOUS
+     *
+     * If GNSS is still searching for a satellite, the corresponding state must be set to
+     * STATE_UNKNOWN(0).
+     *
+     * The received satellite time is relative to the beginning of the system week for all
+     * constellations except for Glonass where it is relative to the beginning of the Glonass system
+     * day.
+     *
+     * The table below indicates the valid range of the received GNSS satellite time.  These ranges
+     * depend on the constellation and code being tracked and the state of the tracking algorithms
+     * given by the getState method. If the state flag is set, then the valid measurement range is
+     * zero to the value in the table. The state flag with the widest range indicates the range of
+     * the received GNSS satellite time value.
+     *
+     * +---------------------------+--------------------+-----+-----------+--------------------+------+
+     * |                           |GPS/QZSS	        |GLNS |BDS        |GAL                 |SBAS  |
+     * +---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |State Flag	               |L1    |L5I   |L5Q   |L1OF |B1I   |B1I |E1B   |E1C   |E5AQ  |L1    |
+     * |                           |C/A	  |      |      |     |(D1)  |(D2)|      |      |      |C/A   |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_UNKNOWN              |0	  |0	 |0	    |0	  |0	 |0   |0     |0     |0	   |0     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_CODE_LOCK            |1ms   |1 ms  |1 ms  |1 ms |1 ms  |1 ms|-     |-     |1 ms  |1 ms  |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_SYMBOL_SYNC          |20ms  |10 ms |1 ms  |10 ms|20 ms |2 ms|4 ms  |4 ms  |1 ms  |2 ms  |
+     * |                           |(opt.)|	     |(opt.)|     |(opt.)|    |(opt.)|(opt.)|(opt.)|      |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_BIT_SYNC             |20 ms |20 ms |1 ms  |20 ms|20 ms |-   |8 ms  |-     |1 ms  |4 ms  |
+     * |                           |      |      |(opt.)|     |      |    |      |      |(opt.)|      |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_SUBFRAME_SYNC        |6s    |6s    |-     |2 s  |6 s   |-   |-     |-     |100 ms|-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_TOW_DECODED          |1 week|-     |-     |1 day|1 week|-   |1 week|-     |-     |1 week|
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_TOW_KNOWN            |1 week|-     |-     |1 day|1 week|-   |1 week|-     |-     |1 week|
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GLO_STRING_SYNC      |-     |-     |-     |2 s  |-     |-   |-     |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GLO_TOD_DECODED      |-     |-     |-     |1 day|-     |-   |-     |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GLO_TOD_KNOWN        |-     |-     |-     |1 day|-     |-   |-     |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_BDS_D2_BIT_SYNC      |-     |-     |-     |-    |-     |2 ms|-     |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_BDS_D2_SUBFRAME_SYNC |-     |-     |-     |-    |-     |600 |-     |-     |-     |-     |
+     * |                           |      |      |      |     |      |ms  |      |      |      |      |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GAL_E1BC_CODE_LOCK   |-     |-     |-     |-    |-     |-   |4 ms  |4 ms  |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GAL_E1C_2ND_CODE_LOCK|-     |-     |-     |-    |-     |-   |-     |100 ms|-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_2ND_CODE_LOCK        |-     |10 ms |20 ms |-    |-     |-	  |-     |100 ms|100 ms|-     |
+     * |                           |      |(opt.)|      |     |      |    |      |(opt.)|      |      |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GAL_E1B_PAGE_SYNC    |-     |-     |-     |-    |-     |-   |2 s   |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_SBAS_SYNC            |-     |-     |-     |-    |-     |-   |-     |-     |-     |1s    |
+     * +---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     *
+     * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+     * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
+     *
+     * Note well: if there is any ambiguity in integer millisecond, STATE_MSEC_AMBIGUOUS must be
+     * set accordingly, in the 'state' field.  This value must be populated if 'state' !=
+     * STATE_UNKNOWN.
+     *
+     * Note on optional flags:
+     *   - For L1 C/A and B1I, STATE_SYMBOL_SYNC is optional since the symbol length is the
+     *     same as the bit length.
+     *   - For L5Q and E5aQ, STATE_BIT_SYNC and STATE_SYMBOL_SYNC are optional since they are
+     *     implied by STATE_CODE_LOCK.
+     *   - STATE_2ND_CODE_LOCK for L5I is optional since it is implied by STATE_SYMBOL_SYNC.
+     *   - STATE_2ND_CODE_LOCK for E1C is optional since it is implied by
+     *     STATE_GAL_E1C_2ND_CODE_LOCK.
+     *   - For E1B and E1C, STATE_SYMBOL_SYNC is optional, because it is implied by
+     *     STATE_GAL_E1BC_CODE_LOCK.
+     */
+    const int STATE_UNKNOWN                = 0;
+    const int STATE_CODE_LOCK              = 1 << 0;
+    const int STATE_BIT_SYNC               = 1 << 1;
+    const int STATE_SUBFRAME_SYNC          = 1 << 2;
+    const int STATE_TOW_DECODED            = 1 << 3;
+    const int STATE_MSEC_AMBIGUOUS         = 1 << 4;
+    const int STATE_SYMBOL_SYNC            = 1 << 5;
+    const int STATE_GLO_STRING_SYNC        = 1 << 6;
+    const int STATE_GLO_TOD_DECODED        = 1 << 7;
+    const int STATE_BDS_D2_BIT_SYNC        = 1 << 8;
+    const int STATE_BDS_D2_SUBFRAME_SYNC   = 1 << 9;
+    const int STATE_GAL_E1BC_CODE_LOCK     = 1 << 10;
+    const int STATE_GAL_E1C_2ND_CODE_LOCK  = 1 << 11;
+    const int STATE_GAL_E1B_PAGE_SYNC      = 1 << 12;
+    const int STATE_SBAS_SYNC              = 1 << 13;
+    const int STATE_TOW_KNOWN              = 1 << 14;
+    const int STATE_GLO_TOD_KNOWN          = 1 << 15;
+    const int STATE_2ND_CODE_LOCK          = 1 << 16;
+
+    /**
+     * A bitfield of flags indicating the GnssMeasurementState per satellite sync state. It
+     * represents the current sync state for the associated satellite.
+     *
+     * Based on the sync state, the 'received GNSS tow' field must be interpreted accordingly.
+     *
+     * The bit masks are defined in the constants with prefix STATE_.
+     *
+     * This value is mandatory.
+     */
+    int state;
+
+    /**
+     * The received GNSS Time-of-Week at the measurement time, in nanoseconds.
+     * For GNSS & QZSS, this is the received GNSS Time-of-Week at the
+     * measurement time, in nanoseconds. The value is relative to the
+     * beginning of the current GNSS week.
+     *
+     * Given the highest sync state that can be achieved, per each satellite,
+     * valid range for this field can be:
+     * Searching       : [ 0       ] : STATE_UNKNOWN
+     * C/A code lock   : [ 0 1ms   ] : STATE_CODE_LOCK set
+     * Bit sync        : [ 0 20ms  ] : STATE_BIT_SYNC set
+     * Subframe sync   : [ 0  6s   ] : STATE_SUBFRAME_SYNC set
+     * TOW decoded     : [ 0 1week ] : STATE_TOW_DECODED set
+     * TOW Known       : [ 0 1week ] : STATE_TOW_KNOWN set
+     *
+     * Note: TOW Known refers to the case where TOW is possibly not decoded
+     * over the air but has been determined from other sources. If TOW
+     * decoded is set then TOW Known must also be set.
+     *
+     * Note: If there is any ambiguity in integer millisecond,
+     * STATE_MSEC_AMBIGUOUS must be set accordingly, in the
+     * 'state' field.
+     *
+     * This value must be populated if 'state' != STATE_UNKNOWN.
+     *
+     * For Glonass, this is the received Glonass time of day, at the
+     * measurement time in nanoseconds.
+     *
+     * Given the highest sync state that can be achieved, per each satellite,
+     * valid range for this field can be:
+     * Searching           : [ 0       ] : STATE_UNKNOWN set
+     * C/A code lock       : [ 0   1ms ] : STATE_CODE_LOCK set
+     * Symbol sync         : [ 0  10ms ] : STATE_SYMBOL_SYNC set
+     * Bit sync            : [ 0  20ms ] : STATE_BIT_SYNC set
+     * String sync         : [ 0    2s ] : STATE_GLO_STRING_SYNC set
+     * Time of day decoded : [ 0  1day ] : STATE_GLO_TOD_DECODED set
+     * Time of day known   : [ 0  1day ] : STATE_GLO_TOD_KNOWN set
+     *
+     * Note: Time of day known refers to the case where it is possibly not
+     * decoded over the air but has been determined from other sources. If
+     * Time of day decoded is set then Time of day known must also be set.
+     *
+     * For Beidou, this is the received Beidou time of week,
+     * at the measurement time in nanoseconds.
+     *
+     * Given the highest sync state that can be achieved, per each satellite,
+     * valid range for this field can be:
+     * Searching            : [ 0       ] : STATE_UNKNOWN set.
+     * C/A code lock        : [ 0   1ms ] : STATE_CODE_LOCK set.
+     * Bit sync (D2)        : [ 0   2ms ] : STATE_BDS_D2_BIT_SYNC set.
+     * Bit sync (D1)        : [ 0  20ms ] : STATE_BIT_SYNC set.
+     * Subframe (D2)        : [ 0  0.6s ] : STATE_BDS_D2_SUBFRAME_SYNC set.
+     * Subframe (D1)        : [ 0    6s ] : STATE_SUBFRAME_SYNC set.
+     * Time of week decoded : [ 0 1week ] : STATE_TOW_DECODED set.
+     * Time of week known   : [ 0 1week ] : STATE_TOW_KNOWN set
+     *
+     * Note: TOW Known refers to the case where TOW is possibly not decoded
+     * over the air but has been determined from other sources. If TOW
+     * decoded is set then TOW Known must also be set.
+     *
+     * For Galileo, this is the received Galileo time of week,
+     * at the measurement time in nanoseconds.
+     *
+     * E1BC code lock       : [ 0  4ms ] : STATE_GAL_E1BC_CODE_LOCK set.
+     * E1C 2nd code lock    : [ 0 100ms] : STATE_GAL_E1C_2ND_CODE_LOCK set.
+     * E1B page             : [ 0   2s ] : STATE_GAL_E1B_PAGE_SYNC set.
+     * Time of week decoded : [ 0 1week] : STATE_TOW_DECODED is set.
+     * Time of week known   : [ 0 1week] : STATE_TOW_KNOWN set
+     *
+     * Note: TOW Known refers to the case where TOW is possibly not decoded
+     * over the air but has been determined from other sources. If TOW
+     * decoded is set then TOW Known must also be set.
+     *
+     * For SBAS, this is received SBAS time, at the measurement time in
+     * nanoseconds.
+     *
+     * Given the highest sync state that can be achieved, per each satellite,
+     * valid range for this field can be:
+     * Searching    : [ 0     ] : STATE_UNKNOWN
+     * C/A code lock: [ 0 1ms ] : STATE_CODE_LOCK is set
+     * Symbol sync  : [ 0 2ms ] : STATE_SYMBOL_SYNC is set
+     * Message      : [ 0  1s ] : STATE_SBAS_SYNC is set
+     */
+    long receivedSvTimeInNs;
+
+    /**
+     * 1-Sigma uncertainty of the Received GNSS Time-of-Week in nanoseconds.
+     *
+     * This value must be populated if 'state' != STATE_UNKNOWN.
+     */
+    long receivedSvTimeUncertaintyInNs;
+
+    /**
+     * Carrier-to-noise density in dB-Hz, typically in the range [0, 63].
+     * It contains the measured C/N0 value for the signal at the antenna port.
+     *
+     * If a signal has separate components (e.g. Pilot and Data channels) and
+     * the receiver only processes one of the components, then the reported
+     * antennaCN0DbHz reflects only the component that is processed.
+     *
+     * This value is mandatory.
+     */
+    double antennaCN0DbHz;
+
+    /**
+     * Baseband Carrier-to-noise density in dB-Hz, typically in the range [0, 63]. It contains the
+     * measured C/N0 value for the signal measured at the baseband.
+     *
+     * This is typically a few dB weaker than the value estimated for C/N0 at the antenna port,
+     * which is reported in cN0DbHz.
+     *
+     * If a signal has separate components (e.g. Pilot and Data channels) and the receiver only
+     * processes one of the components, then the reported basebandCN0DbHz reflects only the
+     * component that is processed.
+     *
+     * This value is mandatory.
+     */
+    double basebandCN0DbHz;
+
+    /**
+     * Pseudorange rate at the timestamp in m/s. The correction of a given
+     * Pseudorange Rate value includes corrections for receiver and satellite
+     * clock frequency errors. Ensure that this field is independent (see
+     * comment at top of GnssMeasurement struct.)
+     *
+     * It is mandatory to provide the 'uncorrected' 'pseudorange rate', and
+     * provide GnssClock's 'drift' field as well. When providing the
+     * uncorrected pseudorange rate, do not apply the corrections described above.)
+     *
+     * The value includes the 'pseudorange rate uncertainty' in it.
+     * A positive 'uncorrected' value indicates that the SV is moving away from
+     * the receiver.
+     *
+     * The sign of the 'uncorrected' 'pseudorange rate' and its relation to the
+     * sign of 'doppler shift' is given by the equation:
+     *      pseudorange rate = -k * doppler shift   (where k is a constant)
+     *
+     * This must be the most accurate pseudorange rate available, based on
+     * fresh signal measurements from this channel.
+     *
+     * It is mandatory that this value be provided at typical carrier phase PRR
+     * quality (few cm/sec per second of uncertainty, or better) - when signals
+     * are sufficiently strong & stable, e.g. signals from a GNSS simulator at >=
+     * 35 dB-Hz.
+     */
+    double pseudorangeRateMps;
+
+    /**
+     * 1-Sigma uncertainty of the pseudorangeRateMps.
+     * The uncertainty is represented as an absolute (single sided) value.
+     *
+     * This value is mandatory.
+     */
+    double pseudorangeRateUncertaintyMps;
+
+
+    /**
+     * Flags indicating the Accumulated Delta Range's states.
+     *
+     * See the table below for a detailed interpretation of each state.
+     *
+     * +---------------------+-------------------+-----------------------------+
+     * | ADR_STATE           | Time of relevance | Interpretation              |
+     * +---------------------+-------------------+-----------------------------+
+     * | UNKNOWN             | ADR(t)            | No valid carrier phase      |
+     * |                     |                   | information is available    |
+     * |                     |                   | at time t.                  |
+     * +---------------------+-------------------+-----------------------------+
+     * | VALID               | ADR(t)            | Valid carrier phase         |
+     * |                     |                   | information is available    |
+     * |                     |                   | at time t. This indicates   |
+     * |                     |                   | that this measurement can   |
+     * |                     |                   | be used as a reference for  |
+     * |                     |                   | future measurements.        |
+     * |                     |                   | However, to compare it to   |
+     * |                     |                   | previous measurements to    |
+     * |                     |                   | compute delta range,        |
+     * |                     |                   | other bits should be        |
+     * |                     |                   | checked. Specifically, it   |
+     * |                     |                   | can be used for delta range |
+     * |                     |                   | computation if it is valid  |
+     * |                     |                   | and has no reset or cycle   |
+     * |                     |                   | slip at this epoch i.e.     |
+     * |                     |                   | if VALID_BIT == 1 &&        |
+     * |                     |                   | CYCLE_SLIP_BIT == 0 &&      |
+     * |                     |                   | RESET_BIT == 0.             |
+     * +---------------------+-------------------+-----------------------------+
+     * | RESET               | ADR(t) - ADR(t-1) | Carrier phase accumulation  |
+     * |                     |                   | has been restarted between  |
+     * |                     |                   | current time t and previous |
+     * |                     |                   | time t-1. This indicates    |
+     * |                     |                   | that this measurement can   |
+     * |                     |                   | be used as a reference for  |
+     * |                     |                   | future measurements, but it |
+     * |                     |                   | should not be compared to   |
+     * |                     |                   | previous measurements to    |
+     * |                     |                   | compute delta range.        |
+     * +---------------------+-------------------+-----------------------------+
+     * | CYCLE_SLIP          | ADR(t) - ADR(t-1) | Cycle slip(s) have been     |
+     * |                     |                   | detected between the        |
+     * |                     |                   | current time t and previous |
+     * |                     |                   | time t-1. This indicates    |
+     * |                     |                   | that this measurement can   |
+     * |                     |                   | be used as a reference for  |
+     * |                     |                   | future measurements.        |
+     * |                     |                   | Clients can use a           |
+     * |                     |                   | measurement with a cycle    |
+     * |                     |                   | slip to compute delta range |
+     * |                     |                   | against previous            |
+     * |                     |                   | measurements at their own   |
+     * |                     |                   | risk.                       |
+     * +---------------------+-------------------+-----------------------------+
+     * | HALF_CYCLE_RESOLVED | ADR(t)            | Half cycle ambiguity is     |
+     * |                     |                   | resolved at time t.         |
+     * +---------------------+-------------------+-----------------------------+
+     */
+    const int ADR_STATE_UNKNOWN = 0;
+    const int ADR_STATE_VALID = 1 << 0;
+    const int ADR_STATE_RESET = 1 << 1;
+    const int ADR_STATE_CYCLE_SLIP = 1 << 2;
+    const int ADR_STATE_HALF_CYCLE_RESOLVED = 1 << 3;
+
+    /**
+     * A bitfield of flags indicating the accumulated delta range's state. It indicates whether ADR
+     * is reset or there is a cycle slip(indicating loss of lock).
+     *
+     * The bit masks are defined in constants with prefix ADR_STATE_.
+     *
+     * This value is mandatory.
+     */
+    int accumulatedDeltaRangeState;
+
+    /**
+     * Accumulated delta range since the last channel reset in meters.
+     * A positive value indicates that the SV is moving away from the receiver.
+     *
+     * The sign of the 'accumulated delta range' and its relation to the sign of
+     * 'carrier phase' is given by the equation:
+     * accumulated delta range = -k * carrier phase (where k is a constant)
+     *
+     * This value must be populated if 'accumulated delta range state' !=
+     * ADR_STATE_UNKNOWN.
+     * However, it is expected that the data is only accurate when:
+     *      'accumulated delta range state' == ADR_STATE_VALID.
+     *
+     * The alignment of the phase measurement will not be  adjusted by the receiver so the in-phase
+     * and quadrature phase components will have a quarter cycle offset as they do when transmitted
+     * from the satellites. If the measurement is from a combination of the in-phase and quadrature
+     * phase components, then the alignment of the phase measurement will be aligned to the in-phase
+     * component.
+     */
+    double accumulatedDeltaRangeM;
+
+    /**
+     * 1-Sigma uncertainty of the accumulated delta range in meters.
+     * This value must be populated if 'accumulated delta range state' !=
+     * ADR_STATE_UNKNOWN.
+     */
+    double accumulatedDeltaRangeUncertaintyM;
+
+    /**
+     * Carrier frequency of the signal tracked, for example it can be the
+     * GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz, L5 =
+     * 1176.45 MHz, varying GLO channels, etc. If the field is not set, it
+     * is the primary common use central frequency, e.g. L1 = 1575.45 MHz
+     * for GPS.
+     *
+     * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same
+     * time, two raw measurement structs must be reported for this same
+     * satellite, in one of the measurement structs, all the values related
+     * to L1 must be filled, and in the other all of the values related to
+     * L5 must be filled.
+     *
+     * If the data is available, gnssMeasurementFlags must contain
+     * HAS_CARRIER_FREQUENCY.
+     */
+    float carrierFrequencyHz;
+
+    /**
+     * The number of full carrier cycles between the satellite and the
+     * receiver. The reference frequency is given by the field
+     * 'carrierFrequencyHz'. Indications of possible cycle slips and
+     * resets in the accumulation of this value can be inferred from the
+     * accumulatedDeltaRangeState flags.
+     *
+     * If the data is available, gnssMeasurementFlags must contain
+     * HAS_CARRIER_CYCLES.
+     */
+    long carrierCycles;
+
+    /**
+     * The RF phase detected by the receiver, in the range [0.0, 1.0].
+     * This is usually the fractional part of the complete carrier phase
+     * measurement.
+     *
+     * The reference frequency is given by the field 'carrierFrequencyHz'.
+     * The value contains the 'carrier-phase uncertainty' in it.
+     *
+     * If the data is available, gnssMeasurementFlags must contain
+     * HAS_CARRIER_PHASE.
+     */
+    double carrierPhase;
+
+    /**
+     * 1-Sigma uncertainty of the carrier-phase.
+     * If the data is available, gnssMeasurementFlags must contain
+     * HAS_CARRIER_PHASE_UNCERTAINTY.
+     */
+    double carrierPhaseUncertainty;
+
+    /**
+     * An enumeration that indicates the 'multipath' state of the event.
+     *
+     * The multipath Indicator is intended to report the presence of overlapping
+     * signals that manifest as distorted correlation peaks.
+     *
+     * - if there is a distorted correlation peak shape, report that multipath
+     *   is MULTIPATH_INDICATOR_PRESENT.
+     * - if there is no distorted correlation peak shape, report
+     *   MULTIPATH_INDICATOR_NOT_PRESENT
+     * - if signals are too weak to discern this information, report
+     *   MULTIPATH_INDICATOR_UNKNOWN
+     *
+     * Example: when doing the standardized overlapping Multipath Performance
+     * test (3GPP TS 34.171) the Multipath indicator must report
+     * MULTIPATH_INDICATOR_PRESENT for those signals that are tracked, and
+     * contain multipath, and MULTIPATH_INDICATOR_NOT_PRESENT for those
+     * signals that are tracked and do not contain multipath.
+     */
+    GnssMultipathIndicator multipathIndicator;
+
+    /**
+     * Signal-to-noise ratio at correlator output in dB.
+     * If the data is available, GnssMeasurementFlags must contain HAS_SNR.
+     * This is the power ratio of the "correlation peak height above the
+     * observed noise floor" to "the noise RMS".
+     */
+    double snrDb;
+
+    /**
+     * Automatic gain control (AGC) level. AGC acts as a variable gain
+     * amplifier adjusting the power of the incoming signal. The AGC level
+     * may be used to indicate potential interference. When AGC is at a
+     * nominal level, this value must be set as 0. Higher gain (and/or lower
+     * input power) must be output as a positive number. Hence in cases of
+     * strong jamming, in the band of this signal, this value must go more
+     * negative.
+     *
+     * Note: Different hardware designs (e.g. antenna, pre-amplification, or
+     * other RF HW components) may also affect the typical output of this
+     * value on any given hardware design in an open sky test - the
+     * important aspect of this output is that changes in this value are
+     * indicative of changes on input signal power in the frequency band for
+     * this measurement.
+     */
+    double agcLevelDb;
+
+    /**
+     * The full inter-signal bias (ISB) in nanoseconds.
+     *
+     * This value is the sum of the estimated receiver-side and the space-segment-side inter-system
+     * bias, inter-frequency bias and inter-code bias, including
+     *
+     * - Receiver inter-constellation bias (with respect to the constellation in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Receiver inter-frequency bias (with respect to the carrier frequency in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Receiver inter-code bias (with respect to the code type in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPS-UTC Time Offset (TauGps), BDS-GLO
+     *   Time Offset (BGTO)) (with respect to the constellation in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Group delay (e.g., Total Group Delay (TGD))
+     * - Satellite inter-frequency bias (GLO only) (with respect to the carrier frequency in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Satellite inter-code bias (e.g., Differential Code Bias (DCB)) (with respect to the code
+     *   type in GnssClock.referenceSignalTypeForIsb)
+     *
+     * If a component of the above is already compensated in the provided
+     * GnssMeasurement.receivedSvTimeInNs, then it must not be included in the reported full ISB.
+     *
+     * The value does not include the inter-frequency Ionospheric bias.
+     *
+     * The full ISB of GnssClock.referenceSignalTypeForIsb is defined to be 0.0 nanoseconds.
+     */
+    double fullInterSignalBiasNs;
+
+    /**
+     * 1-sigma uncertainty associated with the full inter-signal bias in nanoseconds.
+     */
+    double fullInterSignalBiasUncertaintyNs;
+
+    /**
+     * The satellite inter-signal bias in nanoseconds.
+     *
+     * This value is the sum of the space-segment-side inter-system bias, inter-frequency bias
+     * and inter-code bias, including
+     *
+     * - Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPS-UTC Time Offset (TauGps), BDS-GLO
+     *   Time Offset (BGTO)) (with respect to the constellation in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Group delay (e.g., Total Group Delay (TGD))
+     * - Satellite inter-frequency bias (GLO only) (with respect to the carrier frequency in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Satellite inter-code bias (e.g., Differential Code Bias (DCB)) (with respect to the code
+     *   type in GnssClock.referenceSignalTypeForIsb)
+     *
+     * The satellite ISB of GnssClock.referenceSignalTypeForIsb is defined to be 0.0 nanoseconds.
+     */
+    double satelliteInterSignalBiasNs;
+
+    /**
+     * 1-sigma uncertainty associated with the satellite inter-signal bias in nanoseconds.
+     */
+    double satelliteInterSignalBiasUncertaintyNs;
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssMultipathIndicator.aidl b/gnss/aidl/android/hardware/gnss/GnssMultipathIndicator.aidl
new file mode 100644
index 0000000..ec1ce62
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssMultipathIndicator.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.gnss;
+
+/**
+ * Enumeration of available values for the GNSS Measurement's multipath
+ * indicator.
+ */
+@VintfStability
+@Backing(type="int")
+enum GnssMultipathIndicator {
+    /** The indicator is not available or unknown. */
+    UNKNOWN      = 0,
+    /** The measurement is indicated to be affected by multipath. */
+    PRESENT      = 1,
+    /** The measurement is indicated to be not affected by multipath. */
+    NOT_PRESENT = 2,
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl b/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
new file mode 100644
index 0000000..a284fed
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssConstellationType;
+
+/**
+ * Represents a GNSS signal type.
+ */
+@VintfStability
+parcelable GnssSignalType {
+    /**
+     * Constellation type of the SV that transmits the signal.
+     */
+    GnssConstellationType constellation;
+
+    /**
+     * Carrier frequency of the signal tracked, for example it can be the
+     * GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz, L5 =
+     * 1176.45 MHz, varying GLO channels, etc. If the field is not set, it
+     * is the primary common use central frequency, e.g. L1 = 1575.45 MHz
+     * for GPS.
+     *
+     * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same
+     * time, two raw measurement structs must be reported for this same
+     * satellite, in one of the measurement structs, all the values related
+     * to L1 must be filled, and in the other all of the values related to
+     * L5 must be filled.
+     */
+    double carrierFrequencyHz;
+
+    /**
+     * GNSS signal code type "A" representing GALILEO E1A, GALILEO E6A, IRNSS L5A, IRNSS SA.
+     */
+    const String CODE_TYPE_A = "A";
+
+    /**
+     * GNSS signal code type "B" representing GALILEO E1B, GALILEO E6B, IRNSS L5B, IRNSS SB.
+     */
+    const String CODE_TYPE_B = "B";
+
+    /**
+     * GNSS signal code type "C" representing GPS L1 C/A,  GPS L2 C/A, GLONASS G1 C/A,
+     * GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C.
+     */
+    const String CODE_TYPE_C = "C";
+
+    /**
+     * GNSS signal code type "D" representing BDS B1C D.
+     */
+    const String CODE_TYPE_D = "D";
+
+    /**
+     * GNSS signal code type "I" representing GPS L5 I, GLONASS G3 I, GALILEO E5a I, GALILEO E5b I,
+     * GALILEO E5a+b I, SBAS L5 I, QZSS L5 I, BDS B1 I, BDS B2 I, BDS B3 I.
+     */
+    const String CODE_TYPE_I = "I";
+
+    /**
+     * GNSS signal code type "L" representing GPS L1C (P), GPS L2C (L), QZSS L1C (P), QZSS L2C (L),
+     * LEX(6) L.
+     */
+    const String CODE_TYPE_L = "L";
+
+    /**
+     * GNSS signal code type "M" representing GPS L1M, GPS L2M.
+     */
+    const String CODE_TYPE_M = "M";
+
+    /**
+     * GNSS signal code type "N" representing GPS L1 codeless, GPS L2 codeless.
+     */
+    const String CODE_TYPE_N = "N";
+
+    /**
+     * GNSS signal code type "P" representing GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P, BDS B1C P.
+     */
+    const String CODE_TYPE_P = "P";
+
+    /**
+     * GNSS signal code type "Q" representing GPS L5 Q, GLONASS G3 Q, GALILEO E5a Q, GALILEO E5b Q,
+     * GALILEO E5a+b Q, SBAS L5 Q, QZSS L5 Q, BDS B1 Q, BDS B2 Q, BDS B3 Q.
+     */
+    const String CODE_TYPE_Q = "Q";
+
+    /**
+     * GNSS signal code type "S" represents GPS L1C (D), GPS L2C (M), QZSS L1C (D), QZSS L2C (M),
+     * LEX(6) S.
+     */
+    const String CODE_TYPE_S = "S";
+
+    /**
+     * GNSS signal code type "W" representing GPS L1 Z-tracking, GPS L2 Z-tracking.
+     */
+    const String CODE_TYPE_W = "W";
+
+    /**
+     * GNSS signal code type "X" representing GPS L1C (D+P), GPS L2C (M+L), GPS L5 (I+Q),
+     * GLONASS G3 (I+Q), GALILEO E1 (B+C), GALILEO E5a (I+Q), GALILEO E5b (I+Q), GALILEO E5a+b(I+Q),
+     * GALILEO E6 (B+C), SBAS L5 (I+Q), QZSS L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q),
+     * LEX(6) (S+L), BDS B1 (I+Q), BDS B1C (D+P), BDS B2 (I+Q), BDS B3 (I+Q), IRNSS L5 (B+C).
+     */
+    const String CODE_TYPE_X = "X";
+
+    /**
+     * GNSS signal code type "Y" representing GPS L1Y, GPS L2Y.
+     */
+    const String CODE_TYPE_Y = "Y";
+
+    /**
+     * GNSS signal code type "Z" representing GALILEO E1 (A+B+C), GALILEO E6 (A+B+C), QZSS L1-SAIF.
+     */
+    const String CODE_TYPE_Z = "Z";
+
+    /**
+     * GNSS signal code type "UNKNOWN" representing the GNSS Measurement's code type is unknown.
+     */
+    const String CODE_TYPE_UNKNOWN = "UNKNOWN";
+
+    /**
+     * The type of code that is currently being tracked in the GNSS signal.
+     *
+     * For high precision applications the type of code being tracked needs to be considered
+     * in-order to properly apply code specific corrections to the pseudorange measurements.
+     *
+     * The value is one of the constant Strings with prefix CODE_TYPE_ defined in this parcelable.
+     *
+     * This is used to specify the observation descriptor defined in GNSS Observation Data File
+     * Header Section Description in the RINEX standard (Version 3.XX). In RINEX Version 3.03,
+     * in Appendix Table A2 Attributes are listed as uppercase letters (for instance, "A" for
+     * "A channel"). In the future, if for instance a code "G" was added in the official RINEX
+     * standard, "G" could be specified here.
+     */
+    String codeType;
+}
diff --git a/gnss/aidl/android/hardware/gnss/IGnss.aidl b/gnss/aidl/android/hardware/gnss/IGnss.aidl
index 2af57b5..c815e2d 100644
--- a/gnss/aidl/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnss.aidl
@@ -17,9 +17,10 @@
 package android.hardware.gnss;
 
 import android.hardware.gnss.IGnssCallback;
+import android.hardware.gnss.IGnssConfiguration;
+import android.hardware.gnss.IGnssMeasurementInterface;
 import android.hardware.gnss.IGnssPowerIndication;
 import android.hardware.gnss.IGnssPsds;
-import android.hardware.gnss.IGnssConfiguration;
 
 /**
  * Represents the standard GNSS (Global Navigation Satellite System) interface.
@@ -33,14 +34,6 @@
      */
     const int ERROR_INVALID_ARGUMENT = 1;
 
-    /** Bit mask indicating a valid timestampNs is stored in the ElapsedRealtime parcelable. */
-    const int ELAPSED_REALTIME_HAS_TIMESTAMP_NS = 1 << 0;
-
-    /**
-     * Bit mask indicating a valid timeUncertaintyNs is stored in the ElapsedRealtime parcelable.
-     */
-    const int ELAPSED_REALTIME_HAS_TIME_UNCERTAINTY_NS = 1 << 1;
-
     /**
      * Opens the interface and provides the callback routines to the implementation of this
      * interface.
@@ -74,6 +67,8 @@
     /**
      * This method returns the IGnssPsds interface.
      *
+     * This method must return non-null.
+     *
      * @return Handle to the IGnssPsds interface.
      */
     IGnssPsds getExtensionPsds();
@@ -81,13 +76,26 @@
     /**
      * This method returns the IGnssConfiguration interface.
      *
+     * This method must return non-null.
+     *
      * @return Handle to the IGnssConfiguration interface.
      */
     IGnssConfiguration getExtensionGnssConfiguration();
 
     /**
+     * This methods returns the IGnssMeasurementInterface interface.
+     *
+     * This method must return non-null.
+     *
+     * @return Handle to the IGnssMeasurementInterface interface.
+     */
+    IGnssMeasurementInterface getExtensionGnssMeasurement();
+
+    /**
      * This method returns the IGnssPowerIndication interface.
      *
+     * This method must return non-null.
+     *
      * @return Handle to the IGnssPowerIndication interface.
      */
     IGnssPowerIndication getExtensionGnssPowerIndication();
diff --git a/gnss/aidl/android/hardware/gnss/IGnssMeasurementCallback.aidl b/gnss/aidl/android/hardware/gnss/IGnssMeasurementCallback.aidl
new file mode 100644
index 0000000..328cf2a
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/IGnssMeasurementCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssData;
+
+/**
+ * The callback interface to report GNSS Measurement from the HAL.
+ */
+@VintfStability
+interface IGnssMeasurementCallback {
+    /**
+     * Callback for the hal to pass a GnssData structure back to the client.
+     *
+     * @param data Contains a reading of GNSS measurements.
+     */
+    void gnssMeasurementCb(in GnssData data);
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl b/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl
new file mode 100644
index 0000000..fdeebde
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.IGnssMeasurementCallback;
+
+/**
+ * Extended interface for GNSS Measurement support.
+ */
+@VintfStability
+interface IGnssMeasurementInterface {
+    /**
+     * Initializes the interface and registers the callback routines with the HAL. After a
+     * successful call to 'setCallback' the HAL must begin to provide updates at an average
+     * output rate of 1Hz (occasional intra-measurement time offsets in the range from 0-2000msec
+     * can be tolerated.)
+     *
+     * @param callback Handle to GnssMeasurement callback interface.
+     * @param enableFullTracking If true, GNSS chipset must switch off duty cycling. In such mode
+     *     no clock discontinuities are expected and, when supported, carrier phase should be
+     *     continuous in good signal conditions. All non-blocklisted, healthy constellations,
+     *     satellites and frequency bands that the chipset supports must be reported in this mode.
+     *     The GNSS chipset is allowed to consume more power in this mode. If false, API must
+     *     optimize power via duty cycling, constellations and frequency limits, etc.
+     *
+     * @return initRet Returns SUCCESS if successful. Returns ERROR_ALREADY_INIT if a callback has
+     *     already been registered without a corresponding call to 'close'. Returns ERROR_GENERIC
+     *     for any other error. The HAL must not generate any other updates upon returning this
+     *     error code.
+     */
+    void setCallback(in IGnssMeasurementCallback callback, in boolean enableFullTracking);
+
+    /**
+     * Stops updates from the HAL, and unregisters the callback routines. After a call to close(),
+     * the previously registered callbacks must be considered invalid by the HAL.
+     *
+     * If close() is invoked without a previous setCallback, this function must perform
+     * no work.
+     */
+    void close();
+}
\ No newline at end of file
diff --git a/gnss/aidl/default/Android.bp b/gnss/aidl/default/Android.bp
index 23677ed..6694ce6 100644
--- a/gnss/aidl/default/Android.bp
+++ b/gnss/aidl/default/Android.bp
@@ -50,6 +50,7 @@
         "GnssPowerIndication.cpp",
         "GnssPsds.cpp",
         "GnssConfiguration.cpp",
+        "GnssMeasurementInterface.cpp",
         "service.cpp",
     ],
     static_libs: [
diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp
index 669372b..02bad60 100644
--- a/gnss/aidl/default/Gnss.cpp
+++ b/gnss/aidl/default/Gnss.cpp
@@ -19,6 +19,7 @@
 #include "Gnss.h"
 #include <log/log.h>
 #include "GnssConfiguration.h"
+#include "GnssMeasurementInterface.h"
 #include "GnssPowerIndication.h"
 #include "GnssPsds.h"
 
@@ -74,4 +75,12 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Gnss::getExtensionGnssMeasurement(
+        std::shared_ptr<IGnssMeasurementInterface>* iGnssMeasurement) {
+    ALOGD("Gnss::getExtensionGnssMeasurement");
+
+    *iGnssMeasurement = SharedRefBase::make<GnssMeasurementInterface>();
+    return ndk::ScopedAStatus::ok();
+}
+
 }  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/Gnss.h b/gnss/aidl/default/Gnss.h
index 9f6ef0e..bccc7f2 100644
--- a/gnss/aidl/default/Gnss.h
+++ b/gnss/aidl/default/Gnss.h
@@ -18,6 +18,7 @@
 
 #include <aidl/android/hardware/gnss/BnGnss.h>
 #include <aidl/android/hardware/gnss/BnGnssConfiguration.h>
+#include <aidl/android/hardware/gnss/BnGnssMeasurementInterface.h>
 #include <aidl/android/hardware/gnss/BnGnssPowerIndication.h>
 #include <aidl/android/hardware/gnss/BnGnssPsds.h>
 #include "GnssConfiguration.h"
@@ -33,6 +34,8 @@
             std::shared_ptr<IGnssConfiguration>* iGnssConfiguration) override;
     ndk::ScopedAStatus getExtensionGnssPowerIndication(
             std::shared_ptr<IGnssPowerIndication>* iGnssPowerIndication) override;
+    ndk::ScopedAStatus getExtensionGnssMeasurement(
+            std::shared_ptr<IGnssMeasurementInterface>* iGnssMeasurement) override;
 
     std::shared_ptr<GnssConfiguration> mGnssConfiguration;
 
diff --git a/gnss/aidl/default/GnssConfiguration.h b/gnss/aidl/default/GnssConfiguration.h
index 25fa16e..491733c 100644
--- a/gnss/aidl/default/GnssConfiguration.h
+++ b/gnss/aidl/default/GnssConfiguration.h
@@ -24,8 +24,6 @@
 
 namespace aidl::android::hardware::gnss {
 
-using android::hardware::gnss::GnssConstellationType;
-
 struct BlocklistedSourceHash {
     inline int operator()(const BlocklistedSource& source) const {
         return int(source.constellation) * 1000 + int(source.svid);
@@ -42,7 +40,8 @@
 using std::vector;
 using BlocklistedSourceSet =
         std::unordered_set<BlocklistedSource, BlocklistedSourceHash, BlocklistedSourceEqual>;
-using BlocklistedConstellationSet = std::unordered_set<GnssConstellationType>;
+using BlocklistedConstellationSet =
+        std::unordered_set<android::hardware::gnss::GnssConstellationType>;
 
 struct GnssConfiguration : public BnGnssConfiguration {
   public:
diff --git a/gnss/aidl/default/GnssHidlHal.cpp b/gnss/aidl/default/GnssHidlHal.cpp
index 11fc806..9529ec9 100644
--- a/gnss/aidl/default/GnssHidlHal.cpp
+++ b/gnss/aidl/default/GnssHidlHal.cpp
@@ -17,11 +17,10 @@
 #define LOG_TAG "GnssHidlHal"
 
 #include "GnssHidlHal.h"
-//#include <android/hardware/gnss/1.0/IGnssCallback.h>
 
 namespace aidl::android::hardware::gnss {
 
-namespace V1_0 = ::android::hardware::gnss::V1_0;
+using GnssSvInfo = ::android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo;
 
 GnssHidlHal::GnssHidlHal(const std::shared_ptr<Gnss>& gnssAidl) : mGnssAidl(gnssAidl) {
     Gnss* iGnss = mGnssAidl.get();
@@ -45,8 +44,8 @@
         if (mGnssConfigurationAidl->isBlocklistedV2_1(gnssSvInfoList[i])) {
             ALOGD("Blocklisted constellation: %d, svid: %d",
                   (int)gnssSvInfoList[i].v2_0.constellation, gnssSvInfoList[i].v2_0.v1_0.svid);
-            gnssSvInfoList[i].v2_0.v1_0.svFlag &=
-                    ~static_cast<uint8_t>(V1_0::IGnssCallback::GnssSvFlags::USED_IN_FIX);
+            gnssSvInfoList[i].v2_0.v1_0.svFlag &= ~static_cast<uint8_t>(
+                    ::android::hardware::gnss::V1_0::IGnssCallback::GnssSvFlags::USED_IN_FIX);
         }
     }
     return gnssSvInfoList;
diff --git a/gnss/aidl/default/GnssHidlHal.h b/gnss/aidl/default/GnssHidlHal.h
index 50aad3a..93a79a1 100644
--- a/gnss/aidl/default/GnssHidlHal.h
+++ b/gnss/aidl/default/GnssHidlHal.h
@@ -22,16 +22,16 @@
 
 namespace aidl::android::hardware::gnss {
 
-using ::android::hardware::gnss::common::implementation::GnssTemplate;
-using GnssSvInfo = ::android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo;
-
-class GnssHidlHal : public GnssTemplate<::android::hardware::gnss::V2_1::IGnss> {
+class GnssHidlHal : public ::android::hardware::gnss::common::implementation::GnssTemplate<
+                            ::android::hardware::gnss::V2_1::IGnss> {
   public:
     GnssHidlHal(const std::shared_ptr<Gnss>& gnssAidl);
 
   private:
-    hidl_vec<GnssSvInfo> filterBlocklistedSatellitesV2_1(
-            hidl_vec<GnssSvInfo> gnssSvInfoList) override;
+    hidl_vec<::android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>
+    filterBlocklistedSatellitesV2_1(
+            hidl_vec<::android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo> gnssSvInfoList)
+            override;
 
     std::shared_ptr<Gnss> mGnssAidl;
     std::shared_ptr<GnssConfiguration> mGnssConfigurationAidl;
diff --git a/gnss/aidl/default/GnssMeasurementInterface.cpp b/gnss/aidl/default/GnssMeasurementInterface.cpp
new file mode 100644
index 0000000..d726d95
--- /dev/null
+++ b/gnss/aidl/default/GnssMeasurementInterface.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 "GnssMeasIfaceAidl"
+
+#include "GnssMeasurementInterface.h"
+#include <aidl/android/hardware/gnss/BnGnss.h>
+#include <log/log.h>
+#include "Utils.h"
+
+namespace aidl::android::hardware::gnss {
+
+using Utils = ::android::hardware::gnss::common::Utils;
+
+std::shared_ptr<IGnssMeasurementCallback> GnssMeasurementInterface::sCallback = nullptr;
+
+GnssMeasurementInterface::GnssMeasurementInterface() : mMinIntervalMillis(1000) {}
+
+GnssMeasurementInterface::~GnssMeasurementInterface() {
+    stop();
+}
+
+ndk::ScopedAStatus GnssMeasurementInterface::setCallback(
+        const std::shared_ptr<IGnssMeasurementCallback>& callback, const bool enableFullTracking) {
+    ALOGD("setCallback: enableFullTracking: %d", (int)enableFullTracking);
+    std::unique_lock<std::mutex> lock(mMutex);
+    sCallback = callback;
+
+    if (mIsActive) {
+        ALOGW("GnssMeasurement callback already set. Resetting the callback...");
+        stop();
+    }
+    start();
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus GnssMeasurementInterface::close() {
+    ALOGD("close");
+    stop();
+    std::unique_lock<std::mutex> lock(mMutex);
+    sCallback = nullptr;
+    return ndk::ScopedAStatus::ok();
+}
+
+void GnssMeasurementInterface::start() {
+    ALOGD("start");
+    mIsActive = true;
+    mThread = std::thread([this]() {
+        while (mIsActive == true) {
+            auto measurement = Utils::getMockMeasurement();
+            this->reportMeasurement(measurement);
+
+            std::this_thread::sleep_for(std::chrono::milliseconds(mMinIntervalMillis));
+        }
+    });
+}
+
+void GnssMeasurementInterface::stop() {
+    ALOGD("stop");
+    mIsActive = false;
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+void GnssMeasurementInterface::reportMeasurement(const GnssData& data) {
+    ALOGD("reportMeasurement()");
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (sCallback == nullptr) {
+        ALOGE("%s: GnssMeasurement::sCallback is null.", __func__);
+        return;
+    }
+    sCallback->gnssMeasurementCb(data);
+}
+
+}  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/GnssMeasurementInterface.h b/gnss/aidl/default/GnssMeasurementInterface.h
new file mode 100644
index 0000000..69cd871
--- /dev/null
+++ b/gnss/aidl/default/GnssMeasurementInterface.h
@@ -0,0 +1,51 @@
+/*
+ * 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 <aidl/android/hardware/gnss/BnGnssMeasurementCallback.h>
+#include <aidl/android/hardware/gnss/BnGnssMeasurementInterface.h>
+#include <atomic>
+#include <mutex>
+#include <thread>
+
+namespace aidl::android::hardware::gnss {
+
+struct GnssMeasurementInterface : public BnGnssMeasurementInterface {
+  public:
+    GnssMeasurementInterface();
+    ~GnssMeasurementInterface();
+    ndk::ScopedAStatus setCallback(const std::shared_ptr<IGnssMeasurementCallback>& callback,
+                                   const bool enableFullTracking) override;
+    ndk::ScopedAStatus close() override;
+
+  private:
+    void start();
+    void stop();
+    void reportMeasurement(const GnssData&);
+
+    std::atomic<long> mMinIntervalMillis;
+    std::atomic<bool> mIsActive;
+    std::thread mThread;
+
+    // Guarded by mMutex
+    static std::shared_ptr<IGnssMeasurementCallback> sCallback;
+
+    // Synchronization lock for sCallback
+    mutable std::mutex mMutex;
+};
+
+}  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/GnssPowerIndication.cpp b/gnss/aidl/default/GnssPowerIndication.cpp
index 8ea60f3..429cc8c 100644
--- a/gnss/aidl/default/GnssPowerIndication.cpp
+++ b/gnss/aidl/default/GnssPowerIndication.cpp
@@ -19,6 +19,7 @@
 #include "GnssPowerIndication.h"
 #include <aidl/android/hardware/gnss/BnGnss.h>
 #include <log/log.h>
+#include <utils/SystemClock.h>
 
 namespace aidl::android::hardware::gnss {
 
@@ -43,10 +44,9 @@
     std::unique_lock<std::mutex> lock(mMutex);
 
     ElapsedRealtime elapsedRealtime = {
-            .flags = IGnss::ELAPSED_REALTIME_HAS_TIMESTAMP_NS |
-                     IGnss::ELAPSED_REALTIME_HAS_TIME_UNCERTAINTY_NS,
-            .timestampNs = (long)1323542,
-            .timeUncertaintyNs = (long)1000,
+            .flags = ElapsedRealtime::HAS_TIMESTAMP_NS | ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS,
+            .timestampNs = ::android::elapsedRealtimeNano(),
+            .timeUncertaintyNs = 1000,
     };
     GnssPowerStats gnssPowerStats = {
             .elapsedRealtime = elapsedRealtime,
diff --git a/gnss/aidl/vts/Android.bp b/gnss/aidl/vts/Android.bp
index d102e7f..c10e809 100644
--- a/gnss/aidl/vts/Android.bp
+++ b/gnss/aidl/vts/Android.bp
@@ -22,6 +22,7 @@
         "gnss_hal_test.cpp",
         "gnss_hal_test_cases.cpp",
         "GnssCallbackAidl.cpp",
+        "GnssMeasurementCallbackAidl.cpp",
         "GnssPowerIndicationCallback.cpp",
         "VtsHalGnssTargetTest.cpp",
     ],
diff --git a/gnss/aidl/vts/GnssMeasurementCallbackAidl.cpp b/gnss/aidl/vts/GnssMeasurementCallbackAidl.cpp
new file mode 100644
index 0000000..c4fad7f
--- /dev/null
+++ b/gnss/aidl/vts/GnssMeasurementCallbackAidl.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "GnssMeasurementCallbackAidl"
+
+#include "GnssMeasurementCallbackAidl.h"
+#include <inttypes.h>
+#include <log/log.h>
+
+using android::hardware::gnss::GnssData;
+
+android::binder::Status GnssMeasurementCallbackAidl::gnssMeasurementCb(const GnssData& gnssData) {
+    ALOGI("gnssMeasurementCb");
+    ALOGI("elapsedRealtime: flags = %d, timestampNs: %" PRId64 ", timeUncertaintyNs=%lf",
+          gnssData.elapsedRealtime.flags, gnssData.elapsedRealtime.timestampNs,
+          gnssData.elapsedRealtime.timeUncertaintyNs);
+    for (const auto& measurement : gnssData.measurements) {
+        ALOGI("measurement.receivedSvTimeInNs=%" PRId64, measurement.receivedSvTimeInNs);
+    }
+    gnss_data_cbq_.store(gnssData);
+    return android::binder::Status::ok();
+}
diff --git a/gnss/aidl/vts/GnssMeasurementCallbackAidl.h b/gnss/aidl/vts/GnssMeasurementCallbackAidl.h
new file mode 100644
index 0000000..f6c79cf
--- /dev/null
+++ b/gnss/aidl/vts/GnssMeasurementCallbackAidl.h
@@ -0,0 +1,33 @@
+/*
+ * 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/hardware/gnss/BnGnssMeasurementCallback.h>
+#include "GnssCallbackEventQueue.h"
+
+/** Implementation for IGnssMeasurementCallback. */
+class GnssMeasurementCallbackAidl : public android::hardware::gnss::BnGnssMeasurementCallback {
+  public:
+    GnssMeasurementCallbackAidl() : gnss_data_cbq_("gnss_data") {}
+    ~GnssMeasurementCallbackAidl() {}
+
+    android::binder::Status gnssMeasurementCb(
+            const android::hardware::gnss::GnssData& gnssData) override;
+
+    android::hardware::gnss::common::GnssCallbackEventQueue<android::hardware::gnss::GnssData>
+            gnss_data_cbq_;
+};
diff --git a/gnss/aidl/vts/GnssPowerIndicationCallback.h b/gnss/aidl/vts/GnssPowerIndicationCallback.h
index d4c4539..3416f17 100644
--- a/gnss/aidl/vts/GnssPowerIndicationCallback.h
+++ b/gnss/aidl/vts/GnssPowerIndicationCallback.h
@@ -25,7 +25,6 @@
   public:
     GnssPowerIndicationCallback()
         : capabilities_cbq_("capabilities"),
-          other_mode_names_cbq_("other_mode_names"),
           gnss_power_stats_cbq_("gnss_power_stats") {}
     ~GnssPowerIndicationCallback() {}
 
@@ -35,9 +34,6 @@
 
     android::hardware::gnss::common::GnssCallbackEventQueue<int> capabilities_cbq_;
     int last_capabilities_;
-    android::hardware::gnss::common::GnssCallbackEventQueue<std::vector<std::string>>
-            other_mode_names_cbq_;
-    std::vector<std::string> last_other_mode_names_;
     android::hardware::gnss::common::GnssCallbackEventQueue<android::hardware::gnss::GnssPowerStats>
             gnss_power_stats_cbq_;
     android::hardware::gnss::GnssPowerStats last_gnss_power_stats_;
diff --git a/gnss/aidl/vts/gnss_hal_test.h b/gnss/aidl/vts/gnss_hal_test.h
index eb5301e..f72f7fe 100644
--- a/gnss/aidl/vts/gnss_hal_test.h
+++ b/gnss/aidl/vts/gnss_hal_test.h
@@ -36,7 +36,7 @@
 using android::hardware::gnss::IGnssConfiguration;
 
 // The main test class for GNSS HAL.
-class GnssHalTest : public GnssHalTestTemplate<IGnss_V2_1> {
+class GnssHalTest : public android::hardware::gnss::common::GnssHalTestTemplate<IGnss_V2_1> {
   public:
     GnssHalTest(){};
     ~GnssHalTest(){};
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index 4e8d0bd..857c742 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -16,19 +16,30 @@
 
 #define LOG_TAG "GnssHalTestCases"
 
+#include <android/hardware/gnss/IGnss.h>
+#include <android/hardware/gnss/IGnssMeasurementCallback.h>
+#include <android/hardware/gnss/IGnssMeasurementInterface.h>
 #include <android/hardware/gnss/IGnssPowerIndication.h>
 #include <android/hardware/gnss/IGnssPsds.h>
+#include "GnssMeasurementCallbackAidl.h"
 #include "GnssPowerIndicationCallback.h"
 #include "gnss_hal_test.h"
 
 using android::sp;
 using android::hardware::gnss::BlocklistedSource;
-using GnssConstellationTypeAidl = android::hardware::gnss::GnssConstellationType;
+using android::hardware::gnss::ElapsedRealtime;
+using android::hardware::gnss::GnssClock;
+using android::hardware::gnss::GnssMeasurement;
+using android::hardware::gnss::IGnss;
 using android::hardware::gnss::IGnssConfiguration;
+using android::hardware::gnss::IGnssMeasurementCallback;
+using android::hardware::gnss::IGnssMeasurementInterface;
 using android::hardware::gnss::IGnssPowerIndication;
 using android::hardware::gnss::IGnssPsds;
 using android::hardware::gnss::PsdsType;
 
+using GnssConstellationTypeAidl = android::hardware::gnss::GnssConstellationType;
+
 /*
  * SetupTeardownCreateCleanup:
  * Requests the gnss HAL then calls cleanup
@@ -53,6 +64,62 @@
 }
 
 /*
+ * TestGnssMeasurementExtension:
+ * 1. Gets the GnssMeasurementExtension and verifies that it returns a non-null extension.
+ * 2. Sets a GnssMeasurementCallback, waits for a measurement, and verifies fields are valid.
+ */
+TEST_P(GnssHalTest, TestGnssMeasurementExtension) {
+    const int kFirstGnssMeasurementTimeoutSeconds = 10;
+    sp<IGnssMeasurementInterface> iGnssMeasurement;
+    auto status = aidl_gnss_hal_->getExtensionGnssMeasurement(&iGnssMeasurement);
+    ASSERT_TRUE(status.isOk());
+    ASSERT_TRUE(iGnssMeasurement != nullptr);
+
+    auto callback = sp<GnssMeasurementCallbackAidl>::make();
+    status = iGnssMeasurement->setCallback(callback, /* enableFullTracking= */ true);
+    ASSERT_TRUE(status.isOk());
+
+    android::hardware::gnss::GnssData lastMeasurement;
+    ASSERT_TRUE(callback->gnss_data_cbq_.retrieve(lastMeasurement,
+                                                  kFirstGnssMeasurementTimeoutSeconds));
+    EXPECT_EQ(callback->gnss_data_cbq_.calledCount(), 1);
+    ASSERT_TRUE(lastMeasurement.measurements.size() > 0);
+
+    // Validity check GnssData fields
+    ASSERT_TRUE(
+            lastMeasurement.elapsedRealtime.flags >= 0 &&
+            lastMeasurement.elapsedRealtime.flags <=
+                    (ElapsedRealtime::HAS_TIMESTAMP_NS | ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS));
+    if (lastMeasurement.elapsedRealtime.flags & ElapsedRealtime::HAS_TIMESTAMP_NS) {
+        ASSERT_TRUE(lastMeasurement.elapsedRealtime.timestampNs > 0);
+    }
+    if (lastMeasurement.elapsedRealtime.flags & ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS) {
+        ASSERT_TRUE(lastMeasurement.elapsedRealtime.timeUncertaintyNs > 0);
+    }
+    ASSERT_TRUE(lastMeasurement.clock.gnssClockFlags >= 0 &&
+                lastMeasurement.clock.gnssClockFlags <=
+                        (GnssClock::HAS_LEAP_SECOND | GnssClock::HAS_TIME_UNCERTAINTY |
+                         GnssClock::HAS_FULL_BIAS | GnssClock::HAS_BIAS |
+                         GnssClock::HAS_BIAS_UNCERTAINTY | GnssClock::HAS_DRIFT |
+                         GnssClock::HAS_DRIFT_UNCERTAINTY));
+    for (const auto& measurement : lastMeasurement.measurements) {
+        ASSERT_TRUE(
+                measurement.flags >= 0 &&
+                measurement.flags <=
+                        (GnssMeasurement::HAS_SNR | GnssMeasurement::HAS_CARRIER_FREQUENCY |
+                         GnssMeasurement::HAS_CARRIER_CYCLES | GnssMeasurement::HAS_CARRIER_PHASE |
+                         GnssMeasurement::HAS_CARRIER_PHASE_UNCERTAINTY |
+                         GnssMeasurement::HAS_AUTOMATIC_GAIN_CONTROL |
+                         GnssMeasurement::HAS_FULL_ISB | GnssMeasurement::HAS_FULL_ISB_UNCERTAINTY |
+                         GnssMeasurement::HAS_SATELLITE_ISB |
+                         GnssMeasurement::HAS_SATELLITE_ISB_UNCERTAINTY));
+    }
+
+    status = iGnssMeasurement->close();
+    ASSERT_TRUE(status.isOk());
+}
+
+/*
  * TestGnssPowerIndication
  * 1. Gets the GnssPowerIndicationExtension.
  * 2. Sets a GnssPowerIndicationCallback.
diff --git a/gnss/common/utils/default/Android.bp b/gnss/common/utils/default/Android.bp
index 730de4b..be1d532 100644
--- a/gnss/common/utils/default/Android.bp
+++ b/gnss/common/utils/default/Android.bp
@@ -42,5 +42,6 @@
         "android.hardware.gnss@2.1",
         "android.hardware.gnss.measurement_corrections@1.1",
         "android.hardware.gnss.measurement_corrections@1.0",
+        "android.hardware.gnss-ndk_platform",
     ],
 }
diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp
index d336f1b..dd932d4 100644
--- a/gnss/common/utils/default/Utils.cpp
+++ b/gnss/common/utils/default/Utils.cpp
@@ -17,6 +17,7 @@
 #include <Constants.h>
 #include <MockLocation.h>
 #include <Utils.h>
+#include <aidl/android/hardware/gnss/BnGnss.h>
 #include <utils/SystemClock.h>
 
 namespace android {
@@ -24,16 +25,28 @@
 namespace gnss {
 namespace common {
 
+using IGnss = aidl::android::hardware::gnss::IGnss;
+using IGnssMeasurementCallback = aidl::android::hardware::gnss::IGnssMeasurementCallback;
+using GnssMeasurement = aidl::android::hardware::gnss::GnssMeasurement;
 using GnssSvFlags = V1_0::IGnssCallback::GnssSvFlags;
 using GnssMeasurementFlagsV1_0 = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags;
 using GnssMeasurementFlagsV2_1 = V2_1::IGnssMeasurementCallback::GnssMeasurementFlags;
 using GnssMeasurementStateV2_0 = V2_0::IGnssMeasurementCallback::GnssMeasurementState;
-using ElapsedRealtime = V2_0::ElapsedRealtime;
+using ElapsedRealtime = aidl::android::hardware::gnss::ElapsedRealtime;
 using ElapsedRealtimeFlags = V2_0::ElapsedRealtimeFlags;
 using GnssConstellationTypeV2_0 = V2_0::GnssConstellationType;
 using IGnssMeasurementCallbackV2_0 = V2_0::IGnssMeasurementCallback;
 using GnssSignalType = V2_1::GnssSignalType;
 
+using GnssDataV2_0 = V2_0::IGnssMeasurementCallback::GnssData;
+using GnssDataV2_1 = V2_1::IGnssMeasurementCallback::GnssData;
+using GnssSvInfoV1_0 = V1_0::IGnssCallback::GnssSvInfo;
+using GnssSvInfoV2_0 = V2_0::IGnssCallback::GnssSvInfo;
+using GnssSvInfoV2_1 = V2_1::IGnssCallback::GnssSvInfo;
+using GnssAntennaInfo = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo;
+using Row = V2_1::IGnssAntennaInfoCallback::Row;
+using Coord = V2_1::IGnssAntennaInfoCallback::Coord;
+
 GnssDataV2_1 Utils::getMockMeasurementV2_1() {
     GnssDataV2_0 gnssDataV2_0 = Utils::getMockMeasurementV2_0();
     V2_1::IGnssMeasurementCallback::GnssMeasurement gnssMeasurementV2_1 = {
@@ -110,7 +123,7 @@
                                                        .driftUncertaintyNsps = 310.64968328491528,
                                                        .hwClockDiscontinuityCount = 1};
 
-    ElapsedRealtime timestamp = {
+    V2_0::ElapsedRealtime timestamp = {
             .flags = ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
                      ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS,
             .timestampNs = static_cast<uint64_t>(::android::elapsedRealtimeNano()),
@@ -124,6 +137,52 @@
     return gnssData;
 }
 
+aidl::android::hardware::gnss::GnssData Utils::getMockMeasurement() {
+    aidl::android::hardware::gnss::GnssSignalType signalType = {
+            .constellation = aidl::android::hardware::gnss::GnssConstellationType::GLONASS,
+            .carrierFrequencyHz = 1.59975e+09,
+            .codeType = aidl::android::hardware::gnss::GnssSignalType::CODE_TYPE_C,
+    };
+    aidl::android::hardware::gnss::GnssMeasurement measurement = {
+            .flags = GnssMeasurement::HAS_CARRIER_FREQUENCY,
+            .svid = 6,
+            .signalType = signalType,
+            .timeOffsetNs = 0.0,
+            .receivedSvTimeInNs = 8195997131077,
+            .receivedSvTimeUncertaintyInNs = 15,
+            .antennaCN0DbHz = 30.0,
+            .pseudorangeRateMps = -484.13739013671875,
+            .pseudorangeRateUncertaintyMps = 1.0379999876022339,
+            .accumulatedDeltaRangeState = GnssMeasurement::ADR_STATE_UNKNOWN,
+            .accumulatedDeltaRangeM = 0.0,
+            .accumulatedDeltaRangeUncertaintyM = 0.0,
+            .multipathIndicator = aidl::android::hardware::gnss::GnssMultipathIndicator::UNKNOWN,
+            .state = GnssMeasurement::STATE_CODE_LOCK | GnssMeasurement::STATE_BIT_SYNC |
+                     GnssMeasurement::STATE_SUBFRAME_SYNC | GnssMeasurement::STATE_TOW_DECODED |
+                     GnssMeasurement::STATE_GLO_STRING_SYNC |
+                     GnssMeasurement::STATE_GLO_TOD_DECODED};
+
+    aidl::android::hardware::gnss::GnssClock clock = {.timeNs = 2713545000000,
+                                                      .fullBiasNs = -1226701900521857520,
+                                                      .biasNs = 0.59689998626708984,
+                                                      .biasUncertaintyNs = 47514.989972114563,
+                                                      .driftNsps = -51.757811607455452,
+                                                      .driftUncertaintyNsps = 310.64968328491528,
+                                                      .hwClockDiscontinuityCount = 1};
+
+    ElapsedRealtime timestamp = {
+            .flags = ElapsedRealtime::HAS_TIMESTAMP_NS | ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS,
+            .timestampNs = ::android::elapsedRealtimeNano(),
+            // This is an hardcoded value indicating a 1ms of uncertainty between the two clocks.
+            // In an actual implementation provide an estimate of the synchronization uncertainty
+            // or don't set the field.
+            .timeUncertaintyNs = 1000000};
+
+    aidl::android::hardware::gnss::GnssData gnssData = {
+            .measurements = {measurement}, .clock = clock, .elapsedRealtime = timestamp};
+    return gnssData;
+}
+
 V2_0::GnssLocation Utils::getMockLocationV2_0() {
     const V2_0::ElapsedRealtime timestamp = {
             .flags = V2_0::ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
diff --git a/gnss/common/utils/default/include/NmeaFixInfo.h b/gnss/common/utils/default/include/NmeaFixInfo.h
index fb2c1a4..06eae7e 100644
--- a/gnss/common/utils/default/include/NmeaFixInfo.h
+++ b/gnss/common/utils/default/include/NmeaFixInfo.h
@@ -26,11 +26,6 @@
 namespace hardware {
 namespace gnss {
 namespace common {
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
 
 constexpr char GPGA_RECORD_TAG[] = "$GPGGA";
 constexpr char GPRMC_RECORD_TAG[] = "$GPRMC";
diff --git a/gnss/common/utils/default/include/Utils.h b/gnss/common/utils/default/include/Utils.h
index d9ad5a5..0ca1b00 100644
--- a/gnss/common/utils/default/include/Utils.h
+++ b/gnss/common/utils/default/include/Utils.h
@@ -17,6 +17,7 @@
 #ifndef android_hardware_gnss_common_default_Utils_H_
 #define android_hardware_gnss_common_default_Utils_H_
 
+#include <aidl/android/hardware/gnss/BnGnssMeasurementInterface.h>
 #include <android/hardware/gnss/1.0/IGnss.h>
 #include <android/hardware/gnss/2.0/IGnss.h>
 #include <android/hardware/gnss/2.1/IGnss.h>
@@ -28,28 +29,22 @@
 namespace gnss {
 namespace common {
 
-using GnssDataV2_0 = V2_0::IGnssMeasurementCallback::GnssData;
-using GnssDataV2_1 = V2_1::IGnssMeasurementCallback::GnssData;
-using GnssSvInfoV1_0 = V1_0::IGnssCallback::GnssSvInfo;
-using GnssSvInfoV2_0 = V2_0::IGnssCallback::GnssSvInfo;
-using GnssSvInfoV2_1 = V2_1::IGnssCallback::GnssSvInfo;
-using GnssAntennaInfo = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo;
-using Row = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::Row;
-using Coord = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::Coord;
-
 struct Utils {
-    static GnssDataV2_0 getMockMeasurementV2_0();
-    static GnssDataV2_1 getMockMeasurementV2_1();
+    static aidl::android::hardware::gnss::GnssData getMockMeasurement();
+    static V2_0::IGnssMeasurementCallback::GnssData getMockMeasurementV2_0();
+    static V2_1::IGnssMeasurementCallback::GnssData getMockMeasurementV2_1();
     static V2_0::GnssLocation getMockLocationV2_0();
     static V1_0::GnssLocation getMockLocationV1_0();
-    static hidl_vec<GnssSvInfoV2_1> getMockSvInfoListV2_1();
-    static GnssSvInfoV2_1 getMockSvInfoV2_1(GnssSvInfoV2_0 gnssSvInfoV2_0, float basebandCN0DbHz);
-    static GnssSvInfoV2_0 getMockSvInfoV2_0(GnssSvInfoV1_0 gnssSvInfoV1_0,
-                                            V2_0::GnssConstellationType type);
-    static GnssSvInfoV1_0 getMockSvInfoV1_0(int16_t svid, V1_0::GnssConstellationType type,
-                                            float cN0DbHz, float elevationDegrees,
-                                            float azimuthDegrees);
-    static hidl_vec<GnssAntennaInfo> getMockAntennaInfos();
+    static hidl_vec<V2_1::IGnssCallback::GnssSvInfo> getMockSvInfoListV2_1();
+    static V2_1::IGnssCallback::GnssSvInfo getMockSvInfoV2_1(
+            V2_0::IGnssCallback::GnssSvInfo gnssSvInfoV2_0, float basebandCN0DbHz);
+    static V2_0::IGnssCallback::GnssSvInfo getMockSvInfoV2_0(
+            V1_0::IGnssCallback::GnssSvInfo gnssSvInfoV1_0, V2_0::GnssConstellationType type);
+    static V1_0::IGnssCallback::GnssSvInfo getMockSvInfoV1_0(int16_t svid,
+                                                             V1_0::GnssConstellationType type,
+                                                             float cN0DbHz, float elevationDegrees,
+                                                             float azimuthDegrees);
+    static hidl_vec<V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo> getMockAntennaInfos();
 };
 
 }  // namespace common
diff --git a/gnss/common/utils/default/include/v2_1/GnssAntennaInfo.h b/gnss/common/utils/default/include/v2_1/GnssAntennaInfo.h
index e74ff54..a232499 100644
--- a/gnss/common/utils/default/include/v2_1/GnssAntennaInfo.h
+++ b/gnss/common/utils/default/include/v2_1/GnssAntennaInfo.h
@@ -23,29 +23,25 @@
 
 namespace android::hardware::gnss::V2_1::implementation {
 
-using ::android::sp;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using IGnssAntennaInfo = ::android::hardware::gnss::V2_1::IGnssAntennaInfo;
-using IGnssAntennaInfoCallback = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
-
-struct GnssAntennaInfo : public IGnssAntennaInfo {
+struct GnssAntennaInfo : public ::android::hardware::gnss::V2_1::IGnssAntennaInfo {
     GnssAntennaInfo();
     ~GnssAntennaInfo();
 
     // Methods from ::android::hardware::gnss::V2_1::IGnssAntennaInfo follow.
     Return<GnssAntennaInfoStatus> setCallback(
-            const sp<IGnssAntennaInfoCallback>& callback) override;
+            const sp<::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback>& callback) override;
     Return<void> close() override;
 
   private:
     void start();
     void stop();
     void reportAntennaInfo(
-            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& antennaInfo) const;
+            const hidl_vec<
+                    ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+                    antennaInfo) const;
 
     // Guarded by mMutex
-    static sp<IGnssAntennaInfoCallback> sCallback;
+    static sp<::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback> sCallback;
 
     std::atomic<long> mMinIntervalMillis;
     std::atomic<bool> mIsActive;
diff --git a/gnss/common/utils/default/include/v2_1/GnssConfiguration.h b/gnss/common/utils/default/include/v2_1/GnssConfiguration.h
index 8463a5c..2cfb38f 100644
--- a/gnss/common/utils/default/include/v2_1/GnssConfiguration.h
+++ b/gnss/common/utils/default/include/v2_1/GnssConfiguration.h
@@ -25,35 +25,27 @@
 
 namespace android::hardware::gnss::V2_1::implementation {
 
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-using BlacklistedSourceV2_1 =
-        ::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource;
-using GnssConstellationTypeV2_0 = V2_0::GnssConstellationType;
-using GnssSvInfoV2_1 = V2_1::IGnssCallback::GnssSvInfo;
-
 struct BlacklistedSourceHashV2_1 {
-    inline int operator()(const BlacklistedSourceV2_1& source) const {
+    inline int operator()(
+            const ::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource& source)
+            const {
         return int(source.constellation) * 1000 + int(source.svid);
     }
 };
 
 struct BlacklistedSourceEqualV2_1 {
-    inline bool operator()(const BlacklistedSourceV2_1& s1, const BlacklistedSourceV2_1& s2) const {
+    inline bool operator()(
+            const ::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource& s1,
+            const ::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource& s2)
+            const {
         return (s1.constellation == s2.constellation) && (s1.svid == s2.svid);
     }
 };
 
 using BlacklistedSourceSetV2_1 =
-        std::unordered_set<BlacklistedSourceV2_1, BlacklistedSourceHashV2_1,
-                           BlacklistedSourceEqualV2_1>;
-using BlacklistedConstellationSetV2_1 = std::unordered_set<GnssConstellationTypeV2_0>;
+        std::unordered_set<::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource,
+                           BlacklistedSourceHashV2_1, BlacklistedSourceEqualV2_1>;
+using BlacklistedConstellationSetV2_1 = std::unordered_set<V2_0::GnssConstellationType>;
 
 struct GnssConfiguration : public IGnssConfiguration {
     // Methods from ::android::hardware::gnss::V1_0::IGnssConfiguration follow.
@@ -78,7 +70,7 @@
     Return<bool> setBlacklist_2_1(
             const hidl_vec<V2_1::IGnssConfiguration::BlacklistedSource>& blacklist) override;
 
-    Return<bool> isBlacklistedV2_1(const GnssSvInfoV2_1& gnssSvInfo) const;
+    Return<bool> isBlacklistedV2_1(const V2_1::IGnssCallback::GnssSvInfo& gnssSvInfo) const;
 
   private:
     mutable std::recursive_mutex mMutex;
diff --git a/gnss/common/utils/default/include/v2_1/GnssDebug.h b/gnss/common/utils/default/include/v2_1/GnssDebug.h
index 8580989..481de59 100644
--- a/gnss/common/utils/default/include/v2_1/GnssDebug.h
+++ b/gnss/common/utils/default/include/v2_1/GnssDebug.h
@@ -21,15 +21,8 @@
 
 namespace android::hardware::gnss::V1_1::implementation {
 
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using V1_0::IGnssDebug;
-
 /* Interface for GNSS Debug support. */
-struct GnssDebug : public IGnssDebug {
+struct GnssDebug : public V1_0::IGnssDebug {
     /*
      * Methods from ::android::hardware::gnss::V1_0::IGnssDebug follow.
      * These declarations were generated from IGnssDebug.hal.
diff --git a/gnss/common/utils/default/include/v2_1/GnssMeasurement.h b/gnss/common/utils/default/include/v2_1/GnssMeasurement.h
index 1d1fc9d..db8407b 100644
--- a/gnss/common/utils/default/include/v2_1/GnssMeasurement.h
+++ b/gnss/common/utils/default/include/v2_1/GnssMeasurement.h
@@ -25,17 +25,6 @@
 
 namespace android::hardware::gnss::V2_1::implementation {
 
-using GnssDataV2_1 = V2_1::IGnssMeasurementCallback::GnssData;
-using GnssDataV2_0 = V2_0::IGnssMeasurementCallback::GnssData;
-
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
 struct GnssMeasurement : public IGnssMeasurement {
     GnssMeasurement();
     ~GnssMeasurement();
@@ -59,8 +48,8 @@
   private:
     void start();
     void stop();
-    void reportMeasurement(const GnssDataV2_0&);
-    void reportMeasurement(const GnssDataV2_1&);
+    void reportMeasurement(const V2_0::IGnssMeasurementCallback::GnssData&);
+    void reportMeasurement(const V2_1::IGnssMeasurementCallback::GnssData&);
 
     // Guarded by mMutex
     static sp<V2_1::IGnssMeasurementCallback> sCallback_2_1;
diff --git a/gnss/common/utils/default/include/v2_1/GnssMeasurementCorrections.h b/gnss/common/utils/default/include/v2_1/GnssMeasurementCorrections.h
index eaa7659..54045ad 100644
--- a/gnss/common/utils/default/include/v2_1/GnssMeasurementCorrections.h
+++ b/gnss/common/utils/default/include/v2_1/GnssMeasurementCorrections.h
@@ -22,9 +22,6 @@
 
 namespace android::hardware::gnss::measurement_corrections::V1_1::implementation {
 
-using ::android::sp;
-using ::android::hardware::Return;
-
 struct GnssMeasurementCorrections : public IMeasurementCorrections {
     GnssMeasurementCorrections();
     ~GnssMeasurementCorrections();
diff --git a/gnss/common/utils/default/include/v2_1/GnssTemplate.h b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
index 4d1baa7..4d4ec93 100644
--- a/gnss/common/utils/default/include/v2_1/GnssTemplate.h
+++ b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
@@ -39,16 +39,6 @@
 
 namespace android::hardware::gnss::common::implementation {
 
-using GnssSvInfo = V2_1::IGnssCallback::GnssSvInfo;
-
-using common::NmeaFixInfo;
-using common::Utils;
-using measurement_corrections::V1_1::implementation::GnssMeasurementCorrections;
-
-using V2_1::implementation::GnssAntennaInfo;
-using V2_1::implementation::GnssConfiguration;
-using V2_1::implementation::GnssMeasurement;
-
 constexpr int INPUT_BUFFER_SIZE = 128;
 constexpr char CMD_GET_LOCATION[] = "CMD_GET_LOCATION";
 constexpr char GNSS_PATH[] = "/dev/gnss0";
@@ -120,7 +110,7 @@
     std::unique_ptr<V2_0::GnssLocation> getLocationFromHW();
     void reportLocation(const V2_0::GnssLocation&) const;
     void reportLocation(const V1_0::GnssLocation&) const;
-    void reportSvStatus(const hidl_vec<GnssSvInfo>&) const;
+    void reportSvStatus(const hidl_vec<V2_1::IGnssCallback::GnssSvInfo>&) const;
 
     Return<void> help(const hidl_handle& fd);
     Return<void> setLocation(const hidl_handle& fd, const hidl_vec<hidl_string>& options);
@@ -131,15 +121,15 @@
     static sp<V1_0::IGnssCallback> sGnssCallback_1_0;
 
     std::atomic<long> mMinIntervalMs;
-    sp<GnssConfiguration> mGnssConfiguration;
+    sp<V2_1::implementation::GnssConfiguration> mGnssConfiguration;
     std::atomic<bool> mIsActive;
     std::atomic<bool> mHardwareModeChecked;
     std::atomic<int> mGnssFd;
     std::thread mThread;
 
     mutable std::mutex mMutex;
-    virtual hidl_vec<GnssSvInfo> filterBlocklistedSatellitesV2_1(
-            hidl_vec<GnssSvInfo> gnssSvInfoList);
+    virtual hidl_vec<V2_1::IGnssCallback::GnssSvInfo> filterBlocklistedSatellitesV2_1(
+            hidl_vec<V2_1::IGnssCallback::GnssSvInfo> gnssSvInfoList);
 };
 
 template <class T_IGnss>
@@ -154,7 +144,7 @@
 template <class T_IGnss>
 GnssTemplate<T_IGnss>::GnssTemplate()
     : mMinIntervalMs(1000),
-      mGnssConfiguration{new GnssConfiguration()},
+      mGnssConfiguration{new V2_1::implementation::GnssConfiguration()},
       mHardwareModeChecked(false),
       mGnssFd(-1) {}
 
@@ -237,8 +227,8 @@
 }
 
 template <class T_IGnss>
-hidl_vec<GnssSvInfo> GnssTemplate<T_IGnss>::filterBlocklistedSatellitesV2_1(
-        hidl_vec<GnssSvInfo> gnssSvInfoList) {
+hidl_vec<V2_1::IGnssCallback::GnssSvInfo> GnssTemplate<T_IGnss>::filterBlocklistedSatellitesV2_1(
+        hidl_vec<V2_1::IGnssCallback::GnssSvInfo> gnssSvInfoList) {
     ALOGD("filterBlocklistedSatellitesV2_1");
     for (uint32_t i = 0; i < gnssSvInfoList.size(); i++) {
         if (mGnssConfiguration->isBlacklistedV2_1(gnssSvInfoList[i])) {
@@ -348,7 +338,7 @@
 template <class T_IGnss>
 Return<sp<V1_0::IGnssMeasurement>> GnssTemplate<T_IGnss>::getExtensionGnssMeasurement() {
     ALOGD("Gnss::getExtensionGnssMeasurement");
-    return new GnssMeasurement();
+    return new V2_1::implementation::GnssMeasurement();
 }
 
 template <class T_IGnss>
@@ -501,14 +491,14 @@
 template <class T_IGnss>
 Return<sp<V2_0::IGnssMeasurement>> GnssTemplate<T_IGnss>::getExtensionGnssMeasurement_2_0() {
     ALOGD("Gnss::getExtensionGnssMeasurement_2_0");
-    return new GnssMeasurement();
+    return new V2_1::implementation::GnssMeasurement();
 }
 
 template <class T_IGnss>
 Return<sp<measurement_corrections::V1_0::IMeasurementCorrections>>
 GnssTemplate<T_IGnss>::getExtensionMeasurementCorrections() {
     ALOGD("Gnss::getExtensionMeasurementCorrections()");
-    return new GnssMeasurementCorrections();
+    return new measurement_corrections::V1_1::implementation::GnssMeasurementCorrections();
 }
 
 template <class T_IGnss>
@@ -569,7 +559,7 @@
 template <class T_IGnss>
 Return<sp<V2_1::IGnssMeasurement>> GnssTemplate<T_IGnss>::getExtensionGnssMeasurement_2_1() {
     ALOGD("Gnss::getExtensionGnssMeasurement_2_1");
-    return new GnssMeasurement();
+    return new V2_1::implementation::GnssMeasurement();
 }
 
 template <class T_IGnss>
@@ -582,17 +572,18 @@
 Return<sp<measurement_corrections::V1_1::IMeasurementCorrections>>
 GnssTemplate<T_IGnss>::getExtensionMeasurementCorrections_1_1() {
     ALOGD("Gnss::getExtensionMeasurementCorrections_1_1()");
-    return new GnssMeasurementCorrections();
+    return new measurement_corrections::V1_1::implementation::GnssMeasurementCorrections();
 }
 
 template <class T_IGnss>
 Return<sp<V2_1::IGnssAntennaInfo>> GnssTemplate<T_IGnss>::getExtensionGnssAntennaInfo() {
     ALOGD("Gnss::getExtensionGnssAntennaInfo");
-    return new GnssAntennaInfo();
+    return new V2_1::implementation::GnssAntennaInfo();
 }
 
 template <class T_IGnss>
-void GnssTemplate<T_IGnss>::reportSvStatus(const hidl_vec<GnssSvInfo>& svInfoList) const {
+void GnssTemplate<T_IGnss>::reportSvStatus(
+        const hidl_vec<V2_1::IGnssCallback::GnssSvInfo>& svInfoList) const {
     std::unique_lock<std::mutex> lock(mMutex);
     // TODO(skz): update this to call 2_0 callback if non-null
     if (sGnssCallback_2_1 == nullptr) {
diff --git a/gnss/common/utils/default/v2_1/GnssConfiguration.cpp b/gnss/common/utils/default/v2_1/GnssConfiguration.cpp
index 8b30701..be974bc 100644
--- a/gnss/common/utils/default/v2_1/GnssConfiguration.cpp
+++ b/gnss/common/utils/default/v2_1/GnssConfiguration.cpp
@@ -21,6 +21,9 @@
 
 namespace android::hardware::gnss::V2_1::implementation {
 
+using GnssSvInfoV2_1 = V2_1::IGnssCallback::GnssSvInfo;
+using BlacklistedSourceV2_1 = V2_1::IGnssConfiguration::BlacklistedSource;
+
 // Methods from ::android::hardware::gnss::V1_0::IGnssConfiguration follow.
 Return<bool> GnssConfiguration::setSuplEs(bool enable) {
     ALOGD("setSuplEs enable: %d", enable);
@@ -69,7 +72,7 @@
 
 // Methods from ::android::hardware::gnss::V2_1::IGnssConfiguration follow.
 Return<bool> GnssConfiguration::setBlacklist_2_1(
-        const hidl_vec<V2_1::IGnssConfiguration::BlacklistedSource>& sourceList) {
+        const hidl_vec<BlacklistedSourceV2_1>& sourceList) {
     std::unique_lock<std::recursive_mutex> lock(mMutex);
     mBlacklistedConstellationSet.clear();
     mBlacklistedSourceSet.clear();
diff --git a/gnss/common/utils/default/v2_1/GnssMeasurement.cpp b/gnss/common/utils/default/v2_1/GnssMeasurement.cpp
index 7d3a002..887cb5a 100644
--- a/gnss/common/utils/default/v2_1/GnssMeasurement.cpp
+++ b/gnss/common/utils/default/v2_1/GnssMeasurement.cpp
@@ -114,7 +114,7 @@
     }
 }
 
-void GnssMeasurement::reportMeasurement(const GnssDataV2_0& data) {
+void GnssMeasurement::reportMeasurement(const V2_0::IGnssMeasurementCallback::GnssData& data) {
     ALOGD("reportMeasurement()");
     std::unique_lock<std::mutex> lock(mMutex);
     if (sCallback_2_0 == nullptr) {
@@ -127,7 +127,7 @@
     }
 }
 
-void GnssMeasurement::reportMeasurement(const GnssDataV2_1& data) {
+void GnssMeasurement::reportMeasurement(const V2_1::IGnssMeasurementCallback::GnssData& data) {
     ALOGD("reportMeasurement()");
     std::unique_lock<std::mutex> lock(mMutex);
     if (sCallback_2_1 == nullptr) {
diff --git a/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h b/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h
index 1439158..fec3503 100644
--- a/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h
+++ b/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h
@@ -23,36 +23,10 @@
 #include <gtest/gtest.h>
 #include <chrono>
 
-using ::android::hardware::gnss::common::Utils;
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-
-using android::hardware::gnss::common::GnssCallback;
-using android::hardware::gnss::common::GnssCallbackEventQueue;
-using android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrectionsCallback;
-using android::hardware::gnss::V1_0::GnssLocationFlags;
-using android::hardware::gnss::V2_0::GnssConstellationType;
-using android::hardware::gnss::V2_1::IGnssAntennaInfo;
-using android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
-
-using GnssLocation_1_0 = android::hardware::gnss::V1_0::GnssLocation;
-using GnssLocation_2_0 = android::hardware::gnss::V2_0::GnssLocation;
-
-using IGnssCallback_1_0 = android::hardware::gnss::V1_0::IGnssCallback;
-using IGnssCallback_2_0 = android::hardware::gnss::V2_0::IGnssCallback;
-using IGnssCallback_2_1 = android::hardware::gnss::V2_1::IGnssCallback;
-
-using IGnssMeasurementCallback_1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
-using IGnssMeasurementCallback_1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback;
-using IGnssMeasurementCallback_2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
-using IGnssMeasurementCallback_2_1 = android::hardware::gnss::V2_1::IGnssMeasurementCallback;
-
-using android::sp;
-
 #define TIMEOUT_SEC 2  // for basic commands/responses
 
+namespace android::hardware::gnss::common {
+
 // The main test class for GNSS HAL.
 template <class T_IGnss>
 class GnssHalTestTemplate : public testing::TestWithParam<std::string> {
@@ -62,34 +36,37 @@
     virtual void TearDown() override;
 
     /* Callback class for GnssMeasurement. */
-    class GnssMeasurementCallback : public IGnssMeasurementCallback_2_1 {
+    class GnssMeasurementCallback : public V2_1::IGnssMeasurementCallback {
       public:
-        GnssCallbackEventQueue<IGnssMeasurementCallback_2_1::GnssData> measurement_cbq_;
+        GnssCallbackEventQueue<V2_1::IGnssMeasurementCallback::GnssData> measurement_cbq_;
 
         GnssMeasurementCallback() : measurement_cbq_("measurement"){};
         virtual ~GnssMeasurementCallback() = default;
 
         // Methods from V1_0::IGnssMeasurementCallback follow.
-        Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_1_0::GnssData&) override {
+        Return<void> GnssMeasurementCb(const V1_0::IGnssMeasurementCallback::GnssData&) override {
             return Void();
         }
 
         // Methods from V1_1::IGnssMeasurementCallback follow.
-        Return<void> gnssMeasurementCb(const IGnssMeasurementCallback_1_1::GnssData&) override {
+        Return<void> gnssMeasurementCb(const V1_1::IGnssMeasurementCallback::GnssData&) override {
             return Void();
         }
 
         // Methods from V2_0::IGnssMeasurementCallback follow.
-        Return<void> gnssMeasurementCb_2_0(const IGnssMeasurementCallback_2_0::GnssData&) override {
+        Return<void> gnssMeasurementCb_2_0(
+                const V2_0::IGnssMeasurementCallback::GnssData&) override {
             return Void();
         }
 
         // Methods from V2_1::IGnssMeasurementCallback follow.
-        Return<void> gnssMeasurementCb_2_1(const IGnssMeasurementCallback_2_1::GnssData&) override;
+        Return<void> gnssMeasurementCb_2_1(
+                const V2_1::IGnssMeasurementCallback::GnssData&) override;
     };
 
     /* Callback class for GnssMeasurementCorrections. */
-    class GnssMeasurementCorrectionsCallback : public IMeasurementCorrectionsCallback {
+    class GnssMeasurementCorrectionsCallback
+        : public measurement_corrections::V1_0::IMeasurementCorrectionsCallback {
       public:
         uint32_t last_capabilities_;
         GnssCallbackEventQueue<uint32_t> capabilities_cbq_;
@@ -102,9 +79,9 @@
     };
 
     /* Callback class for GnssAntennaInfo. */
-    class GnssAntennaInfoCallback : public IGnssAntennaInfoCallback {
+    class GnssAntennaInfoCallback : public V2_1::IGnssAntennaInfoCallback {
       public:
-        GnssCallbackEventQueue<hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>>
+        GnssCallbackEventQueue<hidl_vec<V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>>
                 antenna_info_cbq_;
 
         GnssAntennaInfoCallback() : antenna_info_cbq_("info"){};
@@ -112,7 +89,7 @@
 
         // Methods from V2_1::GnssAntennaInfoCallback follow.
         Return<void> gnssAntennaInfoCb(
-                const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
+                const hidl_vec<V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
     };
 
     /*
@@ -138,7 +115,7 @@
      *
      *   check_speed: true if speed related fields are also verified.
      */
-    void CheckLocation(const GnssLocation_2_0& location, const bool check_speed);
+    void CheckLocation(const V2_0::GnssLocation& location, const bool check_speed);
 
     /*
      * StartAndCheckLocations:
@@ -170,7 +147,7 @@
      * Note that location is not stopped in this method. The client should call
      * StopAndClearLocations() after the call.
      */
-    GnssConstellationType startLocationAndGetNonGpsConstellation(
+    V2_0::GnssConstellationType startLocationAndGetNonGpsConstellation(
             const int locations_to_await, const int gnss_sv_info_list_timeout);
 
     sp<T_IGnss> gnss_hal_;      // GNSS HAL to call into
@@ -283,7 +260,7 @@
 }
 
 template <class T_IGnss>
-void GnssHalTestTemplate<T_IGnss>::CheckLocation(const GnssLocation_2_0& location,
+void GnssHalTestTemplate<T_IGnss>::CheckLocation(const V2_0::GnssLocation& location,
                                                  bool check_speed) {
     const bool check_more_accuracies =
             (gnss_cb_->info_cbq_.calledCount() > 0 && gnss_cb_->last_info_.yearOfHw >= 2017);
@@ -315,7 +292,7 @@
 }
 
 template <class T_IGnss>
-GnssConstellationType GnssHalTestTemplate<T_IGnss>::startLocationAndGetNonGpsConstellation(
+V2_0::GnssConstellationType GnssHalTestTemplate<T_IGnss>::startLocationAndGetNonGpsConstellation(
         const int locations_to_await, const int gnss_sv_info_list_timeout) {
     gnss_cb_->location_cbq_.reset();
     StartAndCheckLocations(locations_to_await);
@@ -328,29 +305,29 @@
           sv_info_list_cbq_size, locations_to_await, location_called_count);
 
     // Find first non-GPS constellation to blacklist
-    GnssConstellationType constellation_to_blacklist = GnssConstellationType::UNKNOWN;
+    V2_0::GnssConstellationType constellation_to_blacklist = V2_0::GnssConstellationType::UNKNOWN;
     for (int i = 0; i < sv_info_list_cbq_size; ++i) {
-        hidl_vec<IGnssCallback_2_1::GnssSvInfo> sv_info_vec;
+        hidl_vec<V2_1::IGnssCallback::GnssSvInfo> sv_info_vec;
         gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec, gnss_sv_info_list_timeout);
         for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) {
             const auto& gnss_sv = sv_info_vec[iSv];
-            if ((gnss_sv.v2_0.v1_0.svFlag & IGnssCallback_1_0::GnssSvFlags::USED_IN_FIX) &&
-                (gnss_sv.v2_0.constellation != GnssConstellationType::UNKNOWN) &&
-                (gnss_sv.v2_0.constellation != GnssConstellationType::GPS)) {
+            if ((gnss_sv.v2_0.v1_0.svFlag & V1_0::IGnssCallback::GnssSvFlags::USED_IN_FIX) &&
+                (gnss_sv.v2_0.constellation != V2_0::GnssConstellationType::UNKNOWN) &&
+                (gnss_sv.v2_0.constellation != V2_0::GnssConstellationType::GPS)) {
                 // found a non-GPS constellation
                 constellation_to_blacklist = gnss_sv.v2_0.constellation;
                 break;
             }
         }
-        if (constellation_to_blacklist != GnssConstellationType::UNKNOWN) {
+        if (constellation_to_blacklist != V2_0::GnssConstellationType::UNKNOWN) {
             break;
         }
     }
 
-    if (constellation_to_blacklist == GnssConstellationType::UNKNOWN) {
+    if (constellation_to_blacklist == V2_0::GnssConstellationType::UNKNOWN) {
         ALOGI("No non-GPS constellations found, constellation blacklist test less effective.");
         // Proceed functionally to blacklist something.
-        constellation_to_blacklist = GnssConstellationType::GLONASS;
+        constellation_to_blacklist = V2_0::GnssConstellationType::GLONASS;
     }
 
     return constellation_to_blacklist;
@@ -358,7 +335,7 @@
 
 template <class T_IGnss>
 Return<void> GnssHalTestTemplate<T_IGnss>::GnssMeasurementCallback::gnssMeasurementCb_2_1(
-        const IGnssMeasurementCallback_2_1::GnssData& data) {
+        const V2_1::IGnssMeasurementCallback::GnssData& data) {
     ALOGD("GnssMeasurement v2.1 received. Size = %d", (int)data.measurements.size());
     measurement_cbq_.store(data);
     return Void();
@@ -374,8 +351,10 @@
 
 template <class T_IGnss>
 Return<void> GnssHalTestTemplate<T_IGnss>::GnssAntennaInfoCallback::gnssAntennaInfoCb(
-        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+        const hidl_vec<V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
     ALOGD("GnssAntennaInfo v2.1 received. Size = %d", (int)gnssAntennaInfos.size());
     antenna_info_cbq_.store(gnssAntennaInfos);
     return Void();
 }
+
+}  // namespace android::hardware::gnss::common
diff --git a/graphics/composer/2.1/vts/functional/Android.bp b/graphics/composer/2.1/vts/functional/Android.bp
index e137afb..9e703d8 100644
--- a/graphics/composer/2.1/vts/functional/Android.bp
+++ b/graphics/composer/2.1/vts/functional/Android.bp
@@ -21,6 +21,7 @@
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libbase",
         "libfmq",
         "libsync",
         "android.hardware.graphics.mapper@2.0",
diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
index 4fee560..f0250c0 100644
--- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
+++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "graphics_composer_hidl_hal_test"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <composer-vts/2.1/ComposerVts.h>
 #include <composer-vts/2.1/GraphicsComposerCallback.h>
 #include <composer-vts/2.1/TestCommandReader.h>
@@ -1102,3 +1103,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index 16e9138..6aa836c 100644
--- a/graphics/composer/2.2/vts/functional/Android.bp
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -31,6 +31,7 @@
         "libEGL",
         "libGLESv1_CM",
         "libGLESv2",
+        "libbase",
         "libfmq",
         "libgui",
         "libhidlbase",
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
index 4d7df1c..31ec885 100644
--- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "graphics_composer_hidl_hal_test@2.2"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <composer-vts/2.1/GraphicsComposerCallback.h>
 #include <composer-vts/2.1/TestCommandReader.h>
@@ -700,3 +701,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/graphics/composer/2.3/vts/functional/Android.bp b/graphics/composer/2.3/vts/functional/Android.bp
index 1ab6b3b..1cbb60e 100644
--- a/graphics/composer/2.3/vts/functional/Android.bp
+++ b/graphics/composer/2.3/vts/functional/Android.bp
@@ -21,6 +21,7 @@
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libbase",
         "libfmq",
         "libhidlbase",
         "libsync",
diff --git a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
index a4c1b63..8b42654 100644
--- a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
+++ b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
 #include <composer-vts/2.1/GraphicsComposerCallback.h>
@@ -630,3 +631,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/graphics/composer/2.4/vts/functional/Android.bp b/graphics/composer/2.4/vts/functional/Android.bp
index d0209b7..cab549c 100644
--- a/graphics/composer/2.4/vts/functional/Android.bp
+++ b/graphics/composer/2.4/vts/functional/Android.bp
@@ -21,6 +21,7 @@
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libbase",
         "libfmq",
         "libsync",
         "android.hardware.graphics.mapper@2.0",
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index e6ecf93..d8312a2 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -21,6 +21,7 @@
 #include <thread>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
 #include <composer-vts/2.4/ComposerVts.h>
@@ -57,6 +58,25 @@
 using ContentType = IComposerClient::ContentType;
 using DisplayCapability = IComposerClient::DisplayCapability;
 
+class VtsDisplay {
+  public:
+    VtsDisplay(Display display, int32_t displayWidth, int32_t displayHeight)
+        : mDisplay(display), mDisplayWidth(displayWidth), mDisplayHeight(displayHeight) {}
+
+    Display get() const { return mDisplay; }
+
+    IComposerClient::FRect getCrop() const {
+        return {0, 0, static_cast<float>(mDisplayWidth), static_cast<float>(mDisplayHeight)};
+    }
+
+    IComposerClient::Rect getFrameRect() const { return {0, 0, mDisplayWidth, mDisplayHeight}; }
+
+  private:
+    const Display mDisplay;
+    const int32_t mDisplayWidth;
+    const int32_t mDisplayHeight;
+};
+
 class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> {
   protected:
     void SetUp() override {
@@ -67,15 +87,19 @@
         mComposerCallback = new GraphicsComposerCallback;
         mComposerClient->registerCallback_2_4(mComposerCallback);
 
-        // assume the first display is primary and is never removed
-        mPrimaryDisplay = waitForFirstDisplay();
+        // assume the first displays are built-in and are never removed
+        mDisplays = waitForDisplays();
 
         mInvalidDisplayId = GetInvalidDisplayId();
 
         // explicitly disable vsync
-        mComposerClient->setVsyncEnabled(mPrimaryDisplay, false);
+        for (const auto& display : mDisplays) {
+            mComposerClient->setVsyncEnabled(display.get(), false);
+        }
         mComposerCallback->setVsyncAllowed(false);
 
+        ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
+
         mWriter = std::make_unique<CommandWriterBase>(1024);
         mReader = std::make_unique<TestCommandReader>();
     }
@@ -83,6 +107,7 @@
     void TearDown() override {
         ASSERT_EQ(0, mReader->mErrors.size());
         ASSERT_EQ(0, mReader->mCompositionChanges.size());
+
         if (mComposerCallback != nullptr) {
             EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount());
             EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount());
@@ -97,10 +122,10 @@
     // display.  Currently assuming that a device will never have close to
     // std::numeric_limit<uint64_t>::max() displays registered while running tests
     Display GetInvalidDisplayId() {
-        std::vector<Display> validDisplays = mComposerCallback->getDisplays();
         uint64_t id = std::numeric_limits<uint64_t>::max();
         while (id > 0) {
-            if (std::find(validDisplays.begin(), validDisplays.end(), id) == validDisplays.end()) {
+            if (std::none_of(mDisplays.begin(), mDisplays.end(),
+                             [&](const VtsDisplay& display) { return id == display.get(); })) {
                 return id;
             }
             id--;
@@ -127,6 +152,30 @@
 
     void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
 
+    const native_handle_t* allocate() {
+        return mGralloc->allocate(
+                /*width*/ 64, /*height*/ 64, /*layerCount*/ 1,
+                static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888),
+                static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN));
+    }
+
+    struct TestParameters {
+        nsecs_t delayForChange;
+        bool refreshMiss;
+    };
+
+    void Test_setActiveConfigWithConstraints(const TestParameters& params);
+
+    void sendRefreshFrame(const VtsDisplay& display, const VsyncPeriodChangeTimeline*);
+
+    void waitForVsyncPeriodChange(Display display, const VsyncPeriodChangeTimeline& timeline,
+                                  int64_t desiredTimeNanos, int64_t oldPeriodNanos,
+                                  int64_t newPeriodNanos);
+
+    std::unique_ptr<ComposerClient> mComposerClient;
+    std::vector<VtsDisplay> mDisplays;
+    Display mInvalidDisplayId;
+
     void forEachTwoConfigs(Display display, std::function<void(Config, Config)> func) {
         const auto displayConfigs = mComposerClient->getDisplayConfigs(display);
         for (const Config config1 : displayConfigs) {
@@ -138,88 +187,44 @@
         }
     }
 
-    // use the slot count usually set by SF
-    static constexpr uint32_t kBufferSlotCount = 64;
-
     void Test_setContentType(const ContentType& contentType, const char* contentTypeStr);
     void Test_setContentTypeForDisplay(const Display& display,
                                        const std::vector<ContentType>& capabilities,
                                        const ContentType& contentType, const char* contentTypeStr);
 
-    std::unique_ptr<Composer> mComposer;
-    std::unique_ptr<ComposerClient> mComposerClient;
-    sp<GraphicsComposerCallback> mComposerCallback;
-    // the first display and is assumed never to be removed
-    Display mPrimaryDisplay;
-    Display mInvalidDisplayId;
-    std::unique_ptr<CommandWriterBase> mWriter;
-    std::unique_ptr<TestCommandReader> mReader;
-
   private:
-    Display waitForFirstDisplay() {
+    // use the slot count usually set by SF
+    static constexpr uint32_t kBufferSlotCount = 64;
+
+    std::vector<VtsDisplay> waitForDisplays() {
         while (true) {
+            // Sleep for a small period of time to allow all built-in displays
+            // to post hotplug events
+            std::this_thread::sleep_for(5ms);
             std::vector<Display> displays = mComposerCallback->getDisplays();
             if (displays.empty()) {
-                usleep(5 * 1000);
                 continue;
             }
 
-            return displays[0];
+            std::vector<VtsDisplay> vtsDisplays;
+            vtsDisplays.reserve(displays.size());
+            for (Display display : displays) {
+                const Config activeConfig = mComposerClient->getActiveConfig(display);
+                const int32_t displayWidth = mComposerClient->getDisplayAttribute_2_4(
+                        display, activeConfig, IComposerClient::Attribute::WIDTH);
+                const int32_t displayHeight = mComposerClient->getDisplayAttribute_2_4(
+                        display, activeConfig, IComposerClient::Attribute::HEIGHT);
+                vtsDisplays.emplace_back(VtsDisplay{display, displayWidth, displayHeight});
+            }
+
+            return vtsDisplays;
         }
     }
-};
 
-// Tests for IComposerClient::Command.
-class GraphicsComposerHidlCommandTest : public GraphicsComposerHidlTest {
-  protected:
-    void SetUp() override {
-        ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::SetUp());
-
-        ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
-
-        const Config activeConfig = mComposerClient->getActiveConfig(mPrimaryDisplay);
-        mDisplayWidth = mComposerClient->getDisplayAttribute_2_4(mPrimaryDisplay, activeConfig,
-                                                                 IComposerClient::Attribute::WIDTH);
-        mDisplayHeight = mComposerClient->getDisplayAttribute_2_4(
-                mPrimaryDisplay, activeConfig, IComposerClient::Attribute::HEIGHT);
-
-        mWriter = std::make_unique<CommandWriterBase>(1024);
-        mReader = std::make_unique<TestCommandReader>();
-    }
-
-    void TearDown() override {
-        ASSERT_EQ(0, mReader->mErrors.size());
-        ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::TearDown());
-    }
-
-    const native_handle_t* allocate() {
-        return mGralloc->allocate(
-                /*width*/ 64, /*height*/ 64, /*layerCount*/ 1,
-                static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888),
-                static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN));
-    }
-
-    void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
-
-    struct TestParameters {
-        nsecs_t delayForChange;
-        bool refreshMiss;
-    };
-
-    void Test_setActiveConfigWithConstraints(const TestParameters& params);
-
-    void sendRefreshFrame(const VsyncPeriodChangeTimeline*);
-
-    void waitForVsyncPeriodChange(Display display, const VsyncPeriodChangeTimeline& timeline,
-                                  int64_t desiredTimeNanos, int64_t oldPeriodNanos,
-                                  int64_t newPeriodNanos);
-
+    std::unique_ptr<Composer> mComposer;
     std::unique_ptr<CommandWriterBase> mWriter;
     std::unique_ptr<TestCommandReader> mReader;
-    int32_t mDisplayWidth;
-    int32_t mDisplayHeight;
-
-  private:
+    sp<GraphicsComposerCallback> mComposerCallback;
     std::unique_ptr<Gralloc> mGralloc;
 };
 
@@ -230,9 +235,10 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, getDisplayCapabilities) {
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         std::vector<IComposerClient::DisplayCapability> capabilities;
-        EXPECT_EQ(Error::NONE, mComposerClient->getDisplayCapabilities(display, &capabilities));
+        EXPECT_EQ(Error::NONE,
+                  mComposerClient->getDisplayCapabilities(display.get(), &capabilities));
     }
 }
 
@@ -241,38 +247,40 @@
     EXPECT_EQ(Error::BAD_DISPLAY,
               mComposerClient->getDisplayConnectionType(mInvalidDisplayId, &type));
 
-    for (Display display : mComposerCallback->getDisplays()) {
-        EXPECT_EQ(Error::NONE, mComposerClient->getDisplayConnectionType(display, &type));
+    for (const auto& display : mDisplays) {
+        EXPECT_EQ(Error::NONE, mComposerClient->getDisplayConnectionType(display.get(), &type));
     }
 }
 
 TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute_2_4) {
-    std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay);
-    for (auto config : configs) {
-        const std::array<IComposerClient::Attribute, 4> requiredAttributes = {{
-                IComposerClient::Attribute::WIDTH,
-                IComposerClient::Attribute::HEIGHT,
-                IComposerClient::Attribute::VSYNC_PERIOD,
-                IComposerClient::Attribute::CONFIG_GROUP,
-        }};
-        for (auto attribute : requiredAttributes) {
-            mComposerClient->getRaw()->getDisplayAttribute_2_4(
-                    mPrimaryDisplay, config, attribute,
-                    [&](const auto& tmpError, const auto& value) {
-                        EXPECT_EQ(Error::NONE, tmpError);
-                        EXPECT_NE(-1, value);
-                    });
-        }
+    for (const auto& display : mDisplays) {
+        std::vector<Config> configs = mComposerClient->getDisplayConfigs(display.get());
+        for (auto config : configs) {
+            const std::array<IComposerClient::Attribute, 4> requiredAttributes = {{
+                    IComposerClient::Attribute::WIDTH,
+                    IComposerClient::Attribute::HEIGHT,
+                    IComposerClient::Attribute::VSYNC_PERIOD,
+                    IComposerClient::Attribute::CONFIG_GROUP,
+            }};
+            for (auto attribute : requiredAttributes) {
+                mComposerClient->getRaw()->getDisplayAttribute_2_4(
+                        display.get(), config, attribute,
+                        [&](const auto& tmpError, const auto& value) {
+                            EXPECT_EQ(Error::NONE, tmpError);
+                            EXPECT_NE(-1, value);
+                        });
+            }
 
-        const std::array<IComposerClient::Attribute, 2> optionalAttributes = {{
-                IComposerClient::Attribute::DPI_X,
-                IComposerClient::Attribute::DPI_Y,
-        }};
-        for (auto attribute : optionalAttributes) {
-            mComposerClient->getRaw()->getDisplayAttribute_2_4(
-                    mPrimaryDisplay, config, attribute, [&](const auto& tmpError, const auto&) {
-                        EXPECT_TRUE(tmpError == Error::NONE || tmpError == Error::UNSUPPORTED);
-                    });
+            const std::array<IComposerClient::Attribute, 2> optionalAttributes = {{
+                    IComposerClient::Attribute::DPI_X,
+                    IComposerClient::Attribute::DPI_Y,
+            }};
+            for (auto attribute : optionalAttributes) {
+                mComposerClient->getRaw()->getDisplayAttribute_2_4(
+                        display.get(), config, attribute, [&](const auto& tmpError, const auto&) {
+                            EXPECT_TRUE(tmpError == Error::NONE || tmpError == Error::UNSUPPORTED);
+                        });
+            }
         }
     }
 }
@@ -283,11 +291,12 @@
               mComposerClient->getDisplayVsyncPeriod(mInvalidDisplayId, &vsyncPeriodNanos));
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, getDisplayVsyncPeriod) {
-    for (Display display : mComposerCallback->getDisplays()) {
-        for (Config config : mComposerClient->getDisplayConfigs(display)) {
+TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod) {
+    for (const auto& display : mDisplays) {
+        for (Config config : mComposerClient->getDisplayConfigs(display.get())) {
             VsyncPeriodNanos expectedVsyncPeriodNanos = mComposerClient->getDisplayAttribute_2_4(
-                    display, config, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+                    display.get(), config,
+                    IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
 
             VsyncPeriodChangeTimeline timeline;
             IComposerClient::VsyncPeriodChangeConstraints constraints;
@@ -295,12 +304,12 @@
             constraints.desiredTimeNanos = systemTime();
             constraints.seamlessRequired = false;
             EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
-                                           display, config, constraints, &timeline));
+                                           display.get(), config, constraints, &timeline));
 
             if (timeline.refreshRequired) {
-                sendRefreshFrame(&timeline);
+                sendRefreshFrame(display, &timeline);
             }
-            waitForVsyncPeriodChange(display, timeline, constraints.desiredTimeNanos, 0,
+            waitForVsyncPeriodChange(display.get(), timeline, constraints.desiredTimeNanos, 0,
                                      expectedVsyncPeriodNanos);
 
             VsyncPeriodNanos vsyncPeriodNanos;
@@ -309,7 +318,7 @@
                 std::this_thread::sleep_for(10ms);
                 vsyncPeriodNanos = 0;
                 EXPECT_EQ(Error::NONE,
-                          mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                          mComposerClient->getDisplayVsyncPeriod(display.get(), &vsyncPeriodNanos));
                 --retryCount;
             } while (vsyncPeriodNanos != expectedVsyncPeriodNanos && retryCount > 0);
 
@@ -322,7 +331,7 @@
                 timeout *= 2;
                 vsyncPeriodNanos = 0;
                 EXPECT_EQ(Error::NONE,
-                          mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                          mComposerClient->getDisplayVsyncPeriod(display.get(), &vsyncPeriodNanos));
                 EXPECT_EQ(vsyncPeriodNanos, expectedVsyncPeriodNanos);
             }
         }
@@ -347,31 +356,34 @@
     constraints.seamlessRequired = false;
     constraints.desiredTimeNanos = systemTime();
 
-    for (Display display : mComposerCallback->getDisplays()) {
-        Config invalidConfigId = GetInvalidConfigId(display);
-        EXPECT_EQ(Error::BAD_CONFIG, mComposerClient->setActiveConfigWithConstraints(
-                                             display, invalidConfigId, constraints, &timeline));
+    for (const auto& display : mDisplays) {
+        Config invalidConfigId = GetInvalidConfigId(display.get());
+        EXPECT_EQ(Error::BAD_CONFIG,
+                  mComposerClient->setActiveConfigWithConstraints(display.get(), invalidConfigId,
+                                                                  constraints, &timeline));
     }
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_SeamlessNotAllowed) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_SeamlessNotAllowed) {
     VsyncPeriodChangeTimeline timeline;
     IComposerClient::VsyncPeriodChangeConstraints constraints;
 
     constraints.seamlessRequired = true;
     constraints.desiredTimeNanos = systemTime();
 
-    for (Display display : mComposerCallback->getDisplays()) {
-        forEachTwoConfigs(display, [&](Config config1, Config config2) {
+    for (const auto& display : mDisplays) {
+        forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
             const auto configGroup1 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config1, IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
+                    display.get(), config1,
+                    IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
             const auto configGroup2 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config2, IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
+                    display.get(), config2,
+                    IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
             if (configGroup1 != configGroup2) {
-                mComposerClient->setActiveConfig(display, config1);
-                sendRefreshFrame(nullptr);
+                mComposerClient->setActiveConfig(display.get(), config1);
+                sendRefreshFrame(display, nullptr);
                 EXPECT_EQ(Error::SEAMLESS_NOT_ALLOWED,
-                          mComposerClient->setActiveConfigWithConstraints(display, config2,
+                          mComposerClient->setActiveConfigWithConstraints(display.get(), config2,
                                                                           constraints, &timeline));
             }
         });
@@ -382,7 +394,8 @@
     return std::chrono::time_point<std::chrono::steady_clock>(std::chrono::nanoseconds(time));
 }
 
-void GraphicsComposerHidlCommandTest::sendRefreshFrame(const VsyncPeriodChangeTimeline* timeline) {
+void GraphicsComposerHidlTest::sendRefreshFrame(const VtsDisplay& display,
+                                                const VsyncPeriodChangeTimeline* timeline) {
     if (timeline != nullptr) {
         // Refresh time should be before newVsyncAppliedTimeNanos
         EXPECT_LT(timeline->refreshTimeNanos, timeline->newVsyncAppliedTimeNanos);
@@ -390,29 +403,25 @@
         std::this_thread::sleep_until(toTimePoint(timeline->refreshTimeNanos));
     }
 
-    mWriter->selectDisplay(mPrimaryDisplay);
-    mComposerClient->setPowerMode(mPrimaryDisplay, V2_1::IComposerClient::PowerMode::ON);
-    mComposerClient->setColorMode_2_3(mPrimaryDisplay, ColorMode::NATIVE,
-                                      RenderIntent::COLORIMETRIC);
+    mWriter->selectDisplay(display.get());
+    mComposerClient->setPowerMode(display.get(), V2_1::IComposerClient::PowerMode::ON);
+    mComposerClient->setColorMode_2_3(display.get(), ColorMode::NATIVE, RenderIntent::COLORIMETRIC);
 
     auto handle = allocate();
     ASSERT_NE(nullptr, handle);
 
-    IComposerClient::Rect displayFrame{0, 0, mDisplayWidth, mDisplayHeight};
-
     Layer layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
+    ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(display.get(), kBufferSlotCount));
     mWriter->selectLayer(layer);
     mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
-    mWriter->setLayerDisplayFrame(displayFrame);
+    mWriter->setLayerDisplayFrame(display.getFrameRect());
     mWriter->setLayerPlaneAlpha(1);
-    mWriter->setLayerSourceCrop({0, 0, (float)mDisplayWidth, (float)mDisplayHeight});
+    mWriter->setLayerSourceCrop(display.getCrop());
     mWriter->setLayerTransform(static_cast<Transform>(0));
-    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, displayFrame));
+    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, display.getFrameRect()));
     mWriter->setLayerZOrder(10);
     mWriter->setLayerBlendMode(IComposerClient::BlendMode::NONE);
-    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, displayFrame));
+    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, display.getFrameRect()));
     mWriter->setLayerBuffer(0, handle, -1);
     mWriter->setLayerDataspace(Dataspace::UNKNOWN);
 
@@ -440,9 +449,11 @@
     execute();
 }
 
-void GraphicsComposerHidlCommandTest::waitForVsyncPeriodChange(
-        Display display, const VsyncPeriodChangeTimeline& timeline, int64_t desiredTimeNanos,
-        int64_t oldPeriodNanos, int64_t newPeriodNanos) {
+void GraphicsComposerHidlTest::waitForVsyncPeriodChange(Display display,
+                                                        const VsyncPeriodChangeTimeline& timeline,
+                                                        int64_t desiredTimeNanos,
+                                                        int64_t oldPeriodNanos,
+                                                        int64_t newPeriodNanos) {
     const auto CHANGE_DEADLINE = toTimePoint(timeline.newVsyncAppliedTimeNanos) + 100ms;
     while (std::chrono::steady_clock::now() <= CHANGE_DEADLINE) {
         VsyncPeriodNanos vsyncPeriodNanos;
@@ -456,17 +467,18 @@
     }
 }
 
-void GraphicsComposerHidlCommandTest::Test_setActiveConfigWithConstraints(
-        const TestParameters& params) {
-    for (Display display : mComposerCallback->getDisplays()) {
-        forEachTwoConfigs(display, [&](Config config1, Config config2) {
-            mComposerClient->setActiveConfig(display, config1);
-            sendRefreshFrame(nullptr);
+void GraphicsComposerHidlTest::Test_setActiveConfigWithConstraints(const TestParameters& params) {
+    for (const auto& display : mDisplays) {
+        forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
+            mComposerClient->setActiveConfig(display.get(), config1);
+            sendRefreshFrame(display, nullptr);
 
             int32_t vsyncPeriod1 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config1, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+                    display.get(), config1,
+                    IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
             int32_t vsyncPeriod2 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config2, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+                    display.get(), config2,
+                    IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
 
             if (vsyncPeriod1 == vsyncPeriod2) {
                 return;  // continue
@@ -477,7 +489,7 @@
                     .desiredTimeNanos = systemTime() + params.delayForChange,
                     .seamlessRequired = false};
             EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
-                                           display, config2, constraints, &timeline));
+                                           display.get(), config2, constraints, &timeline));
 
             EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos >= constraints.desiredTimeNanos);
             // Refresh rate should change within a reasonable time
@@ -491,10 +503,10 @@
                     // callback
                     std::this_thread::sleep_until(toTimePoint(timeline.refreshTimeNanos) + 100ms);
                 }
-                sendRefreshFrame(&timeline);
+                sendRefreshFrame(display, &timeline);
             }
-            waitForVsyncPeriodChange(display, timeline, constraints.desiredTimeNanos, vsyncPeriod1,
-                                     vsyncPeriod2);
+            waitForVsyncPeriodChange(display.get(), timeline, constraints.desiredTimeNanos,
+                                     vsyncPeriod1, vsyncPeriod2);
 
             // At this point the refresh rate should have changed already, however in rare
             // cases the implementation might have missed the deadline. In this case a new
@@ -506,30 +518,30 @@
 
             if (newTimeline.has_value()) {
                 if (newTimeline->refreshRequired) {
-                    sendRefreshFrame(&newTimeline.value());
+                    sendRefreshFrame(display, &newTimeline.value());
                 }
-                waitForVsyncPeriodChange(display, newTimeline.value(), constraints.desiredTimeNanos,
-                                         vsyncPeriod1, vsyncPeriod2);
+                waitForVsyncPeriodChange(display.get(), newTimeline.value(),
+                                         constraints.desiredTimeNanos, vsyncPeriod1, vsyncPeriod2);
             }
 
             VsyncPeriodNanos vsyncPeriodNanos;
             EXPECT_EQ(Error::NONE,
-                      mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                      mComposerClient->getDisplayVsyncPeriod(display.get(), &vsyncPeriodNanos));
             EXPECT_EQ(vsyncPeriodNanos, vsyncPeriod2);
         });
     }
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints) {
     Test_setActiveConfigWithConstraints({.delayForChange = 0, .refreshMiss = false});
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_Delayed) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_Delayed) {
     Test_setActiveConfigWithConstraints({.delayForChange = 300'000'000,  // 300ms
                                          .refreshMiss = false});
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_MissRefresh) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_MissRefresh) {
     Test_setActiveConfigWithConstraints({.delayForChange = 0, .refreshMiss = true});
 }
 
@@ -539,9 +551,9 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, setAutoLowLatencyMode) {
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         std::vector<DisplayCapability> capabilities;
-        const auto error = mComposerClient->getDisplayCapabilities(display, &capabilities);
+        const auto error = mComposerClient->getDisplayCapabilities(display.get(), &capabilities);
         EXPECT_EQ(Error::NONE, error);
 
         const bool allmSupport =
@@ -550,16 +562,16 @@
 
         if (!allmSupport) {
             EXPECT_EQ(Error::UNSUPPORTED,
-                      mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, true));
+                      mComposerClient->setAutoLowLatencyMode(display.get(), true));
             EXPECT_EQ(Error::UNSUPPORTED,
-                      mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
+                      mComposerClient->setAutoLowLatencyMode(display.get(), false));
             GTEST_SUCCEED() << "Auto Low Latency Mode is not supported on display "
-                            << std::to_string(display) << ", skipping test";
+                            << std::to_string(display.get()) << ", skipping test";
             return;
         }
 
-        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, true));
-        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
+        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(display.get(), true));
+        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(display.get(), false));
     }
 }
 
@@ -572,10 +584,10 @@
 
 TEST_P(GraphicsComposerHidlTest, getSupportedContentTypes) {
     std::vector<ContentType> supportedContentTypes;
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         supportedContentTypes.clear();
         const auto error =
-                mComposerClient->getSupportedContentTypes(display, &supportedContentTypes);
+                mComposerClient->getSupportedContentTypes(display.get(), &supportedContentTypes);
         const bool noneSupported =
                 std::find(supportedContentTypes.begin(), supportedContentTypes.end(),
                           ContentType::NONE) != supportedContentTypes.end();
@@ -585,8 +597,8 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, setContentTypeNoneAlwaysAccepted) {
-    for (Display display : mComposerCallback->getDisplays()) {
-        const auto error = mComposerClient->setContentType(display, ContentType::NONE);
+    for (const auto& display : mDisplays) {
+        const auto error = mComposerClient->setContentType(display.get(), ContentType::NONE);
         EXPECT_NE(Error::UNSUPPORTED, error);
     }
 }
@@ -618,13 +630,14 @@
 
 void GraphicsComposerHidlTest::Test_setContentType(const ContentType& contentType,
                                                    const char* contentTypeStr) {
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         std::vector<ContentType> supportedContentTypes;
         const auto error =
-                mComposerClient->getSupportedContentTypes(display, &supportedContentTypes);
+                mComposerClient->getSupportedContentTypes(display.get(), &supportedContentTypes);
         EXPECT_EQ(Error::NONE, error);
 
-        Test_setContentTypeForDisplay(display, supportedContentTypes, contentType, contentTypeStr);
+        Test_setContentTypeForDisplay(display.get(), supportedContentTypes, contentType,
+                                      contentTypeStr);
     }
 }
 
@@ -650,13 +663,7 @@
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
         android::hardware::PrintInstanceNameToString);
 
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsComposerHidlCommandTest);
-INSTANTIATE_TEST_SUITE_P(
-        PerInstance, GraphicsComposerHidlCommandTest,
-        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
-        android::hardware::PrintInstanceNameToString);
-
-TEST_P(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) {
+TEST_P(GraphicsComposerHidlTest, getLayerGenericMetadataKeys) {
     std::vector<IComposerClient::LayerGenericMetadataKey> keys;
     mComposerClient->getLayerGenericMetadataKeys(&keys);
 
@@ -685,3 +692,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/keymaster/3.0/default/KeymasterDevice.cpp b/keymaster/3.0/default/KeymasterDevice.cpp
index 7d3e6f2..8b416c3 100644
--- a/keymaster/3.0/default/KeymasterDevice.cpp
+++ b/keymaster/3.0/default/KeymasterDevice.cpp
@@ -22,7 +22,6 @@
 #include <log/log.h>
 
 #include <AndroidKeymaster3Device.h>
-#include <hardware/keymaster0.h>
 #include <hardware/keymaster1.h>
 #include <hardware/keymaster2.h>
 #include <hardware/keymaster_defs.h>
@@ -33,16 +32,6 @@
 namespace V3_0 {
 namespace implementation {
 
-static int get_keymaster0_dev(keymaster0_device_t** dev, const hw_module_t* mod) {
-    int rc = keymaster0_open(mod, dev);
-    if (rc) {
-        ALOGE("Error opening keystore keymaster0 device.");
-        *dev = nullptr;
-        return rc;
-    }
-    return 0;
-}
-
 static int get_keymaster1_dev(keymaster1_device_t** dev, const hw_module_t* mod) {
     int rc = keymaster1_open(mod, dev);
     if (rc) {
@@ -75,11 +64,7 @@
     }
 
     if (mod->module_api_version < KEYMASTER_MODULE_API_VERSION_1_0) {
-        keymaster0_device_t* dev = nullptr;
-        if (get_keymaster0_dev(&dev, mod)) {
-            return nullptr;
-        }
-        return ::keymaster::ng::CreateKeymasterDevice(dev);
+        return nullptr;
     } else if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
         keymaster1_device_t* dev = nullptr;
         if (get_keymaster1_dev(&dev, mod)) {
diff --git a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Tag.aidl b/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Tag.aidl
index fba58af..33a95fe 100644
--- a/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Tag.aidl
+++ b/keymint/aidl/aidl_api/android.hardware.keymint/current/android/hardware/keymint/Tag.aidl
@@ -41,7 +41,7 @@
   MIN_SECONDS_BETWEEN_OPS = 805306771,
   MAX_USES_PER_BOOT = 805306772,
   USER_ID = 805306869,
-  USER_SECURE_ID = 1073742326,
+  USER_SECURE_ID = -1610612234,
   NO_AUTH_REQUIRED = 1879048695,
   USER_AUTH_TYPE = 268435960,
   AUTH_TIMEOUT = 805306873,
diff --git a/keymint/aidl/android/hardware/keymint/Tag.aidl b/keymint/aidl/android/hardware/keymint/Tag.aidl
index e85a8f5..46da096 100644
--- a/keymint/aidl/android/hardware/keymint/Tag.aidl
+++ b/keymint/aidl/android/hardware/keymint/Tag.aidl
@@ -355,7 +355,7 @@
      *
      * Must be hardware-enforced.
      */
-    USER_SECURE_ID = (4 << 28) | 502, /* TagType:UINT_REP */
+    USER_SECURE_ID = (10 << 28) | 502, /* TagType:ULONG_REP */
 
     /**
      * Tag::NO_AUTH_REQUIRED specifies that no authentication is required to use this key.  This tag
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
index fb77cb2..d3d933b 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
@@ -24,20 +24,28 @@
 
 namespace android::nn {
 
-GeneralResult<OperandType> convert(const hal::V1_0::OperandType& operandType);
-GeneralResult<OperationType> convert(const hal::V1_0::OperationType& operationType);
-GeneralResult<Operand::LifeTime> convert(const hal::V1_0::OperandLifeTime& lifetime);
-GeneralResult<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus);
-GeneralResult<Capabilities::PerformanceInfo> convert(
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_0::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_0::OperationType& operationType);
+GeneralResult<Operand::LifeTime> unvalidatedConvert(const hal::V1_0::OperandLifeTime& lifetime);
+GeneralResult<DeviceStatus> unvalidatedConvert(const hal::V1_0::DeviceStatus& deviceStatus);
+GeneralResult<Capabilities::PerformanceInfo> unvalidatedConvert(
         const hal::V1_0::PerformanceInfo& performanceInfo);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_0::Capabilities& capabilities);
+GeneralResult<DataLocation> unvalidatedConvert(const hal::V1_0::DataLocation& location);
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_0::Operand& operand);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation);
+GeneralResult<Model::OperandValues> unvalidatedConvert(
+        const hardware::hidl_vec<uint8_t>& operandValues);
+GeneralResult<Memory> unvalidatedConvert(const hardware::hidl_memory& memory);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model);
+GeneralResult<Request::Argument> unvalidatedConvert(
+        const hal::V1_0::RequestArgument& requestArgument);
+GeneralResult<Request> unvalidatedConvert(const hal::V1_0::Request& request);
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_0::ErrorStatus& status);
+
+GeneralResult<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus);
 GeneralResult<Capabilities> convert(const hal::V1_0::Capabilities& capabilities);
-GeneralResult<DataLocation> convert(const hal::V1_0::DataLocation& location);
-GeneralResult<Operand> convert(const hal::V1_0::Operand& operand);
-GeneralResult<Operation> convert(const hal::V1_0::Operation& operation);
-GeneralResult<Model::OperandValues> convert(const hardware::hidl_vec<uint8_t>& operandValues);
-GeneralResult<Memory> convert(const hardware::hidl_memory& memory);
 GeneralResult<Model> convert(const hal::V1_0::Model& model);
-GeneralResult<Request::Argument> convert(const hal::V1_0::RequestArgument& requestArgument);
 GeneralResult<Request> convert(const hal::V1_0::Request& request);
 GeneralResult<ErrorStatus> convert(const hal::V1_0::ErrorStatus& status);
 
@@ -45,21 +53,28 @@
 
 namespace android::hardware::neuralnetworks::V1_0::utils {
 
-nn::GeneralResult<OperandType> convert(const nn::OperandType& operandType);
-nn::GeneralResult<OperationType> convert(const nn::OperationType& operationType);
-nn::GeneralResult<OperandLifeTime> convert(const nn::Operand::LifeTime& lifetime);
-nn::GeneralResult<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
-nn::GeneralResult<PerformanceInfo> convert(
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& lifetime);
+nn::GeneralResult<DeviceStatus> unvalidatedConvert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<PerformanceInfo> unvalidatedConvert(
         const nn::Capabilities::PerformanceInfo& performanceInfo);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<RequestArgument> unvalidatedConvert(const nn::Request::Argument& requestArgument);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool);
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request);
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& status);
+
+nn::GeneralResult<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
 nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
-nn::GeneralResult<DataLocation> convert(const nn::DataLocation& location);
-nn::GeneralResult<Operand> convert(const nn::Operand& operand);
-nn::GeneralResult<Operation> convert(const nn::Operation& operation);
-nn::GeneralResult<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues);
-nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory);
 nn::GeneralResult<Model> convert(const nn::Model& model);
-nn::GeneralResult<RequestArgument> convert(const nn::Request::Argument& requestArgument);
-nn::GeneralResult<hidl_memory> convert(const nn::Request::MemoryPool& memoryPool);
 nn::GeneralResult<Request> convert(const nn::Request& request);
 nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& status);
 
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
index baa2b95..4cec545 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
@@ -22,25 +22,16 @@
 #include <android-base/logging.h>
 #include <android/hardware/neuralnetworks/1.0/types.h>
 #include <nnapi/Result.h>
-#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
-#include <nnapi/Validation.h>
 
 namespace android::hardware::neuralnetworks::V1_0::utils {
 
-constexpr auto kVersion = nn::Version::ANDROID_OC_MR1;
-
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
     const auto maybeCanonical = nn::convert(halObject);
     if (!maybeCanonical.has_value()) {
         return nn::error() << maybeCanonical.error().message;
     }
-    const auto version = NN_TRY(nn::validate(maybeCanonical.value()));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required "
-                          << utils::kVersion;
-    }
     return {};
 }
 
@@ -53,21 +44,6 @@
     return result.has_value();
 }
 
-template <typename Type>
-decltype(nn::convert(std::declval<Type>())) validatedConvertToCanonical(const Type& halObject) {
-    auto canonical = NN_TRY(nn::convert(halObject));
-    const auto maybeVersion = nn::validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return nn::error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required "
-                          << utils::kVersion;
-    }
-    return canonical;
-}
-
 }  // namespace android::hardware::neuralnetworks::V1_0::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_H
diff --git a/neuralnetworks/1.0/utils/src/Callbacks.cpp b/neuralnetworks/1.0/utils/src/Callbacks.cpp
index f286bcc..b1259c3 100644
--- a/neuralnetworks/1.0/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.0/utils/src/Callbacks.cpp
@@ -45,8 +45,7 @@
 Return<void> PreparedModelCallback::notify(ErrorStatus status,
                                            const sp<IPreparedModel>& preparedModel) {
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
     } else if (preparedModel == nullptr) {
         notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -73,8 +72,7 @@
 
 Return<void> ExecutionCallback::notify(ErrorStatus status) {
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
     } else {
         notifyInternal({});
diff --git a/neuralnetworks/1.0/utils/src/Conversions.cpp b/neuralnetworks/1.0/utils/src/Conversions.cpp
index 6cf9073..fde7346 100644
--- a/neuralnetworks/1.0/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -22,7 +22,9 @@
 #include <nnapi/OperationTypes.h>
 #include <nnapi/Result.h>
 #include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
+#include <nnapi/Validation.h>
 #include <nnapi/hal/CommonUtils.h>
 
 #include <algorithm>
@@ -40,6 +42,8 @@
     return static_cast<std::underlying_type_t<Type>>(value);
 }
 
+constexpr auto kVersion = android::nn::Version::ANDROID_OC_MR1;
+
 }  // namespace
 
 namespace android::nn {
@@ -49,37 +53,53 @@
 using hardware::hidl_vec;
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-GeneralResult<std::vector<ConvertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
-    std::vector<ConvertOutput<Type>> canonical;
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
-        canonical.push_back(NN_TRY(nn::convert(argument)));
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
+    }
+    return canonical;
+}
+
+template <typename Type>
+decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    const auto maybeVersion = validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
     return canonical;
 }
 
 }  // anonymous namespace
 
-GeneralResult<OperandType> convert(const hal::V1_0::OperandType& operandType) {
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_0::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-GeneralResult<OperationType> convert(const hal::V1_0::OperationType& operationType) {
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_0::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-GeneralResult<Operand::LifeTime> convert(const hal::V1_0::OperandLifeTime& lifetime) {
+GeneralResult<Operand::LifeTime> unvalidatedConvert(const hal::V1_0::OperandLifeTime& lifetime) {
     return static_cast<Operand::LifeTime>(lifetime);
 }
 
-GeneralResult<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus) {
+GeneralResult<DeviceStatus> unvalidatedConvert(const hal::V1_0::DeviceStatus& deviceStatus) {
     return static_cast<DeviceStatus>(deviceStatus);
 }
 
-GeneralResult<Capabilities::PerformanceInfo> convert(
+GeneralResult<Capabilities::PerformanceInfo> unvalidatedConvert(
         const hal::V1_0::PerformanceInfo& performanceInfo) {
     return Capabilities::PerformanceInfo{
             .execTime = performanceInfo.execTime,
@@ -87,9 +107,10 @@
     };
 }
 
-GeneralResult<Capabilities> convert(const hal::V1_0::Capabilities& capabilities) {
-    const auto quantized8Performance = NN_TRY(convert(capabilities.quantized8Performance));
-    const auto float32Performance = NN_TRY(convert(capabilities.float32Performance));
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_0::Capabilities& capabilities) {
+    const auto quantized8Performance =
+            NN_TRY(unvalidatedConvert(capabilities.quantized8Performance));
+    const auto float32Performance = NN_TRY(unvalidatedConvert(capabilities.float32Performance));
 
     auto table = hal::utils::makeQuantized8PerformanceConsistentWithP(float32Performance,
                                                                       quantized8Performance);
@@ -101,7 +122,7 @@
     };
 }
 
-GeneralResult<DataLocation> convert(const hal::V1_0::DataLocation& location) {
+GeneralResult<DataLocation> unvalidatedConvert(const hal::V1_0::DataLocation& location) {
     return DataLocation{
             .poolIndex = location.poolIndex,
             .offset = location.offset,
@@ -109,35 +130,35 @@
     };
 }
 
-GeneralResult<Operand> convert(const hal::V1_0::Operand& operand) {
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_0::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
     };
 }
 
-GeneralResult<Operation> convert(const hal::V1_0::Operation& operation) {
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-GeneralResult<Model::OperandValues> convert(const hidl_vec<uint8_t>& operandValues) {
+GeneralResult<Model::OperandValues> unvalidatedConvert(const hidl_vec<uint8_t>& operandValues) {
     return Model::OperandValues(operandValues.data(), operandValues.size());
 }
 
-GeneralResult<Memory> convert(const hidl_memory& memory) {
+GeneralResult<Memory> unvalidatedConvert(const hidl_memory& memory) {
     return createSharedMemoryFromHidlMemory(memory);
 }
 
-GeneralResult<Model> convert(const hal::V1_0::Model& model) {
-    auto operations = NN_TRY(convert(model.operations));
+GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model) {
+    auto operations = NN_TRY(unvalidatedConvert(model.operations));
 
     // Verify number of consumers.
     const auto numberOfConsumers =
@@ -152,7 +173,7 @@
     }
 
     auto main = Model::Subgraph{
-            .operands = NN_TRY(convert(model.operands)),
+            .operands = NN_TRY(unvalidatedConvert(model.operands)),
             .operations = std::move(operations),
             .inputIndexes = model.inputIndexes,
             .outputIndexes = model.outputIndexes,
@@ -160,35 +181,35 @@
 
     return Model{
             .main = std::move(main),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
     };
 }
 
-GeneralResult<Request::Argument> convert(const hal::V1_0::RequestArgument& argument) {
+GeneralResult<Request::Argument> unvalidatedConvert(const hal::V1_0::RequestArgument& argument) {
     const auto lifetime = argument.hasNoValue ? Request::Argument::LifeTime::NO_VALUE
                                               : Request::Argument::LifeTime::POOL;
     return Request::Argument{
             .lifetime = lifetime,
-            .location = NN_TRY(convert(argument.location)),
+            .location = NN_TRY(unvalidatedConvert(argument.location)),
             .dimensions = argument.dimensions,
     };
 }
 
-GeneralResult<Request> convert(const hal::V1_0::Request& request) {
-    auto memories = NN_TRY(convert(request.pools));
+GeneralResult<Request> unvalidatedConvert(const hal::V1_0::Request& request) {
+    auto memories = NN_TRY(unvalidatedConvert(request.pools));
     std::vector<Request::MemoryPool> pools;
     pools.reserve(memories.size());
     std::move(memories.begin(), memories.end(), std::back_inserter(pools));
 
     return Request{
-            .inputs = NN_TRY(convert(request.inputs)),
-            .outputs = NN_TRY(convert(request.outputs)),
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
             .pools = std::move(pools),
     };
 }
 
-GeneralResult<ErrorStatus> convert(const hal::V1_0::ErrorStatus& status) {
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_0::ErrorStatus& status) {
     switch (status) {
         case hal::V1_0::ErrorStatus::NONE:
         case hal::V1_0::ErrorStatus::DEVICE_UNAVAILABLE:
@@ -201,46 +222,81 @@
            << "Invalid ErrorStatus " << underlyingType(status);
 }
 
+GeneralResult<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus) {
+    return validatedConvert(deviceStatus);
+}
+
+GeneralResult<Capabilities> convert(const hal::V1_0::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_0::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<Request> convert(const hal::V1_0::Request& request) {
+    return validatedConvert(request);
+}
+
+GeneralResult<ErrorStatus> convert(const hal::V1_0::ErrorStatus& status) {
+    return validatedConvert(status);
+}
+
 }  // namespace android::nn
 
 namespace android::hardware::neuralnetworks::V1_0::utils {
 namespace {
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<ConvertOutput<Type>>> convert(const std::vector<Type>& arguments) {
-    hidl_vec<ConvertOutput<Type>> halObject(arguments.size());
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
-        halObject[i] = NN_TRY(utils::convert(arguments[i]));
+        halObject[i] = NN_TRY(utils::unvalidatedConvert(arguments[i]));
     }
     return halObject;
 }
 
+template <typename Type>
+decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
+    const auto maybeVersion = nn::validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return nn::error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return utils::unvalidatedConvert(canonical);
+}
+
 }  // anonymous namespace
 
-nn::GeneralResult<OperandType> convert(const nn::OperandType& operandType) {
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-nn::GeneralResult<OperationType> convert(const nn::OperationType& operationType) {
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-nn::GeneralResult<OperandLifeTime> convert(const nn::Operand::LifeTime& lifetime) {
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& lifetime) {
     if (lifetime == nn::Operand::LifeTime::POINTER) {
         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
-               << "Model cannot be converted because it contains pointer-based memory";
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
     }
     return static_cast<OperandLifeTime>(lifetime);
 }
 
-nn::GeneralResult<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+nn::GeneralResult<DeviceStatus> unvalidatedConvert(const nn::DeviceStatus& deviceStatus) {
     return static_cast<DeviceStatus>(deviceStatus);
 }
 
-nn::GeneralResult<PerformanceInfo> convert(
+nn::GeneralResult<PerformanceInfo> unvalidatedConvert(
         const nn::Capabilities::PerformanceInfo& performanceInfo) {
     return PerformanceInfo{
             .execTime = performanceInfo.execTime,
@@ -248,16 +304,16 @@
     };
 }
 
-nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
     return Capabilities{
-            .float32Performance = NN_TRY(convert(
+            .float32Performance = NN_TRY(unvalidatedConvert(
                     capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_FLOAT32))),
-            .quantized8Performance = NN_TRY(convert(
+            .quantized8Performance = NN_TRY(unvalidatedConvert(
                     capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_QUANT8_ASYMM))),
     };
 }
 
-nn::GeneralResult<DataLocation> convert(const nn::DataLocation& location) {
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location) {
     return DataLocation{
             .poolIndex = location.poolIndex,
             .offset = location.offset,
@@ -265,42 +321,43 @@
     };
 }
 
-nn::GeneralResult<Operand> convert(const nn::Operand& operand) {
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .numberOfConsumers = 0,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
     };
 }
 
-nn::GeneralResult<Operation> convert(const nn::Operation& operation) {
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-nn::GeneralResult<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
     return hidl_vec<uint8_t>(operandValues.data(), operandValues.data() + operandValues.size());
 }
 
-nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory) {
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
     return hidl_memory(memory.name, NN_TRY(hal::utils::hidlHandleFromSharedHandle(memory.handle)),
                        memory.size);
 }
 
-nn::GeneralResult<Model> convert(const nn::Model& model) {
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
     if (!hal::utils::hasNoPointerData(model)) {
         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
-               << "Mdoel cannot be converted because it contains pointer-based memory";
+               << "Mdoel cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
-    auto operands = NN_TRY(convert(model.main.operands));
+    auto operands = NN_TRY(unvalidatedConvert(model.main.operands));
 
     // Update number of consumers.
     const auto numberOfConsumers =
@@ -312,45 +369,46 @@
 
     return Model{
             .operands = std::move(operands),
-            .operations = NN_TRY(convert(model.main.operations)),
+            .operations = NN_TRY(unvalidatedConvert(model.main.operations)),
             .inputIndexes = model.main.inputIndexes,
             .outputIndexes = model.main.outputIndexes,
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
     };
 }
 
-nn::GeneralResult<RequestArgument> convert(const nn::Request::Argument& requestArgument) {
+nn::GeneralResult<RequestArgument> unvalidatedConvert(
+        const nn::Request::Argument& requestArgument) {
     if (requestArgument.lifetime == nn::Request::Argument::LifeTime::POINTER) {
         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
-               << "Request cannot be converted because it contains pointer-based memory";
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
     }
     const bool hasNoValue = requestArgument.lifetime == nn::Request::Argument::LifeTime::NO_VALUE;
     return RequestArgument{
             .hasNoValue = hasNoValue,
-            .location = NN_TRY(convert(requestArgument.location)),
+            .location = NN_TRY(unvalidatedConvert(requestArgument.location)),
             .dimensions = requestArgument.dimensions,
     };
 }
 
-nn::GeneralResult<hidl_memory> convert(const nn::Request::MemoryPool& memoryPool) {
-    return convert(std::get<nn::Memory>(memoryPool));
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool) {
+    return unvalidatedConvert(std::get<nn::Memory>(memoryPool));
 }
 
-nn::GeneralResult<Request> convert(const nn::Request& request) {
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
     if (!hal::utils::hasNoPointerData(request)) {
         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
-               << "Request cannot be converted because it contains pointer-based memory";
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
     return Request{
-            .inputs = NN_TRY(convert(request.inputs)),
-            .outputs = NN_TRY(convert(request.outputs)),
-            .pools = NN_TRY(convert(request.pools)),
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
     };
 }
 
-nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& status) {
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& status) {
     switch (status) {
         case nn::ErrorStatus::NONE:
         case nn::ErrorStatus::DEVICE_UNAVAILABLE:
@@ -363,4 +421,24 @@
     }
 }
 
+nn::GeneralResult<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+    return validatedConvert(deviceStatus);
+}
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<Request> convert(const nn::Request& request) {
+    return validatedConvert(request);
+}
+
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& status) {
+    return validatedConvert(status);
+}
+
 }  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
index 671416b..285c515 100644
--- a/neuralnetworks/1.0/utils/src/Device.cpp
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -48,16 +48,15 @@
                                                  << "uninitialized";
     const auto cb = [&result](ErrorStatus status, const Capabilities& capabilities) {
         if (status != ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical) << "getCapabilities failed with " << toString(status);
         } else {
-            result = validatedConvertToCanonical(capabilities);
+            result = nn::convert(capabilities);
         }
     };
 
     const auto ret = device->getCapabilities(cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -120,7 +119,8 @@
 
 nn::GeneralResult<void> Device::wait() const {
     const auto ret = kDevice->ping();
-    return hal::utils::handleTransportError(ret);
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
 }
 
 nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
@@ -135,8 +135,7 @@
                                                   << "uninitialized";
     auto cb = [&result, &model](ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
         if (status != ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical)
                      << "getSupportedOperations failed with " << toString(status);
         } else if (supportedOperations.size() != model.main.operations.size()) {
@@ -150,7 +149,7 @@
     };
 
     const auto ret = kDevice->getSupportedOperations(hidlModel, cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -170,10 +169,9 @@
     const auto scoped = kDeathHandler.protectCallback(cb.get());
 
     const auto ret = kDevice->prepareModel(hidlModel, cb);
-    const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "prepareModel failed with " << toString(status);
     }
 
diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
index 11ccbe3..46dd3f8 100644
--- a/neuralnetworks/1.0/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
@@ -67,11 +67,9 @@
     const auto scoped = kDeathHandler.protectCallback(cb.get());
 
     const auto ret = kPreparedModel->execute(hidlRequest, cb);
-    const auto status =
-            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "execute failed with " << toString(status);
     }
 
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
index 16ddd53..f646462 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
@@ -24,9 +24,14 @@
 
 namespace android::nn {
 
-GeneralResult<OperationType> convert(const hal::V1_1::OperationType& operationType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_1::OperationType& operationType);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_1::Capabilities& capabilities);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_1::Operation& operation);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_1::Model& model);
+GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const hal::V1_1::ExecutionPreference& executionPreference);
+
 GeneralResult<Capabilities> convert(const hal::V1_1::Capabilities& capabilities);
-GeneralResult<Operation> convert(const hal::V1_1::Operation& operation);
 GeneralResult<Model> convert(const hal::V1_1::Model& model);
 GeneralResult<ExecutionPreference> convert(
         const hal::V1_1::ExecutionPreference& executionPreference);
@@ -35,9 +40,14 @@
 
 namespace android::hardware::neuralnetworks::V1_1::utils {
 
-nn::GeneralResult<OperationType> convert(const nn::OperationType& operationType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const nn::ExecutionPreference& executionPreference);
+
 nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
-nn::GeneralResult<Operation> convert(const nn::Operation& operation);
 nn::GeneralResult<Model> convert(const nn::Model& model);
 nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference);
 
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
index 0fee628..052d88e 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
@@ -22,15 +22,12 @@
 #include <android-base/logging.h>
 #include <android/hardware/neuralnetworks/1.1/types.h>
 #include <nnapi/Result.h>
-#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
-#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 
 namespace android::hardware::neuralnetworks::V1_1::utils {
 
 constexpr auto kDefaultExecutionPreference = ExecutionPreference::FAST_SINGLE_ANSWER;
-constexpr auto kVersion = nn::Version::ANDROID_P;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
@@ -38,11 +35,6 @@
     if (!maybeCanonical.has_value()) {
         return nn::error() << maybeCanonical.error().message;
     }
-    const auto version = NN_TRY(nn::validate(maybeCanonical.value()));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required "
-                          << utils::kVersion;
-    }
     return {};
 }
 
@@ -55,21 +47,6 @@
     return result.has_value();
 }
 
-template <typename Type>
-decltype(nn::convert(std::declval<Type>())) validatedConvertToCanonical(const Type& halObject) {
-    auto canonical = NN_TRY(nn::convert(halObject));
-    const auto maybeVersion = nn::validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return nn::error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required "
-                          << utils::kVersion;
-    }
-    return canonical;
-}
-
 }  // namespace android::hardware::neuralnetworks::V1_1::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_H
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index ffe0752..359f68a 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -23,7 +23,9 @@
 #include <nnapi/OperationTypes.h>
 #include <nnapi/Result.h>
 #include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
+#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
 
@@ -33,35 +35,58 @@
 #include <type_traits>
 #include <utility>
 
+namespace {
+
+constexpr auto kVersion = android::nn::Version::ANDROID_P;
+
+}  // namespace
+
 namespace android::nn {
 namespace {
 
 using hardware::hidl_vec;
 
 template <typename Input>
-using convertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-GeneralResult<std::vector<convertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
-    std::vector<convertOutput<Type>> canonical;
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
-        canonical.push_back(NN_TRY(nn::convert(argument)));
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
+    }
+    return canonical;
+}
+
+template <typename Type>
+decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    const auto maybeVersion = validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
     return canonical;
 }
 
 }  // anonymous namespace
 
-GeneralResult<OperationType> convert(const hal::V1_1::OperationType& operationType) {
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_1::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-GeneralResult<Capabilities> convert(const hal::V1_1::Capabilities& capabilities) {
-    const auto quantized8Performance = NN_TRY(convert(capabilities.quantized8Performance));
-    const auto float32Performance = NN_TRY(convert(capabilities.float32Performance));
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_1::Capabilities& capabilities) {
+    const auto quantized8Performance =
+            NN_TRY(unvalidatedConvert(capabilities.quantized8Performance));
+    const auto float32Performance = NN_TRY(unvalidatedConvert(capabilities.float32Performance));
     const auto relaxedFloat32toFloat16Performance =
-            NN_TRY(convert(capabilities.relaxedFloat32toFloat16Performance));
+            NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16Performance));
 
     auto table = hal::utils::makeQuantized8PerformanceConsistentWithP(float32Performance,
                                                                       quantized8Performance);
@@ -73,16 +98,16 @@
     };
 }
 
-GeneralResult<Operation> convert(const hal::V1_1::Operation& operation) {
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_1::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-GeneralResult<Model> convert(const hal::V1_1::Model& model) {
-    auto operations = NN_TRY(convert(model.operations));
+GeneralResult<Model> unvalidatedConvert(const hal::V1_1::Model& model) {
+    auto operations = NN_TRY(unvalidatedConvert(model.operations));
 
     // Verify number of consumers.
     const auto numberOfConsumers =
@@ -97,7 +122,7 @@
     }
 
     auto main = Model::Subgraph{
-            .operands = NN_TRY(convert(model.operands)),
+            .operands = NN_TRY(unvalidatedConvert(model.operands)),
             .operations = std::move(operations),
             .inputIndexes = model.inputIndexes,
             .outputIndexes = model.outputIndexes,
@@ -105,85 +130,114 @@
 
     return Model{
             .main = std::move(main),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
     };
 }
 
-GeneralResult<ExecutionPreference> convert(
+GeneralResult<ExecutionPreference> unvalidatedConvert(
         const hal::V1_1::ExecutionPreference& executionPreference) {
     return static_cast<ExecutionPreference>(executionPreference);
 }
 
+GeneralResult<Capabilities> convert(const hal::V1_1::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_1::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<ExecutionPreference> convert(
+        const hal::V1_1::ExecutionPreference& executionPreference) {
+    return validatedConvert(executionPreference);
+}
+
 }  // namespace android::nn
 
 namespace android::hardware::neuralnetworks::V1_1::utils {
 namespace {
 
-using utils::convert;
+using utils::unvalidatedConvert;
 
-nn::GeneralResult<V1_0::PerformanceInfo> convert(
+nn::GeneralResult<V1_0::PerformanceInfo> unvalidatedConvert(
         const nn::Capabilities::PerformanceInfo& performanceInfo) {
-    return V1_0::utils::convert(performanceInfo);
+    return V1_0::utils::unvalidatedConvert(performanceInfo);
 }
 
-nn::GeneralResult<V1_0::Operand> convert(const nn::Operand& operand) {
-    return V1_0::utils::convert(operand);
+nn::GeneralResult<V1_0::Operand> unvalidatedConvert(const nn::Operand& operand) {
+    return V1_0::utils::unvalidatedConvert(operand);
 }
 
-nn::GeneralResult<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
-    return V1_0::utils::convert(operandValues);
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return V1_0::utils::unvalidatedConvert(operandValues);
 }
 
-nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory) {
-    return V1_0::utils::convert(memory);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+    return V1_0::utils::unvalidatedConvert(memory);
 }
 
 template <typename Input>
-using convertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<convertOutput<Type>>> convert(const std::vector<Type>& arguments) {
-    hidl_vec<convertOutput<Type>> halObject(arguments.size());
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
-        halObject[i] = NN_TRY(convert(arguments[i]));
+        halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
     }
     return halObject;
 }
 
+template <typename Type>
+decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
+    const auto maybeVersion = nn::validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return nn::error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return utils::unvalidatedConvert(canonical);
+}
+
 }  // anonymous namespace
 
-nn::GeneralResult<OperationType> convert(const nn::OperationType& operationType) {
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
     return Capabilities{
-            .float32Performance = NN_TRY(convert(
+            .float32Performance = NN_TRY(unvalidatedConvert(
                     capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_FLOAT32))),
-            .quantized8Performance = NN_TRY(convert(
+            .quantized8Performance = NN_TRY(unvalidatedConvert(
                     capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_QUANT8_ASYMM))),
-            .relaxedFloat32toFloat16Performance =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .relaxedFloat32toFloat16Performance = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
     };
 }
 
-nn::GeneralResult<Operation> convert(const nn::Operation& operation) {
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-nn::GeneralResult<Model> convert(const nn::Model& model) {
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
     if (!hal::utils::hasNoPointerData(model)) {
         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
-               << "Mdoel cannot be converted because it contains pointer-based memory";
+               << "Mdoel cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
-    auto operands = NN_TRY(convert(model.main.operands));
+    auto operands = NN_TRY(unvalidatedConvert(model.main.operands));
 
     // Update number of consumers.
     const auto numberOfConsumers =
@@ -195,17 +249,30 @@
 
     return Model{
             .operands = std::move(operands),
-            .operations = NN_TRY(convert(model.main.operations)),
+            .operations = NN_TRY(unvalidatedConvert(model.main.operations)),
             .inputIndexes = model.main.inputIndexes,
             .outputIndexes = model.main.outputIndexes,
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
     };
 }
 
-nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference) {
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const nn::ExecutionPreference& executionPreference) {
     return static_cast<ExecutionPreference>(executionPreference);
 }
 
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference) {
+    return validatedConvert(executionPreference);
+}
+
 }  // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
index a0378c9..f73d3f8 100644
--- a/neuralnetworks/1.1/utils/src/Device.cpp
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -49,16 +49,15 @@
                                                  << "uninitialized";
     const auto cb = [&result](V1_0::ErrorStatus status, const Capabilities& capabilities) {
         if (status != V1_0::ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical) << "getCapabilities_1_1 failed with " << toString(status);
         } else {
-            result = validatedConvertToCanonical(capabilities);
+            result = nn::convert(capabilities);
         }
     };
 
     const auto ret = device->getCapabilities_1_1(cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -121,7 +120,8 @@
 
 nn::GeneralResult<void> Device::wait() const {
     const auto ret = kDevice->ping();
-    return hal::utils::handleTransportError(ret);
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
 }
 
 nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
@@ -137,8 +137,7 @@
     auto cb = [&result, &model](V1_0::ErrorStatus status,
                                 const hidl_vec<bool>& supportedOperations) {
         if (status != V1_0::ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical)
                      << "getSupportedOperations_1_1 failed with " << toString(status);
         } else if (supportedOperations.size() != model.main.operations.size()) {
@@ -152,7 +151,7 @@
     };
 
     const auto ret = kDevice->getSupportedOperations_1_1(hidlModel, cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -173,10 +172,9 @@
     const auto scoped = kDeathHandler.protectCallback(cb.get());
 
     const auto ret = kDevice->prepareModel_1_1(hidlModel, hidlPreference, cb);
-    const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "prepareModel failed with " << toString(status);
     }
 
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
index 24911fe..5dcbc0b 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
@@ -24,27 +24,34 @@
 
 namespace android::nn {
 
-GeneralResult<OperandType> convert(const hal::V1_2::OperandType& operandType);
-GeneralResult<OperationType> convert(const hal::V1_2::OperationType& operationType);
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_2::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_2::OperationType& operationType);
+GeneralResult<DeviceType> unvalidatedConvert(const hal::V1_2::DeviceType& deviceType);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_2::Capabilities& capabilities);
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const hal::V1_2::Capabilities::OperandPerformance& operandPerformance);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_2::Operation& operation);
+GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert(
+        const hal::V1_2::SymmPerChannelQuantParams& symmPerChannelQuantParams);
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_2::Operand& operand);
+GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const hal::V1_2::Operand::ExtraParams& extraParams);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_2::Model& model);
+GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
+        const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix);
+GeneralResult<OutputShape> unvalidatedConvert(const hal::V1_2::OutputShape& outputShape);
+GeneralResult<MeasureTiming> unvalidatedConvert(const hal::V1_2::MeasureTiming& measureTiming);
+GeneralResult<Timing> unvalidatedConvert(const hal::V1_2::Timing& timing);
+GeneralResult<Extension> unvalidatedConvert(const hal::V1_2::Extension& extension);
+GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
+        const hal::V1_2::Extension::OperandTypeInformation& operandTypeInformation);
+GeneralResult<SharedHandle> unvalidatedConvert(const hardware::hidl_handle& handle);
+
 GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType);
 GeneralResult<Capabilities> convert(const hal::V1_2::Capabilities& capabilities);
-GeneralResult<Capabilities::OperandPerformance> convert(
-        const hal::V1_2::Capabilities::OperandPerformance& operandPerformance);
-GeneralResult<Operation> convert(const hal::V1_2::Operation& operation);
-GeneralResult<Operand::SymmPerChannelQuantParams> convert(
-        const hal::V1_2::SymmPerChannelQuantParams& symmPerChannelQuantParams);
-GeneralResult<Operand> convert(const hal::V1_2::Operand& operand);
-GeneralResult<Operand::ExtraParams> convert(const hal::V1_2::Operand::ExtraParams& extraParams);
 GeneralResult<Model> convert(const hal::V1_2::Model& model);
-GeneralResult<Model::ExtensionNameAndPrefix> convert(
-        const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix);
-GeneralResult<OutputShape> convert(const hal::V1_2::OutputShape& outputShape);
 GeneralResult<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming);
 GeneralResult<Timing> convert(const hal::V1_2::Timing& timing);
-GeneralResult<Extension> convert(const hal::V1_2::Extension& extension);
-GeneralResult<Extension::OperandTypeInformation> convert(
-        const hal::V1_2::Extension::OperandTypeInformation& operandTypeInformation);
-GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle);
 
 GeneralResult<std::vector<Extension>> convert(
         const hardware::hidl_vec<hal::V1_2::Extension>& extensions);
@@ -57,27 +64,34 @@
 
 namespace android::hardware::neuralnetworks::V1_2::utils {
 
-nn::GeneralResult<OperandType> convert(const nn::OperandType& operandType);
-nn::GeneralResult<OperationType> convert(const nn::OperationType& operationType);
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<DeviceType> unvalidatedConvert(const nn::DeviceType& deviceType);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const nn::Capabilities::OperandPerformance& operandPerformance);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<SymmPerChannelQuantParams> unvalidatedConvert(
+        const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
+        const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix);
+nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape);
+nn::GeneralResult<MeasureTiming> unvalidatedConvert(const nn::MeasureTiming& measureTiming);
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing);
+nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension);
+nn::GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
+        const nn::Extension::OperandTypeInformation& operandTypeInformation);
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle);
+
 nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType);
 nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
-nn::GeneralResult<Capabilities::OperandPerformance> convert(
-        const nn::Capabilities::OperandPerformance& operandPerformance);
-nn::GeneralResult<Operation> convert(const nn::Operation& operation);
-nn::GeneralResult<SymmPerChannelQuantParams> convert(
-        const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams);
-nn::GeneralResult<Operand> convert(const nn::Operand& operand);
-nn::GeneralResult<Operand::ExtraParams> convert(const nn::Operand::ExtraParams& extraParams);
 nn::GeneralResult<Model> convert(const nn::Model& model);
-nn::GeneralResult<Model::ExtensionNameAndPrefix> convert(
-        const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix);
-nn::GeneralResult<OutputShape> convert(const nn::OutputShape& outputShape);
 nn::GeneralResult<MeasureTiming> convert(const nn::MeasureTiming& measureTiming);
 nn::GeneralResult<Timing> convert(const nn::Timing& timing);
-nn::GeneralResult<Extension> convert(const nn::Extension& extension);
-nn::GeneralResult<Extension::OperandTypeInformation> convert(
-        const nn::Extension::OperandTypeInformation& operandTypeInformation);
-nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle);
 
 nn::GeneralResult<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions);
 nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles);
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
index bbd5343..a68830d 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
@@ -37,7 +37,6 @@
 nn::GeneralResult<std::string> initVersionString(V1_2::IDevice* device);
 nn::GeneralResult<nn::DeviceType> initDeviceType(V1_2::IDevice* device);
 nn::GeneralResult<std::vector<nn::Extension>> initExtensions(V1_2::IDevice* device);
-nn::GeneralResult<nn::Capabilities> initCapabilities(V1_2::IDevice* device);
 nn::GeneralResult<std::pair<uint32_t, uint32_t>> initNumberOfCacheFilesNeeded(
         V1_2::IDevice* device);
 
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
index a9a6bae..70149a2 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
@@ -22,9 +22,7 @@
 #include <android-base/logging.h>
 #include <android/hardware/neuralnetworks/1.2/types.h>
 #include <nnapi/Result.h>
-#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
-#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/1.1/Conversions.h>
 
@@ -35,7 +33,6 @@
 constexpr auto kDefaultMesaureTiming = MeasureTiming::NO;
 constexpr auto kNoTiming = Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
                                   .timeInDriver = std::numeric_limits<uint64_t>::max()};
-constexpr auto kVersion = nn::Version::ANDROID_Q;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
@@ -43,11 +40,6 @@
     if (!maybeCanonical.has_value()) {
         return nn::error() << maybeCanonical.error().message;
     }
-    const auto version = NN_TRY(nn::validate(maybeCanonical.value()));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required "
-                          << utils::kVersion;
-    }
     return {};
 }
 
@@ -60,21 +52,6 @@
     return result.has_value();
 }
 
-template <typename Type>
-decltype(nn::convert(std::declval<Type>())) validatedConvertToCanonical(const Type& halObject) {
-    auto canonical = NN_TRY(nn::convert(halObject));
-    const auto maybeVersion = nn::validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return nn::error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required "
-                          << utils::kVersion;
-    }
-    return canonical;
-}
-
 }  // namespace android::hardware::neuralnetworks::V1_2::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_H
diff --git a/neuralnetworks/1.2/utils/src/Callbacks.cpp b/neuralnetworks/1.2/utils/src/Callbacks.cpp
index cb739f0..39f88c2 100644
--- a/neuralnetworks/1.2/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.2/utils/src/Callbacks.cpp
@@ -52,8 +52,7 @@
 nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
 convertExecutionGeneralResultsHelper(const hidl_vec<OutputShape>& outputShapes,
                                      const Timing& timing) {
-    return std::make_pair(NN_TRY(validatedConvertToCanonical(outputShapes)),
-                          NN_TRY(validatedConvertToCanonical(timing)));
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
 }
 
 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
@@ -67,8 +66,7 @@
 Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
                                            const sp<V1_0::IPreparedModel>& preparedModel) {
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
     } else if (preparedModel == nullptr) {
         notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -82,8 +80,7 @@
 Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus status,
                                                const sp<IPreparedModel>& preparedModel) {
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
     } else if (preparedModel == nullptr) {
         notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -110,8 +107,7 @@
 
 Return<void> ExecutionCallback::notify(V1_0::ErrorStatus status) {
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
     } else {
         notifyInternal({});
@@ -123,8 +119,7 @@
                                            const hidl_vec<OutputShape>& outputShapes,
                                            const Timing& timing) {
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
     } else {
         notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index 08c94de..f11474f 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -24,6 +24,7 @@
 #include <nnapi/SharedMemory.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
+#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
 #include <nnapi/hal/HandleError.h>
@@ -42,6 +43,8 @@
     return static_cast<std::underlying_type_t<Type>>(value);
 }
 
+constexpr auto kVersion = android::nn::Version::ANDROID_Q;
+
 }  // namespace
 
 namespace android::nn {
@@ -76,42 +79,70 @@
 using hardware::hidl_vec;
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-GeneralResult<std::vector<ConvertOutput<Type>>> convertVec(const hidl_vec<Type>& arguments) {
-    std::vector<ConvertOutput<Type>> canonical;
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
-        canonical.push_back(NN_TRY(nn::convert(argument)));
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
     }
     return canonical;
 }
 
 template <typename Type>
-GeneralResult<std::vector<ConvertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
-    return convertVec(arguments);
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
+}
+
+template <typename Type>
+decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    const auto maybeVersion = validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(validatedConvert(argument)));
+    }
+    return canonical;
 }
 
 }  // anonymous namespace
 
-GeneralResult<OperandType> convert(const hal::V1_2::OperandType& operandType) {
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_2::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-GeneralResult<OperationType> convert(const hal::V1_2::OperationType& operationType) {
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_2::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
+GeneralResult<DeviceType> unvalidatedConvert(const hal::V1_2::DeviceType& deviceType) {
     return static_cast<DeviceType>(deviceType);
 }
 
-GeneralResult<Capabilities> convert(const hal::V1_2::Capabilities& capabilities) {
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_2::Capabilities& capabilities) {
     const bool validOperandTypes = std::all_of(
             capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
             [](const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
-                const auto maybeType = convert(operandPerformance.type);
+                const auto maybeType = unvalidatedConvert(operandPerformance.type);
                 return !maybeType.has_value() ? false : validOperandType(maybeType.value());
             });
     if (!validOperandTypes) {
@@ -120,10 +151,10 @@
     }
 
     const auto relaxedFloat32toFloat16PerformanceScalar =
-            NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceScalar));
+            NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar));
     const auto relaxedFloat32toFloat16PerformanceTensor =
-            NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor));
-    auto operandPerformance = NN_TRY(convert(capabilities.operandPerformance));
+            NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor));
+    auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance));
 
     auto table = NN_TRY(hal::utils::makeGeneralFailure(
             Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
@@ -136,23 +167,23 @@
     };
 }
 
-GeneralResult<Capabilities::OperandPerformance> convert(
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
     return Capabilities::OperandPerformance{
-            .type = NN_TRY(convert(operandPerformance.type)),
-            .info = NN_TRY(convert(operandPerformance.info)),
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
     };
 }
 
-GeneralResult<Operation> convert(const hal::V1_2::Operation& operation) {
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_2::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-GeneralResult<Operand::SymmPerChannelQuantParams> convert(
+GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert(
         const hal::V1_2::SymmPerChannelQuantParams& symmPerChannelQuantParams) {
     return Operand::SymmPerChannelQuantParams{
             .scales = symmPerChannelQuantParams.scales,
@@ -160,25 +191,26 @@
     };
 }
 
-GeneralResult<Operand> convert(const hal::V1_2::Operand& operand) {
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_2::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
-            .extraParams = NN_TRY(convert(operand.extraParams)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
     };
 }
 
-GeneralResult<Operand::ExtraParams> convert(const hal::V1_2::Operand::ExtraParams& extraParams) {
+GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const hal::V1_2::Operand::ExtraParams& extraParams) {
     using Discriminator = hal::V1_2::Operand::ExtraParams::hidl_discriminator;
     switch (extraParams.getDiscriminator()) {
         case Discriminator::none:
             return Operand::NoParams{};
         case Discriminator::channelQuant:
-            return convert(extraParams.channelQuant());
+            return unvalidatedConvert(extraParams.channelQuant());
         case Discriminator::extension:
             return extraParams.extension();
     }
@@ -187,8 +219,8 @@
            << underlyingType(extraParams.getDiscriminator());
 }
 
-GeneralResult<Model> convert(const hal::V1_2::Model& model) {
-    auto operations = NN_TRY(convert(model.operations));
+GeneralResult<Model> unvalidatedConvert(const hal::V1_2::Model& model) {
+    auto operations = NN_TRY(unvalidatedConvert(model.operations));
 
     // Verify number of consumers.
     const auto numberOfConsumers =
@@ -203,7 +235,7 @@
     }
 
     auto main = Model::Subgraph{
-            .operands = NN_TRY(convert(model.operands)),
+            .operands = NN_TRY(unvalidatedConvert(model.operands)),
             .operations = std::move(operations),
             .inputIndexes = model.inputIndexes,
             .outputIndexes = model.outputIndexes,
@@ -211,14 +243,14 @@
 
     return Model{
             .main = std::move(main),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
-            .extensionNameToPrefix = NN_TRY(convert(model.extensionNameToPrefix)),
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
     };
 }
 
-GeneralResult<Model::ExtensionNameAndPrefix> convert(
+GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
         const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
     return Model::ExtensionNameAndPrefix{
             .name = extensionNameAndPrefix.name,
@@ -226,29 +258,29 @@
     };
 }
 
-GeneralResult<OutputShape> convert(const hal::V1_2::OutputShape& outputShape) {
+GeneralResult<OutputShape> unvalidatedConvert(const hal::V1_2::OutputShape& outputShape) {
     return OutputShape{
             .dimensions = outputShape.dimensions,
             .isSufficient = outputShape.isSufficient,
     };
 }
 
-GeneralResult<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming) {
+GeneralResult<MeasureTiming> unvalidatedConvert(const hal::V1_2::MeasureTiming& measureTiming) {
     return static_cast<MeasureTiming>(measureTiming);
 }
 
-GeneralResult<Timing> convert(const hal::V1_2::Timing& timing) {
+GeneralResult<Timing> unvalidatedConvert(const hal::V1_2::Timing& timing) {
     return Timing{.timeOnDevice = timing.timeOnDevice, .timeInDriver = timing.timeInDriver};
 }
 
-GeneralResult<Extension> convert(const hal::V1_2::Extension& extension) {
+GeneralResult<Extension> unvalidatedConvert(const hal::V1_2::Extension& extension) {
     return Extension{
             .name = extension.name,
-            .operandTypes = NN_TRY(convert(extension.operandTypes)),
+            .operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)),
     };
 }
 
-GeneralResult<Extension::OperandTypeInformation> convert(
+GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
         const hal::V1_2::Extension::OperandTypeInformation& operandTypeInformation) {
     return Extension::OperandTypeInformation{
             .type = operandTypeInformation.type,
@@ -257,21 +289,41 @@
     };
 }
 
-GeneralResult<SharedHandle> convert(const hidl_handle& hidlHandle) {
+GeneralResult<SharedHandle> unvalidatedConvert(const hidl_handle& hidlHandle) {
     return hal::utils::sharedHandleFromNativeHandle(hidlHandle.getNativeHandle());
 }
 
+GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
+    return validatedConvert(deviceType);
+}
+
+GeneralResult<Capabilities> convert(const hal::V1_2::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_2::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming) {
+    return validatedConvert(measureTiming);
+}
+
+GeneralResult<Timing> convert(const hal::V1_2::Timing& timing) {
+    return validatedConvert(timing);
+}
+
 GeneralResult<std::vector<Extension>> convert(const hidl_vec<hal::V1_2::Extension>& extensions) {
-    return convertVec(extensions);
+    return validatedConvert(extensions);
 }
 
 GeneralResult<std::vector<SharedHandle>> convert(const hidl_vec<hidl_handle>& handles) {
-    return convertVec(handles);
+    return validatedConvert(handles);
 }
 
 GeneralResult<std::vector<OutputShape>> convert(
         const hidl_vec<hal::V1_2::OutputShape>& outputShapes) {
-    return convertVec(outputShapes);
+    return validatedConvert(outputShapes);
 }
 
 }  // namespace android::nn
@@ -279,44 +331,48 @@
 namespace android::hardware::neuralnetworks::V1_2::utils {
 namespace {
 
-using utils::convert;
+using utils::unvalidatedConvert;
 
-nn::GeneralResult<V1_0::OperandLifeTime> convert(const nn::Operand::LifeTime& lifetime) {
-    return V1_0::utils::convert(lifetime);
+nn::GeneralResult<V1_0::OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& lifetime) {
+    return V1_0::utils::unvalidatedConvert(lifetime);
 }
 
-nn::GeneralResult<V1_0::PerformanceInfo> convert(
+nn::GeneralResult<V1_0::PerformanceInfo> unvalidatedConvert(
         const nn::Capabilities::PerformanceInfo& performanceInfo) {
-    return V1_0::utils::convert(performanceInfo);
+    return V1_0::utils::unvalidatedConvert(performanceInfo);
 }
 
-nn::GeneralResult<V1_0::DataLocation> convert(const nn::DataLocation& location) {
-    return V1_0::utils::convert(location);
+nn::GeneralResult<V1_0::DataLocation> unvalidatedConvert(const nn::DataLocation& location) {
+    return V1_0::utils::unvalidatedConvert(location);
 }
 
-nn::GeneralResult<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
-    return V1_0::utils::convert(operandValues);
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return V1_0::utils::unvalidatedConvert(operandValues);
 }
 
-nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory) {
-    return V1_0::utils::convert(memory);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+    return V1_0::utils::unvalidatedConvert(memory);
 }
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<ConvertOutput<Type>>> convertVec(const std::vector<Type>& arguments) {
-    hidl_vec<ConvertOutput<Type>> halObject(arguments.size());
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
-        halObject[i] = NN_TRY(convert(arguments[i]));
+        halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
     }
     return halObject;
 }
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<ConvertOutput<Type>>> convert(const std::vector<Type>& arguments) {
-    return convertVec(arguments);
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
 }
 
 nn::GeneralResult<Operand::ExtraParams> makeExtraParams(nn::Operand::NoParams /*noParams*/) {
@@ -326,7 +382,7 @@
 nn::GeneralResult<Operand::ExtraParams> makeExtraParams(
         const nn::Operand::SymmPerChannelQuantParams& channelQuant) {
     Operand::ExtraParams ret;
-    ret.channelQuant(NN_TRY(convert(channelQuant)));
+    ret.channelQuant(NN_TRY(unvalidatedConvert(channelQuant)));
     return ret;
 }
 
@@ -337,17 +393,40 @@
     return ret;
 }
 
+template <typename Type>
+decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
+    const auto maybeVersion = nn::validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return nn::error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return utils::unvalidatedConvert(canonical);
+}
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> validatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(validatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
 }  // anonymous namespace
 
-nn::GeneralResult<OperandType> convert(const nn::OperandType& operandType) {
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-nn::GeneralResult<OperationType> convert(const nn::OperationType& operationType) {
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) {
+nn::GeneralResult<DeviceType> unvalidatedConvert(const nn::DeviceType& deviceType) {
     switch (deviceType) {
         case nn::DeviceType::UNKNOWN:
             return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Invalid DeviceType UNKNOWN";
@@ -361,7 +440,7 @@
            << "Invalid DeviceType " << underlyingType(deviceType);
 }
 
-nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
     std::vector<nn::Capabilities::OperandPerformance> operandPerformance;
     operandPerformance.reserve(capabilities.operandPerformance.asVector().size());
     std::copy_if(capabilities.operandPerformance.asVector().begin(),
@@ -372,31 +451,31 @@
                  });
 
     return Capabilities{
-            .relaxedFloat32toFloat16PerformanceScalar =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
-            .relaxedFloat32toFloat16PerformanceTensor =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
-            .operandPerformance = NN_TRY(convert(operandPerformance)),
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .operandPerformance = NN_TRY(unvalidatedConvert(operandPerformance)),
     };
 }
 
-nn::GeneralResult<Capabilities::OperandPerformance> convert(
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const nn::Capabilities::OperandPerformance& operandPerformance) {
     return Capabilities::OperandPerformance{
-            .type = NN_TRY(convert(operandPerformance.type)),
-            .info = NN_TRY(convert(operandPerformance.info)),
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
     };
 }
 
-nn::GeneralResult<Operation> convert(const nn::Operation& operation) {
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-nn::GeneralResult<SymmPerChannelQuantParams> convert(
+nn::GeneralResult<SymmPerChannelQuantParams> unvalidatedConvert(
         const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams) {
     return SymmPerChannelQuantParams{
             .scales = symmPerChannelQuantParams.scales,
@@ -404,30 +483,31 @@
     };
 }
 
-nn::GeneralResult<Operand> convert(const nn::Operand& operand) {
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .numberOfConsumers = 0,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
-            .extraParams = NN_TRY(convert(operand.extraParams)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
     };
 }
 
-nn::GeneralResult<Operand::ExtraParams> convert(const nn::Operand::ExtraParams& extraParams) {
+nn::GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams) {
     return std::visit([](const auto& x) { return makeExtraParams(x); }, extraParams);
 }
 
-nn::GeneralResult<Model> convert(const nn::Model& model) {
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
     if (!hal::utils::hasNoPointerData(model)) {
         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
-               << "Model cannot be converted because it contains pointer-based memory";
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
-    auto operands = NN_TRY(convert(model.main.operands));
+    auto operands = NN_TRY(unvalidatedConvert(model.main.operands));
 
     // Update number of consumers.
     const auto numberOfConsumers =
@@ -439,17 +519,17 @@
 
     return Model{
             .operands = std::move(operands),
-            .operations = NN_TRY(convert(model.main.operations)),
+            .operations = NN_TRY(unvalidatedConvert(model.main.operations)),
             .inputIndexes = model.main.inputIndexes,
             .outputIndexes = model.main.outputIndexes,
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
-            .extensionNameToPrefix = NN_TRY(convert(model.extensionNameToPrefix)),
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
     };
 }
 
-nn::GeneralResult<Model::ExtensionNameAndPrefix> convert(
+nn::GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
         const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
     return Model::ExtensionNameAndPrefix{
             .name = extensionNameAndPrefix.name,
@@ -457,27 +537,27 @@
     };
 }
 
-nn::GeneralResult<OutputShape> convert(const nn::OutputShape& outputShape) {
+nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape) {
     return OutputShape{.dimensions = outputShape.dimensions,
                        .isSufficient = outputShape.isSufficient};
 }
 
-nn::GeneralResult<MeasureTiming> convert(const nn::MeasureTiming& measureTiming) {
+nn::GeneralResult<MeasureTiming> unvalidatedConvert(const nn::MeasureTiming& measureTiming) {
     return static_cast<MeasureTiming>(measureTiming);
 }
 
-nn::GeneralResult<Timing> convert(const nn::Timing& timing) {
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) {
     return Timing{.timeOnDevice = timing.timeOnDevice, .timeInDriver = timing.timeInDriver};
 }
 
-nn::GeneralResult<Extension> convert(const nn::Extension& extension) {
+nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension) {
     return Extension{
             .name = extension.name,
-            .operandTypes = NN_TRY(convert(extension.operandTypes)),
+            .operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)),
     };
 }
 
-nn::GeneralResult<Extension::OperandTypeInformation> convert(
+nn::GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
         const nn::Extension::OperandTypeInformation& operandTypeInformation) {
     return Extension::OperandTypeInformation{
             .type = operandTypeInformation.type,
@@ -486,20 +566,40 @@
     };
 }
 
-nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle) {
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
     return hal::utils::hidlHandleFromSharedHandle(handle);
 }
 
+nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) {
+    return validatedConvert(deviceType);
+}
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<MeasureTiming> convert(const nn::MeasureTiming& measureTiming) {
+    return validatedConvert(measureTiming);
+}
+
+nn::GeneralResult<Timing> convert(const nn::Timing& timing) {
+    return validatedConvert(timing);
+}
+
 nn::GeneralResult<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions) {
-    return convertVec(extensions);
+    return validatedConvert(extensions);
 }
 
 nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles) {
-    return convertVec(handles);
+    return validatedConvert(handles);
 }
 
 nn::GeneralResult<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes) {
-    return convertVec(outputShapes);
+    return validatedConvert(outputShapes);
 }
 
 }  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
index 517d61f..0061065 100644
--- a/neuralnetworks/1.2/utils/src/Device.cpp
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -42,6 +42,29 @@
 #include <vector>
 
 namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+nn::GeneralResult<nn::Capabilities> initCapabilities(V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                 << "uninitialized";
+    const auto cb = [&result](V1_0::ErrorStatus status, const Capabilities& capabilities) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "getCapabilities_1_2 failed with " << toString(status);
+        } else {
+            result = nn::convert(capabilities);
+        }
+    };
+
+    const auto ret = device->getCapabilities_1_2(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+}  // namespace
 
 nn::GeneralResult<std::string> initVersionString(V1_2::IDevice* device) {
     CHECK(device != nullptr);
@@ -50,8 +73,7 @@
                                             << "uninitialized";
     const auto cb = [&result](V1_0::ErrorStatus status, const hidl_string& versionString) {
         if (status != V1_0::ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical) << "getVersionString failed with " << toString(status);
         } else {
             result = versionString;
@@ -59,7 +81,7 @@
     };
 
     const auto ret = device->getVersionString(cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -71,8 +93,7 @@
                                                << "uninitialized";
     const auto cb = [&result](V1_0::ErrorStatus status, DeviceType deviceType) {
         if (status != V1_0::ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical) << "getDeviceType failed with " << toString(status);
         } else {
             result = nn::convert(deviceType);
@@ -80,7 +101,7 @@
     };
 
     const auto ret = device->getType(cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -92,8 +113,7 @@
             NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
     const auto cb = [&result](V1_0::ErrorStatus status, const hidl_vec<Extension>& extensions) {
         if (status != V1_0::ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical) << "getExtensions failed with " << toString(status);
         } else {
             result = nn::convert(extensions);
@@ -101,28 +121,7 @@
     };
 
     const auto ret = device->getSupportedExtensions(cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
-
-    return result;
-}
-
-nn::GeneralResult<nn::Capabilities> initCapabilities(V1_2::IDevice* device) {
-    CHECK(device != nullptr);
-
-    nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-                                                 << "uninitialized";
-    const auto cb = [&result](V1_0::ErrorStatus status, const Capabilities& capabilities) {
-        if (status != V1_0::ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
-            result = NN_ERROR(canonical) << "getCapabilities_1_2 failed with " << toString(status);
-        } else {
-            result = validatedConvertToCanonical(capabilities);
-        }
-    };
-
-    const auto ret = device->getCapabilities_1_2(cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -136,8 +135,7 @@
     const auto cb = [&result](V1_0::ErrorStatus status, uint32_t numModelCache,
                               uint32_t numDataCache) {
         if (status != V1_0::ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical)
                      << "getNumberOfCacheFilesNeeded failed with " << toString(status);
         } else {
@@ -146,7 +144,7 @@
     };
 
     const auto ret = device->getNumberOfCacheFilesNeeded(cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -219,7 +217,8 @@
 
 nn::GeneralResult<void> Device::wait() const {
     const auto ret = kDevice->ping();
-    return hal::utils::handleTransportError(ret);
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
 }
 
 nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
@@ -235,8 +234,7 @@
     auto cb = [&result, &model](V1_0::ErrorStatus status,
                                 const hidl_vec<bool>& supportedOperations) {
         if (status != V1_0::ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical)
                      << "getSupportedOperations_1_2 failed with " << toString(status);
         } else if (supportedOperations.size() != model.main.operations.size()) {
@@ -250,7 +248,7 @@
     };
 
     const auto ret = kDevice->getSupportedOperations_1_2(hidlModel, cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -275,10 +273,9 @@
 
     const auto ret = kDevice->prepareModel_1_2(hidlModel, hidlPreference, hidlModelCache,
                                                hidlDataCache, hidlToken, cb);
-    const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "prepareModel_1_2 failed with " << toString(status);
     }
 
@@ -296,10 +293,9 @@
     const auto scoped = kDeathHandler.protectCallback(cb.get());
 
     const auto ret = kDevice->prepareModelFromCache(hidlModelCache, hidlDataCache, hidlToken, cb);
-    const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "prepareModelFromCache failed with " << toString(status);
     }
 
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index ff9db21..dad9a7e 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -42,8 +42,7 @@
 
 nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
 convertExecutionResultsHelper(const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
-    return std::make_pair(NN_TRY(validatedConvertToCanonical(outputShapes)),
-                          NN_TRY(validatedConvertToCanonical(timing)));
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
 }
 
 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
@@ -76,8 +75,7 @@
     const auto cb = [&result](V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
                               const Timing& timing) {
         if (status != V1_0::ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical) << "executeSynchronously failed with " << toString(status);
         } else {
             result = convertExecutionResults(outputShapes, timing);
@@ -85,7 +83,7 @@
     };
 
     const auto ret = kPreparedModel->executeSynchronously(request, measure, cb);
-    NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -96,11 +94,9 @@
     const auto scoped = kDeathHandler.protectCallback(cb.get());
 
     const auto ret = kPreparedModel->execute_1_2(request, measure, cb);
-    const auto status =
-            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "execute failed with " << toString(status);
     }
 
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
index 64aa96e..9653a05 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
@@ -25,26 +25,41 @@
 
 namespace android::nn {
 
-GeneralResult<OperandType> convert(const hal::V1_3::OperandType& operandType);
-GeneralResult<OperationType> convert(const hal::V1_3::OperationType& operationType);
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_3::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_3::OperationType& operationType);
+GeneralResult<Priority> unvalidatedConvert(const hal::V1_3::Priority& priority);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_3::Capabilities& capabilities);
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const hal::V1_3::Capabilities::OperandPerformance& operandPerformance);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_3::Operation& operation);
+GeneralResult<Operand::LifeTime> unvalidatedConvert(
+        const hal::V1_3::OperandLifeTime& operandLifeTime);
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_3::Operand& operand);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_3::Model& model);
+GeneralResult<Model::Subgraph> unvalidatedConvert(const hal::V1_3::Subgraph& subgraph);
+GeneralResult<BufferDesc> unvalidatedConvert(const hal::V1_3::BufferDesc& bufferDesc);
+GeneralResult<BufferRole> unvalidatedConvert(const hal::V1_3::BufferRole& bufferRole);
+GeneralResult<Request> unvalidatedConvert(const hal::V1_3::Request& request);
+GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const hal::V1_3::Request::MemoryPool& memoryPool);
+GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const hal::V1_3::OptionalTimePoint& optionalTimePoint);
+GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
+        const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration);
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_3::ErrorStatus& errorStatus);
+
 GeneralResult<Priority> convert(const hal::V1_3::Priority& priority);
 GeneralResult<Capabilities> convert(const hal::V1_3::Capabilities& capabilities);
-GeneralResult<Capabilities::OperandPerformance> convert(
-        const hal::V1_3::Capabilities::OperandPerformance& operandPerformance);
-GeneralResult<Operation> convert(const hal::V1_3::Operation& operation);
-GeneralResult<Operand::LifeTime> convert(const hal::V1_3::OperandLifeTime& operandLifeTime);
-GeneralResult<Operand> convert(const hal::V1_3::Operand& operand);
 GeneralResult<Model> convert(const hal::V1_3::Model& model);
-GeneralResult<Model::Subgraph> convert(const hal::V1_3::Subgraph& subgraph);
 GeneralResult<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc);
-GeneralResult<BufferRole> convert(const hal::V1_3::BufferRole& bufferRole);
 GeneralResult<Request> convert(const hal::V1_3::Request& request);
-GeneralResult<Request::MemoryPool> convert(const hal::V1_3::Request::MemoryPool& memoryPool);
 GeneralResult<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint);
 GeneralResult<OptionalTimeoutDuration> convert(
         const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration);
 GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus);
 
+GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle);
+GeneralResult<Memory> convert(const hardware::hidl_memory& memory);
 GeneralResult<std::vector<BufferRole>> convert(
         const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles);
 
@@ -52,26 +67,40 @@
 
 namespace android::hardware::neuralnetworks::V1_3::utils {
 
-nn::GeneralResult<OperandType> convert(const nn::OperandType& operandType);
-nn::GeneralResult<OperationType> convert(const nn::OperationType& operationType);
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const nn::Capabilities::OperandPerformance& operandPerformance);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& operandLifeTime);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph);
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc);
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole);
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request);
+nn::GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const nn::Request::MemoryPool& memoryPool);
+nn::GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const nn::OptionalTimePoint& optionalTimePoint);
+nn::GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
+        const nn::OptionalTimeoutDuration& optionalTimeoutDuration);
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus);
+
 nn::GeneralResult<Priority> convert(const nn::Priority& priority);
 nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
-nn::GeneralResult<Capabilities::OperandPerformance> convert(
-        const nn::Capabilities::OperandPerformance& operandPerformance);
-nn::GeneralResult<Operation> convert(const nn::Operation& operation);
-nn::GeneralResult<OperandLifeTime> convert(const nn::Operand::LifeTime& operandLifeTime);
-nn::GeneralResult<Operand> convert(const nn::Operand& operand);
 nn::GeneralResult<Model> convert(const nn::Model& model);
-nn::GeneralResult<Subgraph> convert(const nn::Model::Subgraph& subgraph);
 nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc);
-nn::GeneralResult<BufferRole> convert(const nn::BufferRole& bufferRole);
 nn::GeneralResult<Request> convert(const nn::Request& request);
-nn::GeneralResult<Request::MemoryPool> convert(const nn::Request::MemoryPool& memoryPool);
 nn::GeneralResult<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint);
 nn::GeneralResult<OptionalTimeoutDuration> convert(
         const nn::OptionalTimeoutDuration& optionalTimeoutDuration);
 nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
 
+nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle);
+nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory);
 nn::GeneralResult<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles);
 
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
index e61859d..29b0c80 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
@@ -22,9 +22,7 @@
 #include <android-base/logging.h>
 #include <android/hardware/neuralnetworks/1.3/types.h>
 #include <nnapi/Result.h>
-#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
-#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/1.1/Conversions.h>
 #include <nnapi/hal/1.2/Conversions.h>
@@ -32,7 +30,6 @@
 namespace android::hardware::neuralnetworks::V1_3::utils {
 
 constexpr auto kDefaultPriority = Priority::MEDIUM;
-constexpr auto kVersion = nn::Version::ANDROID_R;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
@@ -40,11 +37,6 @@
     if (!maybeCanonical.has_value()) {
         return nn::error() << maybeCanonical.error().message;
     }
-    const auto version = NN_TRY(nn::validate(maybeCanonical.value()));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required "
-                          << utils::kVersion;
-    }
     return {};
 }
 
@@ -57,21 +49,6 @@
     return result.has_value();
 }
 
-template <typename Type>
-decltype(nn::convert(std::declval<Type>())) validatedConvertToCanonical(const Type& halObject) {
-    auto canonical = NN_TRY(nn::convert(halObject));
-    const auto maybeVersion = nn::validate(canonical);
-    if (!maybeVersion.has_value()) {
-        return nn::error() << maybeVersion.error();
-    }
-    const auto version = maybeVersion.value();
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "Insufficient version: " << version << " vs required "
-                          << utils::kVersion;
-    }
-    return canonical;
-}
-
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_H
diff --git a/neuralnetworks/1.3/utils/src/Buffer.cpp b/neuralnetworks/1.3/utils/src/Buffer.cpp
index f3fe9b5..ffdeccd 100644
--- a/neuralnetworks/1.3/utils/src/Buffer.cpp
+++ b/neuralnetworks/1.3/utils/src/Buffer.cpp
@@ -61,13 +61,12 @@
 }
 
 nn::GeneralResult<void> Buffer::copyTo(const nn::Memory& dst) const {
-    const auto hidlDst = NN_TRY(V1_0::utils::convert(dst));
+    const auto hidlDst = NN_TRY(convert(dst));
 
     const auto ret = kBuffer->copyTo(hidlDst);
-    const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "IBuffer::copyTo failed with " << toString(status);
     }
 
@@ -76,14 +75,13 @@
 
 nn::GeneralResult<void> Buffer::copyFrom(const nn::Memory& src,
                                          const nn::Dimensions& dimensions) const {
-    const auto hidlSrc = NN_TRY(V1_0::utils::convert(src));
+    const auto hidlSrc = NN_TRY(convert(src));
     const auto hidlDimensions = hidl_vec<uint32_t>(dimensions);
 
     const auto ret = kBuffer->copyFrom(hidlSrc, hidlDimensions);
-    const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "IBuffer::copyFrom failed with " << toString(status);
     }
 
diff --git a/neuralnetworks/1.3/utils/src/Callbacks.cpp b/neuralnetworks/1.3/utils/src/Callbacks.cpp
index ff81275..e3c6074 100644
--- a/neuralnetworks/1.3/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.3/utils/src/Callbacks.cpp
@@ -60,8 +60,7 @@
 nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
 convertExecutionGeneralResultsHelper(const hidl_vec<V1_2::OutputShape>& outputShapes,
                                      const V1_2::Timing& timing) {
-    return std::make_pair(NN_TRY(validatedConvertToCanonical(outputShapes)),
-                          NN_TRY(validatedConvertToCanonical(timing)));
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
 }
 
 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
@@ -76,8 +75,7 @@
 Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
                                            const sp<V1_0::IPreparedModel>& preparedModel) {
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
     } else if (preparedModel == nullptr) {
         notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -91,8 +89,7 @@
 Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus status,
                                                const sp<V1_2::IPreparedModel>& preparedModel) {
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
     } else if (preparedModel == nullptr) {
         notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -106,8 +103,7 @@
 Return<void> PreparedModelCallback::notify_1_3(ErrorStatus status,
                                                const sp<IPreparedModel>& preparedModel) {
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
     } else if (preparedModel == nullptr) {
         notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -134,8 +130,7 @@
 
 Return<void> ExecutionCallback::notify(V1_0::ErrorStatus status) {
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
     } else {
         notifyInternal({});
@@ -147,8 +142,7 @@
                                            const hidl_vec<V1_2::OutputShape>& outputShapes,
                                            const V1_2::Timing& timing) {
     if (status != V1_0::ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
     } else {
         notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
@@ -160,8 +154,7 @@
                                            const hidl_vec<V1_2::OutputShape>& outputShapes,
                                            const V1_2::Timing& timing) {
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
     } else {
         notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 0dc0785..949dd0d 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -24,6 +24,7 @@
 #include <nnapi/SharedMemory.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
+#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/1.2/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
@@ -44,6 +45,8 @@
     return static_cast<std::underlying_type_t<Type>>(value);
 }
 
+constexpr auto kVersion = android::nn::Version::ANDROID_R;
+
 }  // namespace
 
 namespace android::nn {
@@ -77,110 +80,140 @@
 using hardware::hidl_vec;
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-GeneralResult<std::vector<ConvertOutput<Type>>> convertVec(const hidl_vec<Type>& arguments) {
-    std::vector<ConvertOutput<Type>> canonical;
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
-        canonical.push_back(NN_TRY(nn::convert(argument)));
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
     }
     return canonical;
 }
 
 template <typename Type>
-GeneralResult<std::vector<ConvertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
-    return convertVec(arguments);
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
+}
+
+template <typename Type>
+decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    const auto maybeVersion = validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(validatedConvert(argument)));
+    }
+    return canonical;
 }
 
 }  // anonymous namespace
 
-GeneralResult<OperandType> convert(const hal::V1_3::OperandType& operandType) {
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_3::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-GeneralResult<OperationType> convert(const hal::V1_3::OperationType& operationType) {
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_3::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-GeneralResult<Priority> convert(const hal::V1_3::Priority& priority) {
+GeneralResult<Priority> unvalidatedConvert(const hal::V1_3::Priority& priority) {
     return static_cast<Priority>(priority);
 }
 
-GeneralResult<Capabilities> convert(const hal::V1_3::Capabilities& capabilities) {
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_3::Capabilities& capabilities) {
     const bool validOperandTypes = std::all_of(
             capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
             [](const hal::V1_3::Capabilities::OperandPerformance& operandPerformance) {
-                const auto maybeType = convert(operandPerformance.type);
+                const auto maybeType = unvalidatedConvert(operandPerformance.type);
                 return !maybeType.has_value() ? false : validOperandType(maybeType.value());
             });
     if (!validOperandTypes) {
         return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-               << "Invalid OperandType when converting OperandPerformance in Capabilities";
+               << "Invalid OperandType when unvalidatedConverting OperandPerformance in "
+                  "Capabilities";
     }
 
-    auto operandPerformance = NN_TRY(convert(capabilities.operandPerformance));
+    auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance));
     auto table = NN_TRY(hal::utils::makeGeneralFailure(
             Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
             nn::ErrorStatus::GENERAL_FAILURE));
 
     return Capabilities{
-            .relaxedFloat32toFloat16PerformanceScalar =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
-            .relaxedFloat32toFloat16PerformanceTensor =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
             .operandPerformance = std::move(table),
-            .ifPerformance = NN_TRY(convert(capabilities.ifPerformance)),
-            .whilePerformance = NN_TRY(convert(capabilities.whilePerformance)),
+            .ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)),
+            .whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)),
     };
 }
 
-GeneralResult<Capabilities::OperandPerformance> convert(
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const hal::V1_3::Capabilities::OperandPerformance& operandPerformance) {
     return Capabilities::OperandPerformance{
-            .type = NN_TRY(convert(operandPerformance.type)),
-            .info = NN_TRY(convert(operandPerformance.info)),
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
     };
 }
 
-GeneralResult<Operation> convert(const hal::V1_3::Operation& operation) {
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_3::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-GeneralResult<Operand::LifeTime> convert(const hal::V1_3::OperandLifeTime& operandLifeTime) {
+GeneralResult<Operand::LifeTime> unvalidatedConvert(
+        const hal::V1_3::OperandLifeTime& operandLifeTime) {
     return static_cast<Operand::LifeTime>(operandLifeTime);
 }
 
-GeneralResult<Operand> convert(const hal::V1_3::Operand& operand) {
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_3::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
-            .extraParams = NN_TRY(convert(operand.extraParams)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
     };
 }
 
-GeneralResult<Model> convert(const hal::V1_3::Model& model) {
+GeneralResult<Model> unvalidatedConvert(const hal::V1_3::Model& model) {
     return Model{
-            .main = NN_TRY(convert(model.main)),
-            .referenced = NN_TRY(convert(model.referenced)),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .main = NN_TRY(unvalidatedConvert(model.main)),
+            .referenced = NN_TRY(unvalidatedConvert(model.referenced)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
-            .extensionNameToPrefix = NN_TRY(convert(model.extensionNameToPrefix)),
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
     };
 }
 
-GeneralResult<Model::Subgraph> convert(const hal::V1_3::Subgraph& subgraph) {
-    auto operations = NN_TRY(convert(subgraph.operations));
+GeneralResult<Model::Subgraph> unvalidatedConvert(const hal::V1_3::Subgraph& subgraph) {
+    auto operations = NN_TRY(unvalidatedConvert(subgraph.operations));
 
     // Verify number of consumers.
     const auto numberOfConsumers =
@@ -196,18 +229,18 @@
     }
 
     return Model::Subgraph{
-            .operands = NN_TRY(convert(subgraph.operands)),
+            .operands = NN_TRY(unvalidatedConvert(subgraph.operands)),
             .operations = std::move(operations),
             .inputIndexes = subgraph.inputIndexes,
             .outputIndexes = subgraph.outputIndexes,
     };
 }
 
-GeneralResult<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc) {
+GeneralResult<BufferDesc> unvalidatedConvert(const hal::V1_3::BufferDesc& bufferDesc) {
     return BufferDesc{.dimensions = bufferDesc.dimensions};
 }
 
-GeneralResult<BufferRole> convert(const hal::V1_3::BufferRole& bufferRole) {
+GeneralResult<BufferRole> unvalidatedConvert(const hal::V1_3::BufferRole& bufferRole) {
     return BufferRole{
             .modelIndex = bufferRole.modelIndex,
             .ioIndex = bufferRole.ioIndex,
@@ -215,15 +248,16 @@
     };
 }
 
-GeneralResult<Request> convert(const hal::V1_3::Request& request) {
+GeneralResult<Request> unvalidatedConvert(const hal::V1_3::Request& request) {
     return Request{
-            .inputs = NN_TRY(convert(request.inputs)),
-            .outputs = NN_TRY(convert(request.outputs)),
-            .pools = NN_TRY(convert(request.pools)),
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
     };
 }
 
-GeneralResult<Request::MemoryPool> convert(const hal::V1_3::Request::MemoryPool& memoryPool) {
+GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const hal::V1_3::Request::MemoryPool& memoryPool) {
     using Discriminator = hal::V1_3::Request::MemoryPool::hidl_discriminator;
     switch (memoryPool.getDiscriminator()) {
         case Discriminator::hidlMemory:
@@ -236,12 +270,14 @@
            << underlyingType(memoryPool.getDiscriminator());
 }
 
-GeneralResult<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint) {
+GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const hal::V1_3::OptionalTimePoint& optionalTimePoint) {
     constexpr auto kTimePointMaxCount = TimePoint::max().time_since_epoch().count();
     const auto makeTimePoint = [](uint64_t count) -> GeneralResult<OptionalTimePoint> {
         if (count > kTimePointMaxCount) {
             return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-                   << "Unable to convert OptionalTimePoint because the count exceeds the max";
+                   << "Unable to unvalidatedConvert OptionalTimePoint because the count exceeds "
+                      "the max";
         }
         const auto nanoseconds = std::chrono::nanoseconds{count};
         return TimePoint{nanoseconds};
@@ -259,13 +295,14 @@
            << underlyingType(optionalTimePoint.getDiscriminator());
 }
 
-GeneralResult<OptionalTimeoutDuration> convert(
+GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
         const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration) {
     constexpr auto kTimeoutDurationMaxCount = TimeoutDuration::max().count();
     const auto makeTimeoutDuration = [](uint64_t count) -> GeneralResult<OptionalTimeoutDuration> {
         if (count > kTimeoutDurationMaxCount) {
             return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-                   << "Unable to convert OptionalTimeoutDuration because the count exceeds the max";
+                   << "Unable to unvalidatedConvert OptionalTimeoutDuration because the count "
+                      "exceeds the max";
         }
         return TimeoutDuration{count};
     };
@@ -282,7 +319,7 @@
            << underlyingType(optionalTimeoutDuration.getDiscriminator());
 }
 
-GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& status) {
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_3::ErrorStatus& status) {
     switch (status) {
         case hal::V1_3::ErrorStatus::NONE:
         case hal::V1_3::ErrorStatus::DEVICE_UNAVAILABLE:
@@ -299,9 +336,50 @@
            << "Invalid ErrorStatus " << underlyingType(status);
 }
 
+GeneralResult<Priority> convert(const hal::V1_3::Priority& priority) {
+    return validatedConvert(priority);
+}
+
+GeneralResult<Capabilities> convert(const hal::V1_3::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_3::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc) {
+    return validatedConvert(bufferDesc);
+}
+
+GeneralResult<Request> convert(const hal::V1_3::Request& request) {
+    return validatedConvert(request);
+}
+
+GeneralResult<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint) {
+    return validatedConvert(optionalTimePoint);
+}
+
+GeneralResult<OptionalTimeoutDuration> convert(
+        const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration) {
+    return validatedConvert(optionalTimeoutDuration);
+}
+
+GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus) {
+    return validatedConvert(errorStatus);
+}
+
+GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle) {
+    return validatedConvert(handle);
+}
+
+GeneralResult<Memory> convert(const hardware::hidl_memory& memory) {
+    return validatedConvert(memory);
+}
+
 GeneralResult<std::vector<BufferRole>> convert(
         const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles) {
-    return convertVec(bufferRoles);
+    return validatedConvert(bufferRoles);
 }
 
 }  // namespace android::nn
@@ -309,58 +387,67 @@
 namespace android::hardware::neuralnetworks::V1_3::utils {
 namespace {
 
-using utils::convert;
+using utils::unvalidatedConvert;
 
-nn::GeneralResult<V1_0::PerformanceInfo> convert(
+nn::GeneralResult<V1_0::PerformanceInfo> unvalidatedConvert(
         const nn::Capabilities::PerformanceInfo& performanceInfo) {
-    return V1_0::utils::convert(performanceInfo);
+    return V1_0::utils::unvalidatedConvert(performanceInfo);
 }
 
-nn::GeneralResult<V1_0::DataLocation> convert(const nn::DataLocation& dataLocation) {
-    return V1_0::utils::convert(dataLocation);
+nn::GeneralResult<V1_0::DataLocation> unvalidatedConvert(const nn::DataLocation& dataLocation) {
+    return V1_0::utils::unvalidatedConvert(dataLocation);
 }
 
-nn::GeneralResult<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
-    return V1_0::utils::convert(operandValues);
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return V1_0::utils::unvalidatedConvert(operandValues);
 }
 
-nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory) {
-    return V1_0::utils::convert(memory);
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
+    return V1_2::utils::unvalidatedConvert(handle);
 }
 
-nn::GeneralResult<V1_0::RequestArgument> convert(const nn::Request::Argument& argument) {
-    return V1_0::utils::convert(argument);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+    return V1_0::utils::unvalidatedConvert(memory);
 }
 
-nn::GeneralResult<V1_2::Operand::ExtraParams> convert(const nn::Operand::ExtraParams& extraParams) {
-    return V1_2::utils::convert(extraParams);
+nn::GeneralResult<V1_0::RequestArgument> unvalidatedConvert(const nn::Request::Argument& argument) {
+    return V1_0::utils::unvalidatedConvert(argument);
 }
 
-nn::GeneralResult<V1_2::Model::ExtensionNameAndPrefix> convert(
+nn::GeneralResult<V1_2::Operand::ExtraParams> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams) {
+    return V1_2::utils::unvalidatedConvert(extraParams);
+}
+
+nn::GeneralResult<V1_2::Model::ExtensionNameAndPrefix> unvalidatedConvert(
         const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
-    return V1_2::utils::convert(extensionNameAndPrefix);
+    return V1_2::utils::unvalidatedConvert(extensionNameAndPrefix);
 }
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<ConvertOutput<Type>>> convertVec(const std::vector<Type>& arguments) {
-    hidl_vec<ConvertOutput<Type>> halObject(arguments.size());
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
-        halObject[i] = NN_TRY(convert(arguments[i]));
+        halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
     }
     return halObject;
 }
 
 template <typename Type>
-nn::GeneralResult<hidl_vec<ConvertOutput<Type>>> convert(const std::vector<Type>& arguments) {
-    return convertVec(arguments);
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
 }
 
 nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::Memory& memory) {
     Request::MemoryPool ret;
-    ret.hidlMemory(NN_TRY(convert(memory)));
+    ret.hidlMemory(NN_TRY(unvalidatedConvert(memory)));
     return ret;
 }
 
@@ -374,21 +461,46 @@
     return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Unable to make memory pool from IBuffer";
 }
 
+using utils::unvalidatedConvert;
+
+template <typename Type>
+decltype(unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
+    const auto maybeVersion = nn::validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return nn::error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return unvalidatedConvert(canonical);
+}
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> validatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(validatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
 }  // anonymous namespace
 
-nn::GeneralResult<OperandType> convert(const nn::OperandType& operandType) {
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-nn::GeneralResult<OperationType> convert(const nn::OperationType& operationType) {
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-nn::GeneralResult<Priority> convert(const nn::Priority& priority) {
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority) {
     return static_cast<Priority>(priority);
 }
 
-nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
     std::vector<nn::Capabilities::OperandPerformance> operandPerformance;
     operandPerformance.reserve(capabilities.operandPerformance.asVector().size());
     std::copy_if(capabilities.operandPerformance.asVector().begin(),
@@ -399,71 +511,72 @@
                  });
 
     return Capabilities{
-            .relaxedFloat32toFloat16PerformanceScalar =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
-            .relaxedFloat32toFloat16PerformanceTensor =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
-            .operandPerformance = NN_TRY(convert(operandPerformance)),
-            .ifPerformance = NN_TRY(convert(capabilities.ifPerformance)),
-            .whilePerformance = NN_TRY(convert(capabilities.whilePerformance)),
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .operandPerformance = NN_TRY(unvalidatedConvert(operandPerformance)),
+            .ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)),
+            .whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)),
     };
 }
 
-nn::GeneralResult<Capabilities::OperandPerformance> convert(
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const nn::Capabilities::OperandPerformance& operandPerformance) {
     return Capabilities::OperandPerformance{
-            .type = NN_TRY(convert(operandPerformance.type)),
-            .info = NN_TRY(convert(operandPerformance.info)),
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
     };
 }
 
-nn::GeneralResult<Operation> convert(const nn::Operation& operation) {
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-nn::GeneralResult<OperandLifeTime> convert(const nn::Operand::LifeTime& operandLifeTime) {
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(
+        const nn::Operand::LifeTime& operandLifeTime) {
     if (operandLifeTime == nn::Operand::LifeTime::POINTER) {
         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
-               << "Model cannot be converted because it contains pointer-based memory";
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
     }
     return static_cast<OperandLifeTime>(operandLifeTime);
 }
 
-nn::GeneralResult<Operand> convert(const nn::Operand& operand) {
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .numberOfConsumers = 0,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
-            .extraParams = NN_TRY(convert(operand.extraParams)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
     };
 }
 
-nn::GeneralResult<Model> convert(const nn::Model& model) {
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
     if (!hal::utils::hasNoPointerData(model)) {
         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
-               << "Model cannot be converted because it contains pointer-based memory";
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
     return Model{
-            .main = NN_TRY(convert(model.main)),
-            .referenced = NN_TRY(convert(model.referenced)),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .main = NN_TRY(unvalidatedConvert(model.main)),
+            .referenced = NN_TRY(unvalidatedConvert(model.referenced)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
-            .extensionNameToPrefix = NN_TRY(convert(model.extensionNameToPrefix)),
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
     };
 }
 
-nn::GeneralResult<Subgraph> convert(const nn::Model::Subgraph& subgraph) {
-    auto operands = NN_TRY(convert(subgraph.operands));
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph) {
+    auto operands = NN_TRY(unvalidatedConvert(subgraph.operands));
 
     // Update number of consumers.
     const auto numberOfConsumers =
@@ -475,17 +588,17 @@
 
     return Subgraph{
             .operands = std::move(operands),
-            .operations = NN_TRY(convert(subgraph.operations)),
+            .operations = NN_TRY(unvalidatedConvert(subgraph.operations)),
             .inputIndexes = subgraph.inputIndexes,
             .outputIndexes = subgraph.outputIndexes,
     };
 }
 
-nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc) {
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc) {
     return BufferDesc{.dimensions = bufferDesc.dimensions};
 }
 
-nn::GeneralResult<BufferRole> convert(const nn::BufferRole& bufferRole) {
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole) {
     return BufferRole{
             .modelIndex = bufferRole.modelIndex,
             .ioIndex = bufferRole.ioIndex,
@@ -493,30 +606,33 @@
     };
 }
 
-nn::GeneralResult<Request> convert(const nn::Request& request) {
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
     if (!hal::utils::hasNoPointerData(request)) {
         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
-               << "Request cannot be converted because it contains pointer-based memory";
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
     return Request{
-            .inputs = NN_TRY(convert(request.inputs)),
-            .outputs = NN_TRY(convert(request.outputs)),
-            .pools = NN_TRY(convert(request.pools)),
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
     };
 }
 
-nn::GeneralResult<Request::MemoryPool> convert(const nn::Request::MemoryPool& memoryPool) {
+nn::GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const nn::Request::MemoryPool& memoryPool) {
     return std::visit([](const auto& o) { return makeMemoryPool(o); }, memoryPool);
 }
 
-nn::GeneralResult<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint) {
+nn::GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const nn::OptionalTimePoint& optionalTimePoint) {
     OptionalTimePoint ret;
     if (optionalTimePoint.has_value()) {
         const auto count = optionalTimePoint.value().time_since_epoch().count();
         if (count < 0) {
             return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-                   << "Unable to convert OptionalTimePoint because time since epoch count is "
+                   << "Unable to unvalidatedConvert OptionalTimePoint because time since epoch "
+                      "count is "
                       "negative";
         }
         ret.nanosecondsSinceEpoch(count);
@@ -524,21 +640,22 @@
     return ret;
 }
 
-nn::GeneralResult<OptionalTimeoutDuration> convert(
+nn::GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
         const nn::OptionalTimeoutDuration& optionalTimeoutDuration) {
     OptionalTimeoutDuration ret;
     if (optionalTimeoutDuration.has_value()) {
         const auto count = optionalTimeoutDuration.value().count();
         if (count < 0) {
             return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-                   << "Unable to convert OptionalTimeoutDuration because count is negative";
+                   << "Unable to unvalidatedConvert OptionalTimeoutDuration because count is "
+                      "negative";
         }
         ret.nanoseconds(count);
     }
     return ret;
 }
 
-nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus) {
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus) {
     switch (errorStatus) {
         case nn::ErrorStatus::NONE:
         case nn::ErrorStatus::DEVICE_UNAVAILABLE:
@@ -555,8 +672,49 @@
     }
 }
 
+nn::GeneralResult<Priority> convert(const nn::Priority& priority) {
+    return validatedConvert(priority);
+}
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc) {
+    return validatedConvert(bufferDesc);
+}
+
+nn::GeneralResult<Request> convert(const nn::Request& request) {
+    return validatedConvert(request);
+}
+
+nn::GeneralResult<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint) {
+    return validatedConvert(optionalTimePoint);
+}
+
+nn::GeneralResult<OptionalTimeoutDuration> convert(
+        const nn::OptionalTimeoutDuration& optionalTimeoutDuration) {
+    return validatedConvert(optionalTimeoutDuration);
+}
+
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus) {
+    return validatedConvert(errorStatus);
+}
+
+nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle) {
+    return validatedConvert(handle);
+}
+
+nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory) {
+    return validatedConvert(memory);
+}
+
 nn::GeneralResult<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles) {
-    return convertVec(bufferRoles);
+    return validatedConvert(bufferRoles);
 }
 
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
index 5e3d5c2..82837ba 100644
--- a/neuralnetworks/1.3/utils/src/Device.cpp
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -71,6 +71,26 @@
     return NN_TRY(std::move(result));
 }
 
+nn::GeneralResult<nn::Capabilities> initCapabilities(V1_3::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                 << "uninitialized";
+    const auto cb = [&result](ErrorStatus status, const Capabilities& capabilities) {
+        if (status != ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "getCapabilities_1_3 failed with " << toString(status);
+        } else {
+            result = nn::convert(capabilities);
+        }
+    };
+
+    const auto ret = device->getCapabilities_1_3(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
 }  // namespace
 
 nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
@@ -87,7 +107,7 @@
     auto versionString = NN_TRY(V1_2::utils::initVersionString(device.get()));
     const auto deviceType = NN_TRY(V1_2::utils::initDeviceType(device.get()));
     auto extensions = NN_TRY(V1_2::utils::initExtensions(device.get()));
-    auto capabilities = NN_TRY(V1_2::utils::initCapabilities(device.get()));
+    auto capabilities = NN_TRY(initCapabilities(device.get()));
     const auto numberOfCacheFilesNeeded =
             NN_TRY(V1_2::utils::initNumberOfCacheFilesNeeded(device.get()));
 
@@ -142,7 +162,8 @@
 
 nn::GeneralResult<void> Device::wait() const {
     const auto ret = kDevice->ping();
-    return hal::utils::handleTransportError(ret);
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
 }
 
 nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
@@ -157,8 +178,7 @@
                                                   << "uninitialized";
     auto cb = [&result, &model](ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
         if (status != ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical)
                      << "IDevice::getSupportedOperations_1_3 failed with " << toString(status);
         } else if (supportedOperations.size() != model.main.operations.size()) {
@@ -172,7 +192,7 @@
     };
 
     const auto ret = kDevice->getSupportedOperations_1_3(hidlModel, cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -200,10 +220,9 @@
     const auto ret =
             kDevice->prepareModel_1_3(hidlModel, hidlPreference, hidlPriority, hidlDeadline,
                                       hidlModelCache, hidlDataCache, hidlToken, cb);
-    const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "prepareModel_1_3 failed with " << toString(status);
     }
 
@@ -223,10 +242,9 @@
 
     const auto ret = kDevice->prepareModelFromCache_1_3(hidlDeadline, hidlModelCache, hidlDataCache,
                                                         hidlToken, cb);
-    const auto status = NN_TRY(hal::utils::handleTransportError(ret));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "prepareModelFromCache_1_3 failed with " << toString(status);
     }
 
@@ -246,8 +264,7 @@
                                                  << "uninitialized";
     auto cb = [&result](ErrorStatus status, const sp<IBuffer>& buffer, uint32_t token) {
         if (status != ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical) << "IDevice::allocate failed with " << toString(status);
         } else if (buffer == nullptr) {
             result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Returned buffer is nullptr";
@@ -261,7 +278,7 @@
 
     const auto ret =
             kDevice->allocate(hidlDesc, hidlPreparedModels, hidlInputRoles, hidlOutputRoles, cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index 2781053..49b9b0b 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -27,6 +27,7 @@
 #include <android/hardware/neuralnetworks/1.3/types.h>
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/1.2/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
@@ -44,8 +45,7 @@
 nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
 convertExecutionResultsHelper(const hidl_vec<V1_2::OutputShape>& outputShapes,
                               const V1_2::Timing& timing) {
-    return std::make_pair(NN_TRY(validatedConvertToCanonical(outputShapes)),
-                          NN_TRY(validatedConvertToCanonical(timing)));
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
 }
 
 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
@@ -55,8 +55,7 @@
 
 nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionCallbackResults(
         const V1_2::Timing& timingLaunched, const V1_2::Timing& timingFenced) {
-    return std::make_pair(NN_TRY(validatedConvertToCanonical(timingLaunched)),
-                          NN_TRY(validatedConvertToCanonical(timingFenced)));
+    return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
 }
 
 nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
@@ -64,9 +63,9 @@
                             const sp<IFencedExecutionCallback>& callback) {
     auto resultSyncFence = nn::SyncFence::createAsSignaled();
     if (syncFence.getNativeHandle() != nullptr) {
-        auto nativeHandle = NN_TRY(validatedConvertToCanonical(syncFence));
+        auto sharedHandle = NN_TRY(nn::convert(syncFence));
         resultSyncFence = NN_TRY(hal::utils::makeGeneralFailure(
-                nn::SyncFence::create(std::move(nativeHandle)), nn::ErrorStatus::GENERAL_FAILURE));
+                nn::SyncFence::create(std::move(sharedHandle)), nn::ErrorStatus::GENERAL_FAILURE));
     }
 
     if (callback == nullptr) {
@@ -81,8 +80,8 @@
         auto cb = [&result](ErrorStatus status, const V1_2::Timing& timingLaunched,
                             const V1_2::Timing& timingFenced) {
             if (status != ErrorStatus::NONE) {
-                const auto canonical = validatedConvertToCanonical(status).value_or(
-                        nn::ErrorStatus::GENERAL_FAILURE);
+                const auto canonical =
+                        nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
                 result = NN_ERROR(canonical) << "getExecutionInfo failed with " << toString(status);
             } else {
                 result = convertFencedExecutionCallbackResults(timingLaunched, timingFenced);
@@ -90,7 +89,7 @@
         };
 
         const auto ret = callback->getExecutionInfo(cb);
-        NN_TRY(hal::utils::handleTransportError(ret));
+        HANDLE_TRANSPORT_FAILURE(ret);
 
         return result;
     };
@@ -125,8 +124,7 @@
     const auto cb = [&result](ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
                               const V1_2::Timing& timing) {
         if (status != ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical) << "executeSynchronously failed with " << toString(status);
         } else {
             result = convertExecutionResults(outputShapes, timing);
@@ -135,7 +133,7 @@
 
     const auto ret = kPreparedModel->executeSynchronously_1_3(request, measure, deadline,
                                                               loopTimeoutDuration, cb);
-    NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
+    HANDLE_TRANSPORT_FAILURE(ret);
 
     return result;
 }
@@ -149,11 +147,9 @@
 
     const auto ret =
             kPreparedModel->execute_1_3(request, measure, deadline, loopTimeoutDuration, cb);
-    const auto status =
-            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::handleTransportError(ret)));
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != ErrorStatus::NONE) {
-        const auto canonical =
-                validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
         return NN_ERROR(canonical) << "executeAsynchronously failed with " << toString(status);
     }
 
@@ -223,8 +219,7 @@
     auto cb = [&result](ErrorStatus status, const hidl_handle& syncFence,
                         const sp<IFencedExecutionCallback>& callback) {
         if (status != ErrorStatus::NONE) {
-            const auto canonical =
-                    validatedConvertToCanonical(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
             result = NN_ERROR(canonical) << "executeFenced failed with " << toString(status);
         } else {
             result = convertExecuteFencedResults(syncFence, callback);
@@ -234,7 +229,7 @@
     const auto ret = kPreparedModel->executeFenced(hidlRequest, hidlWaitFor, hidlMeasure,
                                                    hidlDeadline, hidlLoopTimeoutDuration,
                                                    hidlTimeoutDurationAfterFence, cb);
-    NN_TRY(hal::utils::handleTransportError(ret));
+    HANDLE_TRANSPORT_FAILURE(ret);
     auto [syncFence, callback] = NN_TRY(std::move(result));
 
     // If executeFenced required the request memory to be moved into shared memory, block here until
diff --git a/neuralnetworks/TEST_MAPPING b/neuralnetworks/TEST_MAPPING
index 0cefffa..ca5041d 100644
--- a/neuralnetworks/TEST_MAPPING
+++ b/neuralnetworks/TEST_MAPPING
@@ -21,7 +21,9 @@
           "include-filter": "-*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*"
         }
       ]
-    },
+    }
+  ],
+  "presubmit-large": [
     {
       "name": "VtsHalNeuralnetworksV1_2TargetTest",
       "options": [
diff --git a/neuralnetworks/utils/README.md b/neuralnetworks/utils/README.md
new file mode 100644
index 0000000..0dee103
--- /dev/null
+++ b/neuralnetworks/utils/README.md
@@ -0,0 +1,50 @@
+# NNAPI Conversions
+
+`convert` fails if either the source type or the destination type is invalid, and it yields a valid
+object if the conversion succeeds. For example, let's say that an enumeration in the current
+version has fewer possible values than the "same" canonical enumeration, such as `OperationType`.
+The new value of `HARD_SWISH` (introduced in Android R / NN HAL 1.3) does not map to any valid
+existing value in `OperationType`, but an older value of `ADD` (introduced in Android OC-MR1 / NN
+HAL 1.0) is valid. This can be seen in the following model conversions:
+
+```cpp
+// Unsuccessful conversion
+const nn::Model canonicalModel = createModelWhichHasV1_3Operations();
+const nn::Result<V1_0::Model> maybeVersionedModel = V1_0::utils::convert(canonicalModel);
+EXPECT_FALSE(maybeVersionedModel.has_value());
+```
+```cpp
+// Successful conversion
+const nn::Model canonicalModel = createModelWhichHasOnlyV1_0Operations();
+const nn::Result<V1_0::Model> maybeVersionedModel = V1_0::utils::convert(canonicalModel);
+ASSERT_TRUE(maybeVersionedModel.has_value());
+const V1_0::Model& versionedModel = maybeVersionedModel.value();
+EXPECT_TRUE(V1_0::utils::valid(versionedModel));
+```
+
+`V1_X::utils::convert` does not guarantee that all information is preserved. For example, In the
+case of `nn::ErrorStatus`, the new value of `MISSED_DEADLINE_TRANSIENT` can be represented by the
+existing value of `V1_0::GENERAL_FAILURE`:
+
+```cpp
+// Lossy Canonical -> HAL -> Canonical conversion
+const nn::ErrorStatus canonicalBefore = nn::ErrorStatus::MISSED_DEADLINE_TRANSIENT;
+const V1_0::ErrorStatus versioned = V1_0::utils::convert(canonicalBefore).value();
+const nn::ErrorStatus canonicalAfter = nn::convert(versioned).value();
+EXPECT_NE(canonicalBefore, canonicalAfter);
+```
+
+However, `nn::convert` is guaranteed to preserve all information:
+
+```cpp
+// Lossless HAL -> Canonical -> HAL conversion
+const V1_0::ErrorStatus versionedBefore = V1_0::ErrorStatus::GENERAL_FAILURE;
+const nn::ErrorStatus canonical = nn::convert(versionedBefore).value();
+const V1_0::ErrorStatus versionedAfter = V1_0::utils::convert(canonical).value();
+EXPECT_EQ(versionedBefore, versionedAfter);
+```
+
+The `convert` functions operate only on types that used in a HIDL method call directly. The
+`unvalidatedConvert` functions operate on types that are either used in a HIDL method call directly
+(i.e., not as a nested class) or used in a subsequent version of the NN HAL. Prefer using `convert`
+over `unvalidatedConvert`.
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
index e4046b5..78b2a12 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
@@ -19,65 +19,46 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 
+#include <type_traits>
+
 namespace android::hardware::neuralnetworks::utils {
 
 template <typename Type>
 nn::GeneralResult<Type> handleTransportError(const hardware::Return<Type>& ret) {
     if (ret.isDeadObject()) {
-        return NN_ERROR(nn::ErrorStatus::DEAD_OBJECT)
+        return nn::error(nn::ErrorStatus::DEAD_OBJECT)
                << "Return<>::isDeadObject returned true: " << ret.description();
     }
     if (!ret.isOk()) {
-        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+        return nn::error(nn::ErrorStatus::GENERAL_FAILURE)
                << "Return<>::isOk returned false: " << ret.description();
     }
-    return ret;
+    if constexpr (!std::is_same_v<Type, void>) {
+        return static_cast<Type>(ret);
+    } else {
+        return {};
+    }
 }
 
-template <>
-inline nn::GeneralResult<void> handleTransportError(const hardware::Return<void>& ret) {
-    if (ret.isDeadObject()) {
-        return NN_ERROR(nn::ErrorStatus::DEAD_OBJECT)
-               << "Return<>::isDeadObject returned true: " << ret.description();
-    }
-    if (!ret.isOk()) {
-        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-               << "Return<>::isOk returned false: " << ret.description();
-    }
-    return {};
-}
+#define HANDLE_TRANSPORT_FAILURE(ret)                                                        \
+    ({                                                                                       \
+        auto result = ::android::hardware::neuralnetworks::utils::handleTransportError(ret); \
+        if (!result.has_value()) {                                                           \
+            return NN_ERROR(result.error().code) << result.error().message;                  \
+        }                                                                                    \
+        std::move(result).value();                                                           \
+    })
 
 template <typename Type>
 nn::GeneralResult<Type> makeGeneralFailure(nn::Result<Type> result, nn::ErrorStatus status) {
     if (!result.has_value()) {
         return nn::error(status) << std::move(result).error();
     }
-    return std::move(result).value();
-}
-
-template <>
-inline nn::GeneralResult<void> makeGeneralFailure(nn::Result<void> result, nn::ErrorStatus status) {
-    if (!result.has_value()) {
-        return nn::error(status) << std::move(result).error();
+    if constexpr (!std::is_same_v<Type, void>) {
+        return std::move(result).value();
+    } else {
+        return {};
     }
-    return {};
-}
-
-template <typename Type>
-nn::ExecutionResult<Type> makeExecutionFailure(nn::Result<Type> result, nn::ErrorStatus status) {
-    if (!result.has_value()) {
-        return nn::error(status) << std::move(result).error();
-    }
-    return std::move(result).value();
-}
-
-template <>
-inline nn::ExecutionResult<void> makeExecutionFailure(nn::Result<void> result,
-                                                      nn::ErrorStatus status) {
-    if (!result.has_value()) {
-        return nn::error(status) << std::move(result).error();
-    }
-    return {};
 }
 
 template <typename Type>
@@ -86,16 +67,16 @@
         const auto [message, status] = std::move(result).error();
         return nn::error(status) << message;
     }
-    return std::move(result).value();
+    if constexpr (!std::is_same_v<Type, void>) {
+        return std::move(result).value();
+    } else {
+        return {};
+    }
 }
 
-template <>
-inline nn::ExecutionResult<void> makeExecutionFailure(nn::GeneralResult<void> result) {
-    if (!result.has_value()) {
-        const auto [message, status] = std::move(result).error();
-        return nn::error(status) << message;
-    }
-    return {};
+template <typename Type>
+nn::ExecutionResult<Type> makeExecutionFailure(nn::Result<Type> result, nn::ErrorStatus status) {
+    return makeExecutionFailure(makeGeneralFailure(result, status));
 }
 
 }  // namespace android::hardware::neuralnetworks::utils
\ No newline at end of file
diff --git a/neuralnetworks/utils/common/src/ProtectCallback.cpp b/neuralnetworks/utils/common/src/ProtectCallback.cpp
index 1d9a307..abe4cb6 100644
--- a/neuralnetworks/utils/common/src/ProtectCallback.cpp
+++ b/neuralnetworks/utils/common/src/ProtectCallback.cpp
@@ -58,7 +58,7 @@
     auto deathRecipient = sp<DeathRecipient>::make();
 
     const auto ret = object->linkToDeath(deathRecipient, /*cookie=*/0);
-    const bool success = NN_TRY(handleTransportError(ret));
+    const bool success = HANDLE_TRANSPORT_FAILURE(ret);
     if (!success) {
         return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "IBase::linkToDeath returned false";
     }
diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal
index c73745a..4566558 100644
--- a/radio/1.6/IRadio.hal
+++ b/radio/1.6/IRadio.hal
@@ -217,6 +217,13 @@
      * Each subsequent request to this method is processed only after the
      * completion of the previous one.
      *
+     * When the SIM is in POWER_DOWN, the modem should send an empty vector of
+     * AppStatus in CardStatus.applications. If a SIM in the POWER_DOWN state
+     * is removed and a new SIM is inserted, the new SIM should be in POWER_UP
+     * mode by default. If the device is turned off or restarted while the SIM
+     * is in POWER_DOWN, then the SIM should turn on normally in POWER_UP mode
+     * when the device turns back on.
+     *
      * Response callback is IRadioResponse.setSimCardPowerResponse_1_6().
      * Note that this differs from setSimCardPower_1_1 in that the response
      * callback should only be sent once the device has finished executing
@@ -320,12 +327,26 @@
      * @param serial Serial number of request.
      * @param networkTypeBitmap a 32-bit bearer bitmap of RadioAccessFamily
      *
-     * Response callbask is IRadioResponse.setNetworkTypeBitmapResponse()
+     * Response callback is IRadioResponse.setNetworkTypeBitmapResponse()
      */
     oneway setAllowedNetworkTypeBitmap(
             uint32_t serial, bitfield<RadioAccessFamily> networkTypeBitmap);
 
     /**
+     * Requests bitmap representing the currently allowed network types.
+     *
+     * Requests the bitmap set by the corresponding method
+     * setAllowedNetworkTypeBitmap, which sets a strict set of RATs for the
+     * radio to use. Differs from getPreferredNetworkType and getPreferredNetworkTypeBitmap
+     * in that those request *preferences*.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getNetworkTypeBitmapResponse()
+     */
+    oneway getAllowedNetworkTypeBitmap(uint32_t serial);
+
+    /**
      * Control data throttling at modem.
      *   - DataThrottlingAction:NO_DATA_THROTTLING should clear any existing
      *     data throttling within the requested completion window.
@@ -358,4 +379,45 @@
      * Response callback is IRadioResponse.getSystemSelectionChannelsResponse()
      */
     oneway getSystemSelectionChannels(int32_t serial);
+
+   /**
+     * Request all of the current cell information known to the radio. The radio
+     * must return list of all current cells, including the neighboring cells. If for a particular
+     * cell information isn't known then the appropriate unknown value will be returned.
+     * This does not cause or change the rate of unsolicited cellInfoList().
+     *
+     * This is identitcal to getCellInfoList in V1.0, but it requests updated version of CellInfo.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getCellInfoListResponse()
+     */
+    oneway getCellInfoList_1_6(int32_t serial);
+
+    /**
+     * Request current voice registration state.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getVoiceRegistrationStateResponse_1_6()
+     */
+    oneway getVoiceRegistrationState_1_6(int32_t serial);
+
+    /**
+     * Requests current signal strength and associated information.  Must succeed if radio is on.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getSignalStrengthResponse_1_6()
+     */
+    oneway getSignalStrength_1_6(int32_t serial);
+
+    /**
+     * Request current data registration state.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getDataRegistrationStateResponse_1_6()
+     */
+    oneway getDataRegistrationState_1_6(int32_t serial);
 };
diff --git a/radio/1.6/IRadioIndication.hal b/radio/1.6/IRadioIndication.hal
index f195c0e..1b56d40 100644
--- a/radio/1.6/IRadioIndication.hal
+++ b/radio/1.6/IRadioIndication.hal
@@ -18,8 +18,11 @@
 
 import @1.0::RadioIndicationType;
 import @1.5::IRadioIndication;
-import @1.6::SetupDataCallResult;
+import @1.6::CellInfo;
 import @1.6::LinkCapacityEstimate;
+import @1.6::NetworkScanResult;
+import @1.6::SignalStrength;
+import @1.6::SetupDataCallResult;
 
 /**
  * Interface declaring unsolicited radio indications.
@@ -67,4 +70,35 @@
      * @param lce LinkCapacityEstimate
      */
     oneway currentLinkCapacityEstimate_1_6(RadioIndicationType type, LinkCapacityEstimate lce);
+
+
+    /**
+     * Indicates current signal strength of the radio.
+     *
+     * This is identical to currentSignalStrength_1_4 but uses an updated version of
+     * SignalStrength.
+     *
+     * @param type Type of radio indication
+     * @param signalStrength SignalStrength information
+     */
+    oneway currentSignalStrength_1_6(RadioIndicationType type, SignalStrength signalStrength);
+
+    /**
+     * Report all of the current cell information known to the radio.
+     *
+     * This indication is updated from IRadioIndication@1.5 to report the @1.6 version of
+     * CellInfo.
+     *
+     * @param type Type of radio indication
+     * @param records Current cell information
+     */
+    oneway cellInfoList_1_6(RadioIndicationType type, vec<CellInfo> records);
+
+    /**
+     * Incremental network scan results.
+     *
+     * This indication is updated from IRadioIndication@1.5 to report the @1.6 version of
+     * CellInfo.
+     */
+    oneway networkScanResult_1_6(RadioIndicationType type, NetworkScanResult result);
 };
diff --git a/radio/1.6/IRadioResponse.hal b/radio/1.6/IRadioResponse.hal
index c545db0..f13ee2b 100644
--- a/radio/1.6/IRadioResponse.hal
+++ b/radio/1.6/IRadioResponse.hal
@@ -17,9 +17,13 @@
 package android.hardware.radio@1.6;
 
 import @1.0::SendSmsResult;
-import @1.6::RadioResponseInfo;
+import @1.4::RadioAccessFamily;
 import @1.5::IRadioResponse;
+import @1.6::CellInfo;
+import @1.6::RegStateResult;
+import @1.6::RadioResponseInfo;
 import @1.6::SetupDataCallResult;
+import @1.6::SignalStrength;
 
 /**
  * Interface declaring response functions to solicited radio requests.
@@ -207,7 +211,6 @@
      * Valid errors returned:
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:INVALID_ARGUMENTS
      *   RadioError:SIM_ERR (indicates a timeout or other issue making the SIM unresponsive)
      *
@@ -308,6 +311,23 @@
     oneway setAllowedNetworkTypeBitmapResponse(RadioResponseInfo info);
 
     /**
+     * Callback of IRadio.getAllowedNetworkTypeBitmap(int, bitfield<RadioAccessFamily>)
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:MODE_NOT_SUPPORTED
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:MODEM_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:NO_RESOURCES
+     */
+    oneway getAllowedNetworkTypeBitmapResponse(
+            RadioResponseInfo info, bitfield<RadioAccessFamily> networkTypeBitmap);
+
+    /**
      * @param info Response info struct containing response type, serial no. and error
      *
      *  Valid errors returned:
@@ -328,4 +348,57 @@
      *   RadioError:INVALID_ARGUMENTS
      */
     oneway getSystemSelectionChannelsResponse(RadioResponseInfo info);
+
+    /**
+     * This is identical to getCellInfoListResponse_1_5 but uses an updated version of CellInfo.
+     *
+     * @param info Response info struct containing response type, serial no. and error
+     * @param cellInfo List of current cell information known to radio
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getCellInfoListResponse_1_6(RadioResponseInfo info, vec<CellInfo> cellInfo);
+
+    /**
+     * This is identical to getSignalStrengthResponse_1_4 but uses an updated version of
+     * SignalStrength.
+     *
+     * @param signalStrength Current signal strength
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getSignalStrengthResponse_1_6(RadioResponseInfo info, SignalStrength signalStrength);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param voiceRegResponse Current Voice registration response as defined by RegStateResult
+     *        in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getVoiceRegistrationStateResponse_1_6(RadioResponseInfo info,
+            RegStateResult voiceRegResponse);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param dataRegResponse Current Data registration response as defined by RegStateResult in
+     *        types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NOT_PROVISIONED
+     */
+    oneway getDataRegistrationStateResponse_1_6(RadioResponseInfo info,
+            RegStateResult dataRegResponse);
 };
diff --git a/radio/1.6/types.hal b/radio/1.6/types.hal
index 2cba915..20dc612 100644
--- a/radio/1.6/types.hal
+++ b/radio/1.6/types.hal
@@ -16,12 +16,35 @@
 
 package android.hardware.radio@1.6;
 
+import @1.0::CdmaSignalStrength;
+import @1.0::EvdoSignalStrength;
+import @1.0::GsmSignalStrength;
+import @1.0::LteSignalStrength;
 import @1.0::RadioError;
 import @1.0::RadioResponseType;
+import @1.0::RegState;
+import @1.1::ScanStatus;
+import @1.2::CellInfoCdma;
+import @1.2::CellConnectionStatus;
+import @1.2::TdscdmaSignalStrength;
+import @1.2::WcdmaSignalStrength;
 import @1.4::DataCallFailCause;
 import @1.4::DataConnActiveStatus;
+import @1.4::NrSignalStrength;
 import @1.4::PdpProtocolType;
+import @1.4::RadioTechnology;
+import @1.5::CellIdentity;
+import @1.5::CellIdentityLte;
+import @1.5::CellIdentityNr;
+import @1.5::CellInfoGsm;
+import @1.5::CellInfoWcdma;
+import @1.5::CellInfoTdscdma;
 import @1.5::LinkAddress;
+import @1.5::RegStateResult.AccessTechnologySpecificInfo.Cdma2000RegistrationInfo;
+import @1.5::RegStateResult.AccessTechnologySpecificInfo.EutranRegistrationInfo;
+import @1.5::RegistrationFailCause;
+import @1.5::SetupDataCallResult;
+
 import android.hidl.safe_union@1.0::Monostate;
 
 struct QosBandwidth {
@@ -416,3 +439,287 @@
      */
     HOLD = 3
 };
+
+/**
+ * Defines the values for VoPS indicator of NR as per 3gpp spec 24.501 sec 9.10.3.5
+ */
+enum VopsIndicator : uint8_t {
+    /** IMS voice over PS session not supported */
+    VOPS_NOT_SUPPORTED = 0,
+    /** IMS voice over PS session supported over 3GPP access */
+    VOPS_OVER_3GPP = 1,
+    /** IMS voice over PS session supported over non-3GPP access */
+    VOPS_OVER_NON_3GPP = 2,
+};
+
+/**
+ * Defines the values for emergency service indicator of NR
+ * as per 3gpp spec 24.501 sec 9.10.3.5
+ */
+enum EmcIndicator : uint8_t {
+    /** Emergency services not supported */
+    EMC_NOT_SUPPORTED = 0,
+    /** Emergency services supported in NR connected to 5GCN only */
+    EMC_NR_CONNECTED_TO_5GCN = 1,
+    /** Emergency services supported in E-UTRA connected to 5GCN only */
+    EMC_EUTRA_CONNECTED_TO_5GCN = 2,
+    /** Emergency services supported in NR connected to 5GCN and E-UTRA connected to 5GCN */
+    EMC_BOTH_NR_EUTRA_CONNECTED_TO_5GCN = 3
+};
+
+/**
+ * Defines the values for emergency service fallback indicator of NR
+ * as per 3gpp spec 24.501 sec 9.10.3.5
+ */
+enum EmfIndicator : uint8_t {
+    /** Emergency services fallback not supported */
+    EMF_NOT_SUPPORTED = 0,
+    /** Emergency services fallback supported in NR connected to 5GCN only */
+    EMF_NR_CONNECTED_TO_5GCN = 1,
+    /** Emergency services fallback supported in E-UTRA connected to 5GCN only */
+    EMF_EUTRA_CONNECTED_TO_5GCN = 2,
+    /**
+     * Emergency services fallback supported in NR connected to 5GCN and E-UTRA
+     * connected to 5GCN.
+     */
+    EMF_BOTH_NR_EUTRA_CONNECTED_TO_5GCN = 3
+};
+
+/**
+ * Type to define the NR specific network capabilities for voice over PS including
+ * emergency and normal voice calls.
+ */
+struct NrVopsInfo {
+    /**
+     * This indicates if the camped network supports VoNR services, and what kind of services
+     * it supports. This information is received from NR network during NR NAS registration
+     * procedure through NR REGISTRATION ACCEPT.
+     * Refer 3GPP 24.501 EPS 5GS network feature support -> IMS VoPS
+     */
+    VopsIndicator vopsSupported;
+
+    /**
+     * This indicates if the camped network supports VoNR emergency service. This information
+     * is received from NR network through two sources:
+     * a. During NR NAS registration procedure through NR REGISTRATION ACCEPT.
+     *    Refer 3GPP 24.501 EPS 5GS network feature support -> EMC
+     * b. In case the device is not registered on the network.
+     *    Refer 3GPP 38.331 SIB1 : ims-EmergencySupport
+     *    If device is registered on NR, then this field indicates whether the cell
+     *    supports IMS emergency bearer services for UEs in limited service mode.
+     */
+    EmcIndicator emcSupported;
+
+    /**
+     * This indicates if the camped network supports VoNR emergency service fallback. This
+     * information is received from NR network during NR NAS registration procedure through
+     * NR REGISTRATION ACCEPT.
+     * Refer 3GPP 24.501 EPS 5GS network feature support -> EMF
+     */
+    EmfIndicator emfSupported;
+};
+
+struct LteSignalStrength {
+    @1.0::LteSignalStrength base;
+
+    /**
+     * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+     * The definition of CQI in each table is different.
+     *
+     * Reference: 3GPP TS 136.213 section 7.2.3.
+     *
+     * Range [1, 6], INT_MAX means invalid/unreported.
+     */
+    uint32_t cqiTableIndex;
+};
+
+struct NrSignalStrength {
+    @1.4::NrSignalStrength base;
+
+    /**
+     * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+     * The definition of CQI in each table is different.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * Range [1, 3], INT_MAX means invalid/unreported.
+     */
+    uint32_t csiCqiTableIndex;
+
+    /**
+     * CSI channel quality indicator (CQI) for all subbands.
+     *
+     * If the CQI report is for the entire wideband, a single CQI index is provided.
+     * If the CQI report is for all subbands, one CQI index is provided for each subband,
+     * in ascending order of subband index.
+     * If CQI is not available, the CQI report is empty.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * Range [0, 15], INT_MAX means invalid/unreported.
+     */
+    vec<uint32_t> csiCqiReport;
+};
+
+/**
+ * Overwritten from @1.4::SignalStrength in order to update LteSignalStrength and NrSignalStrength.
+ */
+struct SignalStrength {
+    /**
+     * If GSM measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    GsmSignalStrength gsm;
+
+    /**
+     * If CDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    CdmaSignalStrength cdma;
+
+    /**
+     * If EvDO measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    EvdoSignalStrength evdo;
+
+    /**
+     * If LTE measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    LteSignalStrength lte;
+
+    /**
+     * If TD-SCDMA measurements are provided, this structure must contain valid measurements;
+     * otherwise all fields should be set to INT_MAX to mark them as invalid.
+     */
+    TdscdmaSignalStrength tdscdma;
+
+    /**
+     * If WCDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    WcdmaSignalStrength wcdma;
+
+    /**
+     * If NR 5G measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    NrSignalStrength nr;
+};
+
+/** Overwritten from @1.5::CellInfoLte in order to update LteSignalStrength. */
+struct CellInfoLte {
+    CellIdentityLte cellIdentityLte;
+    LteSignalStrength signalStrengthLte;
+};
+
+/** Overwritten from @1.5::CellInfoNr in order to update NrSignalStrength. */
+struct CellInfoNr {
+    CellIdentityNr cellIdentityNr;
+    NrSignalStrength signalStrengthNr;
+};
+
+/** Overwritten from @1.5::CellInfo in order to update LteSignalStrength and NrSignalStrength. */
+struct CellInfo {
+    /**
+     * True if this cell is registered false if not registered.
+     */
+    bool registered;
+    /**
+     * Connection status for the cell.
+     */
+    CellConnectionStatus connectionStatus;
+
+    safe_union CellInfoRatSpecificInfo {
+        /**
+         * 3gpp CellInfo types.
+         */
+        CellInfoGsm gsm;
+        CellInfoWcdma wcdma;
+        CellInfoTdscdma tdscdma;
+        CellInfoLte lte;
+        CellInfoNr nr;
+
+        /**
+         * 3gpp2 CellInfo types;
+         */
+        CellInfoCdma cdma;
+    } ratSpecificInfo;
+};
+
+/** Overwritten from @1.5::NetworkScanResult in order to update the CellInfo to 1.6 version. */
+struct NetworkScanResult {
+    /**
+     * The status of the scan.
+     */
+    ScanStatus status;
+
+    /**
+     * The error code of the incremental result.
+     */
+    RadioError error;
+
+    /**
+     * List of network information as CellInfo.
+     */
+    vec<CellInfo> networkInfos;
+};
+
+/**
+ * Overwritten from @1.5::RegStateResult to 1.6 to support NrRegistrationInfo
+ * version.
+ */
+struct RegStateResult {
+    /**
+     * Registration state
+     *
+     * If the RAT is indicated as a GERAN, UTRAN, or CDMA2000 technology, this value reports
+     * registration in the Circuit-switched domain.
+     * If the RAT is indicated as an EUTRAN, NGRAN, or another technology that does not support
+     * circuit-switched services, this value reports registration in the Packet-switched domain.
+     */
+    RegState regState;
+
+    /**
+     * Indicates the available voice radio technology, valid values as
+     * defined by RadioTechnology.
+     */
+    RadioTechnology rat;
+
+    /**
+     * Cause code reported by the network in case registration fails. This will be a mobility
+     * management cause code defined for MM, GMM, MME or equivalent as appropriate for the RAT.
+     */
+    RegistrationFailCause reasonForDenial;
+
+    /** CellIdentity */
+    CellIdentity cellIdentity;
+
+    /**
+     * The most-recent PLMN-ID upon which the UE registered (or attempted to register if a failure
+     * is reported in the reasonForDenial field). This PLMN shall be in standard format consisting
+     * of a 3 digit MCC concatenated with a 2 or 3 digit MNC.
+     */
+    string registeredPlmn;
+
+    /**
+     * Access-technology-specific registration information, such as for CDMA2000.
+     */
+    safe_union AccessTechnologySpecificInfo {
+        Monostate noinit;
+
+        Cdma2000RegistrationInfo cdmaInfo;
+
+        EutranRegistrationInfo eutranInfo;
+
+        struct NgranRegistrationInfo {
+            /**
+             * Network capabilities for voice over PS services. This info is valid only on NR
+             * network and must be present when the device is camped on NR. VopsInfo must be
+             * empty when the device is not camped on NR.
+             */
+            NrVopsInfo nrVopsInfo;
+        } ngranInfo;
+    } accessTechnologySpecificInfo;
+};
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
index 6b1ff27..75772cd 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -321,7 +321,6 @@
     res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::THROTTLE_ANCHOR_CARRIER,
                                         60000);
     ASSERT_OK(res);
-
     EXPECT_EQ(std::cv_status::no_timeout, wait());
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
@@ -351,7 +350,6 @@
 
     res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::NO_DATA_THROTTLING, 60000);
     ASSERT_OK(res);
-
     EXPECT_EQ(std::cv_status::no_timeout, wait());
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
@@ -361,4 +359,50 @@
                               ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
                               ::android::hardware::radio::V1_6::RadioError::NONE,
                               ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
-}
\ No newline at end of file
+}
+
+/*
+ * Test IRadio.setSimCardPower_1_6() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, setSimCardPower_1_6) {
+    /* Test setSimCardPower power down */
+    serial = GetRandomSerialNumber();
+    radio_v1_6->setSimCardPower_1_6(serial, CardPowerState::POWER_DOWN);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    ASSERT_TRUE(
+            CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+                             {::android::hardware::radio::V1_6::RadioError::NONE,
+                              ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+                              ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE}));
+
+    // setSimCardPower_1_6 does not return  until the request is handled, and should not trigger
+    // CardState::ABSENT when turning off power
+    if (radioRsp_v1_6->rspInfo.error == ::android::hardware::radio::V1_6::RadioError::NONE) {
+        /* Wait some time for setting sim power down and then verify it */
+        updateSimCardStatus();
+        EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
+        // applications should be an empty vector of AppStatus
+        EXPECT_EQ(0, cardStatus.applications.size());
+    }
+
+    /* Test setSimCardPower power up */
+    serial = GetRandomSerialNumber();
+    radio_v1_6->setSimCardPower_1_6(serial, CardPowerState::POWER_UP);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    ASSERT_TRUE(
+            CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+                             {::android::hardware::radio::V1_6::RadioError::NONE,
+                              ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+                              ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE}));
+
+    // setSimCardPower_1_6 does not return  until the request is handled. Just verify that we still
+    // have CardState::PRESENT after turning the power back on
+    if (radioRsp_v1_6->rspInfo.error == ::android::hardware::radio::V1_6::RadioError::NONE) {
+        updateSimCardStatus();
+        EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
+    }
+}
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
index 85be903..ab56540 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
+++ b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
@@ -793,11 +793,34 @@
     Return<void> setAllowedNetworkTypeBitmapResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
 
+    Return<void> getAllowedNetworkTypeBitmapResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::hidl_bitfield<
+                    ::android::hardware::radio::V1_4::RadioAccessFamily>
+                    networkTypeBitmap);
+
     Return<void> setDataThrottlingResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
 
     Return<void> getSystemSelectionChannelsResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> getSignalStrengthResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::SignalStrength& sig_strength);
+
+    Return<void> getCellInfoListResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::CellInfo>&
+                    cellInfo);
+
+    Return<void> getVoiceRegistrationStateResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::RegStateResult& regResponse);
+
+    Return<void> getDataRegistrationStateResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::RegStateResult& regResponse);
 };
 
 /* Callback class for radio indication */
@@ -817,6 +840,19 @@
     Return<void> unthrottleApn(RadioIndicationType type,
                                const ::android::hardware::hidl_string& apn);
 
+    Return<void> currentSignalStrength_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_6::SignalStrength& signalStrength);
+
+    Return<void> networkScanResult_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_6::NetworkScanResult& result);
+
+    Return<void> cellInfoList_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::CellInfo>&
+                    records);
+
     /* 1.5 Api */
     Return<void> uiccApplicationsEnablementChanged(RadioIndicationType type, bool enabled);
 
diff --git a/radio/1.6/vts/functional/radio_indication.cpp b/radio/1.6/vts/functional/radio_indication.cpp
index afde291..bfc54c0 100644
--- a/radio/1.6/vts/functional/radio_indication.cpp
+++ b/radio/1.6/vts/functional/radio_indication.cpp
@@ -386,3 +386,22 @@
                                               const ::android::hardware::hidl_string& /*reason*/) {
     return Void();
 }
+
+Return<void> RadioIndication_v1_6::currentSignalStrength_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_6::SignalStrength& /*signalStrength*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::networkScanResult_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_6::NetworkScanResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cellInfoList_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_6::CellInfo>& /*records*/) {
+    return Void();
+}
diff --git a/radio/1.6/vts/functional/radio_response.cpp b/radio/1.6/vts/functional/radio_response.cpp
index 7da675e..e37efdd 100644
--- a/radio/1.6/vts/functional/radio_response.cpp
+++ b/radio/1.6/vts/functional/radio_response.cpp
@@ -808,12 +808,6 @@
     return Void();
 }
 
-Return<void> RadioResponse_v1_6::getSignalStrengthResponse_1_4(
-        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
-        const ::android::hardware::radio::V1_4::SignalStrength& /*sig_strength*/) {
-    return Void();
-}
-
 Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_2(
         const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
         const ::android::hardware::hidl_vec<
@@ -867,6 +861,12 @@
     return Void();
 }
 
+Return<void> RadioResponse_v1_6::getSignalStrengthResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_4::SignalStrength& /*sig_strength*/) {
+    return Void();
+}
+
 Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_4(
         const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
         const ::android::hardware::hidl_vec<
@@ -1157,6 +1157,14 @@
     return Void();
 }
 
+Return<void> RadioResponse_v1_6::getAllowedNetworkTypeBitmapResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_bitfield<
+                ::android::hardware::radio::V1_4::RadioAccessFamily>
+        /*networkTypeBitmap*/) {
+    return Void();
+}
+
 Return<void> RadioResponse_v1_6::setDataThrottlingResponse(
         const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
     rspInfo = info;
@@ -1164,9 +1172,38 @@
     return Void();
 }
 
+Return<void> RadioResponse_v1_6::getSignalStrengthResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_6::SignalStrength& /*sig_strength*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_6::CellInfo>& /*cellInfo*/) {
+    return Void();
+}
+
 Return<void> RadioResponse_v1_6::getSystemSelectionChannelsResponse(
         const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
     rspInfo = info;
     parent_v1_6.notify(info.serial);
     return Void();
 }
+
+Return<void> RadioResponse_v1_6::getVoiceRegistrationStateResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_6::RegStateResult& /*regResponse*/) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataRegistrationStateResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_6::RegStateResult& /*regResponse*/) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
diff --git a/renderscript/1.0/types.hal b/renderscript/1.0/types.hal
index 7c32188..bb39baa 100644
--- a/renderscript/1.0/types.hal
+++ b/renderscript/1.0/types.hal
@@ -170,7 +170,6 @@
 };
 
 // Script to Script
-@export(name="RsScriptCall")
 struct ScriptCall {
     ForEachStrategy strategy;
     uint32_t        xStart;
diff --git a/sensors/2.1/default/SensorsV2_1.cpp b/sensors/2.1/default/SensorsV2_1.cpp
index 2e3d315..4c5386a 100644
--- a/sensors/2.1/default/SensorsV2_1.cpp
+++ b/sensors/2.1/default/SensorsV2_1.cpp
@@ -45,7 +45,8 @@
         mSensorInfo.fifoReservedEventCount = 0;
         mSensorInfo.fifoMaxEventCount = 0;
         mSensorInfo.requiredPermission = "";
-        mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE);
+        mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE |
+                                                  V1_0::SensorFlagBits::WAKE_UP);
     }
 };
 
diff --git a/sensors/common/default/2.X/Sensor.cpp b/sensors/common/default/2.X/Sensor.cpp
index 870980f..642fc89 100644
--- a/sensors/common/default/2.X/Sensor.cpp
+++ b/sensors/common/default/2.X/Sensor.cpp
@@ -26,6 +26,7 @@
 namespace V2_X {
 namespace implementation {
 
+using ::android::hardware::sensors::V1_0::EventPayload;
 using ::android::hardware::sensors::V1_0::MetaDataEventType;
 using ::android::hardware::sensors::V1_0::OperationMode;
 using ::android::hardware::sensors::V1_0::Result;
@@ -133,20 +134,13 @@
 }
 
 std::vector<Event> Sensor::readEvents() {
-    // For an accelerometer sensor type, default the z-direction
-    // value to -9.8
-    float zValue = (mSensorInfo.type == SensorType::ACCELEROMETER)
-        ? -9.8 : 0.0;
-
     std::vector<Event> events;
     Event event;
     event.sensorHandle = mSensorInfo.sensorHandle;
     event.sensorType = mSensorInfo.type;
     event.timestamp = ::android::elapsedRealtimeNano();
-    event.u.vec3.x = 0;
-    event.u.vec3.y = 0;
-    event.u.vec3.z = zValue;
-    event.u.vec3.status = SensorStatus::ACCURACY_HIGH;
+    memset(&event.u, 0, sizeof(event.u));
+    readEventPayload(event.u);
     events.push_back(event);
     return events;
 }
@@ -194,7 +188,7 @@
 
     for (auto iter = events.begin(); iter != events.end(); ++iter) {
         Event ev = *iter;
-        if (ev.u.vec3 != mPreviousEvent.u.vec3 || !mPreviousEventSet) {
+        if (!mPreviousEventSet || memcmp(&mPreviousEvent.u, &ev.u, sizeof(ev.u)) != 0) {
             outputEvents.push_back(ev);
             mPreviousEvent = ev;
             mPreviousEventSet = true;
@@ -213,7 +207,7 @@
     mSensorInfo.maxRange = 78.4f;  // +/- 8g
     mSensorInfo.resolution = 1.52e-5;
     mSensorInfo.power = 0.001f;        // mA
-    mSensorInfo.minDelay = 20 * 1000;  // microseconds
+    mSensorInfo.minDelay = 10 * 1000;  // microseconds
     mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
@@ -221,6 +215,13 @@
     mSensorInfo.flags = static_cast<uint32_t>(SensorFlagBits::DATA_INJECTION);
 };
 
+void AccelSensor::readEventPayload(EventPayload& payload) {
+    payload.vec3.x = 0;
+    payload.vec3.y = 0;
+    payload.vec3.z = -9.8;
+    payload.vec3.status = SensorStatus::ACCURACY_HIGH;
+}
+
 PressureSensor::PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
     : Sensor(callback) {
     mSensorInfo.sensorHandle = sensorHandle;
@@ -240,6 +241,10 @@
     mSensorInfo.flags = 0;
 };
 
+void PressureSensor::readEventPayload(EventPayload& payload) {
+    payload.scalar = 1013.25f;
+}
+
 MagnetometerSensor::MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
     : Sensor(callback) {
     mSensorInfo.sensorHandle = sensorHandle;
diff --git a/sensors/common/default/2.X/Sensor.h b/sensors/common/default/2.X/Sensor.h
index a792797..8ef11be 100644
--- a/sensors/common/default/2.X/Sensor.h
+++ b/sensors/common/default/2.X/Sensor.h
@@ -47,6 +47,7 @@
     using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
     using Result = ::android::hardware::sensors::V1_0::Result;
     using Event = ::android::hardware::sensors::V2_1::Event;
+    using EventPayload = ::android::hardware::sensors::V1_0::EventPayload;
     using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
     using SensorType = ::android::hardware::sensors::V2_1::SensorType;
 
@@ -65,6 +66,7 @@
   protected:
     void run();
     virtual std::vector<Event> readEvents();
+    virtual void readEventPayload(EventPayload&) {}
     static void startThread(Sensor* sensor);
 
     bool isWakeUpSensor();
@@ -101,6 +103,9 @@
 class AccelSensor : public Sensor {
   public:
     AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
+
+  protected:
+    virtual void readEventPayload(EventPayload& payload) override;
 };
 
 class GyroSensor : public Sensor {
@@ -116,6 +121,9 @@
 class PressureSensor : public Sensor {
   public:
     PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
+
+  protected:
+    virtual void readEventPayload(EventPayload& payload) override;
 };
 
 class MagnetometerSensor : public Sensor {
diff --git a/soundtrigger/2.1/ISoundTriggerHwCallback.hal b/soundtrigger/2.1/ISoundTriggerHwCallback.hal
index 42e851b..b7720b6 100644
--- a/soundtrigger/2.1/ISoundTriggerHwCallback.hal
+++ b/soundtrigger/2.1/ISoundTriggerHwCallback.hal
@@ -22,7 +22,6 @@
 /**
  * SoundTrigger HAL Callback interface. Obtained during SoundTrigger setup.
  */
-@hidl_callback
 interface ISoundTriggerHwCallback extends @2.0::ISoundTriggerHwCallback {
 
     /**
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
index 1765915..7b130ea 100644
--- a/tv/tuner/1.0/vts/functional/Android.bp
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -41,6 +41,9 @@
     shared_libs: [
         "libbinder",
     ],
+    data: [
+        ":tuner_frontend_input_ts",
+    ],
     test_suites: [
         "general-tests",
         "vts",
diff --git a/tv/tuner/1.0/vts/functional/AndroidTest.xml b/tv/tuner/1.0/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..3a2db27
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalTvTunerV1_0TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalTvTunerV1_0TargetTest->/data/local/tmp/VtsHalTvTunerV1_0TargetTest" />
+        <option name="push" value="test.es->/data/local/tmp/test.es" />
+        <option name="push" value="segment000000.ts->/data/local/tmp/segment000000.ts" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalTvTunerV1_0TargetTest" />
+    </test>
+</configuration>
diff --git a/tv/tuner/1.1/default/Filter.cpp b/tv/tuner/1.1/default/Filter.cpp
index 4fa1746..6b2413c 100644
--- a/tv/tuner/1.1/default/Filter.cpp
+++ b/tv/tuner/1.1/default/Filter.cpp
@@ -165,8 +165,9 @@
 Return<Result> Filter::releaseAvHandle(const hidl_handle& avMemory, uint64_t avDataId) {
     ALOGV("%s", __FUNCTION__);
 
-    if ((avMemory.getNativeHandle()->numFds > 0) &&
+    if (mSharedAvMemHandle != NULL && avMemory != NULL &&
         (mSharedAvMemHandle.getNativeHandle()->numFds > 0) &&
+        (avMemory.getNativeHandle()->numFds > 0) &&
         (sameFile(avMemory.getNativeHandle()->data[0],
                   mSharedAvMemHandle.getNativeHandle()->data[0]))) {
         freeSharedAvHandle();
diff --git a/tv/tuner/1.1/vts/functional/Android.bp b/tv/tuner/1.1/vts/functional/Android.bp
index 1fc36f1..73cd057 100644
--- a/tv/tuner/1.1/vts/functional/Android.bp
+++ b/tv/tuner/1.1/vts/functional/Android.bp
@@ -40,6 +40,9 @@
     shared_libs: [
         "libbinder",
     ],
+    data: [
+        ":tuner_frontend_input_es",
+    ],
     test_suites: [
         "general-tests",
         "vts",
diff --git a/tv/tuner/1.1/vts/functional/AndroidTest.xml b/tv/tuner/1.1/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..28f95db
--- /dev/null
+++ b/tv/tuner/1.1/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalTvTunerV1_1TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalTvTunerV1_1TargetTest->/data/local/tmp/VtsHalTvTunerV1_1TargetTest" />
+        <option name="push" value="test.es->/data/local/tmp/test.es" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalTvTunerV1_1TargetTest" />
+    </test>
+</configuration>
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
index e872762..2dcb9a1 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
@@ -179,7 +179,7 @@
     configSingleFilterInDemuxTest(filterArray[IP_IP0], frontendArray[DVBT]);
 }
 
-TEST_P(TunerFilterHidlTest, ReonfigFilterToReceiveStartId) {
+TEST_P(TunerFilterHidlTest, ReconfigFilterToReceiveStartId) {
     description("Recofigure and restart a filter to test start id.");
     // TODO use parameterized tests
     reconfigSingleFilterInDemuxTest(filterArray[TS_VIDEO0], filterArray[TS_VIDEO1],
diff --git a/tv/tuner/assets/Android.bp b/tv/tuner/assets/Android.bp
new file mode 100644
index 0000000..b58b645
--- /dev/null
+++ b/tv/tuner/assets/Android.bp
@@ -0,0 +1,17 @@
+genrule {
+    name: "tuner_frontend_input_es",
+    srcs: ["tuner_frontend_input.es"],
+    out: [
+        "test.es",
+    ],
+    cmd: "cp -f $(in) $(genDir)/test.es",
+}
+
+genrule {
+    name: "tuner_frontend_input_ts",
+    srcs: ["tuner_frontend_input.ts"],
+    out: [
+        "segment000000.ts",
+    ],
+    cmd: "cp -f $(in) $(genDir)/segment000000.ts",
+}
diff --git a/tv/tuner/assets/tuner_frontend_input.es b/tv/tuner/assets/tuner_frontend_input.es
new file mode 100644
index 0000000..b53c957
--- /dev/null
+++ b/tv/tuner/assets/tuner_frontend_input.es
Binary files differ
diff --git a/tv/tuner/assets/tuner_frontend_input.ts b/tv/tuner/assets/tuner_frontend_input.ts
new file mode 100644
index 0000000..8992c7b
--- /dev/null
+++ b/tv/tuner/assets/tuner_frontend_input.ts
Binary files differ
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
index 0b21248..cd7b603 100644
--- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -206,7 +206,10 @@
      * device/board configuration files ensuring that no ID is assigned to
      * multiple clients. No client should use this API unless explicitly
      * assigned an always-on source ID. Clients must develop their own way to
-     * get IDs from vendor in a stable way.
+     * get IDs from vendor in a stable way. For instance, a client may expose
+     * a stable API (via HAL, sysprops, or xml overlays) to allow vendor to
+     * associate a hardware ID with a specific usecase. When that usecase is
+     * triggered, a client would use that hardware ID here.
      *
      * @param id The device-specific always-on source ID to enable.
      * @param effect The type of haptic event to trigger.
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index dfd2524..adbb0cf 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -116,7 +116,7 @@
 }
 
 TEST_P(VibratorAidl, OnWithCallback) {
-    if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) return;
+    if (!(capabilities & IVibrator::CAP_ON_CALLBACK)) return;
 
     std::promise<void> completionPromise;
     std::future<void> completionFuture{completionPromise.get_future()};
@@ -130,7 +130,7 @@
 }
 
 TEST_P(VibratorAidl, OnCallbackNotSupported) {
-    if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) {
+    if (!(capabilities & IVibrator::CAP_ON_CALLBACK)) {
         sp<CompletionCallback> callback = new CompletionCallback([] {});
         EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, vibrator->on(250, callback).exceptionCode());
     }
diff --git a/wifi/1.5/Android.bp b/wifi/1.5/Android.bp
index 5a62a3a..e2c38ce 100644
--- a/wifi/1.5/Android.bp
+++ b/wifi/1.5/Android.bp
@@ -7,6 +7,7 @@
         "types.hal",
         "IWifi.hal",
         "IWifiChip.hal",
+        "IWifiApIface.hal",
         "IWifiNanIface.hal",
         "IWifiNanIfaceEventCallback.hal",
         "IWifiStaIface.hal",
diff --git a/wifi/1.5/IWifiApIface.hal b/wifi/1.5/IWifiApIface.hal
new file mode 100644
index 0000000..9625a6b
--- /dev/null
+++ b/wifi/1.5/IWifiApIface.hal
@@ -0,0 +1,40 @@
+/*
+ * 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@1.5;
+
+import @1.4::IWifiApIface;
+import @1.0::MacAddress;
+import @1.0::WifiStatus;
+
+/**
+ * Represents a network interface in AP mode.
+ *
+ * This can be obtained through @1.0::IWifiChip.getApIface() and casting
+ * IWifiApIface up to 1.5.
+ */
+interface IWifiApIface extends @1.4::IWifiApIface {
+    /**
+     * Reset all of the AP interfaces MAC address to the factory MAC address.
+     *
+     * @return status WifiStatus of the operation
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|,
+     *         |WifiStatusCode.ERROR_UNKNOWN|
+     */
+    resetToFactoryMacAddress() generates (WifiStatus status);
+};
diff --git a/wifi/1.5/IWifiChip.hal b/wifi/1.5/IWifiChip.hal
index dcc9279..e9caa3d 100644
--- a/wifi/1.5/IWifiChip.hal
+++ b/wifi/1.5/IWifiChip.hal
@@ -17,6 +17,8 @@
 package android.hardware.wifi@1.5;
 
 import @1.0::WifiStatus;
+import @1.0::IfaceType;
+import @1.5::IWifiApIface;
 import @1.0::IWifiIface;
 import @1.3::IWifiChip;
 import @1.4::IWifiChip;
@@ -127,4 +129,85 @@
      *         |WifiStatusCode.ERROR_INVALID_ARGS|
      */
     setMultiStaUseCase(MultiStaUseCase useCase) generates (WifiStatus status);
+
+    /**
+     * Create bridged IWifiApIface.
+     *
+     * Depending on the mode the chip is configured in, the interface creation
+     * may fail (code: |ERROR_NOT_AVAILABLE|) if we've already reached the maximum
+     * allowed (specified in |ChipIfaceCombination|) number of ifaces of the AP
+     * type.
+     *
+     * @return status WifiStatus of the operation.
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_NOT_SUPPORTED|,
+     *         |WifiStatusCode.ERROR_NOT_AVAILABLE|
+     * @return iface HIDL interface object representing the iface if
+     *         successful, null otherwise.
+     */
+    createBridgedApIface() generates (WifiStatus status, IWifiApIface iface);
+
+    /**
+     * Removes one of the instance on the AP Iface with the provided ifaceName and
+     * ifaceInstanceName.
+     *
+     * Use the API: removeApIface with brIfaceName in the V1_0::WifiChip.hal to remove bridge Iface.
+     *
+     * @param brIfaceName Name of the bridged AP iface.
+     * @param ifaceInstanceName Name of the instance. The empty instance is
+     * invalid.
+     * @return status WifiStatus of the operation.
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_INVALID_ARGS|,
+     *         |WifiStatusCode.ERROR_NOT_AVAILABLE|
+     */
+    removeIfaceInstanceFromBridgedApIface(string brIfaceName, string ifaceInstanceName)
+        generates (WifiStatus status);
+
+    /**
+     * Representation of a Wi-Fi channel for Wi-Fi coex channel avoidance.
+     */
+    struct CoexUnsafeChannel {
+        /* The band of the channel */
+        WifiBand band;
+        /* The channel number */
+        uint32_t channel;
+        /** The power cap will be a maximum power value in dbm that is allowed to be transmitted by
+            the chip on this channel. A value of PowerCapConstant.NO_POWER_CAP means no limitation
+            on transmitted power is needed by the chip for this channel.
+        */
+        int32_t powerCapDbm;
+    };
+
+    enum PowerCapConstant : int32_t {
+        NO_POWER_CAP = 0x7FFFFFFF,
+    };
+
+    /**
+     * Invoked to indicate that the provided |CoexUnsafeChannels| should be avoided with the
+     * specified restrictions.
+     *
+     * Channel avoidance is a suggestion and should be done on a best-effort approach. If a provided
+     * channel is used, then the specified power cap should be applied.
+     *
+     * In addition, hard restrictions on the Wifi modes may be indicated by |IfaceType| bits
+     * (STA, AP, P2P, NAN, etc) in the |restrictions| bitfield. If a hard restriction is provided,
+     * then the channels should be completely avoided for the provided Wifi modes instead of by
+     * best-effort.
+     *
+     * @param unsafeChannels List of |CoexUnsafeChannels| to avoid.
+     * @param restrictions Bitset of |IfaceType| values indicating Wifi modes to completely avoid.
+     * @return status WifiStatus of the operation.
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_INVALID_ARGS|,
+     */
+    setCoexUnsafeChannels(
+        vec<CoexUnsafeChannel> unsafeChannels, bitfield<IfaceType> restrictions)
+            generates (WifiStatus status);
 };
diff --git a/wifi/1.5/default/hidl_struct_util.cpp b/wifi/1.5/default/hidl_struct_util.cpp
index 83d06fe..8e2e647 100644
--- a/wifi/1.5/default/hidl_struct_util.cpp
+++ b/wifi/1.5/default/hidl_struct_util.cpp
@@ -2800,6 +2800,47 @@
     }
     CHECK(false);
 }
+
+bool convertHidlCoexUnsafeChannelToLegacy(
+    const IWifiChip::CoexUnsafeChannel& hidl_unsafe_channel,
+    legacy_hal::wifi_coex_unsafe_channel* legacy_unsafe_channel) {
+    if (!legacy_unsafe_channel) {
+        return false;
+    }
+    *legacy_unsafe_channel = {};
+    switch (hidl_unsafe_channel.band) {
+        case WifiBand::BAND_24GHZ:
+            legacy_unsafe_channel->band = legacy_hal::WLAN_MAC_2_4_BAND;
+            break;
+        case WifiBand::BAND_5GHZ:
+            legacy_unsafe_channel->band = legacy_hal::WLAN_MAC_5_0_BAND;
+            break;
+        default:
+            return false;
+    };
+    legacy_unsafe_channel->channel = hidl_unsafe_channel.channel;
+    legacy_unsafe_channel->power_cap_dbm = hidl_unsafe_channel.powerCapDbm;
+    return true;
+}
+
+bool convertHidlVectorOfCoexUnsafeChannelToLegacy(
+    const std::vector<IWifiChip::CoexUnsafeChannel>& hidl_unsafe_channels,
+    std::vector<legacy_hal::wifi_coex_unsafe_channel>* legacy_unsafe_channels) {
+    if (!legacy_unsafe_channels) {
+        return false;
+    }
+    *legacy_unsafe_channels = {};
+    for (const auto& hidl_unsafe_channel : hidl_unsafe_channels) {
+        legacy_hal::wifi_coex_unsafe_channel legacy_unsafe_channel;
+        if (!hidl_struct_util::convertHidlCoexUnsafeChannelToLegacy(
+                hidl_unsafe_channel, &legacy_unsafe_channel)) {
+            return false;
+        }
+        legacy_unsafe_channels->push_back(legacy_unsafe_channel);
+    }
+    return true;
+}
+
 }  // namespace hidl_struct_util
 }  // namespace implementation
 }  // namespace V1_5
diff --git a/wifi/1.5/default/hidl_struct_util.h b/wifi/1.5/default/hidl_struct_util.h
index 49d8a12..feb47ef 100644
--- a/wifi/1.5/default/hidl_struct_util.h
+++ b/wifi/1.5/default/hidl_struct_util.h
@@ -71,6 +71,12 @@
     IfaceType hidl_interface_type);
 legacy_hal::wifi_multi_sta_use_case convertHidlMultiStaUseCaseToLegacy(
     IWifiChip::MultiStaUseCase use_case);
+bool convertHidlCoexUnsafeChannelToLegacy(
+    const IWifiChip::CoexUnsafeChannel& hidl_unsafe_channel,
+    legacy_hal::wifi_coex_unsafe_channel* legacy_unsafe_channel);
+bool convertHidlVectorOfCoexUnsafeChannelToLegacy(
+    const std::vector<IWifiChip::CoexUnsafeChannel>& hidl_unsafe_channels,
+    std::vector<legacy_hal::wifi_coex_unsafe_channel>* legacy_unsafe_channels);
 
 // STA iface conversion methods.
 bool convertLegacyFeaturesToHidlStaCapabilities(
diff --git a/wifi/1.5/default/service.cpp b/wifi/1.5/default/service.cpp
index 8539a37..23e2b47 100644
--- a/wifi/1.5/default/service.cpp
+++ b/wifi/1.5/default/service.cpp
@@ -17,6 +17,7 @@
 #include <android-base/logging.h>
 #include <hidl/HidlLazyUtils.h>
 #include <hidl/HidlTransportSupport.h>
+#include <signal.h>
 #include <utils/Looper.h>
 #include <utils/StrongPointer.h>
 
@@ -45,6 +46,7 @@
 #endif
 
 int main(int /*argc*/, char** argv) {
+    signal(SIGPIPE, SIG_IGN);
     android::base::InitLogging(
         argv, android::base::LogdLogger(android::base::SYSTEM));
     LOG(INFO) << "Wifi Hal is booting up...";
diff --git a/wifi/1.5/default/wifi_ap_iface.cpp b/wifi/1.5/default/wifi_ap_iface.cpp
index 04e382a..d98aa45 100644
--- a/wifi/1.5/default/wifi_ap_iface.cpp
+++ b/wifi/1.5/default/wifi_ap_iface.cpp
@@ -29,10 +29,11 @@
 using hidl_return_util::validateAndCall;
 
 WifiApIface::WifiApIface(
-    const std::string& ifname,
+    const std::string& ifname, const std::vector<std::string>& instances,
     const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
     const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util)
     : ifname_(ifname),
+      instances_(instances),
       legacy_hal_(legacy_hal),
       iface_util_(iface_util),
       is_valid_(true) {}
@@ -81,6 +82,14 @@
     getFactoryMacAddress_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
                            &WifiApIface::getFactoryMacAddressInternal,
+                           hidl_status_cb,
+                           instances_.size() > 0 ? instances_[0] : ifname_);
+}
+
+Return<void> WifiApIface::resetToFactoryMacAddress(
+    resetToFactoryMacAddress_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+                           &WifiApIface::resetToFactoryMacAddressInternal,
                            hidl_status_cb);
 }
 
@@ -94,8 +103,8 @@
 
 WifiStatus WifiApIface::setCountryCodeInternal(
     const std::array<int8_t, 2>& code) {
-    legacy_hal::wifi_error legacy_status =
-        legacy_hal_.lock()->setCountryCode(ifname_, code);
+    legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->setCountryCode(
+        instances_.size() > 0 ? instances_[0] : ifname_, code);
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
@@ -107,13 +116,30 @@
     std::vector<uint32_t> valid_frequencies;
     std::tie(legacy_status, valid_frequencies) =
         legacy_hal_.lock()->getValidFrequenciesForBand(
-            ifname_, hidl_struct_util::convertHidlWifiBandToLegacy(band));
+            instances_.size() > 0 ? instances_[0] : ifname_,
+            hidl_struct_util::convertHidlWifiBandToLegacy(band));
     return {createWifiStatusFromLegacyError(legacy_status), valid_frequencies};
 }
 
 WifiStatus WifiApIface::setMacAddressInternal(
     const std::array<uint8_t, 6>& mac) {
-    bool status = iface_util_.lock()->setMacAddress(ifname_, mac);
+    bool status;
+    // Support random MAC up to 2 interfaces
+    if (instances_.size() == 2) {
+        int rbyte = 1;
+        for (auto const& intf : instances_) {
+            std::array<uint8_t, 6> rmac = mac;
+            // reverse the bits to avoid clision
+            rmac[rbyte] = 0xff - rmac[rbyte];
+            status = iface_util_.lock()->setMacAddress(intf, rmac);
+            if (!status) {
+                LOG(INFO) << "Failed to set random mac address on " << intf;
+            }
+            rbyte++;
+        }
+    } else {
+        status = iface_util_.lock()->setMacAddress(ifname_, mac);
+    }
     if (!status) {
         return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
     }
@@ -121,15 +147,37 @@
 }
 
 std::pair<WifiStatus, std::array<uint8_t, 6>>
-WifiApIface::getFactoryMacAddressInternal() {
+WifiApIface::getFactoryMacAddressInternal(const std::string& ifaceName) {
     std::array<uint8_t, 6> mac =
-        iface_util_.lock()->getFactoryMacAddress(ifname_);
+        iface_util_.lock()->getFactoryMacAddress(ifaceName);
     if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 &&
         mac[4] == 0 && mac[5] == 0) {
         return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), mac};
     }
     return {createWifiStatus(WifiStatusCode::SUCCESS), mac};
 }
+
+WifiStatus WifiApIface::resetToFactoryMacAddressInternal() {
+    std::pair<WifiStatus, std::array<uint8_t, 6>> getMacResult;
+    if (instances_.size() == 2) {
+        for (auto const& intf : instances_) {
+            getMacResult = getFactoryMacAddressInternal(intf);
+            LOG(DEBUG) << "Reset MAC to factory MAC on " << intf;
+            if (getMacResult.first.code != WifiStatusCode::SUCCESS ||
+                !iface_util_.lock()->setMacAddress(intf, getMacResult.second)) {
+                return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+            }
+        }
+    } else {
+        getMacResult = getFactoryMacAddressInternal(ifname_);
+        LOG(DEBUG) << "Reset MAC to factory MAC on " << ifname_;
+        if (getMacResult.first.code != WifiStatusCode::SUCCESS ||
+            !iface_util_.lock()->setMacAddress(ifname_, getMacResult.second)) {
+            return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+        }
+    }
+    return createWifiStatus(WifiStatusCode::SUCCESS);
+}
 }  // namespace implementation
 }  // namespace V1_5
 }  // namespace wifi
diff --git a/wifi/1.5/default/wifi_ap_iface.h b/wifi/1.5/default/wifi_ap_iface.h
index 48b444a..02fb2d8 100644
--- a/wifi/1.5/default/wifi_ap_iface.h
+++ b/wifi/1.5/default/wifi_ap_iface.h
@@ -18,7 +18,7 @@
 #define WIFI_AP_IFACE_H_
 
 #include <android-base/macros.h>
-#include <android/hardware/wifi/1.4/IWifiApIface.h>
+#include <android/hardware/wifi/1.5/IWifiApIface.h>
 
 #include "wifi_iface_util.h"
 #include "wifi_legacy_hal.h"
@@ -33,9 +33,10 @@
 /**
  * HIDL interface object used to control a AP Iface instance.
  */
-class WifiApIface : public V1_4::IWifiApIface {
+class WifiApIface : public V1_5::IWifiApIface {
    public:
     WifiApIface(const std::string& ifname,
+                const std::vector<std::string>& instances,
                 const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
                 const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util);
     // Refer to |WifiChip::invalidate()|.
@@ -55,6 +56,8 @@
                                setMacAddress_cb hidl_status_cb) override;
     Return<void> getFactoryMacAddress(
         getFactoryMacAddress_cb hidl_status_cb) override;
+    Return<void> resetToFactoryMacAddress(
+        resetToFactoryMacAddress_cb hidl_status_cb) override;
 
    private:
     // Corresponding worker functions for the HIDL methods.
@@ -64,10 +67,12 @@
     std::pair<WifiStatus, std::vector<WifiChannelInMhz>>
     getValidFrequenciesForBandInternal(V1_0::WifiBand band);
     WifiStatus setMacAddressInternal(const std::array<uint8_t, 6>& mac);
-    std::pair<WifiStatus, std::array<uint8_t, 6>>
-    getFactoryMacAddressInternal();
+    std::pair<WifiStatus, std::array<uint8_t, 6>> getFactoryMacAddressInternal(
+        const std::string& ifaceName);
+    WifiStatus resetToFactoryMacAddressInternal();
 
     std::string ifname_;
+    std::vector<std::string> instances_;
     std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
     std::weak_ptr<iface_util::WifiIfaceUtil> iface_util_;
     bool is_valid_;
diff --git a/wifi/1.5/default/wifi_chip.cpp b/wifi/1.5/default/wifi_chip.cpp
index 1c238c8..1b0353b 100644
--- a/wifi/1.5/default/wifi_chip.cpp
+++ b/wifi/1.5/default/wifi_chip.cpp
@@ -19,6 +19,7 @@
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <cutils/properties.h>
+#include <net/if.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 
@@ -44,6 +45,7 @@
 constexpr char kActiveWlanIfaceNameProperty[] = "wifi.active.interface";
 constexpr char kNoActiveWlanIfaceNamePropertyValue[] = "";
 constexpr unsigned kMaxWlanIfaces = 5;
+constexpr char kApBridgeIfacePrefix[] = "ap_br_";
 
 template <typename Iface>
 void invalidateAndClear(std::vector<sp<Iface>>& ifaces, sp<Iface> iface) {
@@ -101,24 +103,36 @@
     return "wlan" + std::to_string(idx);
 }
 
-// Returns the dedicated iface name if one is defined.
-std::string getApIfaceName() {
+// Returns the dedicated iface name if defined.
+// Returns two ifaces in bridged mode.
+std::vector<std::string> getPredefinedApIfaceNames(bool is_bridged) {
+    std::vector<std::string> ifnames;
     std::array<char, PROPERTY_VALUE_MAX> buffer;
+    buffer.fill(0);
     if (property_get("ro.vendor.wifi.sap.interface", buffer.data(), nullptr) ==
         0) {
-        return {};
+        return ifnames;
     }
-    return buffer.data();
+    ifnames.push_back(buffer.data());
+    if (is_bridged) {
+        buffer.fill(0);
+        if (property_get("ro.vendor.wifi.sap.concurrent.interface",
+                         buffer.data(), nullptr) == 0) {
+            return ifnames;
+        }
+        ifnames.push_back(buffer.data());
+    }
+    return ifnames;
 }
 
-std::string getP2pIfaceName() {
+std::string getPredefinedP2pIfaceName() {
     std::array<char, PROPERTY_VALUE_MAX> buffer;
     property_get("wifi.direct.interface", buffer.data(), "p2p0");
     return buffer.data();
 }
 
 // Returns the dedicated iface name if one is defined.
-std::string getNanIfaceName() {
+std::string getPredefinedNanIfaceName() {
     std::array<char, PROPERTY_VALUE_MAX> buffer;
     if (property_get("wifi.aware.interface", buffer.data(), nullptr) == 0) {
         return {};
@@ -434,6 +448,13 @@
                            &WifiChip::createApIfaceInternal, hidl_status_cb);
 }
 
+Return<void> WifiChip::createBridgedApIface(
+    createBridgedApIface_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+                           &WifiChip::createBridgedApIfaceInternal,
+                           hidl_status_cb);
+}
+
 Return<void> WifiChip::getApIfaceNames(getApIfaceNames_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
                            &WifiChip::getApIfaceNamesInternal, hidl_status_cb);
@@ -453,6 +474,15 @@
                            ifname);
 }
 
+Return<void> WifiChip::removeIfaceInstanceFromBridgedApIface(
+    const hidl_string& ifname, const hidl_string& ifInstanceName,
+    removeIfaceInstanceFromBridgedApIface_cb hidl_status_cb) {
+    return validateAndCall(
+        this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+        &WifiChip::removeIfaceInstanceFromBridgedApIfaceInternal,
+        hidl_status_cb, ifname, ifInstanceName);
+}
+
 Return<void> WifiChip::createNanIface(createNanIface_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
                            &WifiChip::createNanIfaceInternal, hidl_status_cb);
@@ -692,7 +722,17 @@
                            hidl_status_cb, use_case);
 }
 
+Return<void> WifiChip::setCoexUnsafeChannels(
+    const hidl_vec<CoexUnsafeChannel>& unsafeChannels,
+    hidl_bitfield<IfaceType> restrictions,
+    setCoexUnsafeChannels_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+                           &WifiChip::setCoexUnsafeChannelsInternal,
+                           hidl_status_cb, unsafeChannels, restrictions);
+}
+
 void WifiChip::invalidateAndRemoveAllIfaces() {
+    invalidateAndClearBridgedApAll();
     invalidateAndClearAll(ap_ifaces_);
     invalidateAndClearAll(nan_ifaces_);
     invalidateAndClearAll(p2p_ifaces_);
@@ -861,21 +901,28 @@
     return {createWifiStatus(WifiStatusCode::SUCCESS), firmware_dump};
 }
 
-std::pair<WifiStatus, sp<IWifiApIface>> WifiChip::createApIfaceInternal() {
-    if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::AP)) {
-        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
-    }
-    std::string ifname = allocateApIfaceName();
-    legacy_hal::wifi_error legacy_status =
-        legacy_hal_.lock()->createVirtualInterface(
-            ifname,
-            hidl_struct_util::convertHidlIfaceTypeToLegacy(IfaceType::AP));
+WifiStatus WifiChip::createVirtualApInterface(const std::string& apVirtIf) {
+    legacy_hal::wifi_error legacy_status;
+    legacy_status = legacy_hal_.lock()->createVirtualInterface(
+        apVirtIf,
+        hidl_struct_util::convertHidlIfaceTypeToLegacy(IfaceType::AP));
     if (legacy_status != legacy_hal::WIFI_SUCCESS) {
-        LOG(ERROR) << "Failed to add interface: " << ifname << " "
+        LOG(ERROR) << "Failed to add interface: " << apVirtIf << " "
                    << legacyErrorToString(legacy_status);
-        return {createWifiStatusFromLegacyError(legacy_status), {}};
+        return createWifiStatusFromLegacyError(legacy_status);
     }
-    sp<WifiApIface> iface = new WifiApIface(ifname, legacy_hal_, iface_util_);
+    return createWifiStatus(WifiStatusCode::SUCCESS);
+}
+
+sp<WifiApIface> WifiChip::newWifiApIface(std::string& ifname) {
+    std::vector<std::string> ap_instances;
+    for (auto const& it : br_ifaces_ap_instances_) {
+        if (it.first == ifname) {
+            ap_instances = it.second;
+        }
+    }
+    sp<WifiApIface> iface =
+        new WifiApIface(ifname, ap_instances, legacy_hal_, iface_util_);
     ap_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceAdded(IfaceType::AP, ifname).isOk()) {
@@ -883,6 +930,61 @@
         }
     }
     setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
+    return iface;
+}
+
+std::pair<WifiStatus, sp<V1_5::IWifiApIface>>
+WifiChip::createApIfaceInternal() {
+    if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::AP)) {
+        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+    }
+    std::string ifname = allocateApIfaceName();
+    WifiStatus status = createVirtualApInterface(ifname);
+    if (status.code != WifiStatusCode::SUCCESS) {
+        return {status, {}};
+    }
+    sp<WifiApIface> iface = newWifiApIface(ifname);
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
+}
+
+std::pair<WifiStatus, sp<V1_5::IWifiApIface>>
+WifiChip::createBridgedApIfaceInternal() {
+    if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::AP)) {
+        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+    }
+    std::vector<std::string> ap_instances = allocateBridgedApInstanceNames();
+    if (ap_instances.size() < 2) {
+        LOG(ERROR) << "Fail to allocate two instances";
+        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+    }
+    std::string br_ifname = kApBridgeIfacePrefix + ap_instances[0];
+    for (int i = 0; i < 2; i++) {
+        WifiStatus status = createVirtualApInterface(ap_instances[i]);
+        if (status.code != WifiStatusCode::SUCCESS) {
+            if (i != 0) {  // The failure happened when creating second virtual
+                           // iface.
+                legacy_hal_.lock()->deleteVirtualInterface(
+                    ap_instances.front());  // Remove the first virtual iface.
+            }
+            return {status, {}};
+        }
+    }
+    br_ifaces_ap_instances_[br_ifname] = ap_instances;
+    if (!iface_util_.lock()->createBridge(br_ifname)) {
+        LOG(ERROR) << "Failed createBridge - br_name=" << br_ifname.c_str();
+        invalidateAndClearBridgedAp(br_ifname);
+        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+    }
+    for (auto const& instance : ap_instances) {
+        // Bind ap instance interface to AP bridge
+        if (!iface_util_.lock()->addIfaceToBridge(br_ifname, instance)) {
+            LOG(ERROR) << "Failed add if to Bridge - if_name="
+                       << instance.c_str();
+            invalidateAndClearBridgedAp(br_ifname);
+            return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+        }
+    }
+    sp<WifiApIface> iface = newWifiApIface(br_ifname);
     return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
@@ -894,7 +996,7 @@
     return {createWifiStatus(WifiStatusCode::SUCCESS), getNames(ap_ifaces_)};
 }
 
-std::pair<WifiStatus, sp<IWifiApIface>> WifiChip::getApIfaceInternal(
+std::pair<WifiStatus, sp<V1_5::IWifiApIface>> WifiChip::getApIfaceInternal(
     const std::string& ifname) {
     const auto iface = findUsingName(ap_ifaces_, ifname);
     if (!iface.get()) {
@@ -913,12 +1015,8 @@
     // nan/rtt objects over AP iface. But, there is no harm to do it
     // here and not make that assumption all over the place.
     invalidateAndRemoveDependencies(ifname);
-    legacy_hal::wifi_error legacy_status =
-        legacy_hal_.lock()->deleteVirtualInterface(ifname);
-    if (legacy_status != legacy_hal::WIFI_SUCCESS) {
-        LOG(ERROR) << "Failed to remove interface: " << ifname << " "
-                   << legacyErrorToString(legacy_status);
-    }
+    // Clear the bridge interface and the iface instance.
+    invalidateAndClearBridgedAp(ifname);
     invalidateAndClear(ap_ifaces_, iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceRemoved(IfaceType::AP, ifname).isOk()) {
@@ -929,13 +1027,49 @@
     return createWifiStatus(WifiStatusCode::SUCCESS);
 }
 
+WifiStatus WifiChip::removeIfaceInstanceFromBridgedApIfaceInternal(
+    const std::string& ifname, const std::string& ifInstanceName) {
+    legacy_hal::wifi_error legacy_status;
+    const auto iface = findUsingName(ap_ifaces_, ifname);
+    if (!iface.get() || !ifInstanceName.empty()) {
+        return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+    }
+    // Requires to remove one of the instance in bridge mode
+    for (auto const& it : br_ifaces_ap_instances_) {
+        if (it.first == ifname) {
+            for (auto const& iface : it.second) {
+                if (iface == ifInstanceName) {
+                    if (!iface_util_.lock()->removeIfaceFromBridge(it.first,
+                                                                   iface)) {
+                        LOG(ERROR) << "Failed to remove interface: " << iface
+                                   << " from " << ifname << ", error: "
+                                   << legacyErrorToString(legacy_status);
+                        return createWifiStatus(
+                            WifiStatusCode::ERROR_NOT_AVAILABLE);
+                    }
+                    legacy_status =
+                        legacy_hal_.lock()->deleteVirtualInterface(iface);
+                    if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+                        LOG(ERROR) << "Failed to del interface: " << iface
+                                   << " " << legacyErrorToString(legacy_status);
+                        return createWifiStatusFromLegacyError(legacy_status);
+                    }
+                }
+            }
+            break;
+        }
+    }
+    br_ifaces_ap_instances_.erase(ifInstanceName);
+    return createWifiStatus(WifiStatusCode::SUCCESS);
+}
+
 std::pair<WifiStatus, sp<V1_4::IWifiNanIface>>
 WifiChip::createNanIfaceInternal() {
     if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::NAN)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
     bool is_dedicated_iface = true;
-    std::string ifname = getNanIfaceName();
+    std::string ifname = getPredefinedNanIfaceName();
     if (ifname.empty() || !iface_util_.lock()->ifNameToIndex(ifname)) {
         // Use the first shared STA iface (wlan0) if a dedicated aware iface is
         // not defined.
@@ -988,7 +1122,7 @@
     if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::P2P)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
-    std::string ifname = getP2pIfaceName();
+    std::string ifname = getPredefinedP2pIfaceName();
     sp<WifiP2pIface> iface = new WifiP2pIface(ifname, legacy_hal_);
     p2p_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
@@ -1322,6 +1456,18 @@
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
+WifiStatus WifiChip::setCoexUnsafeChannelsInternal(
+    std::vector<CoexUnsafeChannel> unsafe_channels, uint32_t restrictions) {
+    std::vector<legacy_hal::wifi_coex_unsafe_channel> legacy_unsafe_channels;
+    if (!hidl_struct_util::convertHidlVectorOfCoexUnsafeChannelToLegacy(
+            unsafe_channels, &legacy_unsafe_channels)) {
+        return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+    }
+    auto legacy_status = legacy_hal_.lock()->setCoexUnsafeChannels(
+        legacy_unsafe_channels, restrictions);
+    return createWifiStatusFromLegacyError(legacy_status);
+}
+
 WifiStatus WifiChip::handleChipConfiguration(
     /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,
     ChipModeId mode_id) {
@@ -1607,7 +1753,7 @@
 //    ChipIfaceCombination.
 // b) Check if the requested iface type can be added to the current mode.
 bool WifiChip::canCurrentModeSupportIfaceOfType(IfaceType requested_type) {
-    // Check if we can support atleast 1 iface of type.
+    // Check if we can support at least 1 iface of type.
     std::map<IfaceType, size_t> req_iface_combo;
     req_iface_combo[requested_type] = 1;
     return canCurrentModeSupportIfaceCombo(req_iface_combo);
@@ -1623,17 +1769,17 @@
 }
 
 bool WifiChip::isStaApConcurrencyAllowedInCurrentMode() {
-    // Check if we can support atleast 1 STA & 1 AP concurrently.
+    // Check if we can support at least 1 STA & 1 AP concurrently.
     std::map<IfaceType, size_t> req_iface_combo;
     req_iface_combo[IfaceType::AP] = 1;
     req_iface_combo[IfaceType::STA] = 1;
     return canCurrentModeSupportIfaceCombo(req_iface_combo);
 }
 
-bool WifiChip::isDualApAllowedInCurrentMode() {
-    // Check if we can support atleast 1 STA & 1 AP concurrently.
+bool WifiChip::isDualStaConcurrencyAllowedInCurrentMode() {
+    // Check if we can support at least 2 STA concurrently.
     std::map<IfaceType, size_t> req_iface_combo;
-    req_iface_combo[IfaceType::AP] = 2;
+    req_iface_combo[IfaceType::STA] = 2;
     return canCurrentModeSupportIfaceCombo(req_iface_combo);
 }
 
@@ -1653,6 +1799,7 @@
                                                uint32_t start_idx) {
     for (unsigned idx = start_idx; idx < kMaxWlanIfaces; idx++) {
         const auto ifname = getWlanIfaceNameWithType(type, idx);
+        if (findUsingNameFromBridgedApInstances(ifname)) continue;
         if (findUsingName(ap_ifaces_, ifname)) continue;
         if (findUsingName(sta_ifaces_, ifname)) continue;
         return ifname;
@@ -1662,19 +1809,46 @@
     return {};
 }
 
+uint32_t WifiChip::startIdxOfApIface() {
+    if (isDualStaConcurrencyAllowedInCurrentMode()) {
+        // When the HAL support dual STAs, AP should start with idx 2.
+        return 2;
+    } else if (isStaApConcurrencyAllowedInCurrentMode()) {
+        //  When the HAL support STA + AP but it doesn't support dual STAs.
+        //  AP should start with idx 1.
+        return 1;
+    }
+    // No concurrency support.
+    return 0;
+}
+
 // AP iface names start with idx 1 for modes supporting
 // concurrent STA and not dual AP, else start with idx 0.
 std::string WifiChip::allocateApIfaceName() {
     // Check if we have a dedicated iface for AP.
-    std::string ifname = getApIfaceName();
-    if (!ifname.empty()) {
-        return ifname;
+    std::vector<std::string> ifnames = getPredefinedApIfaceNames(false);
+    if (!ifnames.empty()) {
+        return ifnames[0];
     }
-    return allocateApOrStaIfaceName(IfaceType::AP,
-                                    (isStaApConcurrencyAllowedInCurrentMode() &&
-                                     !isDualApAllowedInCurrentMode())
-                                        ? 1
-                                        : 0);
+    return allocateApOrStaIfaceName(IfaceType::AP, startIdxOfApIface());
+}
+
+std::vector<std::string> WifiChip::allocateBridgedApInstanceNames() {
+    // Check if we have a dedicated iface for AP.
+    std::vector<std::string> instances = getPredefinedApIfaceNames(true);
+    if (instances.size() == 2) {
+        return instances;
+    } else {
+        int num_ifaces_need_to_allocate = 2 - instances.size();
+        for (int i = 0; i < num_ifaces_need_to_allocate; i++) {
+            std::string instance_name =
+                allocateApOrStaIfaceName(IfaceType::AP, startIdxOfApIface());
+            if (!instance_name.empty()) {
+                instances.push_back(instance_name);
+            }
+        }
+    }
+    return instances;
 }
 
 // STA iface names start with idx 0.
@@ -1727,6 +1901,48 @@
     return getWlanIfaceName(idx);
 }
 
+void WifiChip::invalidateAndClearBridgedApAll() {
+    for (auto const& it : br_ifaces_ap_instances_) {
+        for (auto const& iface : it.second) {
+            iface_util_.lock()->removeIfaceFromBridge(it.first, iface);
+            legacy_hal_.lock()->deleteVirtualInterface(iface);
+        }
+        iface_util_.lock()->deleteBridge(it.first);
+    }
+    br_ifaces_ap_instances_.clear();
+}
+
+void WifiChip::invalidateAndClearBridgedAp(const std::string& br_name) {
+    if (br_name.empty()) return;
+    // delete managed interfaces
+    for (auto const& it : br_ifaces_ap_instances_) {
+        if (it.first == br_name) {
+            for (auto const& iface : it.second) {
+                iface_util_.lock()->removeIfaceFromBridge(br_name, iface);
+                legacy_hal_.lock()->deleteVirtualInterface(iface);
+            }
+            iface_util_.lock()->deleteBridge(br_name);
+            br_ifaces_ap_instances_.erase(br_name);
+            break;
+        }
+    }
+    return;
+}
+
+bool WifiChip::findUsingNameFromBridgedApInstances(const std::string& name) {
+    for (auto const& it : br_ifaces_ap_instances_) {
+        if (it.first == name) {
+            return true;
+        }
+        for (auto const& iface : it.second) {
+            if (iface == name) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 }  // namespace implementation
 }  // namespace V1_5
 }  // namespace wifi
diff --git a/wifi/1.5/default/wifi_chip.h b/wifi/1.5/default/wifi_chip.h
index 693d480..95c122d 100644
--- a/wifi/1.5/default/wifi_chip.h
+++ b/wifi/1.5/default/wifi_chip.h
@@ -94,11 +94,16 @@
     Return<void> requestFirmwareDebugDump(
         requestFirmwareDebugDump_cb hidl_status_cb) override;
     Return<void> createApIface(createApIface_cb hidl_status_cb) override;
+    Return<void> createBridgedApIface(
+        createBridgedApIface_cb hidl_status_cb) override;
     Return<void> getApIfaceNames(getApIfaceNames_cb hidl_status_cb) override;
     Return<void> getApIface(const hidl_string& ifname,
                             getApIface_cb hidl_status_cb) override;
     Return<void> removeApIface(const hidl_string& ifname,
                                removeApIface_cb hidl_status_cb) override;
+    Return<void> removeIfaceInstanceFromBridgedApIface(
+        const hidl_string& brIfaceName, const hidl_string& ifaceInstanceName,
+        removeIfaceInstanceFromBridgedApIface_cb hidl_status_cb) override;
     Return<void> createNanIface(createNanIface_cb hidl_status_cb) override;
     Return<void> getNanIfaceNames(getNanIfaceNames_cb hidl_status_cb) override;
     Return<void> getNanIface(const hidl_string& ifname,
@@ -169,6 +174,10 @@
     Return<void> setMultiStaUseCase(
         MultiStaUseCase use_case,
         setMultiStaUseCase_cb hidl_status_cb) override;
+    Return<void> setCoexUnsafeChannels(
+        const hidl_vec<CoexUnsafeChannel>& unsafe_channels,
+        hidl_bitfield<IfaceType> restrictions,
+        setCoexUnsafeChannels_cb hidl_status_cb) override;
 
    private:
     void invalidateAndRemoveAllIfaces();
@@ -192,11 +201,17 @@
     requestDriverDebugDumpInternal();
     std::pair<WifiStatus, std::vector<uint8_t>>
     requestFirmwareDebugDumpInternal();
-    std::pair<WifiStatus, sp<IWifiApIface>> createApIfaceInternal();
+    sp<WifiApIface> newWifiApIface(std::string& ifname);
+    WifiStatus createVirtualApInterface(const std::string& apVirtIf);
+    std::pair<WifiStatus, sp<V1_5::IWifiApIface>> createApIfaceInternal();
+    std::pair<WifiStatus, sp<V1_5::IWifiApIface>>
+    createBridgedApIfaceInternal();
     std::pair<WifiStatus, std::vector<hidl_string>> getApIfaceNamesInternal();
-    std::pair<WifiStatus, sp<IWifiApIface>> getApIfaceInternal(
+    std::pair<WifiStatus, sp<V1_5::IWifiApIface>> getApIfaceInternal(
         const std::string& ifname);
     WifiStatus removeApIfaceInternal(const std::string& ifname);
+    WifiStatus removeIfaceInstanceFromBridgedApIfaceInternal(
+        const std::string& brIfaceName, const std::string& ifInstanceName);
     std::pair<WifiStatus, sp<V1_4::IWifiNanIface>> createNanIfaceInternal();
     std::pair<WifiStatus, std::vector<hidl_string>> getNanIfaceNamesInternal();
     std::pair<WifiStatus, sp<V1_4::IWifiNanIface>> getNanIfaceInternal(
@@ -241,6 +256,8 @@
         const sp<V1_4::IWifiChipEventCallback>& event_callback);
     WifiStatus setMultiStaPrimaryConnectionInternal(const std::string& ifname);
     WifiStatus setMultiStaUseCaseInternal(MultiStaUseCase use_case);
+    WifiStatus setCoexUnsafeChannelsInternal(
+        std::vector<CoexUnsafeChannel> unsafe_channels, uint32_t restrictions);
 
     WifiStatus handleChipConfiguration(
         std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id);
@@ -265,13 +282,18 @@
     bool canCurrentModeSupportIfaceOfType(IfaceType requested_type);
     bool isValidModeId(ChipModeId mode_id);
     bool isStaApConcurrencyAllowedInCurrentMode();
-    bool isDualApAllowedInCurrentMode();
+    bool isDualStaConcurrencyAllowedInCurrentMode();
+    uint32_t startIdxOfApIface();
     std::string getFirstActiveWlanIfaceName();
     std::string allocateApOrStaIfaceName(IfaceType type, uint32_t start_idx);
     std::string allocateApIfaceName();
+    std::vector<std::string> allocateBridgedApInstanceNames();
     std::string allocateStaIfaceName();
     bool writeRingbufferFilesInternal();
     std::string getWlanIfaceNameWithType(IfaceType type, unsigned idx);
+    void invalidateAndClearBridgedApAll();
+    void invalidateAndClearBridgedAp(const std::string& br_name);
+    bool findUsingNameFromBridgedApInstances(const std::string& name);
 
     ChipId chip_id_;
     std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
@@ -296,7 +318,7 @@
         event_cb_handler_;
 
     const std::function<void(const std::string&)> subsystemCallbackHandler_;
-
+    std::map<std::string, std::vector<std::string>> br_ifaces_ap_instances_;
     DISALLOW_COPY_AND_ASSIGN(WifiChip);
 };
 
diff --git a/wifi/1.5/default/wifi_legacy_hal.cpp b/wifi/1.5/default/wifi_legacy_hal.cpp
index 76e718b..773dd9b 100644
--- a/wifi/1.5/default/wifi_legacy_hal.cpp
+++ b/wifi/1.5/default/wifi_legacy_hal.cpp
@@ -1521,6 +1521,14 @@
                                                           use_case);
 }
 
+wifi_error WifiLegacyHal::setCoexUnsafeChannels(
+    std::vector<wifi_coex_unsafe_channel> unsafe_channels,
+    uint32_t restrictions) {
+    return global_func_table_.wifi_set_coex_unsafe_channels(
+        global_handle_, unsafe_channels.size(), unsafe_channels.data(),
+        restrictions);
+}
+
 void WifiLegacyHal::invalidate() {
     global_handle_ = nullptr;
     iface_name_to_handle_.clear();
diff --git a/wifi/1.5/default/wifi_legacy_hal.h b/wifi/1.5/default/wifi_legacy_hal.h
index 555c540..6266cf6 100644
--- a/wifi/1.5/default/wifi_legacy_hal.h
+++ b/wifi/1.5/default/wifi_legacy_hal.h
@@ -386,6 +386,11 @@
     virtual wifi_error multiStaSetPrimaryConnection(const std::string& ifname);
     virtual wifi_error multiStaSetUseCase(wifi_multi_sta_use_case use_case);
 
+    // Coex functions.
+    virtual wifi_error setCoexUnsafeChannels(
+        std::vector<wifi_coex_unsafe_channel> unsafe_channels,
+        uint32_t restrictions);
+
    private:
     // Retrieve interface handles for all the available interfaces.
     wifi_error retrieveIfaceHandles();
diff --git a/wifi/1.5/default/wifi_legacy_hal_stubs.cpp b/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
index 71d2ddf..b6c908b 100644
--- a/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
@@ -149,6 +149,7 @@
     populateStubFor(&hal_fn->wifi_get_chip_feature_set);
     populateStubFor(&hal_fn->wifi_multi_sta_set_primary_connection);
     populateStubFor(&hal_fn->wifi_multi_sta_set_use_case);
+    populateStubFor(&hal_fn->wifi_set_coex_unsafe_channels);
 
     return true;
 }
diff --git a/wifi/1.5/default/wifi_sta_iface.cpp b/wifi/1.5/default/wifi_sta_iface.cpp
index f3dcfc5..82bfcf1 100644
--- a/wifi/1.5/default/wifi_sta_iface.cpp
+++ b/wifi/1.5/default/wifi_sta_iface.cpp
@@ -648,6 +648,10 @@
 WifiStaIface::getFactoryMacAddressInternal() {
     std::array<uint8_t, 6> mac =
         iface_util_.lock()->getFactoryMacAddress(ifname_);
+    if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 &&
+        mac[4] == 0 && mac[5] == 0) {
+        return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), mac};
+    }
     return {createWifiStatus(WifiStatusCode::SUCCESS), mac};
 }
 
diff --git a/wifi/1.5/vts/functional/Android.bp b/wifi/1.5/vts/functional/Android.bp
index eb595c9..2d8b412 100644
--- a/wifi/1.5/vts/functional/Android.bp
+++ b/wifi/1.5/vts/functional/Android.bp
@@ -60,3 +60,26 @@
         "vts",
     ],
 }
+
+// SoftAP-specific tests, similar to VtsHalWifiApV1_0TargetTest.
+cc_test {
+    name: "VtsHalWifiApV1_5TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "wifi_chip_hidl_ap_test.cpp",
+    ],
+    static_libs: [
+        "VtsHalWifiV1_0TargetTestUtil",
+        "android.hardware.wifi@1.0",
+        "android.hardware.wifi@1.1",
+        "android.hardware.wifi@1.2",
+        "android.hardware.wifi@1.3",
+        "android.hardware.wifi@1.4",
+        "android.hardware.wifi@1.5",
+        "libwifi-system-iface",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/wifi/1.5/vts/functional/wifi_chip_hidl_ap_test.cpp b/wifi/1.5/vts/functional/wifi_chip_hidl_ap_test.cpp
new file mode 100644
index 0000000..395d317
--- /dev/null
+++ b/wifi/1.5/vts/functional/wifi_chip_hidl_ap_test.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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 <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/logging.h>
+
+#undef NAN  // NAN is defined in bionic/libc/include/math.h:38
+
+#include <android/hardware/wifi/1.4/IWifiChipEventCallback.h>
+#include <android/hardware/wifi/1.5/IWifi.h>
+#include <android/hardware/wifi/1.5/IWifiApIface.h>
+#include <android/hardware/wifi/1.5/IWifiChip.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include "wifi_hidl_call_util.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::wifi::V1_0::ChipModeId;
+using ::android::hardware::wifi::V1_0::IfaceType;
+using ::android::hardware::wifi::V1_0::IWifiIface;
+using ::android::hardware::wifi::V1_0::WifiDebugRingBufferStatus;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_4::IWifiChipEventCallback;
+using ::android::hardware::wifi::V1_5::IWifiApIface;
+using ::android::hardware::wifi::V1_5::IWifiChip;
+
+/**
+ * Fixture for IWifiChip tests that are conditioned on SoftAP support.
+ */
+class WifiChipHidlTest : public ::testing::TestWithParam<std::string> {
+   public:
+    virtual void SetUp() override {
+        // Make sure to start with a clean state
+        stopWifi(GetInstanceName());
+
+        wifi_chip_ = IWifiChip::castFrom(getWifiChip(GetInstanceName()));
+        ASSERT_NE(nullptr, wifi_chip_.get());
+    }
+
+    virtual void TearDown() override { stopWifi(GetInstanceName()); }
+
+   protected:
+    // Helper function to configure the Chip in one of the supported modes.
+    // Most of the non-mode-configuration-related methods require chip
+    // to be first configured.
+    ChipModeId configureChipForIfaceType(IfaceType type, bool expectSuccess) {
+        ChipModeId mode_id;
+        EXPECT_EQ(expectSuccess,
+                  configureChipToSupportIfaceType(wifi_chip_, type, &mode_id));
+        return mode_id;
+    }
+
+    WifiStatusCode createApIface(sp<IWifiApIface>* ap_iface) {
+        configureChipForIfaceType(IfaceType::AP, true);
+        const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createApIface);
+        *ap_iface = IWifiApIface::castFrom(status_and_iface.second);
+        return status_and_iface.first.code;
+    }
+
+    WifiStatusCode createBridgedApIface(sp<IWifiApIface>* ap_iface) {
+        configureChipForIfaceType(IfaceType::AP, true);
+        const auto& status_and_iface =
+            HIDL_INVOKE(wifi_chip_, createBridgedApIface);
+        *ap_iface = status_and_iface.second;
+        return status_and_iface.first.code;
+    }
+
+    sp<IWifiChip> wifi_chip_;
+
+   private:
+    std::string GetInstanceName() { return GetParam(); }
+};
+
+// TODO: b/173999527. Add test for bridged API.
+
+/*
+ * resetToFactoryMacAddress
+ */
+TEST_P(WifiChipHidlTest, resetToFactoryMacAddressTest) {
+    sp<IWifiApIface> wifi_ap_iface;
+    const auto& status_code = createApIface(&wifi_ap_iface);
+    if (status_code != WifiStatusCode::SUCCESS) {
+        EXPECT_EQ(WifiStatusCode::ERROR_NOT_SUPPORTED, status_code);
+    }
+    const auto& status = HIDL_INVOKE(wifi_ap_iface, resetToFactoryMacAddress);
+    EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiChipHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+    PerInstance, WifiChipHidlTest,
+    testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+        ::android::hardware::wifi::V1_5::IWifi::descriptor)),
+    android::hardware::PrintInstanceNameToString);
diff --git a/wifi/supplicant/1.4/ISupplicantStaIface.hal b/wifi/supplicant/1.4/ISupplicantStaIface.hal
index f9e4c9b..7441ba5 100644
--- a/wifi/supplicant/1.4/ISupplicantStaIface.hal
+++ b/wifi/supplicant/1.4/ISupplicantStaIface.hal
@@ -71,8 +71,7 @@
      *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
      *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|
      */
-    initiateVenueUrlAnqpQuery(MacAddress macAddress)
-        generates (SupplicantStatus status);
+    initiateVenueUrlAnqpQuery(MacAddress macAddress) generates (SupplicantStatus status);
 
     /**
      * Get wpa driver capabilities.
@@ -84,4 +83,55 @@
      */
     getWpaDriverCapabilities_1_4() generates (SupplicantStatus status,
         bitfield<WpaDriverCapabilitiesMask> driverCapabilitiesMask);
+
+    /**
+     * Generates DPP bootstrap information: Bootstrap ID, DPP URI and listen
+     * channel for responder mode.
+     *
+     * @param MacAddress MAC address of the interface for the DPP operation.
+     * @param deviceInfo Device specific information.
+     *        As per DPP Specification V1.0 section 5.2,
+     *        allowed Range of ASCII characters in deviceInfo - %x20-7E
+     *        semicolon is not allowed.
+     * @param DppCurve Elliptic curve cryptography type used to generate DPP
+     * public/private key pair.
+     * @return status of operation and bootstrap info.
+     *         Possible status codes:
+     *         |SupplicantStatusCode.SUCCESS|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|,
+     *         |SupplicantStatusCode.FAILURE_UNKNOWN|
+     *         |SupplicantStatusCode.FAILURE_UNSUPPORTED|
+     */
+    generateDppBootstrapInfoForResponder(MacAddress macAddress, string deviceInfo, DppCurve curve)
+        generates (SupplicantStatus status, DppResponderBootstrapInfo bootstrapInfo);
+
+    /**
+     * Start DPP in Enrollee-Responder mode.
+     * Framework must first call |generateDppBootstrapInfoForResponder| to generate
+     * the bootstrapping information: Bootstrap ID, DPP URI and the listen channel.
+     * Then call this API with derived listen channel to start listening for
+     * authentication request from Peer initiator.
+     *
+     * @param listenChannel DPP listen channel.
+     * @return status Status of the operation.
+     *         Possible status codes:
+     *         |SupplicantStatusCode.SUCCESS|,
+     *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|
+     *         |SupplicantStatusCode.FAILURE_UNSUPPORTED|
+     */
+    startDppEnrolleeResponder(uint32_t listenChannel) generates (SupplicantStatus status);
+
+    /**
+     * Stop DPP Responder operation - Remove the bootstrap code and stop listening.
+     *
+     * @param ownBootstrapId Local device's URI ID obtained through
+     *        |generateDppBootstrapInfoForResponder| call.
+     * @return status Status of the operation.
+     *         Possible status codes:
+     *         |SupplicantStatusCode.SUCCESS|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|
+     *         |SupplicantStatusCode.FAILURE_UNSUPPORTED|
+     */
+    stopDppResponder(uint32_t ownBootstrapId) generates (SupplicantStatus status);
 };
diff --git a/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.hal b/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.hal
index 852696d..efcebda 100644
--- a/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.hal
+++ b/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.hal
@@ -40,8 +40,12 @@
         /**
          * Baseline information as defined in HAL 1.0.
          */
-        @1.0::ISupplicantStaIfaceCallback.AnqpData V1_0; /* Container for v1.0 of this struct */
-        vec<uint8_t> venueUrl; /* Venue URL ANQP-element */
+        @1.0::ISupplicantStaIfaceCallback.AnqpData V1_0;
+
+        /*
+         * Container for v1.0 of this struct
+         */
+        vec<uint8_t> venueUrl;
     };
 
     /**
diff --git a/wifi/supplicant/1.4/ISupplicantStaNetwork.hal b/wifi/supplicant/1.4/ISupplicantStaNetwork.hal
index 80beedf..6bed5ab 100644
--- a/wifi/supplicant/1.4/ISupplicantStaNetwork.hal
+++ b/wifi/supplicant/1.4/ISupplicantStaNetwork.hal
@@ -17,7 +17,7 @@
 package android.hardware.wifi.supplicant@1.4;
 
 import @1.3::ISupplicantStaNetwork;
-import @1.4::ISupplicantStaNetworkCallback;
+import ISupplicantStaNetworkCallback;
 
 /**
  * Interface exposed by the supplicant for each station mode network
diff --git a/wifi/supplicant/1.4/types.hal b/wifi/supplicant/1.4/types.hal
index 18af8fe..aec61c4 100644
--- a/wifi/supplicant/1.4/types.hal
+++ b/wifi/supplicant/1.4/types.hal
@@ -18,6 +18,7 @@
 
 import @1.0::SupplicantStatusCode;
 import @1.3::ConnectionCapabilities;
+import @1.3::DppFailureCode;
 import @1.3::WpaDriverCapabilitiesMask;
 
 /**
@@ -40,6 +41,29 @@
 };
 
 /**
+ * DppFailureCode: Error codes for DPP (Easy Connect)
+ */
+enum DppFailureCode : @1.3::DppFailureCode {
+    /**
+     * Failure to generate a DPP URI.
+     */
+    URI_GENERATION,
+};
+
+/**
+ * DppCurve: Elliptic curve cryptography type used to generate DPP
+ * public/private key pair.
+ */
+enum DppCurve : uint32_t {
+    PRIME256V1,
+    SECP384R1,
+    SECP521R1,
+    BRAINPOOLP256R1,
+    BRAINPOOLP384R1,
+    BRAINPOOLP512R1,
+};
+
+/**
  * Connection Capabilities supported by current network and device
  */
 struct ConnectionCapabilities {
@@ -47,6 +71,7 @@
      * Baseline information as defined in HAL 1.3.
      */
     @1.3::ConnectionCapabilities V1_3;
+
     /**
      * detailed network mode for legacy network
      */
@@ -64,13 +89,14 @@
  * Generic structure to return the status of any supplicant operation.
  */
 struct SupplicantStatus {
-  SupplicantStatusCode code;
-  /**
-   * A vendor specific error message to provide more information beyond the
-   * status code.
-   * This will be used for debbuging purposes only.
-   */
-  string debugMessage;
+    SupplicantStatusCode code;
+
+    /**
+     * A vendor specific error message to provide more information beyond the
+     * status code.
+     * This will be used for debbuging purposes only.
+     */
+    string debugMessage;
 };
 
 /**
@@ -78,7 +104,27 @@
  */
 enum WpaDriverCapabilitiesMask : @1.3::WpaDriverCapabilitiesMask {
     /**
-     *
      */
     SAE_PK = 1 << 2,
 };
+
+/**
+ * DPP bootstrap info generated for responder mode operation
+ */
+struct DppResponderBootstrapInfo {
+    /**
+     * Generated bootstrap identifier
+     */
+    uint32_t bootstrapId;
+
+    /**
+     * The Wi-Fi channel that the DPP responder is listening on.
+     */
+    uint32_t listenChannel;
+
+    /**
+     * Bootstrapping URI per DPP specification, "section 5.2 Bootstrapping
+     * information", may contain listen channel, MAC address, public key, or other information.
+     */
+    string uri;
+};
diff --git a/wifi/supplicant/1.4/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.4/vts/functional/supplicant_sta_iface_hidl_test.cpp
index c0b5a70..ccd469d 100644
--- a/wifi/supplicant/1.4/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.4/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -19,6 +19,7 @@
 #include <android/hardware/wifi/supplicant/1.1/ISupplicant.h>
 #include <android/hardware/wifi/supplicant/1.2/types.h>
 #include <android/hardware/wifi/supplicant/1.3/ISupplicant.h>
+#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h>
 #include <android/hardware/wifi/supplicant/1.3/types.h>
 #include <android/hardware/wifi/supplicant/1.4/ISupplicant.h>
 #include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIface.h>
@@ -45,7 +46,10 @@
 using ::android::hardware::wifi::supplicant::V1_2::DppNetRole;
 using ::android::hardware::wifi::supplicant::V1_2::DppProgressCode;
 using ::android::hardware::wifi::supplicant::V1_3::DppSuccessCode;
+using ::android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork;
 using ::android::hardware::wifi::supplicant::V1_4::ConnectionCapabilities;
+using ::android::hardware::wifi::supplicant::V1_4::DppCurve;
+using ::android::hardware::wifi::supplicant::V1_4::DppResponderBootstrapInfo;
 using ::android::hardware::wifi::supplicant::V1_4::ISupplicant;
 using ::android::hardware::wifi::supplicant::V1_4::ISupplicantStaIface;
 using ::android::hardware::wifi::supplicant::V1_4::ISupplicantStaIfaceCallback;
@@ -74,6 +78,24 @@
     sp<ISupplicantStaIface> sta_iface_;
     // MAC address to use for various tests.
     std::array<uint8_t, 6> mac_addr_;
+
+    bool isDppSupported() {
+        uint32_t keyMgmtMask = 0;
+
+        // We need to first get the key management capabilities from the device.
+        // If DPP is not supported, we just pass the test.
+        sta_iface_->getKeyMgmtCapabilities_1_3(
+            [&](const SupplicantStatus& status, uint32_t keyMgmtMaskInternal) {
+                EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+                keyMgmtMask = keyMgmtMaskInternal;
+            });
+
+        if (!(keyMgmtMask & ISupplicantStaNetwork::KeyMgmtMask::DPP)) {
+            // DPP not supported
+            return false;
+        }
+        return true;
+    }
 };
 
 class IfaceCallback : public ISupplicantStaIfaceCallback {
@@ -255,6 +277,48 @@
         });
 }
 
+/*
+ * StartDppEnrolleeResponder
+ */
+TEST_P(SupplicantStaIfaceHidlTest, StartDppEnrolleeResponder) {
+    // We need to first get the key management capabilities from the device.
+    // If DPP is not supported, we just pass the test.
+    if (!isDppSupported()) {
+        // DPP not supported
+        return;
+    }
+
+    hidl_string deviceInfo = "DPP_Responder_Mode_VTS_Test";
+    uint32_t bootstrap_id = 0;
+    uint32_t listen_channel = 0;
+    uint8_t mac_address[6] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+
+    // Generate DPP bootstrap information
+    sta_iface_->generateDppBootstrapInfoForResponder(
+        mac_address, deviceInfo, DppCurve::PRIME256V1,
+        [&](const SupplicantStatusV1_4& status,
+            DppResponderBootstrapInfo bootstrapInfo) {
+            EXPECT_EQ(SupplicantStatusCodeV1_4::SUCCESS, status.code);
+            EXPECT_NE(-1, bootstrapInfo.bootstrapId);
+            EXPECT_NE(0, bootstrapInfo.bootstrapId);
+            bootstrap_id = bootstrapInfo.bootstrapId;
+            listen_channel = bootstrapInfo.listenChannel;
+            EXPECT_NE(0, bootstrapInfo.listenChannel);
+        });
+
+    // Start DPP as Enrollee-Responder.
+    sta_iface_->startDppEnrolleeResponder(
+        listen_channel, [&](const SupplicantStatusV1_4& status) {
+            EXPECT_EQ(SupplicantStatusCodeV1_4::SUCCESS, status.code);
+        });
+
+    // Stop DPP Enrollee-Responder mode, ie remove the URI and stop listen.
+    sta_iface_->stopDppResponder(
+        bootstrap_id, [&](const SupplicantStatusV1_4& status) {
+            EXPECT_EQ(SupplicantStatusCodeV1_4::SUCCESS, status.code);
+        });
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaIfaceHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaIfaceHidlTest,