Revert "Update PhoneCapability to use 1.5 UTRAN bands" am: 7fac7578ca

Change-Id: I347ea8c57d2a12257d7f6c6c8f798e49036badfe
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h
index 8ee3c54..f8b10ca 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h
@@ -61,7 +61,7 @@
 
 struct VmsLayerAndPublisher {
     VmsLayerAndPublisher(VmsLayer layer, int publisher_id)
-        : layer(layer), publisher_id(publisher_id) {}
+        : layer(std::move(layer)), publisher_id(publisher_id) {}
     VmsLayer layer;
     int publisher_id;
 };
@@ -69,6 +69,8 @@
 // A VmsAssociatedLayer is used by subscribers to specify which publisher IDs
 // are acceptable for a given layer.
 struct VmsAssociatedLayer {
+    VmsAssociatedLayer(VmsLayer layer, std::vector<int> publisher_ids)
+        : layer(std::move(layer)), publisher_ids(std::move(publisher_ids)) {}
     VmsLayer layer;
     std::vector<int> publisher_ids;
 };
@@ -77,7 +79,7 @@
 // its dependencies. Dependencies can be empty.
 struct VmsLayerOffering {
     VmsLayerOffering(VmsLayer layer, std::vector<VmsLayer> dependencies)
-        : layer(layer), dependencies(dependencies) {}
+        : layer(std::move(layer)), dependencies(std::move(dependencies)) {}
     VmsLayerOffering(VmsLayer layer) : layer(layer), dependencies() {}
     VmsLayer layer;
     std::vector<VmsLayer> dependencies;
@@ -87,7 +89,7 @@
 // with the specified publisher ID.
 struct VmsOffers {
     VmsOffers(int publisher_id, std::vector<VmsLayerOffering> offerings)
-        : publisher_id(publisher_id), offerings(offerings) {}
+        : publisher_id(publisher_id), offerings(std::move(offerings)) {}
     int publisher_id;
     std::vector<VmsLayerOffering> offerings;
 };
@@ -231,6 +233,24 @@
                                           const int current_service_id, const int current_client_id,
                                           int* new_service_id);
 
+// Returns true if the new sequence number of the availability state message is greater than
+// the last seen availability sequence number.
+bool isAvailabilitySequenceNumberNewer(const VehiclePropValue& availability_state,
+                                       const int last_seen_availability_sequence_number);
+
+// Returns sequence number of the availability state message.
+int32_t getSequenceNumberForAvailabilityState(const VehiclePropValue& availability_state);
+
+// Takes a availability state message and returns the associated layers that are
+// available to publish data.
+//
+// A subscriber can use this function when receiving an availability response or availability
+// change message to determine which associated layers are ready to publish data.
+// The caller of this function can optionally decide to not consume these layers
+// if the availability change has the sequence number less than the last seen
+// sequence number.
+std::vector<VmsAssociatedLayer> getAvailableLayers(const VehiclePropValue& availability_state);
+
 }  // namespace vms
 }  // namespace V2_0
 }  // namespace vehicle
diff --git a/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp b/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp
index 9eba905..a65cded 100644
--- a/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp
@@ -219,12 +219,9 @@
     if (isValidVmsMessage(subscriptions_state) &&
         (parseMessageType(subscriptions_state) == VmsMessageType::SUBSCRIPTIONS_CHANGE ||
          parseMessageType(subscriptions_state) == VmsMessageType::SUBSCRIPTIONS_RESPONSE) &&
-        subscriptions_state.value.int32Values.size() > kSubscriptionStateSequenceNumberIndex) {
-        const int32_t num_of_layers = subscriptions_state.value.int32Values[toInt(
-                VmsSubscriptionsStateIntegerValuesIndex::NUMBER_OF_LAYERS)];
-        const int32_t num_of_associated_layers = subscriptions_state.value.int32Values[toInt(
-                VmsSubscriptionsStateIntegerValuesIndex ::NUMBER_OF_ASSOCIATED_LAYERS)];
-
+        subscriptions_state.value.int32Values.size() >
+                toInt(VmsSubscriptionsStateIntegerValuesIndex::NUMBER_OF_LAYERS)) {
+        int subscriptions_state_int_size = subscriptions_state.value.int32Values.size();
         std::unordered_set<VmsLayer, VmsLayer::VmsLayerHashFunction> offered_layers;
         for (const auto& offer : offers.offerings) {
             offered_layers.insert(offer.layer);
@@ -232,33 +229,52 @@
         std::vector<VmsLayer> subscribed_layers;
 
         int current_index = toInt(VmsSubscriptionsStateIntegerValuesIndex::SUBSCRIPTIONS_START);
+
         // Add all subscribed layers which are offered by the current publisher.
+        const int32_t num_of_layers = subscriptions_state.value.int32Values[toInt(
+                VmsSubscriptionsStateIntegerValuesIndex::NUMBER_OF_LAYERS)];
         for (int i = 0; i < num_of_layers; i++) {
+            if (subscriptions_state_int_size < current_index + kLayerSize) {
+                return {};
+            }
             VmsLayer layer = VmsLayer(subscriptions_state.value.int32Values[current_index],
                                       subscriptions_state.value.int32Values[current_index + 1],
                                       subscriptions_state.value.int32Values[current_index + 2]);
             if (offered_layers.find(layer) != offered_layers.end()) {
-                subscribed_layers.push_back(layer);
+                subscribed_layers.push_back(std::move(layer));
             }
             current_index += kLayerSize;
         }
+
         // Add all subscribed associated layers which are offered by the current publisher.
         // For this, we need to check if the associated layer has a publisher ID which is
         // same as that of the current publisher.
-        for (int i = 0; i < num_of_associated_layers; i++) {
-            VmsLayer layer = VmsLayer(subscriptions_state.value.int32Values[current_index],
-                                      subscriptions_state.value.int32Values[current_index + 1],
-                                      subscriptions_state.value.int32Values[current_index + 2]);
-            current_index += kLayerSize;
-            if (offered_layers.find(layer) != offered_layers.end()) {
-                int32_t num_of_publisher_ids = subscriptions_state.value.int32Values[current_index];
-                current_index++;
-                for (int j = 0; j < num_of_publisher_ids; j++) {
-                    if (subscriptions_state.value.int32Values[current_index] ==
-                        offers.publisher_id) {
-                        subscribed_layers.push_back(layer);
-                    }
+        if (subscriptions_state_int_size >
+            toInt(VmsSubscriptionsStateIntegerValuesIndex::NUMBER_OF_ASSOCIATED_LAYERS)) {
+            const int32_t num_of_associated_layers = subscriptions_state.value.int32Values[toInt(
+                    VmsSubscriptionsStateIntegerValuesIndex::NUMBER_OF_ASSOCIATED_LAYERS)];
+
+            for (int i = 0; i < num_of_associated_layers; i++) {
+                if (subscriptions_state_int_size < current_index + kLayerSize) {
+                    return {};
+                }
+                VmsLayer layer = VmsLayer(subscriptions_state.value.int32Values[current_index],
+                                          subscriptions_state.value.int32Values[current_index + 1],
+                                          subscriptions_state.value.int32Values[current_index + 2]);
+                current_index += kLayerSize;
+                if (offered_layers.find(layer) != offered_layers.end() &&
+                    subscriptions_state_int_size > current_index) {
+                    int32_t num_of_publisher_ids =
+                            subscriptions_state.value.int32Values[current_index];
                     current_index++;
+                    for (int j = 0; j < num_of_publisher_ids; j++) {
+                        if (subscriptions_state_int_size > current_index &&
+                            subscriptions_state.value.int32Values[current_index] ==
+                                    offers.publisher_id) {
+                            subscribed_layers.push_back(std::move(layer));
+                        }
+                        current_index++;
+                    }
                 }
             }
         }
@@ -300,6 +316,64 @@
     return VmsSessionStatus::kInvalidMessage;
 }
 
+bool isAvailabilitySequenceNumberNewer(const VehiclePropValue& availability_state,
+                                       const int last_seen_availability_sequence_number) {
+    return (isValidVmsMessage(availability_state) &&
+            (parseMessageType(availability_state) == VmsMessageType::AVAILABILITY_CHANGE ||
+             parseMessageType(availability_state) == VmsMessageType::AVAILABILITY_RESPONSE) &&
+            availability_state.value.int32Values.size() > kAvailabilitySequenceNumberIndex &&
+            availability_state.value.int32Values[kAvailabilitySequenceNumberIndex] >
+                    last_seen_availability_sequence_number);
+}
+
+int32_t getSequenceNumberForAvailabilityState(const VehiclePropValue& availability_state) {
+    if (isValidVmsMessage(availability_state) &&
+        (parseMessageType(availability_state) == VmsMessageType::AVAILABILITY_CHANGE ||
+         parseMessageType(availability_state) == VmsMessageType::AVAILABILITY_RESPONSE) &&
+        availability_state.value.int32Values.size() > kAvailabilitySequenceNumberIndex) {
+        return availability_state.value.int32Values[kAvailabilitySequenceNumberIndex];
+    }
+    return -1;
+}
+
+std::vector<VmsAssociatedLayer> getAvailableLayers(const VehiclePropValue& availability_state) {
+    if (isValidVmsMessage(availability_state) &&
+        (parseMessageType(availability_state) == VmsMessageType::AVAILABILITY_CHANGE ||
+         parseMessageType(availability_state) == VmsMessageType::AVAILABILITY_RESPONSE) &&
+        availability_state.value.int32Values.size() >
+                toInt(VmsAvailabilityStateIntegerValuesIndex::NUMBER_OF_ASSOCIATED_LAYERS)) {
+        int availability_state_int_size = availability_state.value.int32Values.size();
+        const int32_t num_of_associated_layers = availability_state.value.int32Values[toInt(
+                VmsAvailabilityStateIntegerValuesIndex::NUMBER_OF_ASSOCIATED_LAYERS)];
+        int current_index = toInt(VmsAvailabilityStateIntegerValuesIndex::LAYERS_START);
+        std::vector<VmsAssociatedLayer> available_layers;
+        for (int i = 0; i < num_of_associated_layers; i++) {
+            if (availability_state_int_size < current_index + kLayerSize) {
+                return {};
+            }
+            VmsLayer layer = VmsLayer(availability_state.value.int32Values[current_index],
+                                      availability_state.value.int32Values[current_index + 1],
+                                      availability_state.value.int32Values[current_index + 2]);
+            current_index += kLayerSize;
+            std::vector<int> publisher_ids;
+            if (availability_state_int_size > current_index) {
+                int32_t num_of_publisher_ids = availability_state.value.int32Values[current_index];
+                current_index++;
+                for (int j = 0; j < num_of_publisher_ids; j++) {
+                    if (availability_state_int_size > current_index) {
+                        publisher_ids.push_back(
+                                availability_state.value.int32Values[current_index]);
+                        current_index++;
+                    }
+                }
+            }
+            available_layers.emplace_back(layer, std::move(publisher_ids));
+        }
+        return available_layers;
+    }
+    return {};
+}
+
 }  // namespace vms
 }  // namespace V2_0
 }  // namespace vehicle
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 2948ecb..3070171 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -570,6 +570,16 @@
 
         {.config =
                  {
+                         .prop = toInt(VehicleProperty::DISTANCE_DISPLAY_UNITS),
+                         .access = VehiclePropertyAccess::READ_WRITE,
+                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                         .configArray = {(int)VehicleUnit::KILOMETER, (int)VehicleUnit::MILE},
+                         .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}
+                 },
+         .initialValue = {.int32Values = {(int)VehicleUnit::MILE}}},
+
+        {.config =
+                 {
                          .prop = toInt(VehicleProperty::NIGHT_MODE),
                          .access = VehiclePropertyAccess::READ,
                          .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
diff --git a/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp b/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp
index 8b547f1..a48d19c 100644
--- a/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp
+++ b/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp
@@ -279,7 +279,7 @@
     VmsOffers offers = {123,
                         {VmsLayerOffering(VmsLayer(1, 0, 1), {VmsLayer(4, 1, 1)}),
                          VmsLayerOffering(VmsLayer(2, 0, 1))}};
-    auto message = createBaseVmsMessage(2);
+    auto message = createBaseVmsMessage(16);
     message->value.int32Values = hidl_vec<int32_t>{toInt(type),
                                                    1234,  // sequence number
                                                    2,     // number of layers
@@ -308,9 +308,28 @@
     testSubscribedLayers(VmsMessageType::SUBSCRIPTIONS_RESPONSE);
 }
 
+void testGetSubscribedLayersMalformedData(VmsMessageType type) {
+    VmsOffers offers = {123,
+                        {VmsLayerOffering(VmsLayer(1, 0, 1), {VmsLayer(4, 1, 1)}),
+                         VmsLayerOffering(VmsLayer(2, 0, 1))}};
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values = hidl_vec<int32_t>{toInt(type), 1234};  // sequence number
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    auto result = getSubscribedLayers(*message, offers);
+    EXPECT_EQ(static_cast<int>(result.size()), 0);
+}
+
+TEST(VmsUtilsTest, subscribedLayersForMalformedChange) {
+    testGetSubscribedLayersMalformedData(VmsMessageType::SUBSCRIPTIONS_CHANGE);
+}
+
+TEST(VmsUtilsTest, subscribedLayersForMalformedResponse) {
+    testGetSubscribedLayersMalformedData(VmsMessageType::SUBSCRIPTIONS_RESPONSE);
+}
+
 void testSubscribedLayersWithDifferentSubtype(VmsMessageType type) {
     VmsOffers offers = {123, {VmsLayerOffering(VmsLayer(1, 0, 1))}};
-    auto message = createBaseVmsMessage(2);
+    auto message = createBaseVmsMessage(7);
     message->value.int32Values = hidl_vec<int32_t>{toInt(type),
                                                    1234,  // sequence number
                                                    1,     // number of layers
@@ -332,7 +351,7 @@
 
 void subscribedLayersWithDifferentVersion(VmsMessageType type) {
     VmsOffers offers = {123, {VmsLayerOffering(VmsLayer(1, 0, 1))}};
-    auto message = createBaseVmsMessage(2);
+    auto message = createBaseVmsMessage(7);
     message->value.int32Values = hidl_vec<int32_t>{toInt(type),
                                                    1234,             // sequence number
                                                    1,                // number of layers
@@ -353,7 +372,7 @@
 
 void subscribedLayersWithDifferentPublisherId(VmsMessageType type) {
     VmsOffers offers = {123, {VmsLayerOffering(VmsLayer(1, 0, 1))}};
-    auto message = createBaseVmsMessage(2);
+    auto message = createBaseVmsMessage(9);
     message->value.int32Values = hidl_vec<int32_t>{toInt(type),
                                                    1234,  // sequence number
                                                    0,     // number of layers
@@ -475,6 +494,113 @@
     EXPECT_EQ(new_service_id, 123);
 }
 
+TEST(VmsUtilsTest, newAvailabilitySequenceNumberForExistingSmallerNumberForChange) {
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_CHANGE), 1234};
+    EXPECT_TRUE(isAvailabilitySequenceNumberNewer(*message, 1233));
+}
+
+TEST(VmsUtilsTest, newAvailabilitySequenceNumberForExistingSmallerNumberForResponse) {
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_RESPONSE), 1234};
+    EXPECT_TRUE(isAvailabilitySequenceNumberNewer(*message, 1233));
+}
+
+TEST(VmsUtilsTest, newAvailabilitySequenceNumberForExistingGreaterNumberForChange) {
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_CHANGE), 1234};
+    EXPECT_FALSE(isAvailabilitySequenceNumberNewer(*message, 1235));
+}
+
+TEST(VmsUtilsTest, newAvailabilitySequenceNumberForExistingGreaterNumberForResponse) {
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_RESPONSE), 1234};
+    EXPECT_FALSE(isAvailabilitySequenceNumberNewer(*message, 1235));
+}
+
+TEST(VmsUtilsTest, newAvailabilitySequenceNumberForSameNumberForChange) {
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_CHANGE), 1234};
+    EXPECT_FALSE(isAvailabilitySequenceNumberNewer(*message, 1234));
+}
+
+TEST(VmsUtilsTest, newAvailabilitySequenceNumberForSameNumberForResponse) {
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_RESPONSE), 1234};
+    EXPECT_FALSE(isAvailabilitySequenceNumberNewer(*message, 1234));
+}
+
+TEST(VmsUtilsTest, validSequenceNumberForAvailabilityChange) {
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_CHANGE), 1234};
+    EXPECT_EQ(getSequenceNumberForAvailabilityState(*message), 1234);
+}
+
+TEST(VmsUtilsTest, validSequenceNumberForAvailabilityResponse) {
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_RESPONSE), 1234};
+    EXPECT_EQ(getSequenceNumberForAvailabilityState(*message), 1234);
+}
+
+TEST(VmsUtilsTest, invalidAvailabilityState) {
+    auto message = createBaseVmsMessage(1);
+    EXPECT_EQ(getSequenceNumberForAvailabilityState(*message), -1);
+}
+
+void testGetAvailableLayers(VmsMessageType type) {
+    auto message = createBaseVmsMessage(13);
+    message->value.int32Values = hidl_vec<int32_t>{toInt(type),
+                                                   1234,  // sequence number
+                                                   2,     // number of associated layers
+                                                   1,     // associated layer 1
+                                                   0,           1,
+                                                   2,    // number of publisher IDs
+                                                   111,  // publisher IDs
+                                                   123,
+                                                   2,                   // associated layer 2
+                                                   0,           1, 0};  // number of publisher IDs
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    auto result = getAvailableLayers(*message);
+    EXPECT_EQ(static_cast<int>(result.size()), 2);
+    EXPECT_EQ(result.at(0).layer, VmsLayer(1, 0, 1));
+    EXPECT_EQ(result.at(0).publisher_ids.at(0), 111);
+    EXPECT_EQ(result.at(0).publisher_ids.at(1), 123);
+    EXPECT_EQ(result.at(1).layer, VmsLayer(2, 0, 1));
+    EXPECT_EQ(static_cast<int>(result.at(1).publisher_ids.size()), 0);
+}
+
+TEST(VmsUtilsTest, availableLayersForChange) {
+    testGetAvailableLayers(VmsMessageType::AVAILABILITY_CHANGE);
+}
+
+TEST(VmsUtilsTest, availableLayersForResponse) {
+    testGetAvailableLayers(VmsMessageType::AVAILABILITY_RESPONSE);
+}
+
+void testGetAvailableLayersMalformedData(VmsMessageType type) {
+    auto message = createBaseVmsMessage(2);
+    message->value.int32Values = hidl_vec<int32_t>{toInt(type), 1234};  // sequence number
+    EXPECT_TRUE(isValidVmsMessage(*message));
+    auto result = getAvailableLayers(*message);
+    EXPECT_EQ(static_cast<int>(result.size()), 0);
+}
+
+TEST(VmsUtilsTest, availableLayersForMalformedChange) {
+    testGetAvailableLayersMalformedData(VmsMessageType::AVAILABILITY_CHANGE);
+}
+
+TEST(VmsUtilsTest, availableLayersForMalformedResponse) {
+    testGetAvailableLayersMalformedData(VmsMessageType::AVAILABILITY_RESPONSE);
+}
+
 }  // namespace
 
 }  // namespace vms
diff --git a/boot/1.1/default/boot_control/Android.bp b/boot/1.1/default/boot_control/Android.bp
new file mode 100644
index 0000000..b2e68df
--- /dev/null
+++ b/boot/1.1/default/boot_control/Android.bp
@@ -0,0 +1,61 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+    name: "libboot_control_defaults",
+    vendor: true,
+    recovery_available: true,
+    relative_install_path: "hw",
+
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+
+    shared_libs: [
+        "android.hardware.boot@1.1",
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libbootloader_message_vendor",
+        "libfstab",
+    ],
+}
+
+cc_library_static {
+    name: "libboot_control",
+    defaults: ["libboot_control_defaults"],
+    export_include_dirs: ["include"],
+
+    srcs: ["libboot_control.cpp"],
+}
+
+cc_library_shared {
+    name: "bootctrl.default",
+    defaults: ["libboot_control_defaults"],
+
+    srcs: ["legacy_boot_control.cpp"],
+
+    static_libs: [
+        "libboot_control",
+    ],
+    shared_libs: [
+        "libhardware",
+    ],
+}
diff --git a/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h b/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
new file mode 100644
index 0000000..5468658
--- /dev/null
+++ b/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
@@ -0,0 +1,89 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <string>
+
+#include <android/hardware/boot/1.1/IBootControl.h>
+
+namespace android {
+namespace bootable {
+
+// Helper library to implement the IBootControl HAL using the misc partition.
+class BootControl {
+  using MergeStatus = ::android::hardware::boot::V1_1::MergeStatus;
+
+ public:
+  bool Init();
+  unsigned int GetNumberSlots();
+  unsigned int GetCurrentSlot();
+  bool MarkBootSuccessful();
+  bool SetActiveBootSlot(unsigned int slot);
+  bool SetSlotAsUnbootable(unsigned int slot);
+  bool SetSlotBootable(unsigned int slot);
+  bool IsSlotBootable(unsigned int slot);
+  const char* GetSuffix(unsigned int slot);
+  bool IsSlotMarkedSuccessful(unsigned int slot);
+  bool SetSnapshotMergeStatus(MergeStatus status);
+  MergeStatus GetSnapshotMergeStatus();
+
+  bool IsValidSlot(unsigned int slot);
+
+  const std::string& misc_device() const {
+    return misc_device_;
+  }
+
+ private:
+  // Whether this object was initialized with data from the bootloader message
+  // that doesn't change until next reboot.
+  bool initialized_ = false;
+
+  // The path to the misc_device as reported in the fstab.
+  std::string misc_device_;
+
+  // The number of slots present on the device.
+  unsigned int num_slots_ = 0;
+
+  // The slot where we are running from.
+  unsigned int current_slot_ = 0;
+};
+
+// Helper functions to write the Virtual A/B merge status message. These are
+// separate because BootControl uses bootloader_control_ab in vendor space,
+// whereas the Virtual A/B merge status is in system space. A HAL might not
+// use bootloader_control_ab, but may want to use the AOSP method of maintaining
+// the merge status.
+
+// If the Virtual A/B message has not yet been initialized, then initialize it.
+// This should be called when the BootControl HAL first loads.
+//
+// If the Virtual A/B message in misc was already initialized, true is returned.
+// If initialization was attempted, but failed, false is returned, and the HAL
+// should fail to load.
+bool InitMiscVirtualAbMessageIfNeeded();
+
+// Save the current merge status as well as the current slot.
+bool SetMiscVirtualAbMergeStatus(unsigned int current_slot,
+                                 android::hardware::boot::V1_1::MergeStatus status);
+
+// Return the current merge status. If the saved status is SNAPSHOTTED but the
+// slot hasn't changed, the status returned will be NONE.
+bool GetMiscVirtualAbMergeStatus(unsigned int current_slot,
+                                 android::hardware::boot::V1_1::MergeStatus* status);
+
+}  // namespace bootable
+}  // namespace android
diff --git a/boot/1.1/default/boot_control/include/private/boot_control_definition.h b/boot/1.1/default/boot_control/include/private/boot_control_definition.h
new file mode 100644
index 0000000..8f02111
--- /dev/null
+++ b/boot/1.1/default/boot_control/include/private/boot_control_definition.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * The A/B-specific bootloader message structure (4-KiB).
+ *
+ * We separate A/B boot control metadata from the regular bootloader
+ * message struct and keep it here. Everything that's A/B-specific
+ * stays after struct bootloader_message, which belongs to the vendor
+ * space of /misc partition. Also, the A/B-specific contents should be
+ * managed by the A/B-bootloader or boot control HAL.
+ *
+ * The slot_suffix field is used for A/B implementations where the
+ * bootloader does not set the androidboot.ro.boot.slot_suffix kernel
+ * commandline parameter. This is used by fs_mgr to mount /system and
+ * other partitions with the slotselect flag set in fstab. A/B
+ * implementations are free to use all 32 bytes and may store private
+ * data past the first NUL-byte in this field. It is encouraged, but
+ * not mandatory, to use 'struct bootloader_control' described below.
+ *
+ * The update_channel field is used to store the Omaha update channel
+ * if update_engine is compiled with Omaha support.
+ */
+struct bootloader_message_ab {
+    struct bootloader_message message;
+    char slot_suffix[32];
+    char update_channel[128];
+
+    // Round up the entire struct to 4096-byte.
+    char reserved[1888];
+};
+
+/**
+ * Be cautious about the struct size change, in case we put anything post
+ * bootloader_message_ab struct (b/29159185).
+ */
+#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_message_ab) == 4096,
+              "struct bootloader_message_ab size changes");
+#endif
+
+#define BOOT_CTRL_MAGIC   0x42414342 /* Bootloader Control AB */
+#define BOOT_CTRL_VERSION 1
+
+struct slot_metadata {
+    // Slot priority with 15 meaning highest priority, 1 lowest
+    // priority and 0 the slot is unbootable.
+    uint8_t priority : 4;
+    // Number of times left attempting to boot this slot.
+    uint8_t tries_remaining : 3;
+    // 1 if this slot has booted successfully, 0 otherwise.
+    uint8_t successful_boot : 1;
+    // 1 if this slot is corrupted from a dm-verity corruption, 0
+    // otherwise.
+    uint8_t verity_corrupted : 1;
+    // Reserved for further use.
+    uint8_t reserved : 7;
+} __attribute__((packed));
+
+/* Bootloader Control AB
+ *
+ * This struct can be used to manage A/B metadata. It is designed to
+ * be put in the 'slot_suffix' field of the 'bootloader_message'
+ * structure described above. It is encouraged to use the
+ * 'bootloader_control' structure to store the A/B metadata, but not
+ * mandatory.
+ */
+struct bootloader_control {
+    // NUL terminated active slot suffix.
+    char slot_suffix[4];
+    // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
+    uint32_t magic;
+    // Version of struct being used (see BOOT_CTRL_VERSION).
+    uint8_t version;
+    // Number of slots being managed.
+    uint8_t nb_slot : 3;
+    // Number of times left attempting to boot recovery.
+    uint8_t recovery_tries_remaining : 3;
+    // Status of any pending snapshot merge of dynamic partitions.
+    uint8_t merge_status : 3;
+    // Ensure 4-bytes alignment for slot_info field.
+    uint8_t reserved0[1];
+    // Per-slot information.  Up to 4 slots.
+    struct slot_metadata slot_info[4];
+    // Reserved for further use.
+    uint8_t reserved1[8];
+    // CRC32 of all 28 bytes preceding this field (little endian
+    // format).
+    uint32_t crc32_le;
+} __attribute__((packed));
+
+#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_control) ==
+              sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
+              "struct bootloader_control has wrong size");
+#endif
+
diff --git a/boot/1.1/default/boot_control/legacy_boot_control.cpp b/boot/1.1/default/boot_control/legacy_boot_control.cpp
new file mode 100644
index 0000000..73d3a58
--- /dev/null
+++ b/boot/1.1/default/boot_control/legacy_boot_control.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 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 <string>
+
+#include <hardware/boot_control.h>
+#include <hardware/hardware.h>
+
+#include <libboot_control/libboot_control.h>
+
+using android::bootable::BootControl;
+
+struct boot_control_private_t {
+  // The base struct needs to be first in the list.
+  boot_control_module_t base;
+
+  BootControl impl;
+};
+
+namespace {
+
+void BootControl_init(boot_control_module_t* module) {
+  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
+  impl.Init();
+}
+
+unsigned int BootControl_getNumberSlots(boot_control_module_t* module) {
+  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
+  return impl.GetNumberSlots();
+}
+
+unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) {
+  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
+  return impl.GetCurrentSlot();
+}
+
+int BootControl_markBootSuccessful(boot_control_module_t* module) {
+  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
+  return impl.MarkBootSuccessful() ? 0 : -1;
+}
+
+int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) {
+  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
+  return impl.SetActiveBootSlot(slot) ? 0 : -1;
+}
+
+int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) {
+  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
+  return impl.SetSlotAsUnbootable(slot) ? 0 : -1;
+}
+
+int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) {
+  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
+  return impl.IsSlotBootable(slot) ? 0 : -1;
+}
+
+int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) {
+  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
+  return impl.IsSlotMarkedSuccessful(slot) ? 0 : -1;
+}
+
+const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) {
+  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
+  return impl.GetSuffix(slot);
+}
+
+static int BootControl_open(const hw_module_t* module __unused, const char* id __unused,
+                            hw_device_t** device __unused) {
+  /* Nothing to do currently. */
+  return 0;
+}
+
+struct hw_module_methods_t BootControl_methods = {
+  .open = BootControl_open,
+};
+
+}  // namespace
+
+boot_control_private_t HAL_MODULE_INFO_SYM = {
+  .base =
+      {
+          .common =
+              {
+                  .tag = HARDWARE_MODULE_TAG,
+                  .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1,
+                  .hal_api_version = HARDWARE_HAL_API_VERSION,
+                  .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
+                  .name = "AOSP reference bootctrl HAL",
+                  .author = "The Android Open Source Project",
+                  .methods = &BootControl_methods,
+              },
+          .init = BootControl_init,
+          .getNumberSlots = BootControl_getNumberSlots,
+          .getCurrentSlot = BootControl_getCurrentSlot,
+          .markBootSuccessful = BootControl_markBootSuccessful,
+          .setActiveBootSlot = BootControl_setActiveBootSlot,
+          .setSlotAsUnbootable = BootControl_setSlotAsUnbootable,
+          .isSlotBootable = BootControl_isSlotBootable,
+          .getSuffix = BootControl_getSuffix,
+          .isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful,
+      },
+};
diff --git a/boot/1.1/default/boot_control/libboot_control.cpp b/boot/1.1/default/boot_control/libboot_control.cpp
new file mode 100644
index 0000000..2c6ccaf
--- /dev/null
+++ b/boot/1.1/default/boot_control/libboot_control.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2015 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 <libboot_control/libboot_control.h>
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <bootloader_message/bootloader_message.h>
+
+#include "private/boot_control_definition.h"
+
+namespace android {
+namespace bootable {
+
+using ::android::hardware::boot::V1_1::MergeStatus;
+
+// The number of boot attempts that should be made from a new slot before
+// rolling back to the previous slot.
+constexpr unsigned int kDefaultBootAttempts = 7;
+static_assert(kDefaultBootAttempts < 8, "tries_remaining field only has 3 bits");
+
+constexpr unsigned int kMaxNumSlots =
+    sizeof(bootloader_control::slot_info) / sizeof(bootloader_control::slot_info[0]);
+constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
+constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix);
+
+static uint32_t CRC32(const uint8_t* buf, size_t size) {
+  static uint32_t crc_table[256];
+
+  // Compute the CRC-32 table only once.
+  if (!crc_table[1]) {
+    for (uint32_t i = 0; i < 256; ++i) {
+      uint32_t crc = i;
+      for (uint32_t j = 0; j < 8; ++j) {
+        uint32_t mask = -(crc & 1);
+        crc = (crc >> 1) ^ (0xEDB88320 & mask);
+      }
+      crc_table[i] = crc;
+    }
+  }
+
+  uint32_t ret = -1;
+  for (size_t i = 0; i < size; ++i) {
+    ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF];
+  }
+
+  return ~ret;
+}
+
+// Return the little-endian representation of the CRC-32 of the first fields
+// in |boot_ctrl| up to the crc32_le field.
+uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) {
+  return htole32(
+      CRC32(reinterpret_cast<const uint8_t*>(boot_ctrl), offsetof(bootloader_control, crc32_le)));
+}
+
+bool LoadBootloaderControl(const std::string& misc_device, bootloader_control* buffer) {
+  android::base::unique_fd fd(open(misc_device.c_str(), O_RDONLY));
+  if (fd.get() == -1) {
+    PLOG(ERROR) << "failed to open " << misc_device;
+    return false;
+  }
+  if (lseek(fd, kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
+    PLOG(ERROR) << "failed to lseek " << misc_device;
+    return false;
+  }
+  if (!android::base::ReadFully(fd.get(), buffer, sizeof(bootloader_control))) {
+    PLOG(ERROR) << "failed to read " << misc_device;
+    return false;
+  }
+  return true;
+}
+
+bool UpdateAndSaveBootloaderControl(const std::string& misc_device, bootloader_control* buffer) {
+  buffer->crc32_le = BootloaderControlLECRC(buffer);
+  android::base::unique_fd fd(open(misc_device.c_str(), O_WRONLY | O_SYNC));
+  if (fd.get() == -1) {
+    PLOG(ERROR) << "failed to open " << misc_device;
+    return false;
+  }
+  if (lseek(fd.get(), kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
+    PLOG(ERROR) << "failed to lseek " << misc_device;
+    return false;
+  }
+  if (!android::base::WriteFully(fd.get(), buffer, sizeof(bootloader_control))) {
+    PLOG(ERROR) << "failed to write " << misc_device;
+    return false;
+  }
+  return true;
+}
+
+void InitDefaultBootloaderControl(BootControl* control, bootloader_control* boot_ctrl) {
+  memset(boot_ctrl, 0, sizeof(*boot_ctrl));
+
+  unsigned int current_slot = control->GetCurrentSlot();
+  if (current_slot < kMaxNumSlots) {
+    strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[current_slot], sizeof(boot_ctrl->slot_suffix));
+  }
+  boot_ctrl->magic = BOOT_CTRL_MAGIC;
+  boot_ctrl->version = BOOT_CTRL_VERSION;
+
+  // Figure out the number of slots by checking if the partitions exist,
+  // otherwise assume the maximum supported by the header.
+  boot_ctrl->nb_slot = kMaxNumSlots;
+  std::string base_path = control->misc_device();
+  size_t last_path_sep = base_path.rfind('/');
+  if (last_path_sep != std::string::npos) {
+    // We test the existence of the "boot" partition on each possible slot,
+    // which is a partition required by Android Bootloader Requirements.
+    base_path = base_path.substr(0, last_path_sep + 1) + "boot";
+    int last_existing_slot = -1;
+    int first_missing_slot = -1;
+    for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
+      std::string partition_path = base_path + kSlotSuffixes[slot];
+      struct stat part_stat;
+      int err = stat(partition_path.c_str(), &part_stat);
+      if (!err) {
+        last_existing_slot = slot;
+        LOG(INFO) << "Found slot: " << kSlotSuffixes[slot];
+      } else if (err < 0 && errno == ENOENT && first_missing_slot == -1) {
+        first_missing_slot = slot;
+      }
+    }
+    // We only declare that we found the actual number of slots if we found all
+    // the boot partitions up to the number of slots, and no boot partition
+    // after that. Not finding any of the boot partitions implies a problem so
+    // we just leave the number of slots in the maximum value.
+    if ((last_existing_slot != -1 && last_existing_slot + 1 == first_missing_slot) ||
+        (first_missing_slot == -1 && last_existing_slot + 1 == kMaxNumSlots)) {
+      boot_ctrl->nb_slot = last_existing_slot + 1;
+      LOG(INFO) << "Found a system with " << last_existing_slot + 1 << " slots.";
+    }
+  }
+
+  for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
+    slot_metadata entry = {};
+
+    if (slot < boot_ctrl->nb_slot) {
+      entry.priority = 7;
+      entry.tries_remaining = kDefaultBootAttempts;
+      entry.successful_boot = 0;
+    } else {
+      entry.priority = 0;  // Unbootable
+    }
+
+    // When the boot_control stored on disk is invalid, we assume that the
+    // current slot is successful. The bootloader should repair this situation
+    // before booting and write a valid boot_control slot, so if we reach this
+    // stage it means that the misc partition was corrupted since boot.
+    if (current_slot == slot) {
+      entry.successful_boot = 1;
+    }
+
+    boot_ctrl->slot_info[slot] = entry;
+  }
+  boot_ctrl->recovery_tries_remaining = 0;
+
+  boot_ctrl->crc32_le = BootloaderControlLECRC(boot_ctrl);
+}
+
+// Return the index of the slot suffix passed or -1 if not a valid slot suffix.
+int SlotSuffixToIndex(const char* suffix) {
+  for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
+    if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;
+  }
+  return -1;
+}
+
+// Initialize the boot_control_private struct with the information from
+// the bootloader_message buffer stored in |boot_ctrl|. Returns whether the
+// initialization succeeded.
+bool BootControl::Init() {
+  if (initialized_) return true;
+
+  // Initialize the current_slot from the read-only property. If the property
+  // was not set (from either the command line or the device tree), we can later
+  // initialize it from the bootloader_control struct.
+  std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", "");
+  if (suffix_prop.empty()) {
+    LOG(ERROR) << "Slot suffix property is not set";
+    return false;
+  }
+  current_slot_ = SlotSuffixToIndex(suffix_prop.c_str());
+
+  std::string err;
+  std::string device = get_bootloader_message_blk_device(&err);
+  if (device.empty()) {
+    LOG(ERROR) << "Could not find bootloader message block device: " << err;
+    return false;
+  }
+
+  bootloader_control boot_ctrl;
+  if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) {
+    LOG(ERROR) << "Failed to load bootloader control block";
+    return false;
+  }
+
+  // Note that since there isn't a module unload function this memory is leaked.
+  // We use `device` below sometimes, so it's not moved out of here.
+  misc_device_ = device;
+  initialized_ = true;
+
+  // Validate the loaded data, otherwise we will destroy it and re-initialize it
+  // with the current information.
+  uint32_t computed_crc32 = BootloaderControlLECRC(&boot_ctrl);
+  if (boot_ctrl.crc32_le != computed_crc32) {
+    LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32
+                 << " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing.";
+    InitDefaultBootloaderControl(this, &boot_ctrl);
+    UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl);
+  }
+
+  if (!InitMiscVirtualAbMessageIfNeeded()) {
+    return false;
+  }
+
+  num_slots_ = boot_ctrl.nb_slot;
+  return true;
+}
+
+unsigned int BootControl::GetNumberSlots() {
+  return num_slots_;
+}
+
+unsigned int BootControl::GetCurrentSlot() {
+  return current_slot_;
+}
+
+bool BootControl::MarkBootSuccessful() {
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
+
+  bootctrl.slot_info[current_slot_].successful_boot = 1;
+  // tries_remaining == 0 means that the slot is not bootable anymore, make
+  // sure we mark the current slot as bootable if it succeeds in the last
+  // attempt.
+  bootctrl.slot_info[current_slot_].tries_remaining = 1;
+  return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
+}
+
+bool BootControl::SetActiveBootSlot(unsigned int slot) {
+  if (slot >= kMaxNumSlots || slot >= num_slots_) {
+    // Invalid slot number.
+    return false;
+  }
+
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
+
+  // Set every other slot with a lower priority than the new "active" slot.
+  const unsigned int kActivePriority = 15;
+  const unsigned int kActiveTries = 6;
+  for (unsigned int i = 0; i < num_slots_; ++i) {
+    if (i != slot) {
+      if (bootctrl.slot_info[i].priority >= kActivePriority)
+        bootctrl.slot_info[i].priority = kActivePriority - 1;
+    }
+  }
+
+  // Note that setting a slot as active doesn't change the successful bit.
+  // The successful bit will only be changed by setSlotAsUnbootable().
+  bootctrl.slot_info[slot].priority = kActivePriority;
+  bootctrl.slot_info[slot].tries_remaining = kActiveTries;
+
+  // Setting the current slot as active is a way to revert the operation that
+  // set *another* slot as active at the end of an updater. This is commonly
+  // used to cancel the pending update. We should only reset the verity_corrpted
+  // bit when attempting a new slot, otherwise the verity bit on the current
+  // slot would be flip.
+  if (slot != current_slot_) bootctrl.slot_info[slot].verity_corrupted = 0;
+
+  return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
+}
+
+bool BootControl::SetSlotAsUnbootable(unsigned int slot) {
+  if (slot >= kMaxNumSlots || slot >= num_slots_) {
+    // Invalid slot number.
+    return false;
+  }
+
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
+
+  // The only way to mark a slot as unbootable, regardless of the priority is to
+  // set the tries_remaining to 0.
+  bootctrl.slot_info[slot].successful_boot = 0;
+  bootctrl.slot_info[slot].tries_remaining = 0;
+  return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
+}
+
+bool BootControl::IsSlotBootable(unsigned int slot) {
+  if (slot >= kMaxNumSlots || slot >= num_slots_) {
+    // Invalid slot number.
+    return false;
+  }
+
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
+
+  return bootctrl.slot_info[slot].tries_remaining != 0;
+}
+
+bool BootControl::IsSlotMarkedSuccessful(unsigned int slot) {
+  if (slot >= kMaxNumSlots || slot >= num_slots_) {
+    // Invalid slot number.
+    return false;
+  }
+
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
+
+  return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining;
+}
+
+bool BootControl::IsValidSlot(unsigned int slot) {
+  return slot < kMaxNumSlots && slot < num_slots_;
+}
+
+bool BootControl::SetSnapshotMergeStatus(MergeStatus status) {
+  return SetMiscVirtualAbMergeStatus(current_slot_, status);
+}
+
+MergeStatus BootControl::GetSnapshotMergeStatus() {
+  MergeStatus status;
+  if (!GetMiscVirtualAbMergeStatus(current_slot_, &status)) {
+    return MergeStatus::UNKNOWN;
+  }
+  return status;
+}
+
+const char* BootControl::GetSuffix(unsigned int slot) {
+  if (slot >= kMaxNumSlots || slot >= num_slots_) {
+    return nullptr;
+  }
+  return kSlotSuffixes[slot];
+}
+
+bool InitMiscVirtualAbMessageIfNeeded() {
+  std::string err;
+  misc_virtual_ab_message message;
+  if (!ReadMiscVirtualAbMessage(&message, &err)) {
+    LOG(ERROR) << "Could not read merge status: " << err;
+    return false;
+  }
+
+  if (message.version == MISC_VIRTUAL_AB_MESSAGE_VERSION &&
+      message.magic == MISC_VIRTUAL_AB_MAGIC_HEADER) {
+    // Already initialized.
+    return true;
+  }
+
+  message = {};
+  message.version = MISC_VIRTUAL_AB_MESSAGE_VERSION;
+  message.magic = MISC_VIRTUAL_AB_MAGIC_HEADER;
+  if (!WriteMiscVirtualAbMessage(message, &err)) {
+    LOG(ERROR) << "Could not write merge status: " << err;
+    return false;
+  }
+  return true;
+}
+
+bool SetMiscVirtualAbMergeStatus(unsigned int current_slot,
+                                 android::hardware::boot::V1_1::MergeStatus status) {
+  std::string err;
+  misc_virtual_ab_message message;
+
+  if (!ReadMiscVirtualAbMessage(&message, &err)) {
+    LOG(ERROR) << "Could not read merge status: " << err;
+    return false;
+  }
+
+  message.merge_status = static_cast<uint8_t>(status);
+  message.source_slot = current_slot;
+  if (!WriteMiscVirtualAbMessage(message, &err)) {
+    LOG(ERROR) << "Could not write merge status: " << err;
+    return false;
+  }
+  return true;
+}
+
+bool GetMiscVirtualAbMergeStatus(unsigned int current_slot,
+                                 android::hardware::boot::V1_1::MergeStatus* status) {
+  std::string err;
+  misc_virtual_ab_message message;
+
+  if (!ReadMiscVirtualAbMessage(&message, &err)) {
+    LOG(ERROR) << "Could not read merge status: " << err;
+    return false;
+  }
+
+  // If the slot reverted after having created a snapshot, then the snapshot will
+  // be thrown away at boot. Thus we don't count this as being in a snapshotted
+  // state.
+  *status = static_cast<MergeStatus>(message.merge_status);
+  if (*status == MergeStatus::SNAPSHOTTED && current_slot == message.source_slot) {
+    *status = MergeStatus::NONE;
+  }
+  return true;
+}
+
+}  // namespace bootable
+}  // namespace android
diff --git a/current.txt b/current.txt
index 2638134..d61fc03 100644
--- a/current.txt
+++ b/current.txt
@@ -298,7 +298,13 @@
 3661fa0623056922fdc4235ac5a9c91a2d066ab6f1ab4297e3b240fe302ba500 android.hardware.audio.effect@4.0::IPresetReverbEffect
 e88e520f8c98a62fccd8d5316c6687808f775de145d1405a7a9a66587ee6a001 android.hardware.audio.effect@4.0::IVirtualizerEffect
 fe28829dab10d171783b79ac9cc45412739f8ff275e90228d7c6370ef189b859 android.hardware.audio.effect@4.0::IVisualizerEffect
-21c8a702579356480236c6851b5b2c16b9bd369ce12bdd6ffdc4626a89f34f73  android.hardware.audio.effect@4.0::types
+21c8a702579356480236c6851b5b2c16b9bd369ce12bdd6ffdc4626a89f34f73 android.hardware.audio.effect@4.0::types
+a0f93c768c353cecee6237fe479bce47404eb10b629fafe07e32a054fd67f2af android.hardware.automotive.audiocontrol@1.0::IAudioControl
+f2904a4c108ad1b93eb2fa4e43b82bd01ce1ff26156316e49d1d9fc80dfecaad android.hardware.automotive.evs@1.0::IEvsCamera
+94cba6ad04c83aa840de2ed52b74ba2126a26dd960225e61ac36703315279a80 android.hardware.automotive.evs@1.0::IEvsCameraStream
+5ea36fb043d9e3b413219de3dfd7b046b48af4fda39f167f3528652e986cb76d android.hardware.automotive.evs@1.0::IEvsDisplay
+b15c5d8f28be4f0469c11d184ebca616895f109d553a6c31018789d8c1bc0ac5 android.hardware.automotive.evs@1.0::IEvsEnumerator
+3b17c1fdfc389e0abe626c37054954b07201127d890c2bc05d47613ec1f4de4f android.hardware.automotive.evs@1.0::types
 42a06dc288f61b0690580f3d37b30b663c31d74d50bb58d0772386b550d5faab android.hardware.authsecret@1.0::IAuthSecret
 32cc50cc2a7658ec613c0c2dd2accbf6a05113b749852879e818b8b7b438db19 android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioHost
 ff4be64d7992f8bec97dff37f35450e79b3430c61f85f54322ce45bef229dc3b android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioOffload
@@ -628,9 +634,9 @@
 9db064ee44268a876be0367ff771e618362d39ec603b6ecab17e1575725fcd87 android.hardware.neuralnetworks@1.3::IDevice
 4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback
 2fa3679ad7c94b5e88724adcd560c561041068a4ca565c63830e68101988746a android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
-237b23b126a66f3432658020fed78cdd06ba6297459436fe6bae0ba753370833 android.hardware.neuralnetworks@1.3::IPreparedModel
+43088ffc71945b463a7279262cfe2e290f6ed2f15d3fd6032798a3be299fb08f android.hardware.neuralnetworks@1.3::IPreparedModel
 0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback
-2fabd246f985d94a0172dacefb0d6cf19e2aeb2d5f17752653988ef39570a52d android.hardware.neuralnetworks@1.3::types
+306fda32ac969fd51d75d066352cadcb769944ec4823be4cdd3f86fdb9e97511 android.hardware.neuralnetworks@1.3::types
 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
 a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant
 44445b8a03d7b9e68b2fbd954672c18a8fce9e32851b0692f4f4ab3407f86ecb android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
diff --git a/drm/1.0/default/CryptoPlugin.cpp b/drm/1.0/default/CryptoPlugin.cpp
index 8ddc380..2db3607 100644
--- a/drm/1.0/default/CryptoPlugin.cpp
+++ b/drm/1.0/default/CryptoPlugin.cpp
@@ -152,6 +152,7 @@
                 return Void();
             }
 
+            base = static_cast<uint8_t *>(static_cast<void *>(destBase->getPointer()));
             destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset);
         } else if (destination.type == BufferType::NATIVE_HANDLE) {
             if (!secure) {
diff --git a/health/2.0/default/HealthImplDefault.cpp b/health/2.0/default/HealthImplDefault.cpp
index e3cbefd..08fee9e 100644
--- a/health/2.0/default/HealthImplDefault.cpp
+++ b/health/2.0/default/HealthImplDefault.cpp
@@ -21,18 +21,6 @@
 using android::hardware::health::V2_0::implementation::Health;
 
 static struct healthd_config gHealthdConfig = {
-    .batteryStatusPath = android::String8(android::String8::kEmptyString),
-    .batteryHealthPath = android::String8(android::String8::kEmptyString),
-    .batteryPresentPath = android::String8(android::String8::kEmptyString),
-    .batteryCapacityPath = android::String8(android::String8::kEmptyString),
-    .batteryVoltagePath = android::String8(android::String8::kEmptyString),
-    .batteryTemperaturePath = android::String8(android::String8::kEmptyString),
-    .batteryTechnologyPath = android::String8(android::String8::kEmptyString),
-    .batteryCurrentNowPath = android::String8(android::String8::kEmptyString),
-    .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString),
-    .batteryChargeCounterPath = android::String8(android::String8::kEmptyString),
-    .batteryFullChargePath = android::String8(android::String8::kEmptyString),
-    .batteryCycleCountPath = android::String8(android::String8::kEmptyString),
     .energyCounter = nullptr,
     .boot_min_cap = 0,
     .screen_on = nullptr};
diff --git a/health/utils/libhealthloop/utils.cpp b/health/utils/libhealthloop/utils.cpp
index 053fd19..cd8c7a9 100644
--- a/health/utils/libhealthloop/utils.cpp
+++ b/health/utils/libhealthloop/utils.cpp
@@ -28,21 +28,6 @@
     *healthd_config = {
             .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
             .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
-            .batteryStatusPath = String8(String8::kEmptyString),
-            .batteryHealthPath = String8(String8::kEmptyString),
-            .batteryPresentPath = String8(String8::kEmptyString),
-            .batteryCapacityPath = String8(String8::kEmptyString),
-            .batteryVoltagePath = String8(String8::kEmptyString),
-            .batteryTemperaturePath = String8(String8::kEmptyString),
-            .batteryTechnologyPath = String8(String8::kEmptyString),
-            .batteryCurrentNowPath = String8(String8::kEmptyString),
-            .batteryCurrentAvgPath = String8(String8::kEmptyString),
-            .batteryChargeCounterPath = String8(String8::kEmptyString),
-            .batteryFullChargePath = String8(String8::kEmptyString),
-            .batteryCycleCountPath = String8(String8::kEmptyString),
-            .batteryCapacityLevelPath = String8(String8::kEmptyString),
-            .batteryChargeTimeToFullNowPath = String8(String8::kEmptyString),
-            .batteryFullChargeDesignCapacityUahPath = String8(String8::kEmptyString),
             .energyCounter = NULL,
             .boot_min_cap = 0,
             .screen_on = NULL,
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
index ba2062d..89f7f35 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -16,6 +16,9 @@
 
 #define LOG_TAG "WritableIdentityCredential"
 
+#include "WritableIdentityCredential.h"
+#include "IdentityCredentialStore.h"
+
 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
 
 #include <android-base/logging.h>
@@ -23,6 +26,8 @@
 #include <cppbor/cppbor.h>
 #include <cppbor/cppbor_parse.h>
 
+#include <utility>
+
 #include "IdentityCredentialStore.h"
 #include "Util.h"
 #include "WritableIdentityCredential.h"
@@ -33,26 +38,6 @@
 using namespace ::android::hardware::identity;
 
 bool WritableIdentityCredential::initialize() {
-    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
-    if (!keyPair) {
-        LOG(ERROR) << "Error creating credentialKey";
-        return false;
-    }
-
-    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
-    if (!pubKey) {
-        LOG(ERROR) << "Error getting public part of credentialKey";
-        return false;
-    }
-    credentialPubKey_ = pubKey.value();
-
-    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
-    if (!privKey) {
-        LOG(ERROR) << "Error getting private part of credentialKey";
-        return false;
-    }
-    credentialPrivKey_ = privKey.value();
-
     optional<vector<uint8_t>> random = support::getRandom(16);
     if (!random) {
         LOG(ERROR) << "Error creating storageKey";
@@ -63,88 +48,54 @@
     return true;
 }
 
-// TODO: use |attestationApplicationId| and |attestationChallenge| and also
-//       ensure the returned certificate chain satisfy the requirements listed in
-//       the docs for IWritableIdentityCredential::getAttestationCertificate()
-//
+// This function generates the attestation certificate using the passed in
+// |attestationApplicationId| and |attestationChallenge|.  It will generate an
+// attestation certificate with current time and expires one year from now.  The
+// certificate shall contain all values as specified in hal.
 ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
-        const vector<int8_t>& /*attestationApplicationId*/,
-        const vector<int8_t>& /*attestationChallenge*/, vector<Certificate>* outCertificateChain) {
-    // For now, we dynamically generate an attestion key on each and every
-    // request and use that to sign CredentialKey. In a real implementation this
-    // would look very differently.
-    optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
-    if (!attestationKeyPair) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey"));
-    }
-
-    optional<vector<uint8_t>> attestationPubKey =
-            support::ecKeyPairGetPublicKey(attestationKeyPair.value());
-    if (!attestationPubKey) {
+        const vector<int8_t>& attestationApplicationId,  //
+        const vector<int8_t>& attestationChallenge,      //
+        vector<Certificate>* outCertificateChain) {
+    if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting public part of attestationKey"));
+                "Error attestation certificate previously generated"));
     }
 
-    optional<vector<uint8_t>> attestationPrivKey =
-            support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
-    if (!attestationPrivKey) {
+    vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
+    vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
+
+    optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
+            support::createEcKeyPairAndAttestation(challenge, appId);
+    if (!keyAttestationPair) {
+        LOG(ERROR) << "Error creating credentialKey and attestation";
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting private part of attestationKey"));
+                "Error creating credentialKey and attestation"));
     }
 
-    string serialDecimal;
-    string issuer;
-    string subject;
-    time_t validityNotBefore = time(nullptr);
-    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+    vector<uint8_t> keyPair = keyAttestationPair.value().first;
+    certificateChain_ = keyAttestationPair.value().second;
 
-    // First create a certificate for |credentialPubKey| which is signed by
-    // |attestationPrivKey|.
-    //
-    serialDecimal = "0";  // TODO: set serial to |attestationChallenge|
-    issuer = "Android Open Source Project";
-    subject = "Android IdentityCredential CredentialKey";
-    optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
-            credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
-            validityNotBefore, validityNotAfter);
-    if (!credentialPubKeyCertificate) {
+    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
+    if (!pubKey) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED,
-                "Error creating certificate for credentialPubKey"));
+                "Error getting public part of credentialKey"));
     }
+    credentialPubKey_ = pubKey.value();
 
-    // This is followed by a certificate for |attestationPubKey| self-signed by
-    // |attestationPrivKey|.
-    serialDecimal = "0";  // TODO: set serial
-    issuer = "Android Open Source Project";
-    subject = "Android IdentityCredential AttestationKey";
-    optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
-            attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
-            validityNotBefore, validityNotAfter);
-    if (!attestationKeyCertificate) {
+    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
+    if (!privKey) {
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                 IIdentityCredentialStore::STATUS_FAILED,
-                "Error creating certificate for attestationPubKey"));
+                "Error getting private part of credentialKey"));
     }
+    credentialPrivKey_ = privKey.value();
 
-    // Concatenate the certificates to form the chain.
-    vector<uint8_t> certificateChain;
-    certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
-                            credentialPubKeyCertificate.value().end());
-    certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
-                            attestationKeyCertificate.value().end());
-
-    optional<vector<vector<uint8_t>>> splitCertChain =
-            support::certificateChainSplit(certificateChain);
-    if (!splitCertChain) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain"));
-    }
+    // convert from vector<vector<uint8_t>>> to vector<Certificate>*
     *outCertificateChain = vector<Certificate>();
-    for (const vector<uint8_t>& cert : splitCertChain.value()) {
+    for (const vector<uint8_t>& cert : certificateChain_) {
         Certificate c = Certificate();
         c.encodedCertificate = byteStringToSigned(cert);
         outCertificateChain->push_back(std::move(c));
diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/WritableIdentityCredential.h
index b380f89..b182862 100644
--- a/identity/aidl/default/WritableIdentityCredential.h
+++ b/identity/aidl/default/WritableIdentityCredential.h
@@ -64,10 +64,13 @@
     string docType_;
     bool testCredential_;
 
-    // These are set in initialize().
+    // This is set in initialize().
     vector<uint8_t> storageKey_;
+
+    // These are set in getAttestationCertificate().
     vector<uint8_t> credentialPrivKey_;
     vector<uint8_t> credentialPubKey_;
+    vector<vector<uint8_t>> certificateChain_;
 
     // These fields are initialized during startPersonalization()
     size_t numAccessControlProfileRemaining_;
diff --git a/identity/support/Android.bp b/identity/support/Android.bp
index 7b4546b..2b6c695 100644
--- a/identity/support/Android.bp
+++ b/identity/support/Android.bp
@@ -23,10 +23,14 @@
         "include",
     ],
     shared_libs: [
+        "android.hardware.keymaster@4.0",
         "libcrypto",
         "libbase",
         "libhidlbase",
         "libhardware",
+        "libkeymaster_portable",
+        "libsoft_attestation_cert",
+        "libpuresoftkeymasterdevice",
     ],
     static_libs: [
         "libcppbor",
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index 4533ad9..507e914 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -21,6 +21,7 @@
 #include <optional>
 #include <string>
 #include <tuple>
+#include <utility>
 #include <vector>
 
 namespace android {
@@ -106,6 +107,17 @@
 // ---------------------------------------------------------------------------
 // EC crypto functionality / abstraction (only supports P-256).
 // ---------------------------------------------------------------------------
+// Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
+// PKCS#8 encoded key-pair.  Also generates an attestation
+// certificate using the |challenge| and |applicationId|, and returns the generated
+// certificate in X.509 certificate chain format.
+//
+// The attestation time fields used will be the current time, and expires in one year.
+//
+// The first parameter of the return value is the keyPair generated, second return in
+// the pair is the attestation certificate generated.
+optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
+        const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId);
 
 // Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
 // PKCS#8 encoded key-pair.
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index e2828bf..bf6a5c3 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -47,6 +47,13 @@
 #include <cppbor.h>
 #include <cppbor_parse.h>
 
+#include <android/hardware/keymaster/4.0/types.h>
+#include <keymaster/authorization_set.h>
+#include <keymaster/contexts/pure_soft_keymaster_context.h>
+#include <keymaster/contexts/soft_attestation_cert.h>
+#include <keymaster/keymaster_tags.h>
+#include <keymaster/km_openssl/attestation_utils.h>
+
 namespace android {
 namespace hardware {
 namespace identity {
@@ -816,6 +823,138 @@
     return hmac;
 }
 
+// Generates the attestation certificate with the parameters passed in.  Note
+// that the passed in |activeTimeMilliSeconds| |expireTimeMilliSeconds| are in
+// milli seconds since epoch.  We are setting them to milliseconds due to
+// requirement in AuthorizationSet KM_DATE fields.  The certificate created is
+// actually in seconds.
+optional<vector<vector<uint8_t>>> createAttestation(const EVP_PKEY* key,
+                                                    const vector<uint8_t>& applicationId,
+                                                    const vector<uint8_t>& challenge,
+                                                    uint64_t activeTimeMilliSeconds,
+                                                    uint64_t expireTimeMilliSeconds) {
+    ::keymaster::AuthorizationSet auth_set(
+            ::keymaster::AuthorizationSetBuilder()
+                    .Authorization(::keymaster::TAG_ATTESTATION_CHALLENGE, challenge.data(),
+                                   challenge.size())
+                    .Authorization(::keymaster::TAG_ACTIVE_DATETIME, activeTimeMilliSeconds)
+                    // Even though identity attestation hal said the application
+                    // id should be in software enforced authentication set,
+                    // keymaster portable lib expect the input in this
+                    // parameter because the software enforced in input to keymaster
+                    // refers to the key software enforced properties. And this
+                    // parameter refers to properties of the attestation which
+                    // includes app id.
+                    .Authorization(::keymaster::TAG_ATTESTATION_APPLICATION_ID,
+                                   applicationId.data(), applicationId.size())
+                    .Authorization(::keymaster::TAG_USAGE_EXPIRE_DATETIME, expireTimeMilliSeconds));
+
+    // Unique id and device id is not applicable for identity credential attestation,
+    // so we don't need to set those or application id.
+    ::keymaster::AuthorizationSet swEnforced(::keymaster::AuthorizationSetBuilder().Authorization(
+            ::keymaster::TAG_CREATION_DATETIME, activeTimeMilliSeconds));
+
+    ::keymaster::AuthorizationSet hwEnforced(
+            ::keymaster::AuthorizationSetBuilder()
+                    .Authorization(::keymaster::TAG_PURPOSE, KM_PURPOSE_SIGN)
+                    .Authorization(::keymaster::TAG_KEY_SIZE, 256)
+                    .Authorization(::keymaster::TAG_ALGORITHM, KM_ALGORITHM_EC)
+                    .Authorization(::keymaster::TAG_NO_AUTH_REQUIRED)
+                    .Authorization(::keymaster::TAG_DIGEST, KM_DIGEST_SHA_2_256)
+                    .Authorization(::keymaster::TAG_EC_CURVE, KM_EC_CURVE_P_256)
+                    .Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY));
+
+    const keymaster_cert_chain_t* attestation_chain =
+            ::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
+
+    if (attestation_chain == nullptr) {
+        LOG(ERROR) << "Error getting attestation chain";
+        return {};
+    }
+
+    const keymaster_key_blob_t* attestation_signing_key =
+            ::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
+    if (attestation_signing_key == nullptr) {
+        LOG(ERROR) << "Error getting attestation key";
+        return {};
+    }
+
+    keymaster_error_t error;
+    ::keymaster::CertChainPtr cert_chain_out;
+    ::keymaster::PureSoftKeymasterContext context;
+
+    // set identity version to 10 per hal requirements specified in IWriteableCredential.hal
+    // For now, the identity version in the attestation is set in the keymaster
+    // version field in the portable keymaster lib, which is a bit misleading.
+    uint identity_version = 10;
+    error = generate_attestation_from_EVP(key, swEnforced, hwEnforced, auth_set, context,
+                                          identity_version, *attestation_chain,
+                                          *attestation_signing_key, &cert_chain_out);
+
+    if (KM_ERROR_OK != error || !cert_chain_out) {
+        LOG(ERROR) << "Error generate attestation from EVP key" << error;
+        return {};
+    }
+
+    // translate certificate format from keymaster_cert_chain_t to vector<uint8_t>.
+    vector<vector<uint8_t>> attestationCertificate;
+    for (int i = 0; i < cert_chain_out->entry_count; i++) {
+        attestationCertificate.insert(
+                attestationCertificate.end(),
+                vector<uint8_t>(
+                        cert_chain_out->entries[i].data,
+                        cert_chain_out->entries[i].data + cert_chain_out->entries[i].data_length));
+    }
+
+    return attestationCertificate;
+}
+
+optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
+        const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
+    auto ec_key = ::keymaster::EC_KEY_Ptr(EC_KEY_new());
+    auto pkey = ::keymaster::EVP_PKEY_Ptr(EVP_PKEY_new());
+    auto group = ::keymaster::EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+
+    if (ec_key.get() == nullptr || pkey.get() == nullptr) {
+        LOG(ERROR) << "Memory allocation failed";
+        return {};
+    }
+
+    if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 ||
+        EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) {
+        LOG(ERROR) << "Error generating key";
+        return {};
+    }
+
+    if (EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()) != 1) {
+        LOG(ERROR) << "Error getting private key";
+        return {};
+    }
+
+    uint64_t now = time(nullptr);
+    uint64_t secondsInOneYear = 365 * 24 * 60 * 60;
+    uint64_t expireTimeMs = (now + secondsInOneYear) * 1000;
+
+    optional<vector<vector<uint8_t>>> attestationCert =
+            createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs);
+    if (!attestationCert) {
+        LOG(ERROR) << "Error create attestation from key and challenge";
+        return {};
+    }
+
+    int size = i2d_PrivateKey(pkey.get(), nullptr);
+    if (size == 0) {
+        LOG(ERROR) << "Error generating public key encoding";
+        return {};
+    }
+
+    vector<uint8_t> keyPair(size);
+    unsigned char* p = keyPair.data();
+    i2d_PrivateKey(pkey.get(), &p);
+
+    return make_pair(keyPair, attestationCert.value());
+}
+
 optional<vector<uint8_t>> createEcKeyPair() {
     auto ec_key = EC_KEY_Ptr(EC_KEY_new());
     auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
diff --git a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
index 6c186f6..40eb142 100644
--- a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
@@ -101,6 +101,7 @@
 DECLARE_KM_4_1_TYPED_TAG(EARLY_BOOT_ONLY);
 DECLARE_KM_4_1_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION);
 DECLARE_KM_4_1_TYPED_TAG(STORAGE_KEY);
+DECLARE_KM_4_1_TYPED_TAG(IDENTITY_CREDENTIAL_KEY);
 
 }  // namespace android::hardware::keymaster::V4_1
 
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index 595ad85..e28605d 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -42,10 +42,11 @@
 
 Model createModel(const TestModel& testModel) {
     // Model operands.
-    hidl_vec<Operand> operands(testModel.operands.size());
+    CHECK_EQ(testModel.referenced.size(), 0u);  // Not supported in 1.0.
+    hidl_vec<Operand> operands(testModel.main.operands.size());
     size_t constCopySize = 0, constRefSize = 0;
-    for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-        const auto& op = testModel.operands[i];
+    for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+        const auto& op = testModel.main.operands[i];
 
         DataLocation loc = {};
         if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
@@ -70,9 +71,9 @@
     }
 
     // Model operations.
-    hidl_vec<Operation> operations(testModel.operations.size());
-    std::transform(testModel.operations.begin(), testModel.operations.end(), operations.begin(),
-                   [](const TestOperation& op) -> Operation {
+    hidl_vec<Operation> operations(testModel.main.operations.size());
+    std::transform(testModel.main.operations.begin(), testModel.main.operations.end(),
+                   operations.begin(), [](const TestOperation& op) -> Operation {
                        return {.type = static_cast<OperationType>(op.type),
                                .inputs = op.inputs,
                                .outputs = op.outputs};
@@ -80,8 +81,8 @@
 
     // Constant copies.
     hidl_vec<uint8_t> operandValues(constCopySize);
-    for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-        const auto& op = testModel.operands[i];
+    for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+        const auto& op = testModel.main.operands[i];
         if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
             const uint8_t* begin = op.data.get<uint8_t>();
             const uint8_t* end = begin + op.data.size();
@@ -102,8 +103,8 @@
                 reinterpret_cast<uint8_t*>(static_cast<void*>(mappedMemory->getPointer()));
         CHECK(mappedPtr != nullptr);
 
-        for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-            const auto& op = testModel.operands[i];
+        for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+            const auto& op = testModel.main.operands[i];
             if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
                 const uint8_t* begin = op.data.get<uint8_t>();
                 const uint8_t* end = begin + op.data.size();
@@ -114,8 +115,8 @@
 
     return {.operands = std::move(operands),
             .operations = std::move(operations),
-            .inputIndexes = testModel.inputIndexes,
-            .outputIndexes = testModel.outputIndexes,
+            .inputIndexes = testModel.main.inputIndexes,
+            .outputIndexes = testModel.main.outputIndexes,
             .operandValues = std::move(operandValues),
             .pools = std::move(pools)};
 }
diff --git a/neuralnetworks/1.0/vts/functional/Utils.cpp b/neuralnetworks/1.0/vts/functional/Utils.cpp
index 5b630fd..0dba85a 100644
--- a/neuralnetworks/1.0/vts/functional/Utils.cpp
+++ b/neuralnetworks/1.0/vts/functional/Utils.cpp
@@ -42,10 +42,10 @@
 
 Request createRequest(const TestModel& testModel) {
     // Model inputs.
-    hidl_vec<RequestArgument> inputs(testModel.inputIndexes.size());
+    hidl_vec<RequestArgument> inputs(testModel.main.inputIndexes.size());
     size_t inputSize = 0;
-    for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
-        const auto& op = testModel.operands[testModel.inputIndexes[i]];
+    for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
         if (op.data.size() == 0) {
             // Omitted input.
             inputs[i] = {.hasNoValue = true};
@@ -59,10 +59,10 @@
     }
 
     // Model outputs.
-    hidl_vec<RequestArgument> outputs(testModel.outputIndexes.size());
+    hidl_vec<RequestArgument> outputs(testModel.main.outputIndexes.size());
     size_t outputSize = 0;
-    for (uint32_t i = 0; i < testModel.outputIndexes.size(); i++) {
-        const auto& op = testModel.operands[testModel.outputIndexes[i]];
+    for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
 
         // In the case of zero-sized output, we should at least provide a one-byte buffer.
         // This is because zero-sized tensors are only supported internally to the driver, or
@@ -90,8 +90,8 @@
     CHECK(inputPtr != nullptr);
 
     // Copy input data to the memory pool.
-    for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
-        const auto& op = testModel.operands[testModel.inputIndexes[i]];
+    for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
         if (op.data.size() > 0) {
             const uint8_t* begin = op.data.get<uint8_t>();
             const uint8_t* end = begin + op.data.size();
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
index 7a929d6..cee15a3 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
@@ -49,10 +49,11 @@
 
 Model createModel(const TestModel& testModel) {
     // Model operands.
-    hidl_vec<Operand> operands(testModel.operands.size());
+    CHECK_EQ(testModel.referenced.size(), 0u);  // Not supported in 1.1.
+    hidl_vec<Operand> operands(testModel.main.operands.size());
     size_t constCopySize = 0, constRefSize = 0;
-    for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-        const auto& op = testModel.operands[i];
+    for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+        const auto& op = testModel.main.operands[i];
 
         DataLocation loc = {};
         if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
@@ -77,9 +78,9 @@
     }
 
     // Model operations.
-    hidl_vec<Operation> operations(testModel.operations.size());
-    std::transform(testModel.operations.begin(), testModel.operations.end(), operations.begin(),
-                   [](const TestOperation& op) -> Operation {
+    hidl_vec<Operation> operations(testModel.main.operations.size());
+    std::transform(testModel.main.operations.begin(), testModel.main.operations.end(),
+                   operations.begin(), [](const TestOperation& op) -> Operation {
                        return {.type = static_cast<OperationType>(op.type),
                                .inputs = op.inputs,
                                .outputs = op.outputs};
@@ -87,8 +88,8 @@
 
     // Constant copies.
     hidl_vec<uint8_t> operandValues(constCopySize);
-    for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-        const auto& op = testModel.operands[i];
+    for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+        const auto& op = testModel.main.operands[i];
         if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
             const uint8_t* begin = op.data.get<uint8_t>();
             const uint8_t* end = begin + op.data.size();
@@ -109,8 +110,8 @@
                 reinterpret_cast<uint8_t*>(static_cast<void*>(mappedMemory->getPointer()));
         CHECK(mappedPtr != nullptr);
 
-        for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-            const auto& op = testModel.operands[i];
+        for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+            const auto& op = testModel.main.operands[i];
             if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
                 const uint8_t* begin = op.data.get<uint8_t>();
                 const uint8_t* end = begin + op.data.size();
@@ -121,8 +122,8 @@
 
     return {.operands = std::move(operands),
             .operations = std::move(operations),
-            .inputIndexes = testModel.inputIndexes,
-            .outputIndexes = testModel.outputIndexes,
+            .inputIndexes = testModel.main.inputIndexes,
+            .outputIndexes = testModel.main.outputIndexes,
             .operandValues = std::move(operandValues),
             .pools = std::move(pools),
             .relaxComputationFloat32toFloat16 = testModel.isRelaxed};
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
index 2130a76..10dec79 100644
--- a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -207,10 +207,10 @@
     };
 
     return {
-            .operands = std::move(operands),
-            .operations = std::move(operations),
-            .inputIndexes = {1},
-            .outputIndexes = {len * 2 + 1},
+            .main = {.operands = std::move(operands),
+                     .operations = std::move(operations),
+                     .inputIndexes = {1},
+                     .outputIndexes = {len * 2 + 1}},
             .isRelaxed = false,
     };
 }
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
index 599fd1d..4c8fede 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
@@ -75,10 +75,11 @@
 
 Model createModel(const TestModel& testModel) {
     // Model operands.
-    hidl_vec<Operand> operands(testModel.operands.size());
+    CHECK_EQ(testModel.referenced.size(), 0u);  // Not supported in 1.1.
+    hidl_vec<Operand> operands(testModel.main.operands.size());
     size_t constCopySize = 0, constRefSize = 0;
-    for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-        const auto& op = testModel.operands[i];
+    for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+        const auto& op = testModel.main.operands[i];
 
         DataLocation loc = {};
         if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
@@ -110,9 +111,9 @@
     }
 
     // Model operations.
-    hidl_vec<Operation> operations(testModel.operations.size());
-    std::transform(testModel.operations.begin(), testModel.operations.end(), operations.begin(),
-                   [](const TestOperation& op) -> Operation {
+    hidl_vec<Operation> operations(testModel.main.operations.size());
+    std::transform(testModel.main.operations.begin(), testModel.main.operations.end(),
+                   operations.begin(), [](const TestOperation& op) -> Operation {
                        return {.type = static_cast<OperationType>(op.type),
                                .inputs = op.inputs,
                                .outputs = op.outputs};
@@ -120,8 +121,8 @@
 
     // Constant copies.
     hidl_vec<uint8_t> operandValues(constCopySize);
-    for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-        const auto& op = testModel.operands[i];
+    for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+        const auto& op = testModel.main.operands[i];
         if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
             const uint8_t* begin = op.data.get<uint8_t>();
             const uint8_t* end = begin + op.data.size();
@@ -142,8 +143,8 @@
                 reinterpret_cast<uint8_t*>(static_cast<void*>(mappedMemory->getPointer()));
         CHECK(mappedPtr != nullptr);
 
-        for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-            const auto& op = testModel.operands[i];
+        for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+            const auto& op = testModel.main.operands[i];
             if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
                 const uint8_t* begin = op.data.get<uint8_t>();
                 const uint8_t* end = begin + op.data.size();
@@ -154,15 +155,15 @@
 
     return {.operands = std::move(operands),
             .operations = std::move(operations),
-            .inputIndexes = testModel.inputIndexes,
-            .outputIndexes = testModel.outputIndexes,
+            .inputIndexes = testModel.main.inputIndexes,
+            .outputIndexes = testModel.main.outputIndexes,
             .operandValues = std::move(operandValues),
             .pools = std::move(pools),
             .relaxComputationFloat32toFloat16 = testModel.isRelaxed};
 }
 
 static bool isOutputSizeGreaterThanOne(const TestModel& testModel, uint32_t index) {
-    const auto byteSize = testModel.operands[testModel.outputIndexes[index]].data.size();
+    const auto byteSize = testModel.main.operands[testModel.main.outputIndexes[index]].data.size();
     return byteSize > 1u;
 }
 
@@ -302,17 +303,17 @@
             // either empty, or have the same number of elements as the number of outputs.
             ASSERT_EQ(ErrorStatus::NONE, executionStatus);
             ASSERT_TRUE(outputShapes.size() == 0 ||
-                        outputShapes.size() == testModel.outputIndexes.size());
+                        outputShapes.size() == testModel.main.outputIndexes.size());
             break;
         case OutputType::UNSPECIFIED:
             // If the model output operands are not fully specified, outputShapes must have
             // the same number of elements as the number of outputs.
             ASSERT_EQ(ErrorStatus::NONE, executionStatus);
-            ASSERT_EQ(outputShapes.size(), testModel.outputIndexes.size());
+            ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
             break;
         case OutputType::INSUFFICIENT:
             ASSERT_EQ(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, executionStatus);
-            ASSERT_EQ(outputShapes.size(), testModel.outputIndexes.size());
+            ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
             ASSERT_FALSE(outputShapes[0].isSufficient);
             return;
     }
@@ -320,7 +321,7 @@
     // Go through all outputs, check returned output shapes.
     for (uint32_t i = 0; i < outputShapes.size(); i++) {
         EXPECT_TRUE(outputShapes[i].isSufficient);
-        const auto& expect = testModel.operands[testModel.outputIndexes[i]].dimensions;
+        const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
         const std::vector<uint32_t> actual = outputShapes[i].dimensions;
         EXPECT_EQ(expect, actual);
     }
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index d645de7..4ce3691 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -92,6 +92,17 @@
      * @param deadline The time by which the execution must complete. If the
      *                 execution cannot be finished by the deadline, the
      *                 execution must be aborted.
+     * @param loopTimeoutDuration The maximum amount of time that should be spent
+     *                            executing a {@link OperationType::WHILE}
+     *                            operation. If a loop condition model does not
+     *                            output false within this duration, the
+     *                            execution must be aborted. If the model
+     *                            contains a {@link OperationType::WHILE}
+     *                            operation and no loop timeout duration is
+     *                            provided, the maximum amount of time is {@link
+     *                            LoopTimeoutDurationNs::DEFAULT}. When
+     *                            provided, the duration must not exceed {@link
+     *                            LoopTimeoutDurationNs::MAXIMUM}.
      * @param callback A callback object used to return the error status of
      *                 the execution, shape information of model output operands, and
      *                 duration of execution. The callback object's notify function must
@@ -111,7 +122,7 @@
      *                  driver
      */
     execute_1_3(Request request, MeasureTiming measure, OptionalTimePoint deadline,
-                IExecutionCallback callback)
+                OptionalTimeoutDuration loopTimeoutDuration, IExecutionCallback callback)
         generates (ErrorStatus status);
 
     /**
@@ -163,6 +174,17 @@
      * @param deadline The time by which the execution must complete. If the
      *                 execution cannot be finished by the deadline, the
      *                 execution must be aborted.
+     * @param loopTimeoutDuration The maximum amount of time that should be spent
+     *                            executing a {@link OperationType::WHILE}
+     *                            operation. If a loop condition model does not
+     *                            output false within this duration, the
+     *                            execution must be aborted. If the model
+     *                            contains a {@link OperationType::WHILE}
+     *                            operation and no loop timeout duration is
+     *                            provided, the maximum amount of time is {@link
+     *                            LoopTimeoutDurationNs::DEFAULT}. When
+     *                            provided, the duration must not exceed {@link
+     *                            LoopTimeoutDurationNs::MAXIMUM}.
      * @return status Error status of the execution, must be:
      *                - NONE if execution is performed successfully
      *                - DEVICE_UNAVAILABLE if driver is offline or busy
@@ -187,7 +209,8 @@
      *                measurement is not available.
      */
     executeSynchronously_1_3(Request request, MeasureTiming measure,
-                             OptionalTimePoint deadline)
+                             OptionalTimePoint deadline,
+                             OptionalTimeoutDuration loopTimeoutDuration)
                   generates (ErrorStatus status, vec<OutputShape> outputShapes,
                              Timing timing);
 
@@ -243,6 +266,17 @@
      * @param deadline The time by which the execution must complete. If the
      *                 execution cannot be finished by the deadline, the
      *                 execution must be aborted.
+     * @param loopTimeoutDuration The maximum amount of time that should be spent
+     *                            executing a {@link OperationType::WHILE}
+     *                            operation. If a loop condition model does not
+     *                            output false within this duration, the
+     *                            execution must be aborted. If the model
+     *                            contains a {@link OperationType::WHILE}
+     *                            operation and no loop timeout duration is
+     *                            provided, the maximum amount of time is {@link
+     *                            LoopTimeoutDurationNs::DEFAULT}. When
+     *                            provided, the duration must not exceed {@link
+     *                            LoopTimeoutDurationNs::MAXIMUM}.
      * @param duration The length of time within which the execution must
      *                 complete after all sync fences in waitFor are signaled. If the
      *                 execution cannot be finished within the duration, the execution
@@ -264,6 +298,7 @@
      *                  and error status when the execution is completed.
      */
     executeFenced(Request request, vec<handle> waitFor, MeasureTiming measure,
-                  OptionalTimePoint deadline, OptionalTimeoutDuration duration)
+                  OptionalTimePoint deadline, OptionalTimeoutDuration loopTimeoutDuration,
+                  OptionalTimeoutDuration duration)
         generates (ErrorStatus status, handle syncFence, IFencedExecutionCallback callback);
 };
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index 530f984..a808a2e 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -5176,8 +5176,10 @@
 /**
  * The capabilities of a driver.
  *
- * Performance of an operation comes from the type of its first operand.
- * This represents performance for non extension operand types.
+ * This represents performance of non-extension operations.
+ *
+ * Performance of an operation other than {@link OperationType::IF} and
+ * {@link OperationType::WHILE} comes from the type of its first operand.
  */
 struct Capabilities {
     /**
@@ -5200,11 +5202,32 @@
 
     /**
      * Performance by operand type. Must be sorted by OperandType.
-     * If a particular OperandType is not present in operandPerformance,
+     *
+     * If a particular {@link OperandType} is not present in operandPerformance,
      * its performance is treated as
      * { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
+     *
+     * Performance does not apply to {@link OperandType::SUBGRAPH}, and a driver
+     * must not report operand performance for {@link OperandType::SUBGRAPH}.
      */
     vec<OperandPerformance> operandPerformance;
+
+    /**
+     * Performance of an {@link OperationType::IF} operation is the sum of
+     * {@link Capabilities::ifPerformance} and the mean of performance for the
+     * two branch subgraphs, where performance for a subgraph is the sum of the
+     * performance of all operations within the subgraph.
+     */
+    PerformanceInfo ifPerformance;
+
+    /**
+     * Performance of a {@link OperationType::WHILE} operation is the sum of
+     * {@link Capabilities::whilePerformance}, performance for the condition
+     * subgraph and performance for the body subgraph, where performance for a
+     * subgraph is the sum of the performance of all operations within the
+     * subgraph.
+     */
+    PerformanceInfo whilePerformance;
 };
 
 /**
@@ -5648,3 +5671,14 @@
      */
     RESOURCE_EXHAUSTED_PERSISTENT,
 };
+
+/**
+ * Each {@link OperationType::WHILE} operation in the model has an implicit
+ * execution timeout duration associated with it ("loop timeout duration").
+ * This duration is configurable on a per-execution basis and must not exceed
+ * 15 seconds. The default value is 2 seconds.
+ */
+enum LoopTimeoutDurationNs : uint64_t {
+    DEFAULT = 2000000000,
+    MAXIMUM = 15000000000,
+};
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
index 3d0d02d..0a6e45e 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -103,8 +103,10 @@
 /**
  * The capabilities of a driver.
  *
- * Performance of an operation comes from the type of its first operand.
- * This represents performance for non extension operand types.
+ * This represents performance of non-extension operations.
+ *
+ * Performance of an operation other than {@link OperationType::IF} and
+ * {@link OperationType::WHILE} comes from the type of its first operand.
  */
 struct Capabilities {
     /**
@@ -127,11 +129,32 @@
 
     /**
      * Performance by operand type. Must be sorted by OperandType.
-     * If a particular OperandType is not present in operandPerformance,
+     *
+     * If a particular {@link OperandType} is not present in operandPerformance,
      * its performance is treated as
      * { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
+     *
+     * Performance does not apply to {@link OperandType::SUBGRAPH}, and a driver
+     * must not report operand performance for {@link OperandType::SUBGRAPH}.
      */
     vec<OperandPerformance> operandPerformance;
+
+    /**
+     * Performance of an {@link OperationType::IF} operation is the sum of
+     * {@link Capabilities::ifPerformance} and the mean of performance for the
+     * two branch subgraphs, where performance for a subgraph is the sum of the
+     * performance of all operations within the subgraph.
+     */
+    PerformanceInfo ifPerformance;
+
+    /**
+     * Performance of a {@link OperationType::WHILE} operation is the sum of
+     * {@link Capabilities::whilePerformance}, performance for the condition
+     * subgraph and performance for the body subgraph, where performance for a
+     * subgraph is the sum of the performance of all operations within the
+     * subgraph.
+     */
+    PerformanceInfo whilePerformance;
 };
 
 /**
@@ -575,3 +598,14 @@
      */
     RESOURCE_EXHAUSTED_PERSISTENT,
 };
+
+/**
+ * Each {@link OperationType::WHILE} operation in the model has an implicit
+ * execution timeout duration associated with it ("loop timeout duration").
+ * This duration is configurable on a per-execution basis and must not exceed
+ * 15 seconds. The default value is 2 seconds.
+ */
+enum LoopTimeoutDurationNs : uint64_t {
+    DEFAULT = 2000000000,
+    MAXIMUM = 15000000000,
+};
diff --git a/neuralnetworks/1.3/vts/functional/BasicTests.cpp b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
index 891850c..1c25369 100644
--- a/neuralnetworks/1.3/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
@@ -57,6 +57,11 @@
                                    [](const OperandPerformance& a, const OperandPerformance& b) {
                                        return a.type < b.type;
                                    }));
+        EXPECT_TRUE(std::all_of(opPerf.begin(), opPerf.end(), [](const OperandPerformance& a) {
+            return a.type != OperandType::SUBGRAPH;
+        }));
+        EXPECT_TRUE(isPositive(capabilities.ifPerformance));
+        EXPECT_TRUE(isPositive(capabilities.whilePerformance));
     });
     EXPECT_TRUE(ret.isOk());
 }
diff --git a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
index 0bd24da..ac18c8f 100644
--- a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
@@ -209,10 +209,10 @@
     };
 
     return {
-            .operands = std::move(operands),
-            .operations = std::move(operations),
-            .inputIndexes = {1},
-            .outputIndexes = {len * 2 + 1},
+            .main = {.operands = std::move(operands),
+                     .operations = std::move(operations),
+                     .inputIndexes = {1},
+                     .outputIndexes = {len * 2 + 1}},
             .isRelaxed = false,
     };
 }
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index 82f34ff..89edfb7 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -169,7 +169,8 @@
         if constexpr (ioType == IOType::INPUT) {
             if (buffer != nullptr) {
                 // TestBuffer -> Shared memory.
-                const auto& testBuffer = kTestModel.operands[kTestModel.inputIndexes[index]].data;
+                const auto& testBuffer =
+                        kTestModel.main.operands[kTestModel.main.inputIndexes[index]].data;
                 ASSERT_GT(testBuffer.size(), 0);
                 hidl_memory tmp = nn::allocateSharedMemory(testBuffer.size());
                 sp<IMemory> inputMemory = mapMemory(tmp);
@@ -195,26 +196,42 @@
     const TestModel& kTestModel;
 };
 
-}  // namespace
+Subgraph createSubgraph(const TestSubgraph& testSubgraph, uint32_t* constCopySize,
+                        std::vector<const TestBuffer*>* constCopies, uint32_t* constRefSize,
+                        std::vector<const TestBuffer*>* constReferences) {
+    CHECK(constCopySize != nullptr);
+    CHECK(constCopies != nullptr);
+    CHECK(constRefSize != nullptr);
+    CHECK(constReferences != nullptr);
 
-Model createModel(const TestModel& testModel) {
-    // Model operands.
-    hidl_vec<Operand> operands(testModel.operands.size());
-    size_t constCopySize = 0, constRefSize = 0;
-    for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-        const auto& op = testModel.operands[i];
+    // Operands.
+    hidl_vec<Operand> operands(testSubgraph.operands.size());
+    for (uint32_t i = 0; i < testSubgraph.operands.size(); i++) {
+        const auto& op = testSubgraph.operands[i];
 
         DataLocation loc = {};
         if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
-            loc = {.poolIndex = 0,
-                   .offset = static_cast<uint32_t>(constCopySize),
-                   .length = static_cast<uint32_t>(op.data.size())};
-            constCopySize += op.data.alignedSize();
+            loc = {
+                    .poolIndex = 0,
+                    .offset = *constCopySize,
+                    .length = static_cast<uint32_t>(op.data.size()),
+            };
+            constCopies->push_back(&op.data);
+            *constCopySize += op.data.alignedSize();
         } else if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
-            loc = {.poolIndex = 0,
-                   .offset = static_cast<uint32_t>(constRefSize),
-                   .length = static_cast<uint32_t>(op.data.size())};
-            constRefSize += op.data.alignedSize();
+            loc = {
+                    .poolIndex = 0,
+                    .offset = *constRefSize,
+                    .length = static_cast<uint32_t>(op.data.size()),
+            };
+            constReferences->push_back(&op.data);
+            *constRefSize += op.data.alignedSize();
+        } else if (op.lifetime == TestOperandLifeTime::SUBGRAPH) {
+            loc = {
+                    .poolIndex = 0,
+                    .offset = *op.data.get<uint32_t>(),
+                    .length = 0,
+            };
         }
 
         V1_2::Operand::ExtraParams extraParams;
@@ -233,25 +250,52 @@
                        .extraParams = std::move(extraParams)};
     }
 
-    // Model operations.
-    hidl_vec<Operation> operations(testModel.operations.size());
-    std::transform(testModel.operations.begin(), testModel.operations.end(), operations.begin(),
-                   [](const TestOperation& op) -> Operation {
+    // Operations.
+    hidl_vec<Operation> operations(testSubgraph.operations.size());
+    std::transform(testSubgraph.operations.begin(), testSubgraph.operations.end(),
+                   operations.begin(), [](const TestOperation& op) -> Operation {
                        return {.type = static_cast<OperationType>(op.type),
                                .inputs = op.inputs,
                                .outputs = op.outputs};
                    });
 
+    return {.operands = std::move(operands),
+            .operations = std::move(operations),
+            .inputIndexes = testSubgraph.inputIndexes,
+            .outputIndexes = testSubgraph.outputIndexes};
+}
+
+void copyTestBuffers(const std::vector<const TestBuffer*>& buffers, uint8_t* output) {
+    uint32_t offset = 0;
+    for (const TestBuffer* buffer : buffers) {
+        const uint8_t* begin = buffer->get<uint8_t>();
+        const uint8_t* end = begin + buffer->size();
+        std::copy(begin, end, output + offset);
+        offset += buffer->alignedSize();
+    }
+}
+
+}  // namespace
+
+Model createModel(const TestModel& testModel) {
+    uint32_t constCopySize = 0;
+    uint32_t constRefSize = 0;
+    std::vector<const TestBuffer*> constCopies;
+    std::vector<const TestBuffer*> constReferences;
+
+    Subgraph mainSubgraph = createSubgraph(testModel.main, &constCopySize, &constCopies,
+                                           &constRefSize, &constReferences);
+    hidl_vec<Subgraph> refSubgraphs(testModel.referenced.size());
+    std::transform(testModel.referenced.begin(), testModel.referenced.end(), refSubgraphs.begin(),
+                   [&constCopySize, &constCopies, &constRefSize,
+                    &constReferences](const TestSubgraph& testSubgraph) {
+                       return createSubgraph(testSubgraph, &constCopySize, &constCopies,
+                                             &constRefSize, &constReferences);
+                   });
+
     // Constant copies.
     hidl_vec<uint8_t> operandValues(constCopySize);
-    for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-        const auto& op = testModel.operands[i];
-        if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
-            const uint8_t* begin = op.data.get<uint8_t>();
-            const uint8_t* end = begin + op.data.size();
-            std::copy(begin, end, operandValues.data() + operands[i].location.offset);
-        }
-    }
+    copyTestBuffers(constCopies, operandValues.data());
 
     // Shared memory.
     hidl_vec<hidl_memory> pools = {};
@@ -266,27 +310,18 @@
                 reinterpret_cast<uint8_t*>(static_cast<void*>(mappedMemory->getPointer()));
         CHECK(mappedPtr != nullptr);
 
-        for (uint32_t i = 0; i < testModel.operands.size(); i++) {
-            const auto& op = testModel.operands[i];
-            if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
-                const uint8_t* begin = op.data.get<uint8_t>();
-                const uint8_t* end = begin + op.data.size();
-                std::copy(begin, end, mappedPtr + operands[i].location.offset);
-            }
-        }
+        copyTestBuffers(constReferences, mappedPtr);
     }
 
-    return {.main = {.operands = std::move(operands),
-                     .operations = std::move(operations),
-                     .inputIndexes = testModel.inputIndexes,
-                     .outputIndexes = testModel.outputIndexes},
+    return {.main = std::move(mainSubgraph),
+            .referenced = std::move(refSubgraphs),
             .operandValues = std::move(operandValues),
             .pools = std::move(pools),
             .relaxComputationFloat32toFloat16 = testModel.isRelaxed};
 }
 
 static bool isOutputSizeGreaterThanOne(const TestModel& testModel, uint32_t index) {
-    const auto byteSize = testModel.operands[testModel.outputIndexes[index]].data.size();
+    const auto byteSize = testModel.main.operands[testModel.main.outputIndexes[index]].data.size();
     return byteSize > 1u;
 }
 
@@ -320,10 +355,10 @@
     std::vector<uint32_t> tokens;
 
     // Model inputs.
-    hidl_vec<RequestArgument> inputs(testModel.inputIndexes.size());
+    hidl_vec<RequestArgument> inputs(testModel.main.inputIndexes.size());
     size_t inputSize = 0;
-    for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
-        const auto& op = testModel.operands[testModel.inputIndexes[i]];
+    for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
         if (op.data.size() == 0) {
             // Omitted input.
             inputs[i] = {.hasNoValue = true};
@@ -350,10 +385,10 @@
     }
 
     // Model outputs.
-    hidl_vec<RequestArgument> outputs(testModel.outputIndexes.size());
+    hidl_vec<RequestArgument> outputs(testModel.main.outputIndexes.size());
     size_t outputSize = 0;
-    for (uint32_t i = 0; i < testModel.outputIndexes.size(); i++) {
-        const auto& op = testModel.operands[testModel.outputIndexes[i]];
+    for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
         if (preferDeviceMemory) {
             SCOPED_TRACE("Output index = " + std::to_string(i));
             auto [buffer, token] = allocator.allocate<IOType::OUTPUT>(i);
@@ -398,9 +433,9 @@
     CHECK(inputMemory.get() != nullptr);
     uint8_t* inputPtr = static_cast<uint8_t*>(static_cast<void*>(inputMemory->getPointer()));
     CHECK(inputPtr != nullptr);
-    for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
+    for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
         if (!inputs[i].hasNoValue && inputs[i].location.poolIndex == kInputPoolIndex) {
-            const auto& op = testModel.operands[testModel.inputIndexes[i]];
+            const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
             const uint8_t* begin = op.data.get<uint8_t>();
             const uint8_t* end = begin + op.data.size();
             std::copy(begin, end, inputPtr + inputs[i].location.offset);
@@ -443,7 +478,7 @@
         if (outputLoc.poolIndex == kOutputPoolIndex) {
             outputBuffers.emplace_back(outputLoc.length, outputPtr + outputLoc.offset);
         } else {
-            const auto& op = testModel.operands[testModel.outputIndexes[i]];
+            const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
             if (op.data.size() == 0) {
                 outputBuffers.emplace_back();
             } else {
@@ -461,7 +496,7 @@
 static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
                                                 const Request& request, MeasureTiming measure,
                                                 sp<ExecutionCallback>& callback) {
-    return preparedModel->execute_1_3(request, measure, {}, callback);
+    return preparedModel->execute_1_3(request, measure, {}, {}, callback);
 }
 static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
                                                 const Request& request, MeasureTiming measure,
@@ -469,7 +504,7 @@
                                                 Timing* timing) {
     ErrorStatus result;
     Return<void> ret = preparedModel->executeSynchronously_1_3(
-            request, measure, {},
+            request, measure, {}, {},
             [&result, outputShapes, timing](ErrorStatus error, const hidl_vec<OutputShape>& shapes,
                                             const Timing& time) {
                 result = error;
@@ -577,7 +612,7 @@
             hidl_handle syncFenceHandle;
             sp<IFencedExecutionCallback> fencedCallback;
             Return<void> ret = preparedModel->executeFenced(
-                    request, {}, testConfig.measureTiming, {}, {},
+                    request, {}, testConfig.measureTiming, {}, {}, {},
                     [&result, &syncFenceHandle, &fencedCallback](
                             ErrorStatus error, const hidl_handle& handle,
                             const sp<IFencedExecutionCallback>& callback) {
@@ -638,17 +673,17 @@
             // either empty, or have the same number of elements as the number of outputs.
             ASSERT_EQ(ErrorStatus::NONE, executionStatus);
             ASSERT_TRUE(outputShapes.size() == 0 ||
-                        outputShapes.size() == testModel.outputIndexes.size());
+                        outputShapes.size() == testModel.main.outputIndexes.size());
             break;
         case OutputType::UNSPECIFIED:
             // If the model output operands are not fully specified, outputShapes must have
             // the same number of elements as the number of outputs.
             ASSERT_EQ(ErrorStatus::NONE, executionStatus);
-            ASSERT_EQ(outputShapes.size(), testModel.outputIndexes.size());
+            ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
             break;
         case OutputType::INSUFFICIENT:
             ASSERT_EQ(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, executionStatus);
-            ASSERT_EQ(outputShapes.size(), testModel.outputIndexes.size());
+            ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
             ASSERT_FALSE(outputShapes[0].isSufficient);
             return;
     }
@@ -656,7 +691,7 @@
     // Go through all outputs, check returned output shapes.
     for (uint32_t i = 0; i < outputShapes.size(); i++) {
         EXPECT_TRUE(outputShapes[i].isSufficient);
-        const auto& expect = testModel.operands[testModel.outputIndexes[i]].dimensions;
+        const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
         const std::vector<uint32_t> actual = outputShapes[i].dimensions;
         EXPECT_EQ(expect, actual);
     }
@@ -862,7 +897,7 @@
                            [](const TestModel& testModel) { return !testModel.expectFailure; });
 
 INSTANTIATE_GENERATED_TEST(QuantizationCouplingTest, [](const TestModel& testModel) {
-    return testModel.hasQuant8CoupledOperands() && testModel.operations.size() == 1;
+    return testModel.hasQuant8CoupledOperands() && testModel.main.operations.size() == 1;
 });
 
 }  // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
index 2f1e05c..fccc612 100644
--- a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
@@ -67,8 +67,10 @@
             deadline.nanosecondsSinceEpoch(nanosecondsSinceEpoch);
         } break;
         case DeadlineBoundType::UNLIMITED: {
-            uint64_t unlimited = std::numeric_limits<uint64_t>::max();
-            deadline.nanosecondsSinceEpoch(unlimited);
+            const auto maxTime = std::chrono::time_point<std::chrono::steady_clock,
+                                                         std::chrono::nanoseconds>::max();
+            const uint64_t nanosecondsSinceEpoch = maxTime.time_since_epoch().count();
+            deadline.nanosecondsSinceEpoch(nanosecondsSinceEpoch);
         } break;
     }
     return deadline;
@@ -169,7 +171,7 @@
 
     // launch execution
     const sp<ExecutionCallback> callback = new ExecutionCallback();
-    Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, callback);
+    Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, {}, callback);
     EXPECT_TRUE(ret.isOk());
     EXPECT_EQ(ErrorStatus::NONE, ret.withDefault(ErrorStatus::GENERAL_FAILURE));
     if (!ret.isOk() || ret != ErrorStatus::NONE) return std::nullopt;
@@ -196,7 +198,7 @@
 
     // run execution
     const Return<void> ret =
-            preparedModel->executeSynchronously_1_3(request, measure, deadline, cb);
+            preparedModel->executeSynchronously_1_3(request, measure, deadline, {}, cb);
     EXPECT_TRUE(ret.isOk());
     if (!ret.isOk()) return std::nullopt;
 
@@ -237,12 +239,13 @@
 
     // If the model output operands are fully specified, outputShapes must be either
     // either empty, or have the same number of elements as the number of outputs.
-    ASSERT_TRUE(outputShapes.size() == 0 || outputShapes.size() == testModel.outputIndexes.size());
+    ASSERT_TRUE(outputShapes.size() == 0 ||
+                outputShapes.size() == testModel.main.outputIndexes.size());
 
     // Go through all outputs, check returned output shapes.
     for (uint32_t i = 0; i < outputShapes.size(); i++) {
         EXPECT_TRUE(outputShapes[i].isSufficient);
-        const auto& expect = testModel.operands[testModel.outputIndexes[i]].dimensions;
+        const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
         const std::vector<uint32_t> actual = outputShapes[i].dimensions;
         EXPECT_EQ(expect, actual);
     }
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index b9ea430..09e9922 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -182,6 +182,7 @@
         case OperandType::TENSOR_FLOAT16:
         case OperandType::TENSOR_FLOAT32:
         case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+        case OperandType::SUBGRAPH:
             return 1.0f;
         case OperandType::TENSOR_INT32:
             return -1.0f;
@@ -220,6 +221,7 @@
         case OperandType::TENSOR_FLOAT32:
         case OperandType::TENSOR_INT32:
         case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+        case OperandType::SUBGRAPH:
             return {1};
         case OperandType::TENSOR_QUANT8_ASYMM:
             return {-1, 256};
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index 2a4269f..20f4fe2 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -70,7 +70,7 @@
 
         sp<ExecutionCallback> executionCallback = new ExecutionCallback();
         Return<ErrorStatus> executeLaunchStatus =
-                preparedModel->execute_1_3(request, measure, deadline, executionCallback);
+                preparedModel->execute_1_3(request, measure, deadline, {}, executionCallback);
         ASSERT_TRUE(executeLaunchStatus.isOk());
         ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
 
@@ -88,7 +88,7 @@
         SCOPED_TRACE(message + " [executeSynchronously_1_3]");
 
         Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
-                request, measure, deadline,
+                request, measure, deadline, {},
                 [](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes,
                    const Timing& timing) {
                     ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -143,7 +143,7 @@
     {
         SCOPED_TRACE(message + " [executeFenced]");
         Return<void> ret =
-                preparedModel->executeFenced(request, {}, MeasureTiming::NO, deadline, {},
+                preparedModel->executeFenced(request, {}, MeasureTiming::NO, deadline, {}, {},
                                              [](ErrorStatus error, const hidl_handle& handle,
                                                 const sp<IFencedExecutionCallback>& callback) {
                                                  ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -196,7 +196,7 @@
 void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request) {
     SCOPED_TRACE("Expecting request to fail [executeSynchronously_1_3]");
     Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
-            request, MeasureTiming::NO, {},
+            request, MeasureTiming::NO, {}, {},
             [](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
                 ASSERT_NE(ErrorStatus::NONE, error);
                 EXPECT_EQ(outputShapes.size(), 0);
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
index 9a87569..16341da 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
@@ -137,7 +137,7 @@
 void validateExecuteFenced(const sp<IPreparedModel>& preparedModel, const Request& request) {
     SCOPED_TRACE("Expecting request to fail [executeFenced]");
     Return<void> ret_null = preparedModel->executeFenced(
-            request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO, {}, {},
+            request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO, {}, {}, {},
             [](ErrorStatus error, const hidl_handle& handle,
                const sp<IFencedExecutionCallback>& callback) {
                 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
diff --git a/sensors/1.0/default/android.hardware.sensors@1.0-service.rc b/sensors/1.0/default/android.hardware.sensors@1.0-service.rc
index 4faa562..b41730b 100644
--- a/sensors/1.0/default/android.hardware.sensors@1.0-service.rc
+++ b/sensors/1.0/default/android.hardware.sensors@1.0-service.rc
@@ -2,6 +2,6 @@
     interface android.hardware.sensors@1.0::ISensors default
     class hal
     user system
-    group system wakelock
+    group system wakelock uhid
     capabilities BLOCK_SUSPEND
     rlimit rtprio 10 10
diff --git a/wifi/1.3/default/hidl_struct_util.cpp b/wifi/1.3/default/hidl_struct_util.cpp
index 2e4db70..d305c09 100644
--- a/wifi/1.3/default/hidl_struct_util.cpp
+++ b/wifi/1.3/default/hidl_struct_util.cpp
@@ -1819,7 +1819,13 @@
         convertHidlNanDataPathChannelCfgToLegacy(
             hidl_request.channelRequestType);
     legacy_request->channel = hidl_request.channel;
-    strcpy(legacy_request->ndp_iface, hidl_request.ifaceName.c_str());
+    if (strnlen(hidl_request.ifaceName.c_str(), IFNAMSIZ + 1) == IFNAMSIZ + 1) {
+        LOG(ERROR) << "convertHidlNanDataPathInitiatorRequestToLegacy: "
+                      "ifaceName too long";
+        return false;
+    }
+    strncpy(legacy_request->ndp_iface, hidl_request.ifaceName.c_str(),
+            IFNAMSIZ + 1);
     legacy_request->ndp_cfg.security_cfg =
         (hidl_request.securityConfig.securityType !=
          NanDataPathSecurityType::OPEN)
@@ -1900,7 +1906,13 @@
                                    ? legacy_hal::NAN_DP_REQUEST_ACCEPT
                                    : legacy_hal::NAN_DP_REQUEST_REJECT;
     legacy_request->ndp_instance_id = hidl_request.ndpInstanceId;
-    strcpy(legacy_request->ndp_iface, hidl_request.ifaceName.c_str());
+    if (strnlen(hidl_request.ifaceName.c_str(), IFNAMSIZ + 1) == IFNAMSIZ + 1) {
+        LOG(ERROR) << "convertHidlNanDataPathIndicationResponseToLegacy: "
+                      "ifaceName too long";
+        return false;
+    }
+    strncpy(legacy_request->ndp_iface, hidl_request.ifaceName.c_str(),
+            IFNAMSIZ + 1);
     legacy_request->ndp_cfg.security_cfg =
         (hidl_request.securityConfig.securityType !=
          NanDataPathSecurityType::OPEN)