Merge changes from topic "bluetooth_audio_hidl2.2"

* changes:
  Add bluetooth.audio@2.2 implementation
  Add hidl 2.2 for LE audio support
diff --git a/automotive/vehicle/2.0/utils/UserHalHelper.cpp b/automotive/vehicle/2.0/utils/UserHalHelper.cpp
index abf59b7..dccdb2b 100644
--- a/automotive/vehicle/2.0/utils/UserHalHelper.cpp
+++ b/automotive/vehicle/2.0/utils/UserHalHelper.cpp
@@ -60,11 +60,22 @@
                        << int32Values.size();
     }
     userInfo->userId = int32Values[startPos];
-    auto userFlags = verifyAndCast<UserFlags>(int32Values[startPos + 1]);
-    if (!userFlags.ok()) {
-        return Error() << "Invalid user flags: " << userFlags.error();
+    int32_t intUserFlags = int32Values[startPos + 1];
+    int32_t expectedUserFlags = 0;
+    for (const auto& v : hidl_enum_range<UserFlags>()) {
+        int32_t intEnumUserFlag = static_cast<int32_t>(v);
+        if ((intUserFlags & intEnumUserFlag) != 0) {
+            expectedUserFlags |= intEnumUserFlag;
+        }
     }
-    userInfo->flags = *userFlags;
+    if (intUserFlags != expectedUserFlags) {
+        return Error() << "Invalid user flags: " << intUserFlags << ", must be '|' of UserFlags";
+    }
+    // intUserFlags is actually not a valid UserFlags enum, instead, it is a 'bit or' of possible
+    // multiple UserFlags. However, because the HAL interface was defined incorrectly, we have to
+    // cast it to UserFlags here, which is defined behavior because the underlying type for
+    // UserFlags is int32_t and our intUserFlags is within the range of int32_t.
+    userInfo->flags = static_cast<UserFlags>(intUserFlags);
     return {};
 }
 
diff --git a/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp b/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
index 7da87a2..0562a54 100644
--- a/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
+++ b/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
@@ -54,6 +54,10 @@
 constexpr int32_t GUEST_USER = static_cast<int32_t>(UserFlags::GUEST);
 constexpr int32_t NONE_USER = static_cast<int32_t>(UserFlags::NONE);
 constexpr int32_t SYSTEM_USER = static_cast<int32_t>(UserFlags::SYSTEM);
+constexpr int32_t ADMIN_USER = static_cast<int32_t>(UserFlags::ADMIN);
+constexpr int32_t SYSTEM_ADMIN_USER = static_cast<int32_t>(UserFlags::SYSTEM | UserFlags::ADMIN);
+// 0x1111 is not a valid UserFlags combination.
+constexpr int32_t INVALID_USER_FLAG = 0x1111;
 
 constexpr int32_t USER_ID_ASSOC_KEY_FOB =
         static_cast<int32_t>(UserIdentificationAssociationType::KEY_FOB);
@@ -72,7 +76,7 @@
 
 }  // namespace
 
-TEST(UserHalHelperTest, TestToInitialUserInfoRequest) {
+TEST(UserHalHelperTest, TestToInitialUserInfoRequestSystemUser) {
     VehiclePropValue propValue{
             .prop = INITIAL_USER_INFO,
             .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, SYSTEM_USER,
@@ -92,6 +96,58 @@
     EXPECT_THAT(actual.value(), Eq(expected));
 }
 
+TEST(UserHalHelperTest, TestToInitialUserInfoRequestAdminUser) {
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, ADMIN_USER, 10,
+                                      NONE_USER}},
+    };
+    InitialUserInfoRequest expected{
+            .requestId = 23,
+            .requestType = InitialUserInfoRequestType::FIRST_BOOT_AFTER_OTA,
+            .usersInfo = {{10, UserFlags::NONE}, 2, {{0, UserFlags::ADMIN}, {10, UserFlags::NONE}}},
+    };
+
+    auto actual = toInitialUserInfoRequest(propValue);
+
+    ASSERT_TRUE(actual.ok()) << actual.error().message();
+    EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestToInitialUserInfoRequestUserFlagsBitCombination) {
+    // SYSTEM_ADMIN_USER is two UserFlags combined and is itself not a defined UserFlags enum.
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0,
+                                      SYSTEM_ADMIN_USER, 10, NONE_USER}},
+    };
+    InitialUserInfoRequest expected{
+            .requestId = 23,
+            .requestType = InitialUserInfoRequestType::FIRST_BOOT_AFTER_OTA,
+            .usersInfo = {{10, UserFlags::NONE},
+                          2,
+                          {{0, static_cast<UserFlags>(SYSTEM_ADMIN_USER)}, {10, UserFlags::NONE}}},
+    };
+
+    auto actual = toInitialUserInfoRequest(propValue);
+
+    ASSERT_TRUE(actual.ok()) << actual.error().message();
+    EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestToInitialUserInfoRequestUserInvalidUserFlag) {
+    // 0x1111 is not a valid UserFlags flag combination.
+    VehiclePropValue propValue{
+            .prop = INITIAL_USER_INFO,
+            .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0,
+                                      INVALID_USER_FLAG, 10, NONE_USER}},
+    };
+
+    auto actual = toInitialUserInfoRequest(propValue);
+
+    EXPECT_FALSE(actual.ok()) << "No error returned on invalid user flags";
+}
+
 TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithMismatchingPropType) {
     VehiclePropValue propValue{
             .prop = INT32_MAX,
diff --git a/bluetooth/1.0/default/h4_protocol.cc b/bluetooth/1.0/default/h4_protocol.cc
index 43abbe4..33238da 100644
--- a/bluetooth/1.0/default/h4_protocol.cc
+++ b/bluetooth/1.0/default/h4_protocol.cc
@@ -30,21 +30,52 @@
 namespace hci {
 
 size_t H4Protocol::Send(uint8_t type, const uint8_t* data, size_t length) {
-  struct iovec iov[] = {{&type, sizeof(type)},
-                        {const_cast<uint8_t*>(data), length}};
-  ssize_t ret = 0;
-  do {
-    ret =
-        TEMP_FAILURE_RETRY(writev(uart_fd_, iov, sizeof(iov) / sizeof(iov[0])));
-  } while (-1 == ret && EAGAIN == errno);
-
-  if (ret == -1) {
-    ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
-  } else if (ret < static_cast<ssize_t>(length + 1)) {
-    ALOGE("%s: %d / %d bytes written - something went wrong...", __func__,
-          static_cast<int>(ret), static_cast<int>(length + 1));
+  struct iovec iov_array[] = {{&type, sizeof(type)},
+                              {const_cast<uint8_t*>(data), length}};
+  struct iovec* iov = iov_array;
+  int iovcnt = sizeof(iov_array) / sizeof(iov_array[0]);
+  size_t total_bytes = 0;
+  for (int i = 0; i < iovcnt; i++) {
+    total_bytes += iov_array[i].iov_len;
   }
-  return ret;
+  size_t bytes_written = 0;
+  size_t remaining_bytes = total_bytes;
+
+  while (remaining_bytes > 0) {
+    ssize_t ret = TEMP_FAILURE_RETRY(writev(uart_fd_, iov, iovcnt));
+    if (ret == -1) {
+      if (errno == EAGAIN) continue;
+      ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
+      break;
+    } else if (ret == 0) {
+      // Nothing written
+      ALOGE("%s zero bytes written - something went wrong...", __func__);
+      break;
+    } else if (ret == remaining_bytes) {
+      // Everything written
+      bytes_written += ret;
+      break;
+    }
+
+    bytes_written += ret;
+    remaining_bytes -= ret;
+    ALOGW("%s: %d/%d bytes written - retrying remaining %d bytes", __func__,
+          static_cast<int>(bytes_written), static_cast<int>(total_bytes),
+          static_cast<int>(remaining_bytes));
+
+    // Remove iovs which are written from the list
+    while (ret >= iov->iov_len) {
+      ret -= iov->iov_len;
+      ++iov;
+      --iovcnt;
+    }
+    // Adjust the iov to point to the remaining data which needs to be written
+    if (ret) {
+      iov->iov_base = static_cast<uint8_t*>(iov->iov_base) + ret;
+      iov->iov_len -= ret;
+    }
+  }
+  return bytes_written;
 }
 
 void H4Protocol::OnPacketReady() {
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 49e00f4..5c886ee 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -6197,14 +6197,13 @@
         return;
     }
 
-    // Test that if more than one color cameras facing the same direction are
-    // supported, there must be at least one logical camera facing that
-    // direction.
+    // Test that if more than one rear-facing color camera is
+    // supported, there must be at least one rear-facing logical camera.
     hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
-    // Front and back facing non-logical color cameras
-    std::set<std::string> frontColorCameras, rearColorCameras;
-    // Front and back facing logical cameras' physical camera Id sets
-    std::set<std::set<std::string>> frontPhysicalIds, rearPhysicalIds;
+    // Back facing non-logical color cameras
+    std::set<std::string> rearColorCameras;
+    // Back facing logical cameras' physical camera Id sets
+    std::set<std::set<std::string>> rearPhysicalIds;
     for (const auto& name : cameraDeviceNames) {
         std::string cameraId;
         int deviceVersion = getCameraDeviceVersionAndId(name, mProviderType, &cameraId);
@@ -6236,8 +6235,8 @@
                         return;
                     }
 
-                    // Check camera facing. Skip if facing is neither FRONT
-                    // nor BACK. If this is not a logical camera, only note down
+                    // Check camera facing. Skip if facing is not BACK.
+                    // If this is not a logical camera, only note down
                     // the camera ID, and skip.
                     camera_metadata_ro_entry entry;
                     int retcode = find_camera_metadata_ro_entry(
@@ -6246,18 +6245,12 @@
                     ASSERT_GT(entry.count, 0);
                     uint8_t facing = entry.data.u8[0];
                     bool isLogicalCamera = (isLogicalMultiCamera(metadata) == Status::OK);
-                    if (facing == ANDROID_LENS_FACING_FRONT) {
-                        if (!isLogicalCamera) {
-                            frontColorCameras.insert(cameraId);
-                            return;
-                        }
-                    } else if (facing == ANDROID_LENS_FACING_BACK) {
-                        if (!isLogicalCamera) {
-                            rearColorCameras.insert(cameraId);
-                            return;
-                        }
-                    } else {
-                        // Not FRONT or BACK facing. Skip.
+                    if (facing != ANDROID_LENS_FACING_BACK) {
+                        // Not BACK facing. Skip.
+                        return;
+                    }
+                    if (!isLogicalCamera) {
+                        rearColorCameras.insert(cameraId);
                         return;
                     }
 
@@ -6266,11 +6259,7 @@
                     std::unordered_set<std::string> physicalCameraIds;
                     Status s = getPhysicalCameraIds(metadata, &physicalCameraIds);
                     ASSERT_EQ(Status::OK, s);
-                    if (facing == ANDROID_LENS_FACING_FRONT) {
-                        frontPhysicalIds.emplace(physicalCameraIds.begin(), physicalCameraIds.end());
-                    } else {
-                        rearPhysicalIds.emplace(physicalCameraIds.begin(), physicalCameraIds.end());
-                    }
+                    rearPhysicalIds.emplace(physicalCameraIds.begin(), physicalCameraIds.end());
                     for (const auto& physicalId : physicalCameraIds) {
                         // Skip if the physicalId is publicly available
                         for (auto& deviceName : cameraDeviceNames) {
@@ -6297,11 +6286,7 @@
                                     (camera_metadata_t*)chars.data();
 
                             if (CameraHidlTest::isColorCamera(physicalMetadata)) {
-                                if (facing == ANDROID_LENS_FACING_FRONT) {
-                                    frontColorCameras.insert(physicalId);
-                                } else if (facing == ANDROID_LENS_FACING_BACK) {
-                                    rearColorCameras.insert(physicalId);
-                                }
+                                rearColorCameras.insert(physicalId);
                             }
                         });
                         ASSERT_TRUE(ret.isOk());
@@ -6319,20 +6304,9 @@
         }
     }
 
-    // If there are more than one color cameras facing one direction, a logical
-    // multi-camera must be defined consisting of all color cameras facing that
-    // direction.
-    if (frontColorCameras.size() > 1) {
-        bool hasFrontLogical = false;
-        for (const auto& physicalIds : frontPhysicalIds) {
-            if (std::includes(physicalIds.begin(), physicalIds.end(),
-                    frontColorCameras.begin(), frontColorCameras.end())) {
-                hasFrontLogical = true;
-                break;
-            }
-        }
-        ASSERT_TRUE(hasFrontLogical);
-    }
+    // If there are more than one rear-facing color camera, a logical
+    // multi-camera must be defined consisting of all rear-facing color
+    // cameras.
     if (rearColorCameras.size() > 1) {
         bool hasRearLogical = false;
         for (const auto& physicalIds : rearPhysicalIds) {
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 0879ab1..ece4de7 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -276,6 +276,15 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <!-- TODO(b/177269435): require health AIDL HAL and deprecate HIDL HAL -->
+    <hal format="aidl" optional="true">
+        <name>android.hardware.health</name>
+        <version>1</version>
+        <interface>
+            <name>IHealth</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.health.storage</name>
         <version>1</version>
@@ -396,6 +405,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.neuralnetworks</name>
+        <version>1-2</version>
         <interface>
             <name>IDevice</name>
             <regex-instance>.*</regex-instance>
diff --git a/contexthub/1.0/vts/functional/OWNERS b/contexthub/1.0/vts/functional/OWNERS
index 1a33a9e..f254cd5 100644
--- a/contexthub/1.0/vts/functional/OWNERS
+++ b/contexthub/1.0/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 156070
 #Context Hub team
 arthuri@google.com
 bduddie@google.com
 stange@google.com
-
-#VTS team
-dshi@google.com
diff --git a/contexthub/1.1/vts/functional/OWNERS b/contexthub/1.1/vts/functional/OWNERS
index 1a33a9e..2cf5bca 100644
--- a/contexthub/1.1/vts/functional/OWNERS
+++ b/contexthub/1.1/vts/functional/OWNERS
@@ -1,7 +1,2 @@
-#Context Hub team
-arthuri@google.com
-bduddie@google.com
-stange@google.com
-
-#VTS team
-dshi@google.com
+# Bug component: 156070
+include ../../../1.0/vts/functional/OWNERS
diff --git a/contexthub/1.2/vts/functional/OWNERS b/contexthub/1.2/vts/functional/OWNERS
index 1a33a9e..2cf5bca 100644
--- a/contexthub/1.2/vts/functional/OWNERS
+++ b/contexthub/1.2/vts/functional/OWNERS
@@ -1,7 +1,2 @@
-#Context Hub team
-arthuri@google.com
-bduddie@google.com
-stange@google.com
-
-#VTS team
-dshi@google.com
+# Bug component: 156070
+include ../../../1.0/vts/functional/OWNERS
diff --git a/graphics/composer/2.2/vts/functional/OWNERS b/graphics/composer/2.2/vts/functional/OWNERS
index ea06752..31b0dc7 100644
--- a/graphics/composer/2.2/vts/functional/OWNERS
+++ b/graphics/composer/2.2/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 25423
 # Graphics team
 adyabr@google.com
 lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/graphics/composer/2.3/vts/functional/OWNERS b/graphics/composer/2.3/vts/functional/OWNERS
index ea06752..31b0dc7 100644
--- a/graphics/composer/2.3/vts/functional/OWNERS
+++ b/graphics/composer/2.3/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 25423
 # Graphics team
 adyabr@google.com
 lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal
index 1a59bbd..9e3cf0e 100644
--- a/graphics/composer/2.4/IComposerClient.hal
+++ b/graphics/composer/2.4/IComposerClient.hal
@@ -34,9 +34,7 @@
         /**
          * The configuration group ID (as int32_t) this config is associated to.
          * Switching between configurations within the same group may be done seamlessly
-         * in some conditions via setActiveConfigWithConstraints. Configurations which
-         * share the same config group are similar in all attributes except for the
-         * vsync period.
+         * in some conditions via setActiveConfigWithConstraints.
          */
         CONFIG_GROUP = 7,
     };
diff --git a/graphics/composer/2.4/vts/functional/OWNERS b/graphics/composer/2.4/vts/functional/OWNERS
index ea06752..31b0dc7 100644
--- a/graphics/composer/2.4/vts/functional/OWNERS
+++ b/graphics/composer/2.4/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 25423
 # Graphics team
 adyabr@google.com
 lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 5aceda7..b071f71 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -19,8 +19,6 @@
 #include <algorithm>
 #include <regex>
 #include <thread>
-#include <unordered_map>
-#include <utility>
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
@@ -317,59 +315,6 @@
     }
 }
 
-TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute_2_4_ConfigsInAGroupDifferOnlyByVsyncPeriod) {
-    struct Resolution {
-        int32_t width, height;
-    };
-    struct Dpi {
-        int32_t x, y;
-    };
-    for (const auto& display : mDisplays) {
-        std::vector<Config> configs = mComposerClient->getDisplayConfigs(display.get());
-        std::unordered_map<int32_t, Resolution> configGroupToResolutionMap;
-        std::unordered_map<int32_t, Dpi> configGroupToDpiMap;
-        for (auto config : configs) {
-            const auto configGroup = mComposerClient->getDisplayAttribute_2_4(
-                    display.get(), config, IComposerClient::Attribute::CONFIG_GROUP);
-            const auto width = mComposerClient->getDisplayAttribute_2_4(
-                    display.get(), config, IComposerClient::Attribute::WIDTH);
-            const auto height = mComposerClient->getDisplayAttribute_2_4(
-                    display.get(), config, IComposerClient::Attribute::HEIGHT);
-            if (configGroupToResolutionMap.find(configGroup) == configGroupToResolutionMap.end()) {
-                configGroupToResolutionMap[configGroup] = {width, height};
-            }
-            EXPECT_EQ(configGroupToResolutionMap[configGroup].width, width);
-            EXPECT_EQ(configGroupToResolutionMap[configGroup].height, height);
-
-            int32_t dpiX = -1;
-            mComposerClient->getRaw()->getDisplayAttribute_2_4(
-                    display.get(), config, IComposerClient::Attribute::DPI_X,
-                    [&](const auto& tmpError, const auto& value) {
-                        if (tmpError == Error::NONE) {
-                            dpiX = value;
-                        }
-                    });
-            int32_t dpiY = -1;
-            mComposerClient->getRaw()->getDisplayAttribute_2_4(
-                    display.get(), config, IComposerClient::Attribute::DPI_Y,
-                    [&](const auto& tmpError, const auto& value) {
-                        if (tmpError == Error::NONE) {
-                            dpiY = value;
-                        }
-                    });
-            if (dpiX == -1 && dpiY == -1) {
-                continue;
-            }
-
-            if (configGroupToDpiMap.find(configGroup) == configGroupToDpiMap.end()) {
-                configGroupToDpiMap[configGroup] = {dpiX, dpiY};
-            }
-            EXPECT_EQ(configGroupToDpiMap[configGroup].x, dpiX);
-            EXPECT_EQ(configGroupToDpiMap[configGroup].y, dpiY);
-        }
-    }
-}
-
 TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod_BadDisplay) {
     VsyncPeriodNanos vsyncPeriodNanos;
     EXPECT_EQ(Error::BAD_DISPLAY,
diff --git a/graphics/mapper/2.0/vts/OWNERS b/graphics/mapper/2.0/vts/OWNERS
index 4177296..62e3f2a 100644
--- a/graphics/mapper/2.0/vts/OWNERS
+++ b/graphics/mapper/2.0/vts/OWNERS
@@ -1,8 +1,5 @@
-# Graphics team
+# Bug component: 25423
 chrisforbes@google.com
 jreck@google.com
 lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/graphics/mapper/2.0/vts/functional/OWNERS b/graphics/mapper/2.0/vts/functional/OWNERS
deleted file mode 100644
index a2ed8c8..0000000
--- a/graphics/mapper/2.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 25423
-sumir@google.com
diff --git a/graphics/mapper/2.1/vts/OWNERS b/graphics/mapper/2.1/vts/OWNERS
index 4177296..43c018a 100644
--- a/graphics/mapper/2.1/vts/OWNERS
+++ b/graphics/mapper/2.1/vts/OWNERS
@@ -1,8 +1,2 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+# Bug component: 25423
+include ../../2.0/vts/OWNERS
diff --git a/graphics/mapper/2.1/vts/functional/OWNERS b/graphics/mapper/2.1/vts/functional/OWNERS
deleted file mode 100644
index a2ed8c8..0000000
--- a/graphics/mapper/2.1/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 25423
-sumir@google.com
diff --git a/graphics/mapper/3.0/vts/OWNERS b/graphics/mapper/3.0/vts/OWNERS
index c9f24d0..43c018a 100644
--- a/graphics/mapper/3.0/vts/OWNERS
+++ b/graphics/mapper/3.0/vts/OWNERS
@@ -1,4 +1,2 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
+# Bug component: 25423
+include ../../2.0/vts/OWNERS
diff --git a/graphics/mapper/4.0/vts/OWNERS b/graphics/mapper/4.0/vts/OWNERS
index c9f24d0..43c018a 100644
--- a/graphics/mapper/4.0/vts/OWNERS
+++ b/graphics/mapper/4.0/vts/OWNERS
@@ -1,4 +1,2 @@
-# Graphics team
-chrisforbes@google.com
-jreck@google.com
-lpy@google.com
+# Bug component: 25423
+include ../../2.0/vts/OWNERS
diff --git a/health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp b/health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp
index 9e1cc70..3c353e6 100644
--- a/health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp
+++ b/health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp
@@ -25,7 +25,26 @@
 namespace V2_0 {
 
 sp<IHealth> get_health_service() {
-    for (auto&& instanceName : {"default", "backup"}) {
+    // For the core and vendor variant, the "backup" instance points to healthd,
+    // which is removed.
+    // For the recovery variant, the "backup" instance has a different
+    // meaning. It points to android.hardware.health@2.0-impl-default.recovery
+    // which was assumed by OEMs to be always installed when a
+    // vendor-specific libhealthd is not necessary. Hence, its behavior
+    // is kept. See health/2.0/README.md.
+    // android.hardware.health@2.0-impl-default.recovery, and subsequently the
+    // special handling of recovery mode below, can be removed once health@2.1
+    // is the minimum required version (i.e. compatibility matrix level 5 is the
+    // minimum supported level). Health 2.1 requires OEMs to install the
+    // implementation to the recovery partition when it is necessary (i.e. on
+    // non-A/B devices, where IsBatteryOk() is needed in recovery).
+    for (auto&& instanceName :
+#ifdef __ANDROID_RECOVERY__
+         { "default", "backup" }
+#else
+         {"default"}
+#endif
+    ) {
         auto ret = IHealth::getService(instanceName);
         if (ret != nullptr) {
             return ret;
diff --git a/health/aidl/Android.bp b/health/aidl/Android.bp
new file mode 100644
index 0000000..65bdbbc
--- /dev/null
+++ b/health/aidl/Android.bp
@@ -0,0 +1,72 @@
+// Copyright (C) 2021 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.health",
+    vendor_available: true,
+    srcs: ["android/hardware/health/*.aidl"],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            enabled: true,
+            sdk_version: "module_current",
+        },
+        ndk: {
+            separate_platform_variant: false,
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
+
+cc_library {
+    name: "android.hardware.health-translate-ndk",
+    vendor_available: true,
+    srcs: ["android/hardware/health/translate-ndk.cpp"],
+    shared_libs: [
+        "libbinder_ndk",
+        "libhidlbase",
+        "android.hardware.health-V1-ndk",
+        "android.hardware.health@2.0",
+        "android.hardware.health@2.1",
+    ],
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: [
+        "android.hardware.health@2.0",
+        "android.hardware.health@2.1",
+    ],
+}
+
+java_library {
+    name: "android.hardware.health-translate-java",
+    srcs: ["android/hardware/health/Translate.java"],
+    libs: [
+        "android.hardware.health-V1-java",
+        "android.hardware.health-V2.0-java",
+        "android.hardware.health-V2.1-java",
+    ],
+}
diff --git a/health/aidl/OWNERS b/health/aidl/OWNERS
new file mode 100644
index 0000000..cd06415
--- /dev/null
+++ b/health/aidl/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 30545
+elsk@google.com
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryCapacityLevel.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryCapacityLevel.aidl
new file mode 100644
index 0000000..e543886
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryCapacityLevel.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file 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.health;
+@Backing(type="int") @VintfStability
+enum BatteryCapacityLevel {
+  UNSUPPORTED = -1,
+  UNKNOWN = 0,
+  CRITICAL = 1,
+  LOW = 2,
+  NORMAL = 3,
+  HIGH = 4,
+  FULL = 5,
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryHealth.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryHealth.aidl
new file mode 100644
index 0000000..4ce7952
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryHealth.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file 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.health;
+@Backing(type="int") @VintfStability
+enum BatteryHealth {
+  UNKNOWN = 1,
+  GOOD = 2,
+  OVERHEAT = 3,
+  DEAD = 4,
+  OVER_VOLTAGE = 5,
+  UNSPECIFIED_FAILURE = 6,
+  COLD = 7,
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryStatus.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryStatus.aidl
new file mode 100644
index 0000000..340b2ec
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryStatus.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file 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.health;
+@Backing(type="int") @VintfStability
+enum BatteryStatus {
+  UNKNOWN = 1,
+  CHARGING = 2,
+  DISCHARGING = 3,
+  NOT_CHARGING = 4,
+  FULL = 5,
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/DiskStats.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/DiskStats.aidl
new file mode 100644
index 0000000..5aa5890
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/DiskStats.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file 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.health;
+@VintfStability
+parcelable DiskStats {
+  long reads;
+  long readMerges;
+  long readSectors;
+  long readTicks;
+  long writes;
+  long writeMerges;
+  long writeSectors;
+  long writeTicks;
+  long ioInFlight;
+  long ioTicks;
+  long ioInQueue;
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl
new file mode 100644
index 0000000..34a87a6
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file 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.health;
+@VintfStability
+parcelable HealthInfo {
+  boolean chargerAcOnline;
+  boolean chargerUsbOnline;
+  boolean chargerWirelessOnline;
+  int maxChargingCurrentMicroamps;
+  int maxChargingVoltageMicrovolts;
+  android.hardware.health.BatteryStatus batteryStatus;
+  android.hardware.health.BatteryHealth batteryHealth;
+  boolean batteryPresent;
+  int batteryLevel;
+  int batteryVoltageMillivolts;
+  int batteryTemperatureTenthsCelsius;
+  int batteryCurrentMicroamps;
+  int batteryCycleCount;
+  int batteryFullChargeUah;
+  int batteryChargeCounterUah;
+  String batteryTechnology;
+  int batteryCurrentAverageMicroamps;
+  android.hardware.health.DiskStats[] diskStats;
+  android.hardware.health.StorageInfo[] storageInfos;
+  android.hardware.health.BatteryCapacityLevel batteryCapacityLevel;
+  long batteryChargeTimeToFullNowSeconds;
+  int batteryFullChargeDesignCapacityUah;
+  const int BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED = -1;
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealth.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealth.aidl
new file mode 100644
index 0000000..7016ae4
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealth.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file 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.health;
+@VintfStability
+interface IHealth {
+  void registerCallback(in android.hardware.health.IHealthInfoCallback callback);
+  void unregisterCallback(in android.hardware.health.IHealthInfoCallback callback);
+  void update();
+  int getChargeCounterUah();
+  int getCurrentNowMicroamps();
+  int getCurrentAverageMicroamps();
+  int getCapacity();
+  long getEnergyCounterNwh();
+  android.hardware.health.BatteryStatus getChargeStatus();
+  android.hardware.health.StorageInfo[] getStorageInfo();
+  android.hardware.health.DiskStats[] getDiskStats();
+  android.hardware.health.HealthInfo getHealthInfo();
+  const int STATUS_UNKNOWN = 2;
+  const int STATUS_CALLBACK_DIED = 4;
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealthInfoCallback.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealthInfoCallback.aidl
new file mode 100644
index 0000000..1b6366f
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealthInfoCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file 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.health;
+@VintfStability
+interface IHealthInfoCallback {
+  oneway void healthInfoChanged(in android.hardware.health.HealthInfo info);
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/StorageInfo.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/StorageInfo.aidl
new file mode 100644
index 0000000..eaae5a6
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/StorageInfo.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file 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.health;
+@VintfStability
+parcelable StorageInfo {
+  int eol;
+  int lifetimeA;
+  int lifetimeB;
+  String version;
+}
diff --git a/health/aidl/android/hardware/health/BatteryCapacityLevel.aidl b/health/aidl/android/hardware/health/BatteryCapacityLevel.aidl
new file mode 100644
index 0000000..0c26fa2
--- /dev/null
+++ b/health/aidl/android/hardware/health/BatteryCapacityLevel.aidl
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 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.health;
+
+/**
+ * Battery capacity level. This enum provides additional information along side
+ * with the battery capacity.
+ * Clients of this HAL must use this value before inferring it from the
+ * battery capacity.
+ */
+@VintfStability
+@Backing(type="int")
+enum BatteryCapacityLevel {
+    /**
+     * Battery capacity level is unsupported.
+     * Battery capacity level must be set to this value if and only if the
+     * implementation is unsupported.
+     */
+    UNSUPPORTED = -1,
+    /**
+     * Battery capacity level is unknown.
+     * Battery capacity level must be set to this value if and only if battery
+     * is not present or the battery capacity level is unknown/uninitialized.
+     */
+    UNKNOWN,
+    /**
+     * Battery is at critical level. The Android framework must schedule a
+     * shutdown when it sees this value from the HAL.
+     */
+    CRITICAL,
+    /**
+     * Battery is low. The Android framework may limit the performance of
+     * the device when it sees this value from the HAL.
+     */
+    LOW,
+    /**
+     * Battery level is normal.
+     */
+    NORMAL,
+    /**
+     * Battery level is high.
+     */
+    HIGH,
+    /**
+     * Battery is full. It must be set to FULL if and only if battery level is
+     * 100.
+     */
+    FULL,
+}
diff --git a/health/aidl/android/hardware/health/BatteryHealth.aidl b/health/aidl/android/hardware/health/BatteryHealth.aidl
new file mode 100644
index 0000000..2b6e51f
--- /dev/null
+++ b/health/aidl/android/hardware/health/BatteryHealth.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 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.health;
+
+/**
+ * Possible values for Battery Health.
+ * Note: These are currently in sync with BatteryManager and must not
+ * be extended / altered.
+ */
+@VintfStability
+@Backing(type="int")
+enum BatteryHealth {
+    UNKNOWN = 1,
+    GOOD = 2,
+    OVERHEAT = 3,
+    DEAD = 4,
+    OVER_VOLTAGE = 5,
+    /**
+     * Battery experienced an unknown/unspecified failure.
+     */
+    UNSPECIFIED_FAILURE = 6,
+    COLD = 7,
+}
diff --git a/health/aidl/android/hardware/health/BatteryStatus.aidl b/health/aidl/android/hardware/health/BatteryStatus.aidl
new file mode 100644
index 0000000..774b28e
--- /dev/null
+++ b/health/aidl/android/hardware/health/BatteryStatus.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 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.health;
+
+/**
+ * Possible values for Battery Status.
+ * Note: These are currently in sync with BatteryManager and must not
+ * be extended / altered.
+ */
+@VintfStability
+@Backing(type="int")
+enum BatteryStatus {
+    UNKNOWN = 1,
+    CHARGING = 2,
+    DISCHARGING = 3,
+    /**
+     * Battery is *not* charging - special case when charger is present
+     * but battery isn't charging
+     */
+    NOT_CHARGING = 4,
+    FULL = 5,
+}
diff --git a/health/aidl/android/hardware/health/DiskStats.aidl b/health/aidl/android/hardware/health/DiskStats.aidl
new file mode 100644
index 0000000..532cbfd
--- /dev/null
+++ b/health/aidl/android/hardware/health/DiskStats.aidl
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 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.health;
+
+/*
+ * Disk statistics since boot.
+ *
+ * See {@code struct disk_stats} in {@code storaged} for interpretations of these fields.
+ *
+ * All integers in this struct must be interpreted as unsigned.
+ */
+@VintfStability
+parcelable DiskStats {
+    /**
+     * Number of reads processed.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long reads;
+    /**
+     * number of read I/Os merged with in-queue I/Os.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long readMerges;
+    /**
+     * number of sectors read.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long readSectors;
+    /**
+     * total wait time for read requests.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long readTicks;
+    /**
+     * number of writes processed.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long writes;
+    /**
+     * number of writes merged with in-queue I/Os.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long writeMerges;
+    /**
+     * number of sectors written.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long writeSectors;
+    /**
+     * total wait time for write requests.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long writeTicks;
+    /**
+     * number of I/Os currently in flight.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long ioInFlight;
+    /**
+     * total time this block device has been active.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long ioTicks;
+    /**
+     * total wait time for all requests.
+     *
+     * Value must be interpreted as unsigned.
+     */
+    long ioInQueue;
+}
diff --git a/health/aidl/android/hardware/health/HealthInfo.aidl b/health/aidl/android/hardware/health/HealthInfo.aidl
new file mode 100644
index 0000000..504e218
--- /dev/null
+++ b/health/aidl/android/hardware/health/HealthInfo.aidl
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2021 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.health;
+
+import android.hardware.health.BatteryCapacityLevel;
+import android.hardware.health.BatteryHealth;
+import android.hardware.health.BatteryStatus;
+import android.hardware.health.DiskStats;
+import android.hardware.health.StorageInfo;
+
+/**
+ * Health Information.
+ */
+@VintfStability
+parcelable HealthInfo {
+    /**
+     * AC charger state - 'true' if online
+     */
+    boolean chargerAcOnline;
+    /**
+     * USB charger state - 'true' if online
+     */
+    boolean chargerUsbOnline;
+    /**
+     * Wireless charger state - 'true' if online
+     */
+    boolean chargerWirelessOnline;
+    /**
+     * Maximum charging current supported by charger in µA
+     */
+    int maxChargingCurrentMicroamps;
+    /**
+     * Maximum charging voltage supported by charger in µV
+     */
+    int maxChargingVoltageMicrovolts;
+
+    android.hardware.health.BatteryStatus batteryStatus;
+
+    android.hardware.health.BatteryHealth batteryHealth;
+    /**
+     * 'true' if battery is present
+     */
+    boolean batteryPresent;
+    /**
+     * Remaining battery capacity in percent
+     */
+    int batteryLevel;
+    /**
+     * Instantaneous battery voltage in millivolts (mV).
+     *
+     * Historically, the unit of this field is microvolts (µV), but all
+     * clients and implementations uses millivolts in practice, making it
+     * the de-facto standard.
+     */
+    int batteryVoltageMillivolts;
+    /**
+     * Instantaneous battery temperature in tenths of degrees Celsius
+     */
+    int batteryTemperatureTenthsCelsius;
+    /**
+     * Instantaneous battery current in µA
+     */
+    int batteryCurrentMicroamps;
+    /**
+     * Battery charge cycle count
+     */
+    int batteryCycleCount;
+    /**
+     * Battery charge value when it is considered to be "full" in µA-h
+     */
+    int batteryFullChargeUah;
+    /**
+     * Instantaneous battery capacity in µA-h
+     */
+    int batteryChargeCounterUah;
+    /**
+     * Battery technology, e.g. "Li-ion, Li-Poly" etc.
+     */
+    String batteryTechnology;
+    /**
+     * Average battery current in µA. Will be 0 if unsupported.
+     */
+    int batteryCurrentAverageMicroamps;
+    /**
+     * Disk Statistics. Will be an empty vector if unsupported.
+     */
+    DiskStats[] diskStats;
+    /**
+     * Information on storage devices. Will be an empty vector if
+     * unsupported.
+     */
+    StorageInfo[] storageInfos;
+    /**
+     * Battery capacity level. See {@link BatteryCapacityLevel} for more details.
+     */
+    BatteryCapacityLevel batteryCapacityLevel;
+
+    /**
+     * Value of {@link #batteryChargeTimeToFullNowSeconds} if it is not
+     * supported.
+     */
+    const int BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED = -1;
+    /**
+     * Estimated time to fully charge the device (in seconds).
+     * Value must be BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED if and
+     * only if the implementation is unsupported.
+     * Value must be 0 if and only if batteryCapacityLevel is FULL or UNKNOWN.
+     * Otherwise, value must be positive.
+     */
+    long batteryChargeTimeToFullNowSeconds;
+    /**
+     * Estimated battery full charge design capacity (in microamp hours, µAh).
+     * Value must be 0 if unknown.
+     * Value must be greater than 100 000 µAh if known.
+     * Value must be less than 100 000 000 µAh if known.
+     */
+    int batteryFullChargeDesignCapacityUah;
+}
diff --git a/health/aidl/android/hardware/health/IHealth.aidl b/health/aidl/android/hardware/health/IHealth.aidl
new file mode 100644
index 0000000..d541eca
--- /dev/null
+++ b/health/aidl/android/hardware/health/IHealth.aidl
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2021 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.health;
+
+import android.hardware.health.BatteryStatus;
+import android.hardware.health.DiskStats;
+import android.hardware.health.HealthInfo;
+import android.hardware.health.IHealthInfoCallback;
+import android.hardware.health.StorageInfo;
+
+/**
+ * IHealth manages health info and posts events on registered callbacks.
+ *
+ * Implementations must send health info to all callbacks periodically.
+ */
+@VintfStability
+interface IHealth {
+    /** Status code for function. The operation encounters an unknown error. */
+    const int STATUS_UNKNOWN = 2;
+
+    /**
+     * Status code for function.
+     * A registered callback object is dead.
+     */
+    const int STATUS_CALLBACK_DIED = 4;
+
+    /**
+     * Register a callback for any health info events.
+     *
+     * Registering a new callback must not unregister the old one; the old
+     * callback remains registered until one of the following happens:
+     * - A client explicitly calls {@link #unregisterCallback} to unregister it.
+     * - The client process that hosts the callback dies.
+     *
+     * @param callback the callback to register.
+     * @return If error, return service specific error with code STATUS_UNKNOWN.
+     */
+    void registerCallback(in IHealthInfoCallback callback);
+
+    /**
+     * Explicitly unregister a callback that is previously registered through
+     * {@link #registerCallback}.
+     *
+     * @param callback the callback to unregister.
+     * @return If error:
+     *         - Return exception with code EX_ILLEGAL_ARGUMENT
+     *           if callback is not registered previously,
+     *         - Return service specific error with code STATUS_UNKNOWN
+     *           for other errors.
+     */
+    void unregisterCallback(in IHealthInfoCallback callback);
+
+    /**
+     * Schedule update.
+     *
+     * When update() is called, the service must notify all registered callbacks
+     * with the most recent health info.
+     *
+     * @return If error, return service specific error with code:
+     *         - STATUS_CALLBACK_DIED if any registered callback is dead,
+     *         - STATUS_UNKNOWN for other errors.
+     */
+    void update();
+
+    /**
+     * Get battery capacity in microampere-hours(µAh).
+     *
+     * @return battery capacity if successful.
+     *         If error:
+     *         - Return exception with code EX_UNSUPPORTED_OPERATION
+     *           if this property is not supported
+     *           (e.g. the file that stores this property does not exist),
+     *         - Retrurn service specific error with code
+     *           STATUS_UNKNOWN for other errors.
+     */
+    int getChargeCounterUah();
+
+    /**
+     * Get instantaneous battery current in microamperes(µA).
+     *
+     * Positive values indicate net current entering the battery from a charge
+     * source, negative values indicate net current discharging from the
+     * battery.
+     *
+     * @return instantaneous battery current if successful.
+     *         If error:
+     *         - Return exception with code EX_UNSUPPORTED_OPERATION
+     *           if this property is not supported
+     *                 (e.g. the file that stores this property does not exist),
+     *         - Return service specific error with code STATUS_UNKNOWN
+     *           for for other errors.
+     */
+    int getCurrentNowMicroamps();
+
+    /**
+     * Get average battery current in microamperes(µA).
+     *
+     * Positive values indicate net current entering the battery from a charge
+     * source, negative values indicate net current discharging from the
+     * battery. The time period over which the average is computed may depend on
+     * the fuel gauge hardware and its configuration.
+     *
+     * @return average battery current if successful.
+     *         If error:
+     *         - Return exception with code EX_UNSUPPORTED_OPERATION
+     *           if this property is not supported
+     *                 (e.g. the file that stores this property does not exist),
+     *         - Return service specific error with code STATUS_UNKNOWN
+     *           for for other errors.
+     */
+    int getCurrentAverageMicroamps();
+
+    /**
+     * Get remaining battery capacity percentage of total capacity
+     * (with no fractional part).
+     *
+     * @return remaining battery capacity if successful.
+     *         If error:
+     *         - Return exception with code EX_UNSUPPORTED_OPERATION
+     *           if this property is not supported
+     *                 (e.g. the file that stores this property does not exist),
+     *         - Return service specific error with code STATUS_UNKNOWN
+     *           for for other errors.
+     */
+    int getCapacity();
+
+    /**
+     * Get battery remaining energy in nanowatt-hours.
+     *
+     * @return remaining energy if successful.
+     *         If error:
+     *         - Return exception with code EX_UNSUPPORTED_OPERATION
+     *           if this property is not supported,
+     *         - Return service specific error with code STATUS_UNKNOWN
+     *           for for other errors.
+     */
+    long getEnergyCounterNwh();
+
+    /**
+     * Get battery charge status.
+     *
+     * @return charge status if successful.
+     *         If error:
+     *         - Return exception with code EX_UNSUPPORTED_OPERATION
+     *           if this property is not supported
+     *                 (e.g. the file that stores this property does not exist),
+     *         - Return service specific error with code STATUS_UNKNOWN
+     *           for other errors.
+     */
+    BatteryStatus getChargeStatus();
+
+    /**
+     * Get storage info.
+     *
+     * @return vector of StorageInfo structs if successful.
+     *         If error:
+     *         - Return exception with code EX_UNSUPPORTED_OPERATION
+     *           if this property is not supported,
+     *         - Return service specific error with code STATUS_UNKNOWN
+     *           for other errors.
+     */
+    StorageInfo[] getStorageInfo();
+
+    /**
+     * Gets disk statistics (number of reads/writes processed, number of I/O
+     * operations in flight etc).
+     *
+     * @return vector of disk statistics if successful.
+     *         The mapping is index 0->sda, 1->sdb and so on.
+     *         If error:
+     *         - Return exception with code EX_UNSUPPORTED_OPERATION
+     *           if this property is not supported,
+     *         - Return service specific error with code STATUS_UNKNOWN
+     *           for other errors.
+     */
+    DiskStats[] getDiskStats();
+
+    /**
+     * Get Health Information.
+     *
+     * @return Health information if successful.
+     *         If error:
+     *         - Return exception with code EX_UNSUPPORTED_OPERATION
+     *           if this API is not supported,
+     *         - Return service specific error with code STATUS_UNKNOWN
+     *           for for other errors.
+     */
+    HealthInfo getHealthInfo();
+}
diff --git a/health/aidl/android/hardware/health/IHealthInfoCallback.aidl b/health/aidl/android/hardware/health/IHealthInfoCallback.aidl
new file mode 100644
index 0000000..e9720fa
--- /dev/null
+++ b/health/aidl/android/hardware/health/IHealthInfoCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 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.health;
+
+import android.hardware.health.HealthInfo;
+
+/**
+ * IHealthInfoCallback is the updated callback interface to
+ * {@link IHealth#registerCallback}.
+ */
+@VintfStability
+interface IHealthInfoCallback {
+    /**
+     * An implementation of IHealth must call healthInfoChanged on all
+     * registered callbacks after health info changes.
+     * @param info the updated HealthInfo
+     */
+    oneway void healthInfoChanged(in HealthInfo info);
+}
diff --git a/health/aidl/android/hardware/health/StorageInfo.aidl b/health/aidl/android/hardware/health/StorageInfo.aidl
new file mode 100644
index 0000000..4c4dace
--- /dev/null
+++ b/health/aidl/android/hardware/health/StorageInfo.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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.health;
+
+/*
+ * Information on storage device including life time estimates, end of life
+ * information and other attributes.
+ *
+ * All integers in this struct must be interpreted as non-negative.
+ */
+@VintfStability
+parcelable StorageInfo {
+    /**
+     * pre-eol (end of life) information. Follows JEDEC standard No.84-B50.
+     *
+     * Value must be interpreted as non-negative.
+     */
+    int eol;
+    /**
+     * device life time estimation (type A). Follows JEDEC standard No.84-B50.
+     *
+     * Value must be interpreted as non-negative.
+     */
+    int lifetimeA;
+    /**
+     * device life time estimation (type B). Follows JEDEC standard No.84-B50.
+     *
+     * Value must be interpreted as non-negative.
+     */
+    int lifetimeB;
+    /**
+     * version string
+     */
+    String version;
+}
diff --git a/health/aidl/android/hardware/health/Translate.java b/health/aidl/android/hardware/health/Translate.java
new file mode 100644
index 0000000..c8ace1c
--- /dev/null
+++ b/health/aidl/android/hardware/health/Translate.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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.health;
+
+public class Translate {
+    static public android.hardware.health.StorageInfo h2aTranslate(
+            android.hardware.health.V2_0.StorageInfo in) {
+        android.hardware.health.StorageInfo out = new android.hardware.health.StorageInfo();
+        out.eol = in.eol;
+        out.lifetimeA = in.lifetimeA;
+        out.lifetimeB = in.lifetimeB;
+        out.version = in.version;
+        return out;
+    }
+
+    static public android.hardware.health.DiskStats h2aTranslate(
+            android.hardware.health.V2_0.DiskStats in) {
+        android.hardware.health.DiskStats out = new android.hardware.health.DiskStats();
+        out.reads = in.reads;
+        out.readMerges = in.readMerges;
+        out.readSectors = in.readSectors;
+        out.readTicks = in.readTicks;
+        out.writes = in.writes;
+        out.writeMerges = in.writeMerges;
+        out.writeSectors = in.writeSectors;
+        out.writeTicks = in.writeTicks;
+        out.ioInFlight = in.ioInFlight;
+        out.ioTicks = in.ioTicks;
+        out.ioInQueue = in.ioInQueue;
+        return out;
+    }
+
+    static public android.hardware.health.HealthInfo h2aTranslate(
+            android.hardware.health.V2_1.HealthInfo in) {
+        android.hardware.health.HealthInfo out = new android.hardware.health.HealthInfo();
+        out.chargerAcOnline = in.legacy.legacy.chargerAcOnline;
+        out.chargerUsbOnline = in.legacy.legacy.chargerUsbOnline;
+        out.chargerWirelessOnline = in.legacy.legacy.chargerWirelessOnline;
+        out.maxChargingCurrentMicroamps = in.legacy.legacy.maxChargingCurrent;
+        out.maxChargingVoltageMicrovolts = in.legacy.legacy.maxChargingVoltage;
+        out.batteryStatus = in.legacy.legacy.batteryStatus;
+        out.batteryHealth = in.legacy.legacy.batteryHealth;
+        out.batteryPresent = in.legacy.legacy.batteryPresent;
+        out.batteryLevel = in.legacy.legacy.batteryLevel;
+        out.batteryVoltageMillivolts = in.legacy.legacy.batteryVoltage;
+        out.batteryTemperatureTenthsCelsius = in.legacy.legacy.batteryTemperature;
+        out.batteryCurrentMicroamps = in.legacy.legacy.batteryCurrent;
+        out.batteryCycleCount = in.legacy.legacy.batteryCycleCount;
+        out.batteryFullChargeUah = in.legacy.legacy.batteryFullCharge;
+        out.batteryChargeCounterUah = in.legacy.legacy.batteryChargeCounter;
+        out.batteryTechnology = in.legacy.legacy.batteryTechnology;
+        out.batteryCurrentAverageMicroamps = in.legacy.batteryCurrentAverage;
+        out.diskStats = new android.hardware.health.DiskStats[in.legacy.diskStats.size()];
+        for (int i = 0; i < in.legacy.diskStats.size(); i++) {
+            out.diskStats[i] = h2aTranslate(in.legacy.diskStats.get(i));
+        }
+        out.storageInfos = new android.hardware.health.StorageInfo[in.legacy.storageInfos.size()];
+        for (int i = 0; i < in.legacy.storageInfos.size(); i++) {
+            out.storageInfos[i] = h2aTranslate(in.legacy.storageInfos.get(i));
+        }
+        out.batteryCapacityLevel = in.batteryCapacityLevel;
+        out.batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;
+        out.batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;
+        return out;
+    }
+}
diff --git a/health/aidl/android/hardware/health/translate-ndk.cpp b/health/aidl/android/hardware/health/translate-ndk.cpp
new file mode 100644
index 0000000..7fe6ced
--- /dev/null
+++ b/health/aidl/android/hardware/health/translate-ndk.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2021 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 "android/hardware/health/translate-ndk.h"
+
+namespace android::h2a {
+
+static_assert(aidl::android::hardware::health::BatteryStatus::UNKNOWN ==
+              static_cast<aidl::android::hardware::health::BatteryStatus>(
+                      ::android::hardware::health::V1_0::BatteryStatus::UNKNOWN));
+static_assert(aidl::android::hardware::health::BatteryStatus::CHARGING ==
+              static_cast<aidl::android::hardware::health::BatteryStatus>(
+                      ::android::hardware::health::V1_0::BatteryStatus::CHARGING));
+static_assert(aidl::android::hardware::health::BatteryStatus::DISCHARGING ==
+              static_cast<aidl::android::hardware::health::BatteryStatus>(
+                      ::android::hardware::health::V1_0::BatteryStatus::DISCHARGING));
+static_assert(aidl::android::hardware::health::BatteryStatus::NOT_CHARGING ==
+              static_cast<aidl::android::hardware::health::BatteryStatus>(
+                      ::android::hardware::health::V1_0::BatteryStatus::NOT_CHARGING));
+static_assert(aidl::android::hardware::health::BatteryStatus::FULL ==
+              static_cast<aidl::android::hardware::health::BatteryStatus>(
+                      ::android::hardware::health::V1_0::BatteryStatus::FULL));
+
+static_assert(aidl::android::hardware::health::BatteryHealth::UNKNOWN ==
+              static_cast<aidl::android::hardware::health::BatteryHealth>(
+                      ::android::hardware::health::V1_0::BatteryHealth::UNKNOWN));
+static_assert(aidl::android::hardware::health::BatteryHealth::GOOD ==
+              static_cast<aidl::android::hardware::health::BatteryHealth>(
+                      ::android::hardware::health::V1_0::BatteryHealth::GOOD));
+static_assert(aidl::android::hardware::health::BatteryHealth::OVERHEAT ==
+              static_cast<aidl::android::hardware::health::BatteryHealth>(
+                      ::android::hardware::health::V1_0::BatteryHealth::OVERHEAT));
+static_assert(aidl::android::hardware::health::BatteryHealth::DEAD ==
+              static_cast<aidl::android::hardware::health::BatteryHealth>(
+                      ::android::hardware::health::V1_0::BatteryHealth::DEAD));
+static_assert(aidl::android::hardware::health::BatteryHealth::OVER_VOLTAGE ==
+              static_cast<aidl::android::hardware::health::BatteryHealth>(
+                      ::android::hardware::health::V1_0::BatteryHealth::OVER_VOLTAGE));
+static_assert(aidl::android::hardware::health::BatteryHealth::UNSPECIFIED_FAILURE ==
+              static_cast<aidl::android::hardware::health::BatteryHealth>(
+                      ::android::hardware::health::V1_0::BatteryHealth::UNSPECIFIED_FAILURE));
+static_assert(aidl::android::hardware::health::BatteryHealth::COLD ==
+              static_cast<aidl::android::hardware::health::BatteryHealth>(
+                      ::android::hardware::health::V1_0::BatteryHealth::COLD));
+
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::UNSUPPORTED ==
+              static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+                      ::android::hardware::health::V2_1::BatteryCapacityLevel::UNSUPPORTED));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::UNKNOWN ==
+              static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+                      ::android::hardware::health::V2_1::BatteryCapacityLevel::UNKNOWN));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::CRITICAL ==
+              static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+                      ::android::hardware::health::V2_1::BatteryCapacityLevel::CRITICAL));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::LOW ==
+              static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+                      ::android::hardware::health::V2_1::BatteryCapacityLevel::LOW));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::NORMAL ==
+              static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+                      ::android::hardware::health::V2_1::BatteryCapacityLevel::NORMAL));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::HIGH ==
+              static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+                      ::android::hardware::health::V2_1::BatteryCapacityLevel::HIGH));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::FULL ==
+              static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+                      ::android::hardware::health::V2_1::BatteryCapacityLevel::FULL));
+
+__attribute__((warn_unused_result)) bool translate(
+        const ::android::hardware::health::V2_0::StorageInfo& in,
+        aidl::android::hardware::health::StorageInfo* out) {
+    out->eol = in.eol;
+    out->lifetimeA = in.lifetimeA;
+    out->lifetimeB = in.lifetimeB;
+    out->version = in.version;
+    return true;
+}
+
+__attribute__((warn_unused_result)) bool translate(
+        const ::android::hardware::health::V2_0::DiskStats& in,
+        aidl::android::hardware::health::DiskStats* out) {
+    out->reads = static_cast<int64_t>(in.reads);
+    out->readMerges = static_cast<int64_t>(in.readMerges);
+    out->readSectors = static_cast<int64_t>(in.readSectors);
+    out->readTicks = static_cast<int64_t>(in.readTicks);
+    out->writes = static_cast<int64_t>(in.writes);
+    out->writeMerges = static_cast<int64_t>(in.writeMerges);
+    out->writeSectors = static_cast<int64_t>(in.writeSectors);
+    out->writeTicks = static_cast<int64_t>(in.writeTicks);
+    out->ioInFlight = static_cast<int64_t>(in.ioInFlight);
+    out->ioTicks = static_cast<int64_t>(in.ioTicks);
+    out->ioInQueue = static_cast<int64_t>(in.ioInQueue);
+    return true;
+}
+
+__attribute__((warn_unused_result)) bool translate(
+        const ::android::hardware::health::V2_1::HealthInfo& in,
+        aidl::android::hardware::health::HealthInfo* out) {
+    out->chargerAcOnline = static_cast<bool>(in.legacy.legacy.chargerAcOnline);
+    out->chargerUsbOnline = static_cast<bool>(in.legacy.legacy.chargerUsbOnline);
+    out->chargerWirelessOnline = static_cast<bool>(in.legacy.legacy.chargerWirelessOnline);
+    out->maxChargingCurrentMicroamps = static_cast<int32_t>(in.legacy.legacy.maxChargingCurrent);
+    out->maxChargingVoltageMicrovolts = static_cast<int32_t>(in.legacy.legacy.maxChargingVoltage);
+    out->batteryStatus = static_cast<aidl::android::hardware::health::BatteryStatus>(
+            in.legacy.legacy.batteryStatus);
+    out->batteryHealth = static_cast<aidl::android::hardware::health::BatteryHealth>(
+            in.legacy.legacy.batteryHealth);
+    out->batteryPresent = static_cast<bool>(in.legacy.legacy.batteryPresent);
+    out->batteryLevel = static_cast<int32_t>(in.legacy.legacy.batteryLevel);
+    out->batteryVoltageMillivolts = static_cast<int32_t>(in.legacy.legacy.batteryVoltage);
+    out->batteryTemperatureTenthsCelsius =
+            static_cast<int32_t>(in.legacy.legacy.batteryTemperature);
+    out->batteryCurrentMicroamps = static_cast<int32_t>(in.legacy.legacy.batteryCurrent);
+    out->batteryCycleCount = static_cast<int32_t>(in.legacy.legacy.batteryCycleCount);
+    out->batteryFullChargeUah = static_cast<int32_t>(in.legacy.legacy.batteryFullCharge);
+    out->batteryChargeCounterUah = static_cast<int32_t>(in.legacy.legacy.batteryChargeCounter);
+    out->batteryTechnology = in.legacy.legacy.batteryTechnology;
+    out->batteryCurrentAverageMicroamps = static_cast<int32_t>(in.legacy.batteryCurrentAverage);
+    out->diskStats.clear();
+    out->diskStats.resize(in.legacy.diskStats.size());
+    for (size_t i = 0; i < in.legacy.diskStats.size(); ++i)
+        if (!translate(in.legacy.diskStats[i], &out->diskStats[i])) return false;
+    out->storageInfos.clear();
+    out->storageInfos.resize(in.legacy.storageInfos.size());
+    for (size_t i = 0; i < in.legacy.storageInfos.size(); ++i)
+        if (!translate(in.legacy.storageInfos[i], &out->storageInfos[i])) return false;
+    out->batteryCapacityLevel = static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+            in.batteryCapacityLevel);
+    out->batteryChargeTimeToFullNowSeconds =
+            static_cast<int64_t>(in.batteryChargeTimeToFullNowSeconds);
+    out->batteryFullChargeDesignCapacityUah =
+            static_cast<int32_t>(in.batteryFullChargeDesignCapacityUah);
+    return true;
+}
+
+}  // namespace android::h2a
diff --git a/health/aidl/default/Android.bp b/health/aidl/default/Android.bp
new file mode 100644
index 0000000..9fc8b7d
--- /dev/null
+++ b/health/aidl/default/Android.bp
@@ -0,0 +1,85 @@
+// Copyright (C) 2021 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_defaults {
+    name: "libhealth_aidl_common_defaults",
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "android.hardware.health-V1-ndk",
+
+        // TODO(b/177269435): remove when BatteryMonitor works with AIDL HealthInfo.
+        "libhidlbase",
+    ],
+    static_libs: [
+        "libbatterymonitor",
+        "libhealthloop",
+
+        // TODO(b/177269435): remove when BatteryMonitor works with AIDL HealthInfo.
+        "android.hardware.health-translate-ndk",
+    ],
+}
+
+// AIDL version of libhealth2impl.
+// A helper library for health HAL implementation.
+// HAL implementations can link to this library and extend the Health class.
+cc_library_static {
+    name: "libhealth_aidl_impl",
+    defaults: [
+        "libhealth_aidl_common_defaults",
+    ],
+    export_include_dirs: ["include"],
+    export_static_lib_headers: [
+        "libbatterymonitor",
+    ],
+    srcs: [
+        "health-convert.cpp",
+        "HalHealthLoop.cpp",
+        "Health.cpp",
+        "LinkedCallback.cpp",
+    ],
+    visibility: [
+        ":__subpackages__",
+        "//hardware/interfaces/tests/extension/health:__subpackages__",
+    ],
+}
+
+// AIDL version of android.hardware.health@2.1-service.
+// Default binder service of the health HAL.
+cc_binary {
+    name: "android.hardware.health-service.example",
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.health-service.example.rc"],
+    vintf_fragments: ["android.hardware.health-service.example.xml"],
+    defaults: [
+        "libhealth_aidl_common_defaults",
+    ],
+    static_libs: [
+        "libhealth_aidl_impl",
+    ],
+    srcs: ["main.cpp"],
+}
diff --git a/health/aidl/default/HalHealthLoop.cpp b/health/aidl/default/HalHealthLoop.cpp
new file mode 100644
index 0000000..c9a081e
--- /dev/null
+++ b/health/aidl/default/HalHealthLoop.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 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 <health-impl/HalHealthLoop.h>
+
+#include <android-base/logging.h>
+
+#include <health-impl/Health.h>
+#include "health-convert.h"
+
+namespace aidl::android::hardware::health {
+
+// Unlike the HIDL version android::hardware::health::V2_1::implementation::HalHealthLoop,
+// do not define HalHealthLoop::Init because we no longer have Health::getHealthConfig.
+// Let the Health class handle Init.
+void HalHealthLoop::Init(struct healthd_config* config) {
+    callback_->OnInit(this, config);
+}
+
+void HalHealthLoop::Heartbeat() {
+    callback_->OnHeartbeat();
+}
+
+void HalHealthLoop::ScheduleBatteryUpdate() {
+    // ignore errors. impl may not be able to handle any callbacks, so
+    // update() may return errors.
+    if (auto res = service_->update(); !res.isOk()) {
+        LOG(WARNING) << "update() on the health HAL implementation failed with "
+                     << res.getDescription();
+    }
+
+    HealthInfo health_info;
+    auto res = service_->getHealthInfo(&health_info);
+    CHECK(res.isOk()) << "getHealthInfo() on the health HAL implementation failed with "
+                      << res.getDescription();
+    OnHealthInfoChanged(health_info);
+}
+
+int HalHealthLoop::PrepareToWait() {
+    return callback_->OnPrepareToWait();
+}
+
+void HalHealthLoop::OnHealthInfoChanged(const HealthInfo& health_info) {
+    callback_->OnHealthInfoChanged(health_info);
+    set_charger_online(health_info);
+    AdjustWakealarmPeriods(charger_online());
+}
+
+void HalHealthLoop::set_charger_online(const HealthInfo& health_info) {
+    charger_online_ = health_info.chargerAcOnline || health_info.chargerUsbOnline ||
+                      health_info.chargerWirelessOnline;
+}
+
+}  // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/Health.cpp b/health/aidl/default/Health.cpp
new file mode 100644
index 0000000..2d91ce0
--- /dev/null
+++ b/health/aidl/default/Health.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2021 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 "health-impl/Health.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/hardware/health/translate-ndk.h>
+#include <health/utils.h>
+
+#include "LinkedCallback.h"
+#include "health-convert.h"
+
+using std::string_literals::operator""s;
+
+namespace aidl::android::hardware::health {
+
+namespace {
+// Wrap LinkedCallback::OnCallbackDied() into a void(void*).
+void OnCallbackDiedWrapped(void* cookie) {
+    LinkedCallback* linked = reinterpret_cast<LinkedCallback*>(cookie);
+    linked->OnCallbackDied();
+}
+}  // namespace
+
+/*
+// If you need to call healthd_board_init, construct the Health instance with
+// the healthd_config after calling healthd_board_init:
+class MyHealth : public Health {
+  protected:
+    MyHealth() : Health(CreateConfig()) {}
+  private:
+    static std::unique_ptr<healthd_config> CreateConfig() {
+      auto config = std::make_unique<healthd_config>();
+      ::android::hardware::health::InitHealthdConfig(config.get());
+      healthd_board_init(config.get());
+      return std::move(config);
+    }
+};
+*/
+Health::Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config)
+    : instance_name_(instance_name),
+      healthd_config_(std::move(config)),
+      death_recipient_(AIBinder_DeathRecipient_new(&OnCallbackDiedWrapped)) {
+    battery_monitor_.init(healthd_config_.get());
+}
+
+//
+// Getters.
+//
+
+template <typename T>
+static ndk::ScopedAStatus GetProperty(::android::BatteryMonitor* monitor, int id, T defaultValue,
+                                      T* out) {
+    *out = defaultValue;
+    struct ::android::BatteryProperty prop;
+    ::android::status_t err = monitor->getProperty(static_cast<int>(id), &prop);
+    if (err == ::android::OK) {
+        *out = static_cast<T>(prop.valueInt64);
+    } else {
+        LOG(DEBUG) << "getProperty(" << id << ")"
+                   << " fails: (" << err << ") " << ::android::statusToString(err);
+    }
+
+    switch (err) {
+        case ::android::OK:
+            return ndk::ScopedAStatus::ok();
+        case ::android::NAME_NOT_FOUND:
+            return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+        default:
+            return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
+    }
+}
+
+ndk::ScopedAStatus Health::getChargeCounterUah(int32_t* out) {
+    return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CHARGE_COUNTER, 0, out);
+}
+
+ndk::ScopedAStatus Health::getCurrentNowMicroamps(int32_t* out) {
+    return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_NOW, 0, out);
+}
+
+ndk::ScopedAStatus Health::getCurrentAverageMicroamps(int32_t* out) {
+    return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_AVG, 0, out);
+}
+
+ndk::ScopedAStatus Health::getCapacity(int32_t* out) {
+    return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CAPACITY, 0, out);
+}
+
+ndk::ScopedAStatus Health::getEnergyCounterNwh(int64_t* out) {
+    return GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_ENERGY_COUNTER, 0, out);
+}
+
+ndk::ScopedAStatus Health::getChargeStatus(BatteryStatus* out) {
+    return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_BATTERY_STATUS,
+                       BatteryStatus::UNKNOWN, out);
+}
+
+ndk::ScopedAStatus Health::getDiskStats(std::vector<DiskStats>*) {
+    // This implementation does not support DiskStats. An implementation may extend this
+    // class and override this function to support disk stats.
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Health::getStorageInfo(std::vector<StorageInfo>*) {
+    // This implementation does not support StorageInfo. An implementation may extend this
+    // class and override this function to support storage info.
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {
+    battery_monitor_.updateValues();
+
+    // TODO(b/177269435): BatteryMonitor should store AIDL HealthInfo instead.
+    auto health_info_2_1 = battery_monitor_.getHealthInfo_2_1();
+    if (!::android::h2a::translate(health_info_2_1, out)) {
+        return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                IHealth::STATUS_UNKNOWN, "Cannot translate HIDL HealthInfo to AIDL");
+    }
+
+    // Fill in storage infos; these aren't retrieved by BatteryMonitor.
+    if (auto res = getStorageInfo(&out->storageInfos); !res.isOk()) {
+        if (res.getServiceSpecificError() == 0 &&
+            res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
+            return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    IHealth::STATUS_UNKNOWN,
+                    ("getStorageInfo fails: " + res.getDescription()).c_str());
+        }
+        LOG(DEBUG) << "getHealthInfo: getStorageInfo fails with service-specific error, clearing: "
+                   << res.getDescription();
+        out->storageInfos = {};
+    }
+    if (auto res = getDiskStats(&out->diskStats); !res.isOk()) {
+        if (res.getServiceSpecificError() == 0 &&
+            res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
+            return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    IHealth::STATUS_UNKNOWN,
+                    ("getDiskStats fails: " + res.getDescription()).c_str());
+        }
+        LOG(DEBUG) << "getHealthInfo: getDiskStats fails with service-specific error, clearing: "
+                   << res.getDescription();
+        out->diskStats = {};
+    }
+
+    // A subclass may want to update health info struct before returning it.
+    UpdateHealthInfo(out);
+
+    return ndk::ScopedAStatus::ok();
+}
+
+binder_status_t Health::dump(int fd, const char**, uint32_t) {
+    battery_monitor_.dumpState(fd);
+
+    ::android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
+    HealthInfo health_info;
+    auto res = getHealthInfo(&health_info);
+    if (res.isOk()) {
+        ::android::base::WriteStringToFd(health_info.toString(), fd);
+    } else {
+        ::android::base::WriteStringToFd(res.getDescription(), fd);
+    }
+
+    fsync(fd);
+    return STATUS_OK;
+}
+
+std::optional<bool> Health::ShouldKeepScreenOn() {
+    if (!healthd_config_->screen_on) {
+        return std::nullopt;
+    }
+
+    HealthInfo health_info;
+    auto res = getHealthInfo(&health_info);
+    if (!res.isOk()) {
+        return std::nullopt;
+    }
+
+    ::android::BatteryProperties props = {};
+    convert(health_info, &props);
+    return healthd_config_->screen_on(&props);
+}
+
+namespace {
+bool IsDeadObjectLogged(const ndk::ScopedAStatus& ret) {
+    if (ret.isOk()) return false;
+    if (ret.getStatus() == ::STATUS_DEAD_OBJECT) return true;
+    LOG(ERROR) << "Cannot call healthInfoChanged on callback: " << ret.getDescription();
+    return false;
+}
+}  // namespace
+
+//
+// Subclass helpers / overrides
+//
+
+void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
+    /*
+        // Sample code for a subclass to implement this:
+        // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
+        health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();
+
+        // If you need to call healthd_board_battery_update, modify its signature
+        // and implementation to operate on HealthInfo directly, then call:
+        healthd_board_battery_update(health_info);
+    */
+}
+
+//
+// Methods that handle callbacks.
+//
+
+ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
+    if (callback == nullptr) {
+        // For now, this shouldn't happen because argument is not nullable.
+        return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+    }
+
+    {
+        std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
+        callbacks_.emplace_back(LinkedCallback::Make(ref<Health>(), callback));
+        // unlock
+    }
+
+    HealthInfo health_info;
+    if (auto res = getHealthInfo(&health_info); !res.isOk()) {
+        LOG(WARNING) << "Cannot call getHealthInfo: " << res.getDescription();
+        // No health info to send, so return early.
+        return ndk::ScopedAStatus::ok();
+    }
+
+    if (auto res = callback->healthInfoChanged(health_info); IsDeadObjectLogged(res)) {
+        (void)unregisterCallback(callback);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Health::unregisterCallback(
+        const std::shared_ptr<IHealthInfoCallback>& callback) {
+    if (callback == nullptr) {
+        // For now, this shouldn't happen because argument is not nullable.
+        return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+    }
+
+    std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
+
+    auto matches = [callback](const auto& linked) {
+        return linked->callback() == callback;  // compares shared_ptr
+    };
+    auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), matches);
+    bool removed = (it != callbacks_.end());
+    callbacks_.erase(it, callbacks_.end());  // calls unlinkToDeath on deleted callbacks.
+    return removed ? ndk::ScopedAStatus::ok()
+                   : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+}
+
+// A combination of the HIDL version
+//   android::hardware::health::V2_1::implementation::Health::update() and
+//   android::hardware::health::V2_1::implementation::BinderHealth::update()
+ndk::ScopedAStatus Health::update() {
+    HealthInfo health_info;
+    if (auto res = getHealthInfo(&health_info); !res.isOk()) {
+        LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
+        // Propagate service specific errors. If there's none, report unknown error.
+        if (res.getServiceSpecificError() != 0 ||
+            res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+            return res;
+        }
+        return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
+    }
+    battery_monitor_.logValues();
+    OnHealthInfoChanged(health_info);
+    return ndk::ScopedAStatus::ok();
+}
+
+void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
+    // Notify all callbacks
+    std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
+    // is_dead notifies a callback and return true if it is dead.
+    auto is_dead = [&](const auto& linked) {
+        auto res = linked->callback()->healthInfoChanged(health_info);
+        return IsDeadObjectLogged(res);
+    };
+    auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), is_dead);
+    callbacks_.erase(it, callbacks_.end());  // calls unlinkToDeath on deleted callbacks.
+    lock.unlock();
+
+    // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
+}
+
+void Health::BinderEvent(uint32_t /*epevents*/) {
+    if (binder_fd_ >= 0) {
+        ABinderProcess_handlePolledCommands();
+    }
+}
+
+void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
+    LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";
+
+    // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
+    // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
+    *config = *healthd_config_.get();
+
+    binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);
+
+    if (status == ::STATUS_OK && binder_fd_ >= 0) {
+        std::shared_ptr<Health> thiz = ref<Health>();
+        auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
+        if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
+            PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
+        }
+    }
+
+    std::string health_name = IHealth::descriptor + "/"s + instance_name_;
+    CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
+            << instance_name_ << ": Failed to register HAL";
+
+    LOG(INFO) << instance_name_ << ": Hal init done";
+}
+
+// Unlike hwbinder, for binder, there's no need to explicitly call flushCommands()
+// in PrepareToWait(). See b/139697085.
+
+}  // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/LinkedCallback.cpp b/health/aidl/default/LinkedCallback.cpp
new file mode 100644
index 0000000..2985ffe
--- /dev/null
+++ b/health/aidl/default/LinkedCallback.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 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 <android-base/logging.h>
+#include <android/binder_ibinder.h>
+
+#include <health-impl/Health.h>
+#include <utils/Errors.h>
+
+#include "LinkedCallback.h"
+
+namespace aidl::android::hardware::health {
+
+std::unique_ptr<LinkedCallback> LinkedCallback::Make(
+        std::shared_ptr<Health> service, std::shared_ptr<IHealthInfoCallback> callback) {
+    std::unique_ptr<LinkedCallback> ret(new LinkedCallback());
+    binder_status_t linkRet =
+            AIBinder_linkToDeath(callback->asBinder().get(), service->death_recipient_.get(),
+                                 reinterpret_cast<void*>(ret.get()));
+    if (linkRet != ::STATUS_OK) {
+        LOG(WARNING) << __func__ << "Cannot link to death: " << linkRet;
+        return nullptr;
+    }
+    ret->service_ = service;
+    ret->callback_ = std::move(callback);
+    return ret;
+}
+
+LinkedCallback::LinkedCallback() = default;
+
+LinkedCallback::~LinkedCallback() {
+    if (callback_ == nullptr) {
+        return;
+    }
+    auto status =
+            AIBinder_unlinkToDeath(callback_->asBinder().get(), service()->death_recipient_.get(),
+                                   reinterpret_cast<void*>(this));
+    if (status != STATUS_OK && status != STATUS_DEAD_OBJECT) {
+        LOG(WARNING) << __func__ << "Cannot unlink to death: " << ::android::statusToString(status);
+    }
+}
+
+std::shared_ptr<Health> LinkedCallback::service() {
+    auto service_sp = service_.lock();
+    CHECK_NE(nullptr, service_sp);
+    return service_sp;
+}
+
+void LinkedCallback::OnCallbackDied() {
+    service()->unregisterCallback(callback_);
+}
+
+}  // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/LinkedCallback.h b/health/aidl/default/LinkedCallback.h
new file mode 100644
index 0000000..82490a7
--- /dev/null
+++ b/health/aidl/default/LinkedCallback.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 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 <memory>
+
+#include <aidl/android/hardware/health/IHealthInfoCallback.h>
+#include <android-base/macros.h>
+#include <android/binder_auto_utils.h>
+
+#include <health-impl/Health.h>
+
+namespace aidl::android::hardware::health {
+
+// Type of the cookie pointer in linkToDeath.
+// A (Health, IHealthInfoCallback) tuple.
+class LinkedCallback {
+  public:
+    // Automatically linkToDeath upon construction with the returned object as the cookie.
+    // service->death_reciepient() should be from CreateDeathRecipient().
+    // Not using a strong reference to |service| to avoid circular reference. The lifetime
+    // of |service| must be longer than this LinkedCallback object.
+    static std::unique_ptr<LinkedCallback> Make(std::shared_ptr<Health> service,
+                                                std::shared_ptr<IHealthInfoCallback> callback);
+
+    // Automatically unlinkToDeath upon destruction. So, it is always safe to reinterpret_cast
+    // the cookie back to the LinkedCallback object.
+    ~LinkedCallback();
+
+    // The wrapped IHealthInfoCallback object.
+    const std::shared_ptr<IHealthInfoCallback>& callback() const { return callback_; }
+
+    // On callback died, unreigster it from the service.
+    void OnCallbackDied();
+
+  private:
+    LinkedCallback();
+    DISALLOW_COPY_AND_ASSIGN(LinkedCallback);
+
+    std::shared_ptr<Health> service();
+
+    std::weak_ptr<Health> service_;
+    std::shared_ptr<IHealthInfoCallback> callback_;
+};
+
+}  // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/android.hardware.health-service.example.rc b/health/aidl/default/android.hardware.health-service.example.rc
new file mode 100644
index 0000000..b393c58
--- /dev/null
+++ b/health/aidl/default/android.hardware.health-service.example.rc
@@ -0,0 +1,6 @@
+service vendor.health-default /vendor/bin/hw/android.hardware.health-service.example
+    class hal
+    user system
+    group system
+    capabilities WAKE_ALARM BLOCK_SUSPEND
+    file /dev/kmsg w
diff --git a/health/aidl/default/android.hardware.health-service.example.xml b/health/aidl/default/android.hardware.health-service.example.xml
new file mode 100644
index 0000000..98026cb
--- /dev/null
+++ b/health/aidl/default/android.hardware.health-service.example.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.health</name>
+        <version>1</version>
+        <fqname>IHealth/default</fqname>
+    </hal>
+</manifest>
diff --git a/health/aidl/default/health-convert.cpp b/health/aidl/default/health-convert.cpp
new file mode 100644
index 0000000..b5251f4
--- /dev/null
+++ b/health/aidl/default/health-convert.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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 "health-convert.h"
+
+namespace aidl::android::hardware::health {
+
+void convert(const HealthInfo& info, struct ::android::BatteryProperties* p) {
+    p->chargerAcOnline = info.chargerAcOnline;
+    p->chargerUsbOnline = info.chargerUsbOnline;
+    p->chargerWirelessOnline = info.chargerWirelessOnline;
+    p->maxChargingCurrent = info.maxChargingCurrentMicroamps;
+    p->maxChargingVoltage = info.maxChargingVoltageMicrovolts;
+    p->batteryStatus = static_cast<int>(info.batteryStatus);
+    p->batteryHealth = static_cast<int>(info.batteryHealth);
+    p->batteryPresent = info.batteryPresent;
+    p->batteryLevel = info.batteryLevel;
+    p->batteryVoltage = info.batteryVoltageMillivolts;
+    p->batteryTemperature = info.batteryTemperatureTenthsCelsius;
+    p->batteryCurrent = info.batteryCurrentMicroamps;
+    p->batteryCycleCount = info.batteryCycleCount;
+    p->batteryFullCharge = info.batteryFullChargeUah;
+    p->batteryChargeCounter = info.batteryChargeCounterUah;
+    p->batteryTechnology = ::android::String8(info.batteryTechnology.c_str());
+}
+
+}  // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/health-convert.h b/health/aidl/default/health-convert.h
new file mode 100644
index 0000000..179ffc4
--- /dev/null
+++ b/health/aidl/default/health-convert.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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/health/HealthInfo.h>
+#include <batteryservice/BatteryService.h>
+#include <healthd/healthd.h>
+
+// Conversion between healthd types and AIDL health HAL types. Note that most
+// of the conversion loses information, because these types have a different
+// set of information.
+
+namespace aidl::android::hardware::health {
+
+void convert(const HealthInfo& info, struct ::android::BatteryProperties* out);
+
+}  // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/include/health-impl/HalHealthLoop.h b/health/aidl/default/include/health-impl/HalHealthLoop.h
new file mode 100644
index 0000000..c46aaa4
--- /dev/null
+++ b/health/aidl/default/include/health-impl/HalHealthLoop.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 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 <memory>
+#include <optional>
+
+#include <aidl/android/hardware/health/IHealth.h>
+#include <health/HealthLoop.h>
+
+namespace aidl::android::hardware::health {
+
+class HalHealthLoop;
+
+class HalHealthLoopCallback {
+  public:
+    virtual ~HalHealthLoopCallback() = default;
+
+    // Called by HalHealthLoop::Init
+    virtual void OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) = 0;
+    // Called by HalHealthLoop::Heartbeat
+    virtual void OnHeartbeat(){};
+    // Called by HalHealthLoop::PrepareToWait
+    virtual int OnPrepareToWait() { return -1; }
+    // Called by HalHealthLoop::ScheduleBatteryUpdate
+    virtual void OnHealthInfoChanged(const HealthInfo&) {}
+};
+
+// AIDL version of android::hardware::health::V2_1::implementation::HalHealthLoop.
+// An implementation of HealthLoop for using a given health HAL.
+class HalHealthLoop final : public ::android::hardware::health::HealthLoop {
+  public:
+    // Caller must ensure that the lifetime of service_ is not shorter than this object.
+    HalHealthLoop(std::shared_ptr<IHealth> service, std::shared_ptr<HalHealthLoopCallback> callback)
+        : service_(std::move(service)), callback_(std::move(callback)) {}
+
+    using HealthLoop::RegisterEvent;
+
+    bool charger_online() const { return charger_online_; }
+
+  protected:
+    virtual void Init(struct healthd_config* config) override;
+    virtual void Heartbeat() override;
+    virtual int PrepareToWait() override;
+    virtual void ScheduleBatteryUpdate() override;
+
+  private:
+    std::shared_ptr<IHealth> service_;
+    std::shared_ptr<HalHealthLoopCallback> callback_;
+    bool charger_online_ = false;
+
+    // Helpers of OnHealthInfoChanged.
+    void set_charger_online(const HealthInfo& health_info);
+
+    // HealthLoop periodically calls ScheduleBatteryUpdate, which calls
+    // OnHealthInfoChanged callback. A subclass of the callback can override
+    // HalHealthLoopCallback::OnHealthInfoChanged to
+    // broadcast the health_info to interested listeners.
+    // This adjust uevents / wakealarm periods.
+    void OnHealthInfoChanged(const HealthInfo& health_info);
+};
+
+}  // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/include/health-impl/Health.h b/health/aidl/default/include/health-impl/Health.h
new file mode 100644
index 0000000..e49f44c
--- /dev/null
+++ b/health/aidl/default/include/health-impl/Health.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 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 <memory>
+#include <optional>
+
+#include <aidl/android/hardware/health/BnHealth.h>
+#include <aidl/android/hardware/health/IHealthInfoCallback.h>
+#include <android/binder_auto_utils.h>
+#include <health-impl/HalHealthLoop.h>
+#include <healthd/BatteryMonitor.h>
+#include <healthd/healthd.h>
+
+namespace aidl::android::hardware::health {
+
+class LinkedCallback;
+
+// AIDL version of android::hardware::health::V2_1::implementation::Health and BinderHealth.
+// There's no need to separate the two in AIDL because AIDL does not support passthrough transport.
+//
+// Instead of inheriting from HalHealthLoop directly, implements the callback interface to
+// HalHealthLoop instead.
+//
+// Sample implementation of health HAL.
+class Health : public BnHealth, public HalHealthLoopCallback {
+  public:
+    // Initialize with |config|.
+    // A subclass may modify |config| before passing it to the parent constructor.
+    // See implementation of Health for code samples.
+    Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config);
+
+    ndk::ScopedAStatus registerCallback(
+            const std::shared_ptr<IHealthInfoCallback>& callback) override;
+    ndk::ScopedAStatus unregisterCallback(
+            const std::shared_ptr<IHealthInfoCallback>& callback) override;
+    ndk::ScopedAStatus update() override;
+
+    // A subclass should not override this. Override UpdateHealthInfo instead.
+    ndk::ScopedAStatus getHealthInfo(HealthInfo* out) override final;
+
+    // A subclass is recommended to override the path in healthd_config in the constructor.
+    // Only override these if there are no suitable kernel interfaces to read these values.
+    ndk::ScopedAStatus getChargeCounterUah(int32_t* out) override;
+    ndk::ScopedAStatus getCurrentNowMicroamps(int32_t* out) override;
+    ndk::ScopedAStatus getCurrentAverageMicroamps(int32_t* out) override;
+    ndk::ScopedAStatus getCapacity(int32_t* out) override;
+    ndk::ScopedAStatus getChargeStatus(BatteryStatus* out) override;
+
+    // A subclass may either override these or provide function pointers in
+    // in healthd_config in the constructor.
+    // Prefer overriding these for efficiency.
+    ndk::ScopedAStatus getEnergyCounterNwh(int64_t* out) override;
+
+    // A subclass may override these for a specific device.
+    // The default implementations return nothing in |out|.
+    ndk::ScopedAStatus getDiskStats(std::vector<DiskStats>* out) override;
+    ndk::ScopedAStatus getStorageInfo(std::vector<StorageInfo>* out) override;
+
+    // A subclass may override these to provide a different implementation.
+    binder_status_t dump(int fd, const char** args, uint32_t num_args) override;
+
+    // HalHealthLoopCallback implementation.
+    void OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) override;
+    void OnHealthInfoChanged(const HealthInfo& health_info) override;
+
+    // A subclass may override this if it wants to handle binder events differently.
+    virtual void BinderEvent(uint32_t epevents);
+
+    // A subclass may override this to determine whether screen should be kept on in charger mode.
+    // Default is to invoke healthd_config->screen_on() on the BatteryProperties converted
+    // from getHealthInfo.
+    // Prefer overriding these to providing screen_on in healthd_config in the constructor
+    // for efficiency.
+    virtual std::optional<bool> ShouldKeepScreenOn();
+
+  protected:
+    // A subclass can override this to modify any health info object before
+    // returning to clients. This is similar to healthd_board_battery_update().
+    // By default, it does nothing.
+    // See implementation of Health for code samples.
+    virtual void UpdateHealthInfo(HealthInfo* health_info);
+
+  private:
+    friend LinkedCallback;  // for exposing death_recipient_
+
+    bool unregisterCallbackInternal(std::shared_ptr<IHealthInfoCallback> callback);
+
+    std::string instance_name_;
+    ::android::BatteryMonitor battery_monitor_;
+    std::unique_ptr<struct healthd_config> healthd_config_;
+
+    ndk::ScopedAIBinder_DeathRecipient death_recipient_;
+    int binder_fd_ = -1;
+    std::mutex callbacks_lock_;
+    std::vector<std::unique_ptr<LinkedCallback>> callbacks_;
+};
+
+}  // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/main.cpp b/health/aidl/default/main.cpp
new file mode 100644
index 0000000..014ae34
--- /dev/null
+++ b/health/aidl/default/main.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 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 <android-base/logging.h>
+#include <android/binder_interface_utils.h>
+#include <health-impl/Health.h>
+#include <health/utils.h>
+
+using aidl::android::hardware::health::HalHealthLoop;
+using aidl::android::hardware::health::Health;
+
+static constexpr const char* gInstanceName = "default";
+
+int main() {
+    // TODO(b/203246116): handle charger
+    // make a default health service
+    auto config = std::make_unique<healthd_config>();
+    ::android::hardware::health::InitHealthdConfig(config.get());
+    auto binder = ndk::SharedRefBase::make<Health>(gInstanceName, std::move(config));
+    auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder);
+    return hal_health_loop->StartLoop();
+}
diff --git a/health/aidl/include/android/hardware/health/translate-ndk.h b/health/aidl/include/android/hardware/health/translate-ndk.h
new file mode 100644
index 0000000..2f8fe04
--- /dev/null
+++ b/health/aidl/include/android/hardware/health/translate-ndk.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 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/health/BatteryCapacityLevel.h>
+#include <aidl/android/hardware/health/DiskStats.h>
+#include <aidl/android/hardware/health/HealthInfo.h>
+#include <aidl/android/hardware/health/StorageInfo.h>
+#include <android/hardware/health/2.0/types.h>
+#include <android/hardware/health/2.1/types.h>
+#include <limits>
+
+namespace android::h2a {
+
+__attribute__((warn_unused_result)) bool translate(
+        const ::android::hardware::health::V2_0::StorageInfo& in,
+        aidl::android::hardware::health::StorageInfo* out);
+__attribute__((warn_unused_result)) bool translate(
+        const ::android::hardware::health::V2_0::DiskStats& in,
+        aidl::android::hardware::health::DiskStats* out);
+__attribute__((warn_unused_result)) bool translate(
+        const ::android::hardware::health::V2_1::HealthInfo& in,
+        aidl::android::hardware::health::HealthInfo* out);
+
+}  // namespace android::h2a
diff --git a/health/utils/libhealthloop/include/health/HealthLoop.h b/health/utils/libhealthloop/include/health/HealthLoop.h
index 693e6cb..54b2740 100644
--- a/health/utils/libhealthloop/include/health/HealthLoop.h
+++ b/health/utils/libhealthloop/include/health/HealthLoop.h
@@ -46,6 +46,12 @@
     // Init is called right after epollfd_ is initialized (so RegisterEvent
     // is allowed) but before other things are initialized (so SetChargerOnline
     // is not allowed.)
+    // The implementation of Init() should pull configuration from the
+    // underlying health HAL (via getHealthConfig()), and store it into
+    // |config|. The implementation may not initialize:
+    // - screen_on, because charger calls getScreenOn() from the HAL directly
+    // - ignorePowerSupplyNames, because it isn't used by any clients of the
+    // health HAL.
     virtual void Init(healthd_config* config) = 0;
     virtual void Heartbeat() = 0;
     virtual int PrepareToWait() = 0;
diff --git a/neuralnetworks/1.0/utils/Android.bp b/neuralnetworks/1.0/utils/Android.bp
index 8c51c67..31cdded 100644
--- a/neuralnetworks/1.0/utils/Android.bp
+++ b/neuralnetworks/1.0/utils/Android.bp
@@ -31,6 +31,7 @@
     export_include_dirs: ["include"],
     cflags: ["-Wthread-safety"],
     static_libs: [
+        "libarect",
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
     ],
@@ -40,6 +41,11 @@
     export_static_lib_headers: [
         "neuralnetworks_utils_hal_common",
     ],
+    target: {
+        android: {
+            shared_libs: ["libnativewindow"],
+        },
+    },
 }
 
 cc_test {
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
index 3b32e1d..1ab9dcb 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
@@ -24,9 +24,10 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
 
+#include "nnapi/hal/1.0/ProtectCallback.h"
+
 // See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
 // lifetimes across processes and for protecting asynchronous calls across HIDL.
 
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 5d4bdbc..a770d06 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
@@ -36,6 +36,7 @@
 GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation);
 GeneralResult<Model::OperandValues> unvalidatedConvert(
         const hardware::hidl_vec<uint8_t>& operandValues);
+GeneralResult<SharedHandle> unvalidatedConvert(const hardware::hidl_handle& handle);
 GeneralResult<SharedMemory> unvalidatedConvert(const hardware::hidl_memory& memory);
 GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model);
 GeneralResult<Request::Argument> unvalidatedConvert(
@@ -65,6 +66,7 @@
 nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
 nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
         const nn::Model::OperandValues& operandValues);
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle);
 nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory);
 nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
 nn::GeneralResult<RequestArgument> unvalidatedConvert(const nn::Request::Argument& requestArgument);
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
index db3b2ad..0a6ca3e 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
@@ -24,7 +24,8 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
+
+#include "nnapi/hal/1.0/ProtectCallback.h"
 
 #include <functional>
 #include <memory>
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
index e201e25..66497c2 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
@@ -22,9 +22,9 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include "PreparedModel.h"
+#include "nnapi/hal/1.0/ProtectCallback.h"
 
 #include <memory>
 #include <utility>
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/HandleError.h
similarity index 60%
rename from neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
rename to neuralnetworks/1.0/utils/include/nnapi/hal/1.0/HandleError.h
index 209b663..8e02633 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/HandleError.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_HANDLE_ERROR_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_HANDLE_ERROR_H
 
 #include <android/hidl/base/1.0/IBase.h>
 #include <hidl/HidlSupport.h>
@@ -27,7 +27,7 @@
 namespace android::hardware::neuralnetworks::utils {
 
 template <typename Type>
-nn::GeneralResult<Type> handleTransportError(const hardware::Return<Type>& ret) {
+nn::GeneralResult<Type> handleTransportError(const Return<Type>& ret) {
     if (ret.isDeadObject()) {
         return nn::error(nn::ErrorStatus::DEAD_OBJECT)
                << "Return<>::isDeadObject returned true: " << ret.description();
@@ -52,45 +52,13 @@
         std::move(result).value();                                                           \
     })
 
-template <typename Type>
-nn::GeneralResult<Type> makeGeneralFailure(
-        nn::Result<Type> result, nn::ErrorStatus status = nn::ErrorStatus::GENERAL_FAILURE) {
-    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 {};
-    }
-}
-
-template <typename Type>
-nn::ExecutionResult<Type> makeExecutionFailure(nn::GeneralResult<Type> result) {
-    if (!result.has_value()) {
-        const auto [message, status] = std::move(result).error();
-        return nn::error(status) << message;
-    }
-    if constexpr (!std::is_same_v<Type, void>) {
-        return std::move(result).value();
-    } else {
-        return {};
-    }
-}
-
-template <typename Type>
-nn::ExecutionResult<Type> makeExecutionFailure(
-        nn::Result<Type> result, nn::ErrorStatus status = nn::ErrorStatus::GENERAL_FAILURE) {
-    return makeExecutionFailure(makeGeneralFailure(result, status));
-}
-
-#define HANDLE_HAL_STATUS(status)                                       \
-    if (const auto canonical = ::android::nn::convert(status).value_or( \
-                ::android::nn::ErrorStatus::GENERAL_FAILURE);           \
-        canonical == ::android::nn::ErrorStatus::NONE) {                \
-    } else                                                              \
+#define HANDLE_STATUS_HIDL(status)                                                            \
+    if (const ::android::nn::ErrorStatus canonical = ::android::nn::convert(status).value_or( \
+                ::android::nn::ErrorStatus::GENERAL_FAILURE);                                 \
+        canonical == ::android::nn::ErrorStatus::NONE) {                                      \
+    } else                                                                                    \
         return NN_ERROR(canonical)
 
 }  // namespace android::hardware::neuralnetworks::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_HANDLE_ERROR_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
index 48be595..bdb5b54 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
@@ -22,7 +22,8 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
+
+#include "nnapi/hal/1.0/ProtectCallback.h"
 
 #include <memory>
 #include <tuple>
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/ProtectCallback.h
similarity index 93%
rename from neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
rename to neuralnetworks/1.0/utils/include/nnapi/hal/1.0/ProtectCallback.h
index 05110bc..7418cfa 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/ProtectCallback.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PROTECT_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PROTECT_CALLBACK_H
 
 #include <android-base/scopeguard.h>
 #include <android-base/thread_annotations.h>
@@ -98,4 +98,4 @@
 
 }  // namespace android::hardware::neuralnetworks::utils
 
-#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PROTECT_CALLBACK_H
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 1baabdf..5c1480e 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
@@ -25,7 +25,6 @@
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
 #include <nnapi/Validation.h>
-#include <nnapi/hal/HandleError.h>
 
 namespace android::hardware::neuralnetworks::V1_0::utils {
 
@@ -50,8 +49,8 @@
 }
 
 template <typename Type>
-nn::GeneralResult<void> compliantVersion(const Type& canonical) {
-    const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+nn::Result<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(nn::validate(canonical));
     if (version > kVersion) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
diff --git a/neuralnetworks/1.0/utils/src/Callbacks.cpp b/neuralnetworks/1.0/utils/src/Callbacks.cpp
index ea3ea56..7b478ae 100644
--- a/neuralnetworks/1.0/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.0/utils/src/Callbacks.cpp
@@ -17,7 +17,9 @@
 #include "Callbacks.h"
 
 #include "Conversions.h"
+#include "HandleError.h"
 #include "PreparedModel.h"
+#include "ProtectCallback.h"
 #include "Utils.h"
 
 #include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
@@ -27,8 +29,6 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
 
 #include <utility>
@@ -40,19 +40,19 @@
 
 nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
         ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
-    HANDLE_HAL_STATUS(status) << "get supported operations failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "get supported operations failed with " << toString(status);
     return supportedOperations;
 }
 
 nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
         ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
-    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
     return NN_TRY(PreparedModel::create(preparedModel));
 }
 
 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
         ErrorStatus status) {
-    HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
     return {};
 }
 
diff --git a/neuralnetworks/1.0/utils/src/Conversions.cpp b/neuralnetworks/1.0/utils/src/Conversions.cpp
index c0498eb..daa10fd 100644
--- a/neuralnetworks/1.0/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -37,6 +37,11 @@
 
 #include "Utils.h"
 
+#ifdef __ANDROID__
+#include <android/hardware_buffer.h>
+#include <vndk/hardware_buffer.h>
+#endif  // __ANDROID__
+
 namespace {
 
 template <typename Type>
@@ -49,6 +54,7 @@
 namespace android::nn {
 namespace {
 
+using hardware::hidl_handle;
 using hardware::hidl_memory;
 using hardware::hidl_vec;
 
@@ -74,6 +80,121 @@
     return canonical;
 }
 
+nn::GeneralResult<nn::Memory::Unknown::Handle> unknownHandleFromNativeHandle(
+        const native_handle_t* handle) {
+    if (handle == nullptr) {
+        return NN_ERROR() << "unknownHandleFromNativeHandle failed because handle is nullptr";
+    }
+
+    std::vector<base::unique_fd> fds =
+            NN_TRY(nn::dupFds(handle->data + 0, handle->data + handle->numFds));
+
+    std::vector<int> ints(handle->data + handle->numFds,
+                          handle->data + handle->numFds + handle->numInts);
+
+    return nn::Memory::Unknown::Handle{.fds = std::move(fds), .ints = std::move(ints)};
+}
+
+nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory) {
+    CHECK_LE(memory.size(), std::numeric_limits<size_t>::max());
+    if (!memory.valid()) {
+        return NN_ERROR() << "Unable to convert invalid hidl_memory";
+    }
+
+    if (memory.name() == "ashmem") {
+        if (memory.handle()->numFds != 1) {
+            return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
+                              << memory.handle()->numFds << " numFds, but expected 1";
+        }
+        if (memory.handle()->numInts != 0) {
+            return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
+                              << memory.handle()->numInts << " numInts, but expected 0";
+        }
+        auto handle = nn::Memory::Ashmem{
+                .fd = NN_TRY(nn::dupFd(memory.handle()->data[0])),
+                .size = static_cast<size_t>(memory.size()),
+        };
+        return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
+    }
+
+    if (memory.name() == "mmap_fd") {
+        if (memory.handle()->numFds != 1) {
+            return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
+                              << memory.handle()->numFds << " numFds, but expected 1";
+        }
+        if (memory.handle()->numInts != 3) {
+            return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
+                              << memory.handle()->numInts << " numInts, but expected 3";
+        }
+
+        const int fd = memory.handle()->data[0];
+        const int prot = memory.handle()->data[1];
+        const int lower = memory.handle()->data[2];
+        const int higher = memory.handle()->data[3];
+        const size_t offset = nn::getOffsetFromInts(lower, higher);
+
+        return nn::createSharedMemoryFromFd(static_cast<size_t>(memory.size()), prot, fd, offset);
+    }
+
+    if (memory.name() != "hardware_buffer_blob") {
+        auto handle = nn::Memory::Unknown{
+                .handle = NN_TRY(unknownHandleFromNativeHandle(memory.handle())),
+                .size = static_cast<size_t>(memory.size()),
+                .name = memory.name(),
+        };
+        return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
+    }
+
+#ifdef __ANDROID__
+    constexpr auto roundUpToMultiple = [](uint32_t value, uint32_t multiple) -> uint32_t {
+        return (value + multiple - 1) / multiple * multiple;
+    };
+
+    const auto size = memory.size();
+    const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
+    const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+    const uint32_t width = size;
+    const uint32_t height = 1;  // height is always 1 for BLOB mode AHardwareBuffer.
+    const uint32_t layers = 1;  // layers is always 1 for BLOB mode AHardwareBuffer.
+
+    // AHardwareBuffer_createFromHandle() might fail because an allocator
+    // expects a specific stride value. In that case, we try to guess it by
+    // aligning the width to small powers of 2.
+    // TODO(b/174120849): Avoid stride assumptions.
+    AHardwareBuffer* hardwareBuffer = nullptr;
+    status_t status = UNKNOWN_ERROR;
+    for (uint32_t alignment : {1, 4, 32, 64, 128, 2, 8, 16}) {
+        const uint32_t stride = roundUpToMultiple(width, alignment);
+        AHardwareBuffer_Desc desc{
+                .width = width,
+                .height = height,
+                .layers = layers,
+                .format = format,
+                .usage = usage,
+                .stride = stride,
+        };
+        status = AHardwareBuffer_createFromHandle(&desc, memory.handle(),
+                                                  AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
+                                                  &hardwareBuffer);
+        if (status == NO_ERROR) {
+            break;
+        }
+    }
+    if (status != NO_ERROR) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "Can't create AHardwareBuffer from handle. Error: " << status;
+    }
+
+    return nn::createSharedMemoryFromAHWB(hardwareBuffer, /*takeOwnership=*/true);
+#else   // __ANDROID__
+    LOG(FATAL) << "nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const "
+                  "hidl_memory& memory): Not Available on Host Build";
+    return (NN_ERROR() << "createSharedMemoryFromHidlMemory failed")
+            .
+            operator nn::GeneralResult<nn::SharedMemory>();
+#endif  // __ANDROID__
+}
+
 }  // anonymous namespace
 
 GeneralResult<OperandType> unvalidatedConvert(const hal::V1_0::OperandType& operandType) {
@@ -146,8 +267,20 @@
     return Model::OperandValues(operandValues.data(), operandValues.size());
 }
 
+GeneralResult<SharedHandle> unvalidatedConvert(const hidl_handle& handle) {
+    if (handle.getNativeHandle() == nullptr) {
+        return nullptr;
+    }
+    if (handle->numFds != 1 || handle->numInts != 0) {
+        return NN_ERROR()
+               << "unvalidatedConvert failed because handle does not only hold a single fd";
+    }
+    auto duplicatedFd = NN_TRY(nn::dupFd(handle->data[0]));
+    return std::make_shared<const Handle>(std::move(duplicatedFd));
+}
+
 GeneralResult<SharedMemory> unvalidatedConvert(const hidl_memory& memory) {
-    return hal::utils::createSharedMemoryFromHidlMemory(memory);
+    return createSharedMemoryFromHidlMemory(memory);
 }
 
 GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model) {
@@ -155,7 +288,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
+            NN_TRY(countNumberOfConsumers(model.operands.size(), operations));
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -260,6 +393,82 @@
     return utils::unvalidatedConvert(canonical);
 }
 
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(std::vector<base::unique_fd> fds,
+                                                      const std::vector<int32_t>& ints) {
+    constexpr size_t kIntMax = std::numeric_limits<int>::max();
+    CHECK_LE(fds.size(), kIntMax);
+    CHECK_LE(ints.size(), kIntMax);
+    native_handle_t* nativeHandle =
+            native_handle_create(static_cast<int>(fds.size()), static_cast<int>(ints.size()));
+    if (nativeHandle == nullptr) {
+        return NN_ERROR() << "Failed to create native_handle";
+    }
+
+    for (size_t i = 0; i < fds.size(); ++i) {
+        nativeHandle->data[i] = fds[i].release();
+    }
+    std::copy(ints.begin(), ints.end(), nativeHandle->data + nativeHandle->numFds);
+
+    hidl_handle handle;
+    handle.setTo(nativeHandle, /*shouldOwn=*/true);
+    return handle;
+}
+
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(base::unique_fd fd,
+                                                      const std::vector<int32_t>& ints) {
+    std::vector<base::unique_fd> fds;
+    fds.push_back(std::move(fd));
+    return createNativeHandleFrom(std::move(fds), ints);
+}
+
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(const nn::Memory::Unknown::Handle& handle) {
+    std::vector<base::unique_fd> fds = NN_TRY(nn::dupFds(handle.fds.begin(), handle.fds.end()));
+    return createNativeHandleFrom(std::move(fds), handle.ints);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Ashmem& memory) {
+    auto fd = NN_TRY(nn::dupFd(memory.fd));
+    auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), {}));
+    return hidl_memory("ashmem", std::move(handle), memory.size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Fd& memory) {
+    auto fd = NN_TRY(nn::dupFd(memory.fd));
+
+    const auto [lowOffsetBits, highOffsetBits] = nn::getIntsFromOffset(memory.offset);
+    const std::vector<int> ints = {memory.prot, lowOffsetBits, highOffsetBits};
+
+    auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), ints));
+    return hidl_memory("mmap_fd", std::move(handle), memory.size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::HardwareBuffer& memory) {
+#ifdef __ANDROID__
+    const auto* ahwb = memory.handle.get();
+    AHardwareBuffer_Desc bufferDesc;
+    AHardwareBuffer_describe(ahwb, &bufferDesc);
+
+    const bool isBlob = bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB;
+    const size_t size = isBlob ? bufferDesc.width : 0;
+    const char* const name = isBlob ? "hardware_buffer_blob" : "hardware_buffer";
+
+    const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
+    const hidl_handle hidlHandle(nativeHandle);
+    hidl_handle copiedHandle(hidlHandle);
+
+    return hidl_memory(name, std::move(copiedHandle), size);
+#else   // __ANDROID__
+    LOG(FATAL) << "nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const "
+                  "nn::Memory::HardwareBuffer& memory): Not Available on Host Build";
+    (void)memory;
+    return (NN_ERROR() << "createHidlMemoryFrom failed").operator nn::GeneralResult<hidl_memory>();
+#endif  // __ANDROID__
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Unknown& memory) {
+    return hidl_memory(memory.name, NN_TRY(createNativeHandleFrom(memory.handle)), memory.size);
+}
+
 }  // anonymous namespace
 
 nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
@@ -332,8 +541,19 @@
     return hidl_vec<uint8_t>(operandValues.data(), operandValues.data() + operandValues.size());
 }
 
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
+    if (handle == nullptr) {
+        return {};
+    }
+    base::unique_fd fd = NN_TRY(nn::dupFd(handle->get()));
+    return createNativeHandleFrom(std::move(fd), {});
+}
+
 nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
-    return hal::utils::createHidlMemoryFromSharedMemory(memory);
+    if (memory == nullptr) {
+        return NN_ERROR() << "Memory must be non-empty";
+    }
+    return std::visit([](const auto& x) { return createHidlMemoryFrom(x); }, memory->handle);
 }
 
 nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
@@ -346,7 +566,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
+            NN_TRY(countNumberOfConsumers(operands.size(), model.main.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
index 93bd81a..49913a2 100644
--- a/neuralnetworks/1.0/utils/src/Device.cpp
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -18,6 +18,8 @@
 
 #include "Callbacks.h"
 #include "Conversions.h"
+#include "HandleError.h"
+#include "ProtectCallback.h"
 #include "Utils.h"
 
 #include <android/hardware/neuralnetworks/1.0/IDevice.h>
@@ -29,8 +31,6 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
 
 #include <functional>
@@ -47,7 +47,7 @@
 
 nn::GeneralResult<nn::Capabilities> capabilitiesCallback(ErrorStatus status,
                                                          const Capabilities& capabilities) {
-    HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "getting capabilities failed with " << toString(status);
     return nn::convert(capabilities);
 }
 
@@ -156,7 +156,7 @@
 
     const auto ret = kDevice->prepareModel(hidlModel, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
 
     return cb->get();
 }
diff --git a/neuralnetworks/1.0/utils/src/Execution.cpp b/neuralnetworks/1.0/utils/src/Execution.cpp
index 7a3216b..6e105a6 100644
--- a/neuralnetworks/1.0/utils/src/Execution.cpp
+++ b/neuralnetworks/1.0/utils/src/Execution.cpp
@@ -18,6 +18,8 @@
 
 #include "Callbacks.h"
 #include "Conversions.h"
+#include "HandleError.h"
+#include "ProtectCallback.h"
 #include "Utils.h"
 
 #include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
@@ -27,8 +29,6 @@
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <memory>
 #include <utility>
diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
index 00970c0..00e7d22 100644
--- a/neuralnetworks/1.0/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
@@ -20,6 +20,8 @@
 #include "Callbacks.h"
 #include "Conversions.h"
 #include "Execution.h"
+#include "HandleError.h"
+#include "ProtectCallback.h"
 #include "Utils.h"
 
 #include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
@@ -28,8 +30,6 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <memory>
 #include <tuple>
@@ -63,12 +63,11 @@
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
     hal::utils::RequestRelocation relocation;
-    const nn::Request& requestInShared =
-            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
-                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
-                    &maybeRequestInShared, &relocation)));
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
 
-    const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+    const auto hidlRequest = NN_TRY(convert(requestInShared));
 
     return executeInternal(hidlRequest, relocation);
 }
@@ -85,7 +84,7 @@
 
     const auto ret = kPreparedModel->execute(request, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
 
     auto result = NN_TRY(cb->get());
     if (relocation.output) {
diff --git a/neuralnetworks/utils/common/src/ProtectCallback.cpp b/neuralnetworks/1.0/utils/src/ProtectCallback.cpp
similarity index 98%
rename from neuralnetworks/utils/common/src/ProtectCallback.cpp
rename to neuralnetworks/1.0/utils/src/ProtectCallback.cpp
index 18e1f3b..89539b5 100644
--- a/neuralnetworks/utils/common/src/ProtectCallback.cpp
+++ b/neuralnetworks/1.0/utils/src/ProtectCallback.cpp
@@ -22,7 +22,8 @@
 #include <android/hidl/base/1.0/IBase.h>
 #include <hidl/HidlSupport.h>
 #include <nnapi/Result.h>
-#include <nnapi/hal/HandleError.h>
+
+#include "HandleError.h"
 
 #include <algorithm>
 #include <functional>
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
index 5e224b5..d6bd36a 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
@@ -23,8 +23,8 @@
 #include <nnapi/OperandTypes.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <functional>
 #include <memory>
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 a8cf8cf..4660ff7 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
@@ -26,7 +26,6 @@
 #include <nnapi/Types.h>
 #include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
-#include <nnapi/hal/HandleError.h>
 
 namespace android::hardware::neuralnetworks::V1_1::utils {
 
@@ -52,8 +51,8 @@
 }
 
 template <typename Type>
-nn::GeneralResult<void> compliantVersion(const Type& canonical) {
-    const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+nn::Result<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(nn::validate(canonical));
     if (version > kVersion) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index 467ceb3..5bdbe31 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -100,7 +100,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
+            NN_TRY(countNumberOfConsumers(model.operands.size(), operations));
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -223,7 +223,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
+            NN_TRY(countNumberOfConsumers(operands.size(), model.main.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
index 3197ef4..7d54cab 100644
--- a/neuralnetworks/1.1/utils/src/Device.cpp
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -29,9 +29,9 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <functional>
 #include <memory>
@@ -47,7 +47,7 @@
 
 nn::GeneralResult<nn::Capabilities> capabilitiesCallback(V1_0::ErrorStatus status,
                                                          const Capabilities& capabilities) {
-    HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "getting capabilities failed with " << toString(status);
     return nn::convert(capabilities);
 }
 
@@ -157,7 +157,7 @@
 
     const auto ret = kDevice->prepareModel_1_1(hidlModel, hidlPreference, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
 
     return cb->get();
 }
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
index ba3c1ba..6dd8138 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
@@ -27,8 +27,8 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
 
 // See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
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 272cee7..c3348aa 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
@@ -45,7 +45,6 @@
 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);
@@ -86,7 +85,6 @@
 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);
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 b4bef5e..e7ac172 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
@@ -23,8 +23,8 @@
 #include <nnapi/OperandTypes.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <functional>
 #include <memory>
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
index 9c66446..867f181 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
@@ -21,8 +21,8 @@
 #include <nnapi/IExecution.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include "PreparedModel.h"
 
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
index dae1ff3..8078693 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
@@ -32,8 +32,8 @@
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <atomic>
 #include <chrono>
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
index f7926f5..500aa0c 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
@@ -29,7 +29,7 @@
 #include <nnapi/IBurst.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
-#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 
 #include <atomic>
 #include <chrono>
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
index c662bc3..c081305 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
@@ -23,7 +23,7 @@
 #include <hidl/MQDescriptor.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
-#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 
 #include <atomic>
 #include <chrono>
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
index 35abd79..1150e5e 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
@@ -22,8 +22,8 @@
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <memory>
 #include <tuple>
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 09691b6..23e336a 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
@@ -28,7 +28,6 @@
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/1.1/Conversions.h>
 #include <nnapi/hal/1.1/Utils.h>
-#include <nnapi/hal/HandleError.h>
 
 #include <limits>
 
@@ -61,8 +60,8 @@
 }
 
 template <typename Type>
-nn::GeneralResult<void> compliantVersion(const Type& canonical) {
-    const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+nn::Result<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(nn::validate(canonical));
     if (version > kVersion) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
diff --git a/neuralnetworks/1.2/utils/src/Callbacks.cpp b/neuralnetworks/1.2/utils/src/Callbacks.cpp
index 9f54bb1..cb61f21 100644
--- a/neuralnetworks/1.2/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.2/utils/src/Callbacks.cpp
@@ -29,10 +29,10 @@
 #include <nnapi/Types.h>
 #include <nnapi/hal/1.0/Callbacks.h>
 #include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/HandleError.h>
 #include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
 
 #include <utility>
@@ -62,7 +62,7 @@
 
 nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
         V1_0::ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
-    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
     return NN_TRY(PreparedModel::create(preparedModel, /*executeSynchronously=*/true));
 }
 
@@ -74,9 +74,8 @@
         return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
                << "execution failed with " << toString(status);
     }
-    HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
-    return hal::utils::makeExecutionFailure(
-            convertExecutionGeneralResultsHelper(outputShapes, timing));
+    HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
+    return convertExecutionGeneralResultsHelper(outputShapes, timing);
 }
 
 Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index 29945b7..838d9c4 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -28,7 +28,6 @@
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/1.1/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
 
 #include <algorithm>
 #include <functional>
@@ -120,9 +119,8 @@
             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)),
-            nn::ErrorStatus::GENERAL_FAILURE));
+    auto table =
+            NN_TRY(Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)));
 
     return Capabilities{
             .relaxedFloat32toFloat16PerformanceScalar = relaxedFloat32toFloat16PerformanceScalar,
@@ -188,7 +186,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
+            NN_TRY(countNumberOfConsumers(model.operands.size(), operations));
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -265,14 +263,6 @@
     };
 }
 
-GeneralResult<SharedHandle> unvalidatedConvert(const hidl_handle& hidlHandle) {
-    if (hidlHandle.getNativeHandle() == nullptr) {
-        return nullptr;
-    }
-    auto handle = NN_TRY(hal::utils::sharedHandleFromNativeHandle(hidlHandle.getNativeHandle()));
-    return std::make_shared<const Handle>(std::move(handle));
-}
-
 GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
     return validatedConvert(deviceType);
 }
@@ -335,6 +325,10 @@
     return V1_0::utils::unvalidatedConvert(operandValues);
 }
 
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
+    return V1_0::utils::unvalidatedConvert(handle);
+}
+
 nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
     return V1_0::utils::unvalidatedConvert(memory);
 }
@@ -482,7 +476,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
+            NN_TRY(countNumberOfConsumers(operands.size(), model.main.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
@@ -545,13 +539,6 @@
     };
 }
 
-nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
-    if (handle == nullptr) {
-        return {};
-    }
-    return hal::utils::hidlHandleFromSharedHandle(*handle);
-}
-
 nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) {
     return validatedConvert(deviceType);
 }
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
index 9fe0de2..f12669a 100644
--- a/neuralnetworks/1.2/utils/src/Device.cpp
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -30,10 +30,10 @@
 #include <nnapi/OperandTypes.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/1.1/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <functional>
 #include <memory>
@@ -49,31 +49,31 @@
 
 nn::GeneralResult<nn::Capabilities> capabilitiesCallback(V1_0::ErrorStatus status,
                                                          const Capabilities& capabilities) {
-    HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "getting capabilities failed with " << toString(status);
     return nn::convert(capabilities);
 }
 
 nn::GeneralResult<std::string> versionStringCallback(V1_0::ErrorStatus status,
                                                      const hidl_string& versionString) {
-    HANDLE_HAL_STATUS(status) << "getVersionString failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "getVersionString failed with " << toString(status);
     return versionString;
 }
 
 nn::GeneralResult<nn::DeviceType> deviceTypeCallback(V1_0::ErrorStatus status,
                                                      DeviceType deviceType) {
-    HANDLE_HAL_STATUS(status) << "getDeviceType failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "getDeviceType failed with " << toString(status);
     return nn::convert(deviceType);
 }
 
 nn::GeneralResult<std::vector<nn::Extension>> supportedExtensionsCallback(
         V1_0::ErrorStatus status, const hidl_vec<Extension>& extensions) {
-    HANDLE_HAL_STATUS(status) << "getExtensions failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "getExtensions failed with " << toString(status);
     return nn::convert(extensions);
 }
 
 nn::GeneralResult<std::pair<uint32_t, uint32_t>> numberOfCacheFilesNeededCallback(
         V1_0::ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
-    HANDLE_HAL_STATUS(status) << "getNumberOfCacheFilesNeeded failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "getNumberOfCacheFilesNeeded failed with " << toString(status);
     if (numModelCache > nn::kMaxNumberOfCacheFiles) {
         return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numModelCache files greater "
                              "than allowed max ("
@@ -254,7 +254,7 @@
     const auto ret = kDevice->prepareModel_1_2(hidlModel, hidlPreference, hidlModelCache,
                                                hidlDataCache, hidlToken, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
 
     return cb->get();
 }
@@ -271,7 +271,7 @@
 
     const auto ret = kDevice->prepareModelFromCache(hidlModelCache, hidlDataCache, hidlToken, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    HANDLE_HAL_STATUS(status) << "model preparation from cache failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "model preparation from cache failed with " << toString(status);
 
     return cb->get();
 }
diff --git a/neuralnetworks/1.2/utils/src/Execution.cpp b/neuralnetworks/1.2/utils/src/Execution.cpp
index 18d1c90..320b0e1 100644
--- a/neuralnetworks/1.2/utils/src/Execution.cpp
+++ b/neuralnetworks/1.2/utils/src/Execution.cpp
@@ -29,7 +29,6 @@
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
 
 #include <memory>
 #include <utility>
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
index 8ce96c4..a8ded9e 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
@@ -28,9 +28,9 @@
 #include <nnapi/Types.h>
 #include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
 
 #include <algorithm>
@@ -82,8 +82,8 @@
 
 nn::GeneralResult<sp<IBurstContext>> executionBurstResultCallback(
         V1_0::ErrorStatus status, const sp<IBurstContext>& burstContext) {
-    HANDLE_HAL_STATUS(status) << "IPreparedModel::configureExecutionBurst failed with status "
-                              << toString(status);
+    HANDLE_STATUS_HIDL(status) << "IPreparedModel::configureExecutionBurst failed with status "
+                               << toString(status);
     if (burstContext == nullptr) {
         return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
                << "IPreparedModel::configureExecutionBurst returned nullptr for burst";
@@ -320,8 +320,7 @@
 
     // if the request is valid but of a higher version than what's supported in burst execution,
     // fall back to another execution path
-    if (const auto version = NN_TRY(hal::utils::makeExecutionFailure(nn::validate(request)));
-        version > nn::Version::ANDROID_Q) {
+    if (const auto version = NN_TRY(nn::validate(request)); version > nn::Version::ANDROID_Q) {
         // fallback to another execution path if the packet could not be sent
         return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
     }
@@ -329,17 +328,15 @@
     // ensure that request is ready for IPC
     std::optional<nn::Request> maybeRequestInShared;
     hal::utils::RequestRelocation relocation;
-    const nn::Request& requestInShared =
-            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
-                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
-                    &maybeRequestInShared, &relocation)));
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
 
     // clear pools field of request, as they will be provided via slots
     const auto requestWithoutPools = nn::Request{
             .inputs = requestInShared.inputs, .outputs = requestInShared.outputs, .pools = {}};
-    auto hidlRequest = NN_TRY(
-            hal::utils::makeExecutionFailure(V1_0::utils::unvalidatedConvert(requestWithoutPools)));
-    const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+    auto hidlRequest = NN_TRY(V1_0::utils::unvalidatedConvert(requestWithoutPools));
+    const auto hidlMeasure = NN_TRY(convert(measure));
 
     std::vector<int32_t> slots;
     std::vector<OptionalCacheHold> holds;
@@ -367,8 +364,7 @@
 
     // if the request is valid but of a higher version than what's supported in burst execution,
     // fall back to another execution path
-    if (const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(request)));
-        version > nn::Version::ANDROID_Q) {
+    if (const auto version = NN_TRY(nn::validate(request)); version > nn::Version::ANDROID_Q) {
         // fallback to another execution path if the packet could not be sent
         return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration);
     }
@@ -430,8 +426,7 @@
     }
 
     // get result packet
-    const auto [status, outputShapes, timing] =
-            NN_TRY(hal::utils::makeExecutionFailure(mResultChannelReceiver->getBlocking()));
+    const auto [status, outputShapes, timing] = NN_TRY(mResultChannelReceiver->getBlocking());
 
     if (relocation.output) {
         relocation.output->flush();
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
index c67159e..f30b662 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
@@ -27,8 +27,8 @@
 #include <nnapi/Types.h>
 #include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
 
 #include <algorithm>
@@ -45,14 +45,12 @@
 namespace android::hardware::neuralnetworks::V1_2::utils {
 namespace {
 
-using neuralnetworks::utils::makeExecutionFailure;
-
 constexpr V1_2::Timing kNoTiming = {std::numeric_limits<uint64_t>::max(),
                                     std::numeric_limits<uint64_t>::max()};
 
 nn::GeneralResult<std::vector<nn::SharedMemory>> getMemoriesCallback(
         V1_0::ErrorStatus status, const hidl_vec<hidl_memory>& memories) {
-    HANDLE_HAL_STATUS(status) << "getting burst memories failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "getting burst memories failed with " << toString(status);
     std::vector<nn::SharedMemory> canonicalMemories;
     canonicalMemories.reserve(memories.size());
     for (const auto& memory : memories) {
@@ -241,28 +239,25 @@
                  "ExecutionBurstServer getting memory, executing, and returning results");
 
     // ensure executor with cache has required memory
-    const auto cacheEntries =
-            NN_TRY(makeExecutionFailure(mMemoryCache.getCacheEntries(slotsOfPools)));
+    const auto cacheEntries = NN_TRY(mMemoryCache.getCacheEntries(slotsOfPools));
 
     // convert request, populating its pools
     // This code performs an unvalidated convert because the request object without its pools is
     // invalid because it is incomplete. Instead, the validation is performed after the memory pools
     // have been added to the request.
-    auto canonicalRequest =
-            NN_TRY(makeExecutionFailure(nn::unvalidatedConvert(requestWithoutPools)));
+    auto canonicalRequest = NN_TRY(nn::unvalidatedConvert(requestWithoutPools));
     CHECK(canonicalRequest.pools.empty());
     std::transform(cacheEntries.begin(), cacheEntries.end(),
                    std::back_inserter(canonicalRequest.pools),
                    [](const auto& cacheEntry) { return cacheEntry.first; });
-    NN_TRY(makeExecutionFailure(validate(canonicalRequest)));
+    NN_TRY(validate(canonicalRequest));
 
-    nn::MeasureTiming canonicalMeasure = NN_TRY(makeExecutionFailure(nn::convert(measure)));
+    nn::MeasureTiming canonicalMeasure = NN_TRY(nn::convert(measure));
 
     const auto [outputShapes, timing] =
             NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure, {}, {}));
 
-    return std::make_pair(NN_TRY(makeExecutionFailure(convert(outputShapes))),
-                          NN_TRY(makeExecutionFailure(convert(timing))));
+    return std::make_pair(NN_TRY(convert(outputShapes)), NN_TRY(convert(timing)));
 }
 
 }  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
index 1bdde1e..e0d029a 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
@@ -27,7 +27,7 @@
 #include <hidl/MQDescriptor.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
-#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 
 #include <atomic>
 #include <chrono>
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index d0ef36e..b8a5ae0 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -31,9 +31,9 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <chrono>
 #include <memory>
@@ -82,7 +82,7 @@
     const auto ret = kPreparedModel->execute_1_2(request, measure, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
-        HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+        HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
     }
 
     return cb->get();
@@ -95,13 +95,12 @@
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
     hal::utils::RequestRelocation relocation;
-    const nn::Request& requestInShared =
-            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
-                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
-                    &maybeRequestInShared, &relocation)));
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
 
-    const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
-    const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+    const auto hidlRequest = NN_TRY(convert(requestInShared));
+    const auto hidlMeasure = NN_TRY(convert(measure));
 
     return executeInternal(hidlRequest, hidlMeasure, relocation);
 }
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
index 643172e..4b8ddc1 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
@@ -30,8 +30,8 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
 
 // See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
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 b677c62..ec1e530 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
@@ -113,6 +113,9 @@
 nn::GeneralResult<V1_2::MeasureTiming> convert(const nn::MeasureTiming& measureTiming);
 nn::GeneralResult<V1_2::Timing> convert(const nn::Timing& timing);
 
+nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
+        const std::vector<nn::SyncFence>& fences);
+
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_CONVERSIONS_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
index 84f606a..c3c6fc4 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
@@ -23,8 +23,8 @@
 #include <nnapi/OperandTypes.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <functional>
 #include <memory>
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
index 5acba71..480438d 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
@@ -21,8 +21,8 @@
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <memory>
 #include <tuple>
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 1d76caa..2812db2 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
@@ -30,7 +30,6 @@
 #include <nnapi/hal/1.1/Utils.h>
 #include <nnapi/hal/1.2/Conversions.h>
 #include <nnapi/hal/1.2/Utils.h>
-#include <nnapi/hal/HandleError.h>
 
 namespace android::hardware::neuralnetworks::V1_3::utils {
 
@@ -61,8 +60,8 @@
 }
 
 template <typename Type>
-nn::GeneralResult<void> compliantVersion(const Type& canonical) {
-    const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+nn::Result<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(nn::validate(canonical));
     if (version > kVersion) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
diff --git a/neuralnetworks/1.3/utils/src/Buffer.cpp b/neuralnetworks/1.3/utils/src/Buffer.cpp
index ada5265..34925ea 100644
--- a/neuralnetworks/1.3/utils/src/Buffer.cpp
+++ b/neuralnetworks/1.3/utils/src/Buffer.cpp
@@ -25,7 +25,7 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/1.0/Conversions.h>
-#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/1.0/HandleError.h>
 
 #include "Conversions.h"
 #include "Utils.h"
@@ -66,7 +66,7 @@
 
     const auto ret = kBuffer->copyTo(hidlDst);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    HANDLE_HAL_STATUS(status) << "IBuffer::copyTo failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "IBuffer::copyTo failed with " << toString(status);
 
     return {};
 }
@@ -78,7 +78,7 @@
 
     const auto ret = kBuffer->copyFrom(hidlSrc, hidlDimensions);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    HANDLE_HAL_STATUS(status) << "IBuffer::copyFrom failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "IBuffer::copyFrom failed with " << toString(status);
 
     return {};
 }
diff --git a/neuralnetworks/1.3/utils/src/Callbacks.cpp b/neuralnetworks/1.3/utils/src/Callbacks.cpp
index 8e9fb83..f063862 100644
--- a/neuralnetworks/1.3/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.3/utils/src/Callbacks.cpp
@@ -30,13 +30,13 @@
 #include <nnapi/Types.h>
 #include <nnapi/hal/1.0/Callbacks.h>
 #include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/HandleError.h>
 #include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/1.2/Callbacks.h>
 #include <nnapi/hal/1.2/Conversions.h>
 #include <nnapi/hal/1.2/PreparedModel.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
 
 #include <utility>
@@ -71,13 +71,13 @@
 
 nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
         ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
-    HANDLE_HAL_STATUS(status) << "get supported operations failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "get supported operations failed with " << toString(status);
     return supportedOperations;
 }
 
 nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
         ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
-    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
     return NN_TRY(PreparedModel::create(preparedModel, /*executeSynchronously=*/true));
 }
 
@@ -90,9 +90,8 @@
         return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
                << "execution failed with " << toString(status);
     }
-    HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
-    return hal::utils::makeExecutionFailure(
-            convertExecutionGeneralResultsHelper(outputShapes, timing));
+    HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
+    return convertExecutionGeneralResultsHelper(outputShapes, timing);
 }
 
 Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 11225cf..a1d414c 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -28,7 +28,6 @@
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/1.2/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
 
 #include <algorithm>
 #include <chrono>
@@ -131,9 +130,8 @@
     }
 
     auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance));
-    auto table = NN_TRY(hal::utils::makeGeneralFailure(
-            Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
-            nn::ErrorStatus::GENERAL_FAILURE));
+    auto table =
+            NN_TRY(Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)));
 
     return Capabilities{
             .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
@@ -195,7 +193,7 @@
 
     // Verify number of consumers.
     const auto numberOfConsumers =
-            NN_TRY(hal::utils::countNumberOfConsumers(subgraph.operands.size(), operations));
+            NN_TRY(countNumberOfConsumers(subgraph.operands.size(), operations));
     CHECK(subgraph.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < subgraph.operands.size(); ++i) {
         if (subgraph.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -381,7 +379,7 @@
 }
 
 nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
-    return V1_2::utils::unvalidatedConvert(handle);
+    return V1_0::utils::unvalidatedConvert(handle);
 }
 
 nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
@@ -544,7 +542,7 @@
 
     // Update number of consumers.
     const auto numberOfConsumers =
-            NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), subgraph.operations));
+            NN_TRY(countNumberOfConsumers(operands.size(), subgraph.operations));
     CHECK(operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < operands.size(); ++i) {
         operands[i].numberOfConsumers = numberOfConsumers[i];
@@ -728,4 +726,13 @@
     return V1_2::utils::convert(timing);
 }
 
+nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
+        const std::vector<nn::SyncFence>& syncFences) {
+    std::vector<nn::SharedHandle> handles;
+    handles.reserve(syncFences.size());
+    std::transform(syncFences.begin(), syncFences.end(), std::back_inserter(handles),
+                   [](const nn::SyncFence& syncFence) { return syncFence.getSharedHandle(); });
+    return convert(handles);
+}
+
 }  // 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 d710b85..a73ce82 100644
--- a/neuralnetworks/1.3/utils/src/Device.cpp
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -33,13 +33,13 @@
 #include <nnapi/OperandTypes.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/1.1/Conversions.h>
 #include <nnapi/hal/1.2/Conversions.h>
 #include <nnapi/hal/1.2/Device.h>
 #include <nnapi/hal/1.2/Utils.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <any>
 #include <functional>
@@ -72,7 +72,7 @@
 
 nn::GeneralResult<nn::Capabilities> capabilitiesCallback(ErrorStatus status,
                                                          const Capabilities& capabilities) {
-    HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "getting capabilities failed with " << toString(status);
     return nn::convert(capabilities);
 }
 
@@ -89,7 +89,7 @@
 
 nn::GeneralResult<nn::SharedBuffer> allocationCallback(ErrorStatus status,
                                                        const sp<IBuffer>& buffer, uint32_t token) {
-    HANDLE_HAL_STATUS(status) << "IDevice::allocate failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "IDevice::allocate failed with " << toString(status);
     return Buffer::create(buffer, static_cast<nn::Request::MemoryDomainToken>(token));
 }
 
@@ -208,7 +208,7 @@
             kDevice->prepareModel_1_3(hidlModel, hidlPreference, hidlPriority, hidlDeadline,
                                       hidlModelCache, hidlDataCache, hidlToken, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
 
     return cb->get();
 }
@@ -227,7 +227,7 @@
     const auto ret = kDevice->prepareModelFromCache_1_3(hidlDeadline, hidlModelCache, hidlDataCache,
                                                         hidlToken, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
-    HANDLE_HAL_STATUS(status) << "model preparation from cache failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "model preparation from cache failed with " << toString(status);
 
     return cb->get();
 }
diff --git a/neuralnetworks/1.3/utils/src/Execution.cpp b/neuralnetworks/1.3/utils/src/Execution.cpp
index 3d17cc3..0ec7f56 100644
--- a/neuralnetworks/1.3/utils/src/Execution.cpp
+++ b/neuralnetworks/1.3/utils/src/Execution.cpp
@@ -29,7 +29,6 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
 
 #include <memory>
 #include <utility>
@@ -65,7 +64,7 @@
 
 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
         const nn::OptionalTimePoint& deadline) const {
-    const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    const auto hidlDeadline = NN_TRY(convert(deadline));
     return kPreparedModel->executeInternal(kRequest, kMeasure, hidlDeadline, kLoopTimeoutDuration,
                                            kRelocation);
 }
@@ -73,7 +72,7 @@
 nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
         const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
         const nn::OptionalDuration& timeoutDurationAfterFence) const {
-    const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
+    const auto hidlWaitFor = NN_TRY(convertSyncFences(waitFor));
     const auto hidlDeadline = NN_TRY(convert(deadline));
     const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
     return kPreparedModel->executeFencedInternal(kRequest, hidlWaitFor, kMeasure, hidlDeadline,
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index 1623de5..2c81cb2 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -30,12 +30,12 @@
 #include <nnapi/Result.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
 #include <nnapi/hal/1.2/Conversions.h>
 #include <nnapi/hal/1.2/ExecutionBurstController.h>
 #include <nnapi/hal/1.2/ExecutionBurstUtils.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <memory>
 #include <tuple>
@@ -50,20 +50,19 @@
 
 nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionCallbackResults(
         ErrorStatus status, const V1_2::Timing& timingLaunched, const V1_2::Timing& timingFenced) {
-    HANDLE_HAL_STATUS(status) << "fenced execution callback info failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "fenced execution callback info failed with " << toString(status);
     return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
 }
 
 nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> fencedExecutionCallback(
         ErrorStatus status, const hidl_handle& syncFence,
         const sp<IFencedExecutionCallback>& callback) {
-    HANDLE_HAL_STATUS(status) << "fenced execution failed with " << toString(status);
+    HANDLE_STATUS_HIDL(status) << "fenced execution failed with " << toString(status);
 
     auto resultSyncFence = nn::SyncFence::createAsSignaled();
     if (syncFence.getNativeHandle() != nullptr) {
         auto sharedHandle = NN_TRY(nn::convert(syncFence));
-        resultSyncFence = NN_TRY(hal::utils::makeGeneralFailure(
-                nn::SyncFence::create(std::move(sharedHandle)), nn::ErrorStatus::GENERAL_FAILURE));
+        resultSyncFence = NN_TRY(nn::SyncFence::create(std::move(sharedHandle)));
     }
 
     if (callback == nullptr) {
@@ -128,7 +127,7 @@
             kPreparedModel->execute_1_3(request, measure, deadline, loopTimeoutDuration, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     if (status != ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
-        HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+        HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
     }
 
     return cb->get();
@@ -141,16 +140,14 @@
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
     hal::utils::RequestRelocation relocation;
-    const nn::Request& requestInShared =
-            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
-                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
-                    &maybeRequestInShared, &relocation)));
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
 
-    const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
-    const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
-    const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
-    const auto hidlLoopTimeoutDuration =
-            NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+    const auto hidlRequest = NN_TRY(convert(requestInShared));
+    const auto hidlMeasure = NN_TRY(convert(measure));
+    const auto hidlDeadline = NN_TRY(convert(deadline));
+    const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
 
     return executeInternal(hidlRequest, hidlMeasure, hidlDeadline, hidlLoopTimeoutDuration,
                            relocation);
@@ -189,7 +186,7 @@
             &maybeRequestInShared, &relocation));
 
     const auto hidlRequest = NN_TRY(convert(requestInShared));
-    const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
+    const auto hidlWaitFor = NN_TRY(convertSyncFences(waitFor));
     const auto hidlMeasure = NN_TRY(convert(measure));
     const auto hidlDeadline = NN_TRY(convert(deadline));
     const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index 1382bdb..ab0a018 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -66,7 +66,7 @@
         "VtsHalNeuralNetworksV1_0_utils",
         "VtsHalNeuralNetworksV1_2_utils",
         "VtsHalNeuralNetworksV1_3_utils",
-        "android.hardware.neuralnetworks-V1-ndk",
+        "android.hardware.neuralnetworks-V2-ndk",
         "android.hardware.neuralnetworks@1.0",
         "android.hardware.neuralnetworks@1.1",
         "android.hardware.neuralnetworks@1.2",
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
index de3b438..2eff11b 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
@@ -136,4 +136,6 @@
   HARD_SWISH = 99,
   FILL = 100,
   RANK = 101,
+  BATCH_MATMUL = 102,
+  PACK = 103,
 }
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
index 52d2d70..2ec91ac 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
@@ -1471,6 +1471,7 @@
      * * {@link OperandType::TENSOR_FLOAT32}
      * * {@link OperandType::TENSOR_QUANT8_ASYMM}
      * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     * * {@link OperandType::TENSOR_INT32} (since NNAPI feature level 6)
      *
      * Supported tensor rank: up to 4.
      *
@@ -5236,4 +5237,89 @@
      *      of the input tensor.
      */
     RANK = 101,
+
+    /**
+     * Performs multiplication of two tensors in batches.
+     *
+     * Multiplies all slices of two input tensors and arranges the individual
+     * results in a single output tensor of the same batch size. Each pair of
+     * slices in the same batch have identical {@link OperandType}. Each
+     * slice can optionally be adjointed (transpose and conjugate) before
+     * multiplication.
+     *
+     * The two input tensors and the output tensor must be 2-D or higher and
+     * have the same batch size.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     * * {@link OperandType::TENSOR_INT32}
+     *
+     * Supported tensor rank: at least 2 and up to 4
+     *
+     * Inputs:
+     * * 0: A tensor with 2-D or higher shape [..., r_x, c_x].
+     * * 1: A tensor with 2-D or higher shape [..., r_y, c_y]. It has the same
+     *      {@link OperandType} and batch size as input0.
+     * * 2: An optional {@link OperandType::BOOL} scalar adj_x, default
+     *      to false. Set to true to adjoint the slices of input0.
+     * * 3: An optional {@link OperandType::BOOL} scalar adj_y, default
+     *      to false. Set to true to adjoint the slices of input1.
+     *
+     * Outputs:
+     * * 0: A tensor with 2-D or higher shape [..., r_o, c_o], where
+     *      r_o = c_x if adj_x else r_x
+     *      c_o = r_y if adj_y else c_y
+     */
+    BATCH_MATMUL = 102,
+
+    /**
+     * Packs N input tensors (N >= 1) of rank R into one output tensor of rank R+1.
+     * The tensors are packed along a given axis.
+     *
+     * The input tensors must have identical {@link OperandType} and dimensions.
+     *
+     * For example, suppose there are N input tensors of shape (A, B, C).
+     * If axis is 0, the output tensor will have shape (N, A, B, C).
+     * If axis is 1, the output tensor will have shape (A, N, B, C).
+     *
+     * All dimensions through the axis dimension determine the output tile count;
+     * the remaining dimensions determine the tile shape.
+     *
+     * Return to the example of N input tensors of shape (A, B, C).
+     * If axis is 0, there are N tiles in the output, each of shape (A, B, C).
+     * If axis is 1, there are A*N tiles in the output, each of shape (B, C).
+     *
+     * The coordinates of a tile within the output tensor are (t[0],...,t[axis]).
+     * The coordinates of a tile within an input tensor are (t[0],...,t[axis-1]).
+     * (If axis is 0, an input tensor consists of a single tile.)
+     * If we index input tensors starting with 0 (rather than by operand number),
+     * then output_tile[t[0],...,t[axis]] = input_tile[t[axis]][t[0],...,t[axis-1]].
+     * That is, all output tile coordinates except for the axis coordinate select
+     * the corresponding location within some input tensor; and the axis coordinate
+     * selects the input tensor.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     * * {@link OperandType::TENSOR_INT32}
+     *
+     * Supported input tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A scalar of type {@link OperandType::INT32}, specifying
+     *      the axis along which to pack.  The valid range is [0, R+1).
+     * * 1 ~ N: Input tensors to be packed together.
+     *          For {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *          {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensors,
+     *          the scales and zeroPoint must be the same for all input tensors,
+     *          and will be the same for the output tensor.
+     *
+     * Outputs:
+     * * 0: The packed tensor.
+     */
+    PACK = 103,
 }
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index 508b1ea..2ff7534 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -38,9 +38,8 @@
         "neuralnetworks_utils_hal_common",
     ],
     shared_libs: [
-        "android.hardware.neuralnetworks-V1-ndk",
+        "android.hardware.neuralnetworks-V2-ndk",
         "libbinder_ndk",
-        "libhidlbase",
     ],
     target: {
         android: {
@@ -58,7 +57,7 @@
     static_libs: [
         "android.hardware.common-V2-ndk",
         "android.hardware.graphics.common-V2-ndk",
-        "android.hardware.neuralnetworks-V1-ndk",
+        "android.hardware.neuralnetworks-V2-ndk",
         "libaidlcommonsupport",
         "libgmock",
         "libneuralnetworks_common",
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
index 8651912..168264b 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
@@ -32,8 +32,7 @@
 namespace aidl::android::hardware::neuralnetworks::utils {
 
 // An AIDL callback class to receive the results of IDevice::prepareModel* asynchronously.
-class PreparedModelCallback final : public BnPreparedModelCallback,
-                                    public hal::utils::IProtectedCallback {
+class PreparedModelCallback final : public BnPreparedModelCallback, public IProtectedCallback {
   public:
     using Data = nn::GeneralResult<nn::SharedPreparedModel>;
 
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
index ab1108c..92ed1cd 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
@@ -23,7 +23,6 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <functional>
 #include <mutex>
@@ -34,19 +33,39 @@
 
 namespace aidl::android::hardware::neuralnetworks::utils {
 
+class IProtectedCallback {
+  public:
+    /**
+     * Marks this object as a dead object.
+     */
+    virtual void notifyAsDeadObject() = 0;
+
+    // Public virtual destructor to allow objects to be stored (and destroyed) as smart pointers.
+    // E.g., std::unique_ptr<IProtectedCallback>.
+    virtual ~IProtectedCallback() = default;
+
+  protected:
+    // Protect the non-destructor special member functions to prevent object slicing.
+    IProtectedCallback() = default;
+    IProtectedCallback(const IProtectedCallback&) = default;
+    IProtectedCallback(IProtectedCallback&&) noexcept = default;
+    IProtectedCallback& operator=(const IProtectedCallback&) = default;
+    IProtectedCallback& operator=(IProtectedCallback&&) noexcept = default;
+};
+
 // Thread safe class
 class DeathMonitor final {
   public:
     static void serviceDied(void* cookie);
     void serviceDied();
     // Precondition: `killable` must be non-null.
-    void add(hal::utils::IProtectedCallback* killable) const;
+    void add(IProtectedCallback* killable) const;
     // Precondition: `killable` must be non-null.
-    void remove(hal::utils::IProtectedCallback* killable) const;
+    void remove(IProtectedCallback* killable) const;
 
   private:
     mutable std::mutex mMutex;
-    mutable std::vector<hal::utils::IProtectedCallback*> mObjects GUARDED_BY(mMutex);
+    mutable std::vector<IProtectedCallback*> mObjects GUARDED_BY(mMutex);
 };
 
 class DeathHandler final {
@@ -62,7 +81,7 @@
     using Cleanup = std::function<void()>;
     // Precondition: `killable` must be non-null.
     [[nodiscard]] ::android::base::ScopeGuard<Cleanup> protectCallback(
-            hal::utils::IProtectedCallback* killable) const;
+            IProtectedCallback* killable) const;
 
     std::shared_ptr<DeathMonitor> getDeathMonitor() const { return kDeathMonitor; }
 
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
index 316d34f..f2ab479 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
@@ -24,12 +24,11 @@
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
 #include <nnapi/Validation.h>
-#include <nnapi/hal/HandleError.h>
 
 namespace aidl::android::hardware::neuralnetworks::utils {
 
 constexpr auto kDefaultPriority = Priority::MEDIUM;
-constexpr auto kVersion = nn::Version::ANDROID_S;
+constexpr auto kVersion = nn::Version::FEATURE_LEVEL_6;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
@@ -50,9 +49,8 @@
 }
 
 template <typename Type>
-nn::GeneralResult<void> compliantVersion(const Type& canonical) {
-    const auto version = NN_TRY(::android::hardware::neuralnetworks::utils::makeGeneralFailure(
-            nn::validate(canonical)));
+nn::Result<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(nn::validate(canonical));
     if (version > kVersion) {
         return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
@@ -76,6 +74,13 @@
     for (const auto status = handleTransportError(ret); !status.ok();) \
     return NN_ERROR(status.error().code) << status.error().message << ": "
 
+#define HANDLE_STATUS_AIDL(status)                                                            \
+    if (const ::android::nn::ErrorStatus canonical = ::android::nn::convert(status).value_or( \
+                ::android::nn::ErrorStatus::GENERAL_FAILURE);                                 \
+        canonical == ::android::nn::ErrorStatus::NONE) {                                      \
+    } else                                                                                    \
+        return NN_ERROR(canonical)
+
 }  // namespace aidl::android::hardware::neuralnetworks::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_H
diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp
index 800ac32..fb00b26 100644
--- a/neuralnetworks/aidl/utils/src/Burst.cpp
+++ b/neuralnetworks/aidl/utils/src/Burst.cpp
@@ -26,7 +26,6 @@
 #include <nnapi/Result.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
-#include <nnapi/hal/HandleError.h>
 
 #include <memory>
 #include <mutex>
@@ -176,16 +175,14 @@
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
     hal::utils::RequestRelocation relocation;
-    const nn::Request& requestInShared =
-            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
-                    &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
-                    &maybeRequestInShared, &relocation)));
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+            &maybeRequestInShared, &relocation));
 
-    const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
-    const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
-    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
-    const auto aidlLoopTimeoutDuration =
-            NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+    const auto aidlRequest = NN_TRY(convert(requestInShared));
+    const auto aidlMeasure = NN_TRY(convert(measure));
+    const auto aidlDeadline = NN_TRY(convert(deadline));
+    const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
 
     std::vector<int64_t> memoryIdentifierTokens;
     std::vector<OptionalCacheHold> holds;
@@ -233,8 +230,8 @@
         return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
                << "execution failed with " << nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
     }
-    auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
-            convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
+    auto [outputShapes, timing] =
+            NN_TRY(convertExecutionResults(executionResult.outputShapes, executionResult.timing));
 
     if (relocation.output) {
         relocation.output->flush();
@@ -308,7 +305,7 @@
 
 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> BurstExecution::compute(
         const nn::OptionalTimePoint& deadline) const {
-    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    const auto aidlDeadline = NN_TRY(convert(deadline));
     return kBurst->executeInternal(kRequest, kMemoryIdentifierTokens, kMeasure, aidlDeadline,
                                    kLoopTimeoutDuration, kRelocation);
 }
diff --git a/neuralnetworks/aidl/utils/src/Callbacks.cpp b/neuralnetworks/aidl/utils/src/Callbacks.cpp
index 8055665..a321477 100644
--- a/neuralnetworks/aidl/utils/src/Callbacks.cpp
+++ b/neuralnetworks/aidl/utils/src/Callbacks.cpp
@@ -38,7 +38,7 @@
 // nn::Version::ANDROID_S. On failure, this function returns with the appropriate nn::GeneralError.
 nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
         ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
-    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    HANDLE_STATUS_AIDL(status) << "model preparation failed with " << toString(status);
     return NN_TRY(PreparedModel::create(preparedModel));
 }
 
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
index f087156..45628c8 100644
--- a/neuralnetworks/aidl/utils/src/Conversions.cpp
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -34,7 +34,6 @@
 #include <nnapi/Types.h>
 #include <nnapi/Validation.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
 
 #include <algorithm>
 #include <chrono>
@@ -178,9 +177,8 @@
     }
 
     auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance));
-    auto table = NN_TRY(hal::utils::makeGeneralFailure(
-            Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
-            nn::ErrorStatus::GENERAL_FAILURE));
+    auto table =
+            NN_TRY(Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)));
 
     return Capabilities{
             .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
diff --git a/neuralnetworks/aidl/utils/src/Execution.cpp b/neuralnetworks/aidl/utils/src/Execution.cpp
index 2aee8a6..94edd90 100644
--- a/neuralnetworks/aidl/utils/src/Execution.cpp
+++ b/neuralnetworks/aidl/utils/src/Execution.cpp
@@ -25,7 +25,6 @@
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
 
 #include <memory>
 #include <utility>
@@ -60,7 +59,7 @@
 
 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
         const nn::OptionalTimePoint& deadline) const {
-    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    const auto aidlDeadline = NN_TRY(convert(deadline));
     return kPreparedModel->executeInternal(kRequest, kMeasure, aidlDeadline, kLoopTimeoutDuration,
                                            kRelocation);
 }
diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
index f861d74..f25c2c8 100644
--- a/neuralnetworks/aidl/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
@@ -30,7 +30,6 @@
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
 #include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
 
 #include <memory>
 #include <tuple>
@@ -51,7 +50,7 @@
 nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionResults(
         ErrorStatus status, const aidl_hal::Timing& timingLaunched,
         const aidl_hal::Timing& timingFenced) {
-    HANDLE_HAL_STATUS(status) << "fenced execution callback info failed with " << toString(status);
+    HANDLE_STATUS_AIDL(status) << "fenced execution callback info failed with " << toString(status);
     return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
 }
 
@@ -78,16 +77,14 @@
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
     hal::utils::RequestRelocation relocation;
-    const nn::Request& requestInShared =
-            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
-                    &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
-                    &maybeRequestInShared, &relocation)));
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+            &maybeRequestInShared, &relocation));
 
-    const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
-    const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
-    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
-    const auto aidlLoopTimeoutDuration =
-            NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+    const auto aidlRequest = NN_TRY(convert(requestInShared));
+    const auto aidlMeasure = NN_TRY(convert(measure));
+    const auto aidlDeadline = NN_TRY(convert(deadline));
+    const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
     return executeInternal(aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration,
                            relocation);
 }
@@ -110,8 +107,8 @@
         return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
                << "execution failed with " << nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
     }
-    auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
-            convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
+    auto [outputShapes, timing] =
+            NN_TRY(convertExecutionResults(executionResult.outputShapes, executionResult.timing));
 
     if (relocation.output) {
         relocation.output->flush();
diff --git a/neuralnetworks/aidl/utils/src/ProtectCallback.cpp b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
index 124641c..54a673c 100644
--- a/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
+++ b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
@@ -22,7 +22,6 @@
 #include <android/binder_auto_utils.h>
 #include <android/binder_interface_utils.h>
 #include <nnapi/Result.h>
-#include <nnapi/hal/ProtectCallback.h>
 
 #include <algorithm>
 #include <functional>
@@ -37,7 +36,7 @@
 void DeathMonitor::serviceDied() {
     std::lock_guard guard(mMutex);
     std::for_each(mObjects.begin(), mObjects.end(),
-                  [](hal::utils::IProtectedCallback* killable) { killable->notifyAsDeadObject(); });
+                  [](IProtectedCallback* killable) { killable->notifyAsDeadObject(); });
 }
 
 void DeathMonitor::serviceDied(void* cookie) {
@@ -45,13 +44,13 @@
     deathMonitor->serviceDied();
 }
 
-void DeathMonitor::add(hal::utils::IProtectedCallback* killable) const {
+void DeathMonitor::add(IProtectedCallback* killable) const {
     CHECK(killable != nullptr);
     std::lock_guard guard(mMutex);
     mObjects.push_back(killable);
 }
 
-void DeathMonitor::remove(hal::utils::IProtectedCallback* killable) const {
+void DeathMonitor::remove(IProtectedCallback* killable) const {
     CHECK(killable != nullptr);
     std::lock_guard guard(mMutex);
     const auto removedIter = std::remove(mObjects.begin(), mObjects.end(), killable);
@@ -102,7 +101,7 @@
 }
 
 [[nodiscard]] ::android::base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback(
-        hal::utils::IProtectedCallback* killable) const {
+        IProtectedCallback* killable) const {
     CHECK(killable != nullptr);
     kDeathMonitor->add(killable);
     return ::android::base::make_scope_guard(
diff --git a/neuralnetworks/aidl/vts/functional/Android.bp b/neuralnetworks/aidl/vts/functional/Android.bp
index 8fa9756..3558d12 100644
--- a/neuralnetworks/aidl/vts/functional/Android.bp
+++ b/neuralnetworks/aidl/vts/functional/Android.bp
@@ -51,7 +51,7 @@
     static_libs: [
         "android.hardware.common-V2-ndk",
         "android.hardware.graphics.common-V2-ndk",
-        "android.hardware.neuralnetworks-V1-ndk",
+        "android.hardware.neuralnetworks-V2-ndk",
         "android.hidl.allocator@1.0",
         "android.hidl.memory@1.0",
         "libaidlcommonsupport",
@@ -63,6 +63,7 @@
         "neuralnetworks_utils_hal_aidl",
     ],
     whole_static_libs: [
+        "neuralnetworks_generated_AIDL_V2_example",
         "neuralnetworks_generated_V1_0_example",
         "neuralnetworks_generated_V1_1_example",
         "neuralnetworks_generated_V1_2_example",
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index ac5b96a..f67fd34 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -907,6 +907,20 @@
     const bool deviceIsResponsive =
             ndk::ScopedAStatus::fromStatus(AIBinder_ping(kDevice->asBinder().get())).isOk();
     ASSERT_TRUE(deviceIsResponsive);
+    //  TODO(b/201260787): We should require old drivers to report the model as
+    //  unsupported instead of simply skipping the test.
+    SkipIfDriverOlderThanTestModel();
+}
+
+void GeneratedTestBase::SkipIfDriverOlderThanTestModel() {
+    int32_t deviceVersion;
+    ASSERT_TRUE(kDevice->getInterfaceVersion(&deviceVersion).isOk());
+    const int32_t modelVersion = kTestModel.getAidlVersionInt();
+    if (deviceVersion < modelVersion) {
+        GTEST_SKIP() << "Device interface version " << deviceVersion
+                     << " is older than test model's minimum supported HAL version " << modelVersion
+                     << ". Skipping test.";
+    }
 }
 
 std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h
index ad40f06..da74db9 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h
@@ -34,6 +34,9 @@
     void SetUp() override;
     const std::shared_ptr<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
     const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
+
+  private:
+    void SkipIfDriverOlderThanTestModel();
 };
 
 using FilterFn = std::function<bool(const test_helper::TestModel&)>;
diff --git a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
index 698c054..fdc7eff 100644
--- a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
@@ -1122,6 +1122,7 @@
     //   align_corners and half_pixel_centers parameters.
     // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional axis
     //   parameter.
+    // - PACK has at least 2 inputs, with the first element being INT32.
     switch (op.type) {
         case OperationType::CONCATENATION: {
             if (op.inputs.size() > 2 && input != op.inputs.size() - 1) {
@@ -1178,6 +1179,11 @@
                 return true;
             }
         } break;
+        case OperationType::PACK: {
+            if (op.inputs.size() > 2 && input != 0) {
+                return true;
+            }
+        } break;
         default:
             break;
     }
@@ -1315,8 +1321,8 @@
 
 void validateModel(const std::shared_ptr<IDevice>& device, const Model& model) {
     const auto numberOfConsumers =
-            nn::countNumberOfConsumers(model.main.operands.size(),
-                                       nn::unvalidatedConvert(model.main.operations).value())
+            countNumberOfConsumers(model.main.operands.size(),
+                                   nn::unvalidatedConvert(model.main.operations).value())
                     .value();
     mutateExecutionOrderTest(device, model, numberOfConsumers);
     mutateOperandTypeTest(device, model);
diff --git a/neuralnetworks/utils/adapter/src/PreparedModel.cpp b/neuralnetworks/utils/adapter/src/PreparedModel.cpp
index 8968c2c..40c0888 100644
--- a/neuralnetworks/utils/adapter/src/PreparedModel.cpp
+++ b/neuralnetworks/utils/adapter/src/PreparedModel.cpp
@@ -36,7 +36,6 @@
 #include <nnapi/hal/1.2/Utils.h>
 #include <nnapi/hal/1.3/Conversions.h>
 #include <nnapi/hal/1.3/Utils.h>
-#include <nnapi/hal/HandleError.h>
 #include <sys/types.h>
 
 #include <memory>
@@ -57,6 +56,15 @@
     return result;
 }
 
+nn::GeneralResult<nn::Version> validateRequestForModel(const nn::Request& request,
+                                                       const nn::Model& model) {
+    nn::GeneralResult<nn::Version> version = nn::validateRequestForModel(request, model);
+    if (!version.ok()) {
+        version.error().code = nn::ErrorStatus::INVALID_ARGUMENT;
+    }
+    return version;
+}
+
 class FencedExecutionCallback final : public V1_3::IFencedExecutionCallback {
   public:
     explicit FencedExecutionCallback(const nn::ExecuteFencedInfoCallback& callback)
@@ -148,8 +156,7 @@
     const std::any resource = preparedModel->getUnderlyingResource();
     if (const auto* model = std::any_cast<const nn::Model*>(&resource)) {
         CHECK(*model != nullptr);
-        NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model),
-                                         nn::ErrorStatus::INVALID_ARGUMENT));
+        NN_TRY(adapter::validateRequestForModel(nnRequest, **model));
     }
 
     Task task = [preparedModel, nnRequest = std::move(nnRequest), callback] {
@@ -175,8 +182,7 @@
     const std::any resource = preparedModel->getUnderlyingResource();
     if (const auto* model = std::any_cast<const nn::Model*>(&resource)) {
         CHECK(*model != nullptr);
-        NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model),
-                                         nn::ErrorStatus::INVALID_ARGUMENT));
+        NN_TRY(adapter::validateRequestForModel(nnRequest, **model));
     }
 
     Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, callback] {
@@ -206,8 +212,7 @@
     const std::any resource = preparedModel->getUnderlyingResource();
     if (const auto* model = std::any_cast<const nn::Model*>(&resource)) {
         CHECK(*model != nullptr);
-        NN_TRY(utils::makeGeneralFailure(nn::validateRequestForModel(nnRequest, **model),
-                                         nn::ErrorStatus::INVALID_ARGUMENT));
+        NN_TRY(adapter::validateRequestForModel(nnRequest, **model));
     }
 
     Task task = [preparedModel, nnRequest = std::move(nnRequest), nnMeasure, nnDeadline,
@@ -224,14 +229,14 @@
 nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> executeSynchronously(
         const nn::SharedPreparedModel& preparedModel, const V1_0::Request& request,
         V1_2::MeasureTiming measure) {
-    const auto nnRequest = NN_TRY(utils::makeExecutionFailure(convertInput(request)));
-    const auto nnMeasure = NN_TRY(utils::makeExecutionFailure(convertInput(measure)));
+    const auto nnRequest = NN_TRY(convertInput(request));
+    const auto nnMeasure = NN_TRY(convertInput(measure));
 
     const auto [outputShapes, timing] =
             NN_TRY(preparedModel->execute(nnRequest, nnMeasure, {}, {}));
 
-    auto hidlOutputShapes = NN_TRY(utils::makeExecutionFailure(V1_2::utils::convert(outputShapes)));
-    const auto hidlTiming = NN_TRY(utils::makeExecutionFailure(V1_2::utils::convert(timing)));
+    auto hidlOutputShapes = NN_TRY(V1_2::utils::convert(outputShapes));
+    const auto hidlTiming = NN_TRY(V1_2::utils::convert(timing));
     return std::make_pair(std::move(hidlOutputShapes), hidlTiming);
 }
 
@@ -239,29 +244,30 @@
         const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request,
         V1_2::MeasureTiming measure, const V1_3::OptionalTimePoint& deadline,
         const V1_3::OptionalTimeoutDuration& loopTimeoutDuration) {
-    const auto nnRequest = NN_TRY(utils::makeExecutionFailure(convertInput(request)));
-    const auto nnMeasure = NN_TRY(utils::makeExecutionFailure(convertInput(measure)));
-    const auto nnDeadline = NN_TRY(utils::makeExecutionFailure(convertInput(deadline)));
-    const auto nnLoopTimeoutDuration =
-            NN_TRY(utils::makeExecutionFailure(convertInput(loopTimeoutDuration)));
+    const auto nnRequest = NN_TRY(convertInput(request));
+    const auto nnMeasure = NN_TRY(convertInput(measure));
+    const auto nnDeadline = NN_TRY(convertInput(deadline));
+    const auto nnLoopTimeoutDuration = NN_TRY(convertInput(loopTimeoutDuration));
 
     const auto [outputShapes, timing] =
             NN_TRY(preparedModel->execute(nnRequest, nnMeasure, nnDeadline, nnLoopTimeoutDuration));
 
-    auto hidlOutputShapes = NN_TRY(utils::makeExecutionFailure(V1_3::utils::convert(outputShapes)));
-    const auto hidlTiming = NN_TRY(utils::makeExecutionFailure(V1_3::utils::convert(timing)));
+    auto hidlOutputShapes = NN_TRY(V1_3::utils::convert(outputShapes));
+    const auto hidlTiming = NN_TRY(V1_3::utils::convert(timing));
     return std::make_pair(std::move(hidlOutputShapes), hidlTiming);
 }
 
 nn::GeneralResult<std::vector<nn::SyncFence>> convertSyncFences(
         const hidl_vec<hidl_handle>& handles) {
+    auto nnHandles = NN_TRY(convertInput(handles));
     std::vector<nn::SyncFence> syncFences;
     syncFences.reserve(handles.size());
-    for (const auto& handle : handles) {
-        auto nativeHandle = NN_TRY(convertInput(handle));
-        auto syncFence = NN_TRY(utils::makeGeneralFailure(
-                nn::SyncFence::create(std::move(nativeHandle)), nn::ErrorStatus::INVALID_ARGUMENT));
-        syncFences.push_back(std::move(syncFence));
+    for (auto&& handle : nnHandles) {
+        if (auto syncFence = nn::SyncFence::create(std::move(handle)); !syncFence.ok()) {
+            return nn::error(nn::ErrorStatus::INVALID_ARGUMENT) << std::move(syncFence).error();
+        } else {
+            syncFences.push_back(std::move(syncFence).value());
+        }
     }
     return syncFences;
 }
diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp
index 431885c..e02a202 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/common/Android.bp
@@ -30,20 +30,8 @@
     local_include_dirs: ["include/nnapi/hal"],
     export_include_dirs: ["include"],
     cflags: ["-Wthread-safety"],
-    static_libs: [
-        "libarect",
-        "neuralnetworks_types",
-    ],
-    shared_libs: [
-        "android.hardware.neuralnetworks-V1-ndk",
-        "libhidlbase",
-        "libbinder_ndk",
-    ],
-    target: {
-        android: {
-            shared_libs: ["libnativewindow"],
-        },
-    },
+    static_libs: ["neuralnetworks_types"],
+    shared_libs: ["libbinder_ndk"],
 }
 
 cc_test {
@@ -51,7 +39,6 @@
     host_supported: true,
     srcs: ["test/*.cpp"],
     static_libs: [
-        "android.hardware.neuralnetworks@1.0",
         "libgmock",
         "libneuralnetworks_common",
         "neuralnetworks_types",
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index 702ee92..ae0d092 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -17,8 +17,6 @@
 #ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
 #define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
 
-#include <cutils/native_handle.h>
-#include <hidl/HidlSupport.h>
 #include <nnapi/Result.h>
 #include <nnapi/SharedMemory.h>
 #include <nnapi/Types.h>
@@ -125,18 +123,6 @@
         const nn::Request* request, uint32_t alignment, uint32_t padding,
         std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut);
 
-nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
-        size_t numberOfOperands, const std::vector<nn::Operation>& operations);
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory);
-nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory);
-
-nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::Handle& handle);
-nn::GeneralResult<nn::Handle> sharedHandleFromNativeHandle(const native_handle_t* handle);
-
-nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
-        const std::vector<nn::SyncFence>& fences);
-
 }  // namespace android::hardware::neuralnetworks::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index ae02c88..b249881 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -16,11 +16,7 @@
 
 #include "CommonUtils.h"
 
-#include "HandleError.h"
-
 #include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <hidl/HidlSupport.h>
 #include <nnapi/Result.h>
 #include <nnapi/SharedMemory.h>
 #include <nnapi/TypeUtils.h>
@@ -34,11 +30,6 @@
 #include <variant>
 #include <vector>
 
-#ifdef __ANDROID__
-#include <android/hardware_buffer.h>
-#include <vndk/hardware_buffer.h>
-#endif  // __ANDROID__
-
 namespace android::hardware::neuralnetworks::utils {
 namespace {
 
@@ -92,97 +83,6 @@
                   });
 }
 
-nn::GeneralResult<hidl_handle> createNativeHandleFrom(std::vector<base::unique_fd> fds,
-                                                      const std::vector<int32_t>& ints) {
-    constexpr size_t kIntMax = std::numeric_limits<int>::max();
-    CHECK_LE(fds.size(), kIntMax);
-    CHECK_LE(ints.size(), kIntMax);
-    native_handle_t* nativeHandle =
-            native_handle_create(static_cast<int>(fds.size()), static_cast<int>(ints.size()));
-    if (nativeHandle == nullptr) {
-        return NN_ERROR() << "Failed to create native_handle";
-    }
-
-    for (size_t i = 0; i < fds.size(); ++i) {
-        nativeHandle->data[i] = fds[i].release();
-    }
-    std::copy(ints.begin(), ints.end(), nativeHandle->data + nativeHandle->numFds);
-
-    hidl_handle handle;
-    handle.setTo(nativeHandle, /*shouldOwn=*/true);
-    return handle;
-}
-
-nn::GeneralResult<hidl_handle> createNativeHandleFrom(base::unique_fd fd,
-                                                      const std::vector<int32_t>& ints) {
-    std::vector<base::unique_fd> fds;
-    fds.push_back(std::move(fd));
-    return createNativeHandleFrom(std::move(fds), ints);
-}
-
-nn::GeneralResult<hidl_handle> createNativeHandleFrom(const nn::Memory::Unknown::Handle& handle) {
-    std::vector<base::unique_fd> fds = NN_TRY(nn::dupFds(handle.fds.begin(), handle.fds.end()));
-    return createNativeHandleFrom(std::move(fds), handle.ints);
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Ashmem& memory) {
-    auto fd = NN_TRY(nn::dupFd(memory.fd));
-    auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), {}));
-    return hidl_memory("ashmem", std::move(handle), memory.size);
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Fd& memory) {
-    auto fd = NN_TRY(nn::dupFd(memory.fd));
-
-    const auto [lowOffsetBits, highOffsetBits] = nn::getIntsFromOffset(memory.offset);
-    const std::vector<int> ints = {memory.prot, lowOffsetBits, highOffsetBits};
-
-    auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), ints));
-    return hidl_memory("mmap_fd", std::move(handle), memory.size);
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::HardwareBuffer& memory) {
-#ifdef __ANDROID__
-    const auto* ahwb = memory.handle.get();
-    AHardwareBuffer_Desc bufferDesc;
-    AHardwareBuffer_describe(ahwb, &bufferDesc);
-
-    const bool isBlob = bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB;
-    const size_t size = isBlob ? bufferDesc.width : 0;
-    const char* const name = isBlob ? "hardware_buffer_blob" : "hardware_buffer";
-
-    const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
-    const hidl_handle hidlHandle(nativeHandle);
-    hidl_handle copiedHandle(hidlHandle);
-
-    return hidl_memory(name, std::move(copiedHandle), size);
-#else   // __ANDROID__
-    LOG(FATAL) << "nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const "
-                  "nn::Memory::HardwareBuffer& memory): Not Available on Host Build";
-    (void)memory;
-    return (NN_ERROR() << "createHidlMemoryFrom failed").operator nn::GeneralResult<hidl_memory>();
-#endif  // __ANDROID__
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Unknown& memory) {
-    return hidl_memory(memory.name, NN_TRY(createNativeHandleFrom(memory.handle)), memory.size);
-}
-
-nn::GeneralResult<nn::Memory::Unknown::Handle> unknownHandleFromNativeHandle(
-        const native_handle_t* handle) {
-    if (handle == nullptr) {
-        return NN_ERROR() << "unknownHandleFromNativeHandle failed because handle is nullptr";
-    }
-
-    std::vector<base::unique_fd> fds =
-            NN_TRY(nn::dupFds(handle->data + 0, handle->data + handle->numFds));
-
-    std::vector<int> ints(handle->data + handle->numFds,
-                          handle->data + handle->numFds + handle->numInts);
-
-    return nn::Memory::Unknown::Handle{.fds = std::move(fds), .ints = std::move(ints)};
-}
-
 }  // anonymous namespace
 
 nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWithP(
@@ -331,147 +231,4 @@
     return **maybeRequestInSharedOut;
 }
 
-nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
-        size_t numberOfOperands, const std::vector<nn::Operation>& operations) {
-    return makeGeneralFailure(nn::countNumberOfConsumers(numberOfOperands, operations));
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory) {
-    if (memory == nullptr) {
-        return NN_ERROR() << "Memory must be non-empty";
-    }
-    return std::visit([](const auto& x) { return createHidlMemoryFrom(x); }, memory->handle);
-}
-
-#ifdef __ANDROID__
-static uint32_t roundUpToMultiple(uint32_t value, uint32_t multiple) {
-    return (value + multiple - 1) / multiple * multiple;
-}
-#endif  // __ANDROID__
-
-nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory) {
-    CHECK_LE(memory.size(), std::numeric_limits<size_t>::max());
-    if (!memory.valid()) {
-        return NN_ERROR() << "Unable to convert invalid hidl_memory";
-    }
-
-    if (memory.name() == "ashmem") {
-        if (memory.handle()->numFds != 1) {
-            return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
-                              << memory.handle()->numFds << " numFds, but expected 1";
-        }
-        if (memory.handle()->numInts != 0) {
-            return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
-                              << memory.handle()->numInts << " numInts, but expected 0";
-        }
-        auto handle = nn::Memory::Ashmem{
-                .fd = NN_TRY(nn::dupFd(memory.handle()->data[0])),
-                .size = static_cast<size_t>(memory.size()),
-        };
-        return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
-    }
-
-    if (memory.name() == "mmap_fd") {
-        if (memory.handle()->numFds != 1) {
-            return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
-                              << memory.handle()->numFds << " numFds, but expected 1";
-        }
-        if (memory.handle()->numInts != 3) {
-            return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
-                              << memory.handle()->numInts << " numInts, but expected 3";
-        }
-
-        const int fd = memory.handle()->data[0];
-        const int prot = memory.handle()->data[1];
-        const int lower = memory.handle()->data[2];
-        const int higher = memory.handle()->data[3];
-        const size_t offset = nn::getOffsetFromInts(lower, higher);
-
-        return nn::createSharedMemoryFromFd(static_cast<size_t>(memory.size()), prot, fd, offset);
-    }
-
-    if (memory.name() != "hardware_buffer_blob") {
-        auto handle = nn::Memory::Unknown{
-                .handle = NN_TRY(unknownHandleFromNativeHandle(memory.handle())),
-                .size = static_cast<size_t>(memory.size()),
-                .name = memory.name(),
-        };
-        return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
-    }
-
-#ifdef __ANDROID__
-    const auto size = memory.size();
-    const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
-    const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
-    const uint32_t width = size;
-    const uint32_t height = 1;  // height is always 1 for BLOB mode AHardwareBuffer.
-    const uint32_t layers = 1;  // layers is always 1 for BLOB mode AHardwareBuffer.
-
-    // AHardwareBuffer_createFromHandle() might fail because an allocator
-    // expects a specific stride value. In that case, we try to guess it by
-    // aligning the width to small powers of 2.
-    // TODO(b/174120849): Avoid stride assumptions.
-    AHardwareBuffer* hardwareBuffer = nullptr;
-    status_t status = UNKNOWN_ERROR;
-    for (uint32_t alignment : {1, 4, 32, 64, 128, 2, 8, 16}) {
-        const uint32_t stride = roundUpToMultiple(width, alignment);
-        AHardwareBuffer_Desc desc{
-                .width = width,
-                .height = height,
-                .layers = layers,
-                .format = format,
-                .usage = usage,
-                .stride = stride,
-        };
-        status = AHardwareBuffer_createFromHandle(&desc, memory.handle(),
-                                                  AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
-                                                  &hardwareBuffer);
-        if (status == NO_ERROR) {
-            break;
-        }
-    }
-    if (status != NO_ERROR) {
-        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
-               << "Can't create AHardwareBuffer from handle. Error: " << status;
-    }
-
-    return nn::createSharedMemoryFromAHWB(hardwareBuffer, /*takeOwnership=*/true);
-#else   // __ANDROID__
-    LOG(FATAL) << "nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const "
-                  "hidl_memory& memory): Not Available on Host Build";
-    return (NN_ERROR() << "createSharedMemoryFromHidlMemory failed")
-            .
-            operator nn::GeneralResult<nn::SharedMemory>();
-#endif  // __ANDROID__
-}
-
-nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::Handle& handle) {
-    base::unique_fd fd = NN_TRY(nn::dupFd(handle.get()));
-    return createNativeHandleFrom(std::move(fd), {});
-}
-
-nn::GeneralResult<nn::Handle> sharedHandleFromNativeHandle(const native_handle_t* handle) {
-    if (handle == nullptr) {
-        return NN_ERROR() << "sharedHandleFromNativeHandle failed because handle is nullptr";
-    }
-    if (handle->numFds != 1 || handle->numInts != 0) {
-        return NN_ERROR() << "sharedHandleFromNativeHandle failed because handle does not only "
-                             "hold a single fd";
-    }
-    return nn::dupFd(handle->data[0]);
-}
-
-nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
-        const std::vector<nn::SyncFence>& syncFences) {
-    hidl_vec<hidl_handle> handles(syncFences.size());
-    for (size_t i = 0; i < syncFences.size(); ++i) {
-        const auto& handle = syncFences[i].getSharedHandle();
-        if (handle == nullptr) {
-            return NN_ERROR() << "convertSyncFences failed because sync fence is empty";
-        }
-        handles[i] = NN_TRY(hidlHandleFromSharedHandle(*handle));
-    }
-    return handles;
-}
-
 }  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/service/Android.bp b/neuralnetworks/utils/service/Android.bp
index 653e51a..fbb8679 100644
--- a/neuralnetworks/utils/service/Android.bp
+++ b/neuralnetworks/utils/service/Android.bp
@@ -39,7 +39,7 @@
         "neuralnetworks_utils_hal_common",
     ],
     shared_libs: [
-        "android.hardware.neuralnetworks-V1-ndk",
+        "android.hardware.neuralnetworks-V2-ndk",
         "android.hardware.neuralnetworks@1.0",
         "android.hardware.neuralnetworks@1.1",
         "android.hardware.neuralnetworks@1.2",
diff --git a/oemlock/1.0/vts/functional/OWNERS b/oemlock/1.0/vts/functional/OWNERS
new file mode 100644
index 0000000..ec8c304
--- /dev/null
+++ b/oemlock/1.0/vts/functional/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 186411
+chengyouho@google.com
+frankwoo@google.com
diff --git a/power/1.0/vts/functional/OWNERS b/power/1.0/vts/OWNERS
similarity index 100%
copy from power/1.0/vts/functional/OWNERS
copy to power/1.0/vts/OWNERS
diff --git a/power/1.1/vts/OWNERS b/power/1.1/vts/OWNERS
new file mode 100644
index 0000000..3a64da7
--- /dev/null
+++ b/power/1.1/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 158088
+wvw@google.com
diff --git a/power/1.0/vts/functional/OWNERS b/power/1.1/vts/functional/OWNERS
similarity index 100%
rename from power/1.0/vts/functional/OWNERS
rename to power/1.1/vts/functional/OWNERS
diff --git a/power/1.2/vts/OWNERS b/power/1.2/vts/OWNERS
new file mode 100644
index 0000000..4d8c7e9
--- /dev/null
+++ b/power/1.2/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 158088
+include ../../1.1/vts/OWNERS
diff --git a/power/1.3/vts/OWNERS b/power/1.3/vts/OWNERS
new file mode 100644
index 0000000..4d8c7e9
--- /dev/null
+++ b/power/1.3/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 158088
+include ../../1.1/vts/OWNERS
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 d3f0cf9..44f9865 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -606,7 +606,8 @@
     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);
+        // We cannot assert the consistency of CardState here due to b/203031664
+        // EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
         // applications should be an empty vector of AppStatus
         EXPECT_EQ(0, cardStatus.applications.size());
     }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
index 71b1765..d135a69 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -43,7 +43,7 @@
   oneway void imsNetworkStateChanged(in android.hardware.radio.RadioIndicationType type);
   oneway void networkScanResult(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.NetworkScanResult result);
   oneway void networkStateChanged(in android.hardware.radio.RadioIndicationType type);
-  oneway void nitzTimeReceived(in android.hardware.radio.RadioIndicationType type, in String nitzTime, in long receivedTime);
+  oneway void nitzTimeReceived(in android.hardware.radio.RadioIndicationType type, in String nitzTime, in long receivedTimeMs, in long ageMs);
   oneway void registrationFailed(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.CellIdentity cellIdentity, in String chosenPlmn, in android.hardware.radio.network.Domain domain, in int causeCode, in int additionalCauseCode);
   oneway void restrictedStateChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.PhoneRestrictedState state);
   oneway void suppSvcNotify(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.SuppSvcNotification suppSvc);
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
index a2fac20..ba7610d 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -129,9 +129,15 @@
      *
      * @param type Type of radio indication
      * @param nitzTime NITZ time string in the form "yy/mm/dd,hh:mm:ss(+/-)tz,dt"
-     * @param receivedTime milliseconds since boot that the NITZ time was received
+     * @param receivedTimeMs time (in milliseconds since boot) at which RIL sent the NITZ time to
+     *        the framework
+     * @param ageMs time in milliseconds indicating how long NITZ was cached in RIL and modem.
+     *        This must track true age and therefore must be calculated using clocks that
+     *        include the time spend in sleep / low power states. If it can not be guaranteed,
+     *        there must not be any caching done at the modem and should fill in 0 for ageMs
      */
-    void nitzTimeReceived(in RadioIndicationType type, in String nitzTime, in long receivedTime);
+    void nitzTimeReceived(in RadioIndicationType type, in String nitzTime,
+            in long receivedTimeMs, in long ageMs);
 
     /**
      * Report that Registration or a Location/Routing/Tracking Area update has failed.
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 37acfa9..12ce859 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -1307,7 +1307,8 @@
                                AuthorizationSet expected_sw_enforced,  //
                                AuthorizationSet expected_hw_enforced,  //
                                SecurityLevel security_level,
-                               const vector<uint8_t>& attestation_cert) {
+                               const vector<uint8_t>& attestation_cert,
+                               vector<uint8_t>* unique_id) {
     X509_Ptr cert(parse_cert_blob(attestation_cert));
     EXPECT_TRUE(!!cert.get());
     if (!cert.get()) return false;
@@ -1472,6 +1473,10 @@
     expected_hw_enforced.Sort();
     EXPECT_EQ(filtered_tags(expected_hw_enforced), filtered_tags(att_hw_enforced));
 
+    if (unique_id != nullptr) {
+        *unique_id = att_unique_id;
+    }
+
     return true;
 }
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index ec3fcf6..7b3b9d4 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -338,7 +338,8 @@
                                AuthorizationSet expected_sw_enforced,  //
                                AuthorizationSet expected_hw_enforced,  //
                                SecurityLevel security_level,
-                               const vector<uint8_t>& attestation_cert);
+                               const vector<uint8_t>& attestation_cert,
+                               vector<uint8_t>* unique_id = nullptr);
 
 string bin2hex(const vector<uint8_t>& data);
 X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index e41a851..670043d 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -1621,6 +1621,94 @@
 }
 
 /*
+ * NewKeyGenerationTest.EcdsaAttestationUniqueId
+ *
+ * Verifies that creation of an attested ECDSA key with a UNIQUE_ID included.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestationUniqueId) {
+    auto get_unique_id = [this](const std::string& app_id, uint64_t datetime,
+                                vector<uint8_t>* unique_id) {
+        auto challenge = "hello";
+        auto subject = "cert subj 2";
+        vector<uint8_t> subject_der(make_name_from_str(subject));
+        uint64_t serial_int = 0x1010;
+        vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+        const AuthorizationSetBuilder builder =
+                AuthorizationSetBuilder()
+                        .Authorization(TAG_NO_AUTH_REQUIRED)
+                        .Authorization(TAG_INCLUDE_UNIQUE_ID)
+                        .EcdsaSigningKey(EcCurve::P_256)
+                        .Digest(Digest::NONE)
+                        .AttestationChallenge(challenge)
+                        .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                        .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                        .AttestationApplicationId(app_id)
+                        .Authorization(TAG_CREATION_DATETIME, datetime)
+                        .SetDefaultValidity();
+
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(builder));
+        ASSERT_GT(key_blob_.size(), 0U);
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        ASSERT_GT(cert_chain_.size(), 0);
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics_);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics_);
+
+        // Check that the unique ID field in the extension is non-empty.
+        EXPECT_TRUE(verify_attestation_record(challenge, app_id, sw_enforced, hw_enforced,
+                                              SecLevel(), cert_chain_[0].encodedCertificate,
+                                              unique_id));
+        EXPECT_GT(unique_id->size(), 0);
+        CheckedDeleteKey();
+    };
+
+    // Generate unique ID
+    auto app_id = "foo";
+    uint64_t cert_date = 1619621648000;  // Wed Apr 28 14:54:08 2021 in ms since epoch
+    vector<uint8_t> unique_id;
+    get_unique_id(app_id, cert_date, &unique_id);
+
+    // Generating a new key with the same parameters should give the same unique ID.
+    vector<uint8_t> unique_id2;
+    get_unique_id(app_id, cert_date, &unique_id2);
+    EXPECT_EQ(unique_id, unique_id2);
+
+    // Generating a new key with a slightly different date should give the same unique ID.
+    uint64_t rounded_date = cert_date / 2592000000LLU;
+    uint64_t min_date = rounded_date * 2592000000LLU;
+    uint64_t max_date = ((rounded_date + 1) * 2592000000LLU) - 1;
+
+    vector<uint8_t> unique_id3;
+    get_unique_id(app_id, min_date, &unique_id3);
+    EXPECT_EQ(unique_id, unique_id3);
+
+    vector<uint8_t> unique_id4;
+    get_unique_id(app_id, max_date, &unique_id4);
+    EXPECT_EQ(unique_id, unique_id4);
+
+    // A different attestation application ID should yield a different unique ID.
+    auto app_id2 = "different_foo";
+    vector<uint8_t> unique_id5;
+    get_unique_id(app_id2, cert_date, &unique_id5);
+    EXPECT_NE(unique_id, unique_id5);
+
+    // A radically different date should yield a different unique ID.
+    vector<uint8_t> unique_id6;
+    get_unique_id(app_id, 1611621648000, &unique_id6);
+    EXPECT_NE(unique_id, unique_id6);
+
+    vector<uint8_t> unique_id7;
+    get_unique_id(app_id, max_date + 1, &unique_id7);
+    EXPECT_NE(unique_id, unique_id7);
+
+    vector<uint8_t> unique_id8;
+    get_unique_id(app_id, min_date - 1, &unique_id8);
+    EXPECT_NE(unique_id, unique_id8);
+}
+
+/*
  * NewKeyGenerationTest.EcdsaAttestationTagNoApplicationId
  *
  * Verifies that creation of an attested ECDSA key does not include APPLICATION_ID.
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 38f3586..76fb79b 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -222,7 +222,7 @@
     // Generate an ECDSA key that is attested by the generated P256 keypair.
     AuthorizationSet keyDesc = AuthorizationSetBuilder()
                                        .Authorization(TAG_NO_AUTH_REQUIRED)
-                                       .EcdsaSigningKey(256)
+                                       .EcdsaSigningKey(EcCurve::P_256)
                                        .AttestationChallenge("foo")
                                        .AttestationApplicationId("bar")
                                        .Digest(Digest::NONE)
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index bdb4cdf..e162934 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -25,6 +25,7 @@
 
 cc_library {
     name: "libkeymint_support",
+    vendor_available: true,
     cflags: [
         "-Wall",
         "-Wextra",
@@ -44,6 +45,7 @@
         "libbase",
         "libcrypto",
         "libutils",
+        "libhardware",
     ],
 }
 
diff --git a/security/keymint/support/authorization_set.cpp b/security/keymint/support/authorization_set.cpp
index 25eace3..c1b5d48 100644
--- a/security/keymint/support/authorization_set.cpp
+++ b/security/keymint/support/authorization_set.cpp
@@ -161,11 +161,6 @@
     return EncryptionKey();
 }
 
-AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) {
-    EcdsaKey(key_size);
-    return SigningKey();
-}
-
 AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(EcCurve curve) {
     EcdsaKey(curve);
     return SigningKey();
diff --git a/security/keymint/support/include/keymint_support/authorization_set.h b/security/keymint/support/include/keymint_support/authorization_set.h
index ca51b08..e41a329 100644
--- a/security/keymint/support/include/keymint_support/authorization_set.h
+++ b/security/keymint/support/include/keymint_support/authorization_set.h
@@ -281,7 +281,6 @@
 
     AuthorizationSetBuilder& RsaSigningKey(uint32_t key_size, uint64_t public_exponent);
     AuthorizationSetBuilder& RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent);
-    AuthorizationSetBuilder& EcdsaSigningKey(uint32_t key_size);
     AuthorizationSetBuilder& EcdsaSigningKey(EcCurve curve);
     AuthorizationSetBuilder& AesEncryptionKey(uint32_t key_size);
     AuthorizationSetBuilder& TripleDesEncryptionKey(uint32_t key_size);
diff --git a/vibrator/1.0/vts/OWNERS b/vibrator/1.0/vts/OWNERS
new file mode 100644
index 0000000..75b9a4b
--- /dev/null
+++ b/vibrator/1.0/vts/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 345036
+michaelwr@google.com
+leungv@google.com
diff --git a/vibrator/1.1/vts/OWNERS b/vibrator/1.1/vts/OWNERS
new file mode 100644
index 0000000..44bfe56
--- /dev/null
+++ b/vibrator/1.1/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 345036
+include ../../1.0/vts/OWNERS
diff --git a/vibrator/1.2/vts/OWNERS b/vibrator/1.2/vts/OWNERS
new file mode 100644
index 0000000..44bfe56
--- /dev/null
+++ b/vibrator/1.2/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 345036
+include ../../1.0/vts/OWNERS
diff --git a/vibrator/1.3/vts/OWNERS b/vibrator/1.3/vts/OWNERS
new file mode 100644
index 0000000..44bfe56
--- /dev/null
+++ b/vibrator/1.3/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 345036
+include ../../1.0/vts/OWNERS
diff --git a/vibrator/aidl/OWNERS b/vibrator/aidl/OWNERS
index e3d7e6b..ae10db6 100644
--- a/vibrator/aidl/OWNERS
+++ b/vibrator/aidl/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 345036
 include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
 chasewu@google.com
 leungv@google.com
diff --git a/wifi/supplicant/1.0/vts/OWNERS b/wifi/supplicant/1.0/vts/OWNERS
new file mode 100644
index 0000000..b16dc11
--- /dev/null
+++ b/wifi/supplicant/1.0/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 33618
+include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/1.1/vts/OWNERS b/wifi/supplicant/1.1/vts/OWNERS
new file mode 100644
index 0000000..b16dc11
--- /dev/null
+++ b/wifi/supplicant/1.1/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 33618
+include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/1.2/vts/OWNERS b/wifi/supplicant/1.2/vts/OWNERS
index cf81c79..b16dc11 100644
--- a/wifi/supplicant/1.2/vts/OWNERS
+++ b/wifi/supplicant/1.2/vts/OWNERS
@@ -1,2 +1,2 @@
-arabawy@google.com
-etancohen@google.com
+# Bug component: 33618
+include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/1.3/vts/OWNERS b/wifi/supplicant/1.3/vts/OWNERS
index cf81c79..287152d 100644
--- a/wifi/supplicant/1.3/vts/OWNERS
+++ b/wifi/supplicant/1.3/vts/OWNERS
@@ -1,2 +1,3 @@
+# Bug component: 33618
 arabawy@google.com
 etancohen@google.com
diff --git a/wifi/supplicant/1.4/vts/OWNERS b/wifi/supplicant/1.4/vts/OWNERS
index cf81c79..b16dc11 100644
--- a/wifi/supplicant/1.4/vts/OWNERS
+++ b/wifi/supplicant/1.4/vts/OWNERS
@@ -1,2 +1,2 @@
-arabawy@google.com
-etancohen@google.com
+# Bug component: 33618
+include ../../1.3/vts/OWNERS