Merge "Rename Unix_time property to EPOCH_TIME" into sc-dev
diff --git a/audio/core/all-versions/default/Android.bp b/audio/core/all-versions/default/Android.bp
index 2785739..901b7ee 100644
--- a/audio/core/all-versions/default/Android.bp
+++ b/audio/core/all-versions/default/Android.bp
@@ -56,6 +56,7 @@
         "android.hardware.audio-impl_headers",
         "android.hardware.audio.common.util@all-versions",
         "libaudioclient_headers",
+        "libaudioutils_headers",
         "libaudio_system_headers",
         "libhardware_headers",
         "libmedia_headers",
diff --git a/audio/core/all-versions/default/StreamOut.cpp b/audio/core/all-versions/default/StreamOut.cpp
index 6eed3da..d027231 100644
--- a/audio/core/all-versions/default/StreamOut.cpp
+++ b/audio/core/all-versions/default/StreamOut.cpp
@@ -28,6 +28,7 @@
 
 #include <HidlUtils.h>
 #include <android/log.h>
+#include <audio_utils/Metadata.h>
 #include <hardware/audio.h>
 #include <util/CoreUtils.h>
 #include <utils/Trace.h>
@@ -742,7 +743,11 @@
     switch (event) {
         case STREAM_EVENT_CBK_TYPE_CODEC_FORMAT_CHANGED: {
             hidl_vec<uint8_t> audioMetadata;
-            audioMetadata.setToExternal((uint8_t*)param, strlen((char*)param));
+            // void* param is the byte string buffer from byte_string_from_audio_metadata().
+            // As the byte string buffer may have embedded zeroes, we cannot use strlen()
+            // but instead use audio_utils::metadata::dataByteStringLen().
+            audioMetadata.setToExternal((uint8_t*)param, audio_utils::metadata::dataByteStringLen(
+                                                                 (const uint8_t*)param));
             result = eventCallback->onCodecFormatChanged(audioMetadata);
         } break;
         default:
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 e234aa9..ed99942 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
@@ -1245,6 +1245,7 @@
                                 .access = VehiclePropertyAccess::READ,
                                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                         },
+                .initialValue = {.int32Values = {0 /* ClusterHome */, -1 /* ClusterNone */}},
         },
         {
                 .config =
@@ -1253,6 +1254,8 @@
                                 .access = VehiclePropertyAccess::READ,
                                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                         },
+                .initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */,
+                                                 -1, -1, -1, -1 /* Insets */}},
         },
         {
                 .config =
@@ -1305,6 +1308,9 @@
                                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                                 .configArray = {0, 0, 0, 11, 0, 0, 0, 0, 16},
                         },
+                .initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */,
+                                                 -1, -1, -1, -1 /* Insets */,
+                                                 0 /* ClusterHome */, -1 /* ClusterNone */}},
         },
         {
                 .config =
@@ -1313,6 +1319,7 @@
                                 .access = VehiclePropertyAccess::READ,
                                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                         },
+                .initialValue = {.int32Values = {0 /* ClusterHome */}},
         },
         {
                 .config =
diff --git a/biometrics/face/aidl/vts/Android.bp b/biometrics/face/aidl/vts/Android.bp
index c5660b1..99c8c99 100644
--- a/biometrics/face/aidl/vts/Android.bp
+++ b/biometrics/face/aidl/vts/Android.bp
@@ -14,9 +14,14 @@
         "use_libaidlvintf_gtest_helper_static",
     ],
     srcs: ["VtsHalBiometricsFaceTargetTest.cpp"],
+    static_libs: [
+        "android.hardware.biometrics.common-V1-ndk_platform",
+        "android.hardware.biometrics.face-V1-ndk_platform",
+        "android.hardware.common-V2-ndk_platform",
+        "android.hardware.keymaster-V3-ndk_platform",
+    ],
     shared_libs: [
         "libbinder_ndk",
-        "android.hardware.biometrics.face-V1-ndk_platform",
     ],
     test_suites: [
         "general-tests",
diff --git a/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp b/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
index c548fe5..8443336 100644
--- a/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
@@ -40,8 +40,7 @@
             promise.set_value();
         })));
 
-        auto status = future.wait_for(1s);
-        EXPECT_EQ(status, std::future_status::ready);
+        future.wait();
     }
 }
 
@@ -56,12 +55,11 @@
         // Notify that the task has started.
         promise.set_value();
         // Block for a "very long" time.
-        std::this_thread::sleep_for(2s);
+        std::this_thread::sleep_for(1s);
     })));
 
     // Make sure the long-running task began executing.
-    auto status = future.wait_for(1s);
-    ASSERT_EQ(status, std::future_status::ready);
+    future.wait();
 
     // The first task is already being worked on, which means the queue must be empty.
     // Fill the worker's queue to the maximum.
@@ -91,8 +89,7 @@
     // Schedule a special task to signal when all of the tasks are finished.
     worker.schedule(
             Callable::from([promise = std::move(promise)]() mutable { promise.set_value(); }));
-    auto status = future.wait_for(1s);
-    ASSERT_EQ(status, std::future_status::ready);
+    future.wait();
 
     ASSERT_EQ(results.size(), NUM_TASKS);
     EXPECT_TRUE(std::is_sorted(results.begin(), results.end()));
@@ -115,8 +112,7 @@
         })));
 
         // The first task should start executing.
-        auto status = future1.wait_for(1s);
-        ASSERT_EQ(status, std::future_status::ready);
+        future1.wait();
 
         // The second task should schedule successfully.
         ASSERT_TRUE(
@@ -128,8 +124,7 @@
     }
 
     // The second task should never execute.
-    auto status = future2.wait_for(1s);
-    ASSERT_EQ(status, std::future_status::ready);
+    future2.wait();
     // The future is expected to be ready but contain an exception.
     // Cannot use ASSERT_THROW because exceptions are disabled in this codebase.
     // ASSERT_THROW(future2.get(), std::future_error);
diff --git a/bluetooth/1.0/vts/functional/Android.bp b/bluetooth/1.0/vts/functional/Android.bp
index 4806fef..768142c 100644
--- a/bluetooth/1.0/vts/functional/Android.bp
+++ b/bluetooth/1.0/vts/functional/Android.bp
@@ -31,6 +31,7 @@
         "android.hardware.bluetooth@1.0",
         "libbluetooth-types",
     ],
+    test_config: "VtsHalBluetoothV1_0TargetTest.xml",
     test_suites: [
         "general-tests",
         "vts",
diff --git a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.xml b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.xml
new file mode 100644
index 0000000..09463c9
--- /dev/null
+++ b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs VtsHalBluetoothV1_0TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.StopServicesSetup">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="bluetooth" value="off" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalBluetoothV1_0TargetTest->/data/local/tmp/VtsHalBluetoothV1_0TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalBluetoothV1_0TargetTest" />
+    </test>
+</configuration>
diff --git a/bluetooth/1.1/vts/functional/Android.bp b/bluetooth/1.1/vts/functional/Android.bp
index e64d5a9..7f56647 100644
--- a/bluetooth/1.1/vts/functional/Android.bp
+++ b/bluetooth/1.1/vts/functional/Android.bp
@@ -32,5 +32,6 @@
         "android.hardware.bluetooth@1.0",
         "libbluetooth-types",
     ],
+    test_config: "VtsHalBluetoothV1_1TargetTest.xml",
     test_suites: ["general-tests", "vts"],
 }
diff --git a/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.xml b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.xml
new file mode 100644
index 0000000..d64751a
--- /dev/null
+++ b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs VtsHalBluetoothV1_1TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="bluetooth" value="off" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalBluetoothV1_1TargetTest->/data/local/tmp/VtsHalBluetoothV1_1TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalBluetoothV1_1TargetTest" />
+    </test>
+</configuration>
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 7bb300e..deb420d 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -7155,17 +7155,24 @@
                                 sp<device::V3_5::ICameraDevice>* device3_5 /*out*/,
                                 sp<device::V3_7::ICameraDevice>* device3_7 /*out*/) {
     ASSERT_NE(nullptr, device3_5);
-    if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_5) {
-        auto castResult = device::V3_5::ICameraDevice::castFrom(device);
-        ASSERT_TRUE(castResult.isOk());
-        *device3_5 = castResult;
-    }
-
     ASSERT_NE(nullptr, device3_7);
-    if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_7) {
-        auto castResult = device::V3_7::ICameraDevice::castFrom(device);
-        ASSERT_TRUE(castResult.isOk());
-        *device3_7 = castResult;
+
+    switch (deviceVersion) {
+        case CAMERA_DEVICE_API_VERSION_3_7: {
+            auto castResult = device::V3_7::ICameraDevice::castFrom(device);
+            ASSERT_TRUE(castResult.isOk());
+            *device3_7 = castResult;
+        }
+            [[fallthrough]];
+        case CAMERA_DEVICE_API_VERSION_3_5: {
+            auto castResult = device::V3_5::ICameraDevice::castFrom(device);
+            ASSERT_TRUE(castResult.isOk());
+            *device3_5 = castResult;
+            break;
+        }
+        default:
+            // no-op
+            return;
     }
 }
 
diff --git a/compatibility_matrices/compatibility_matrix.3.xml b/compatibility_matrices/compatibility_matrix.3.xml
index 608890b..a75ed25 100644
--- a/compatibility_matrices/compatibility_matrix.3.xml
+++ b/compatibility_matrices/compatibility_matrix.3.xml
@@ -223,7 +223,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.keymaster</name>
         <version>3.0</version>
         <version>4.0</version>
diff --git a/compatibility_matrices/compatibility_matrix.4.xml b/compatibility_matrices/compatibility_matrix.4.xml
index e5e012c..3b8ee21 100644
--- a/compatibility_matrices/compatibility_matrix.4.xml
+++ b/compatibility_matrices/compatibility_matrix.4.xml
@@ -245,7 +245,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.keymaster</name>
         <version>3.0</version>
         <version>4.0</version>
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index 8e175f0..0fb21a7 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -284,7 +284,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.keymaster</name>
         <version>3.0</version>
         <version>4.0-1</version>
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index bb22974..01cd1f6 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -316,7 +316,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.keymaster</name>
         <version>3.0</version>
         <version>4.0-1</version>
diff --git a/current.txt b/current.txt
index 270880f..3102972 100644
--- a/current.txt
+++ b/current.txt
@@ -771,6 +771,7 @@
 c2f64133b83ede65c9939ef97ab5bd867b73faf3dba0e7e69f77c3c43d9e487e android.hardware.contexthub@1.0::IContexthubCallback
 bda492ec4021d13869de72bd6f8c15c5837b78d6136b8d538efec5320573a5ec android.hardware.gnss@1.0::IGnssMeasurementCallback
 6a271e493907e8ba20912e42771bd0d99ae45431a851d5675ef9496d02510a34 android.hardware.gnss@1.1::IGnssMeasurementCallback
+11e9e1a1fd0c9b3d9648750d4b10dc2a839d3a6688904c3fc49500a4e7ca75b0 android.hardware.gnss@2.1::IGnssMeasurementCallback
 2c331a9605f3a08d9c1e0a36169ca57758bc43c11a78ef3f3730509885e52c15 android.hardware.graphics.composer@2.4::IComposerClient
 3da3ce039247872d95c6bd48621dbfdfa1c2d2a91a90f257862f87ee2bc46300 android.hardware.health@2.1::types
 9679f27a42f75781c8993ef163ed92808a1928de186639834841d0b8e326e63d android.hardware.gatekeeper@1.0::IGatekeeper
@@ -780,10 +781,12 @@
 dabe23dde7c9e3ad65c61def7392f186d7efe7f4216f9b6f9cf0863745b1a9f4 android.hardware.keymaster@4.1::IKeymasterDevice
 cd84ab19c590e0e73dd2307b591a3093ee18147ef95e6d5418644463a6620076 android.hardware.neuralnetworks@1.2::IDevice
 f729ee6a5f136b25d79ea6895d24700fce413df555baaecf2c39e4440d15d043 android.hardware.neuralnetworks@1.0::types
-a84f8dac7a9b75de1cc2936a9b429b9b62b32a31ea88ca52c29f98f5ddc0fa95 android.hardware.neuralnetworks@1.2::types
-cd331b92312d16ab89f475c39296abbf539efc4114a8c5c2b136ad99b904ef33 android.hardware.neuralnetworks@1.3::types
+38c1a3eb5c3dfa4cc40b7cf4be0e9850440e2c57197fba7407081679b358aa22 android.hardware.neuralnetworks@1.2::types
+550619f876cadbea1f718edce120f0e1dd4a6f4bd4c28b59d479677dc86b0aec android.hardware.neuralnetworks@1.3::types
 c3fec5bd470984402997f78a74b6511efc4063b270f2bd9ee7b78f48b683a1bb android.hardware.neuralnetworks@1.3::IDevice
 0fdfad62c2ec33b52e6687004e5a1971c02d10b93ee4d26df5ccff7ce032494a android.hardware.neuralnetworks@1.3::IPreparedModel
+b40c13f9a9affc806c778c1f8c78e90d4acb50f1d6a6be185d933d7a04b91c5b android.hardware.sensors@1.0::ISensors
+432086950205f5876da85dbd42004b0d0d05b429b9494b4f76a4d888758c5bd8 android.hardware.sensors@1.0::types
 e8c86c69c438da8d1549856c1bb3e2d1b8da52722f8235ff49a30f2cce91742c android.hardware.soundtrigger@2.1::ISoundTriggerHwCallback
 b9fbb6e2e061ed0960939d48b785e9700210add1f13ed32ecd688d0f1ca20ef7 android.hardware.renderscript@1.0::types
 0f53d70e1eadf8d987766db4bf6ae2048004682168f4cab118da576787def3fa android.hardware.radio@1.0::types
@@ -797,5 +800,95 @@
 2b5afef68e3e2ff1dab63e4f2ee57337ef2635ec812f49080cadfce966d33b52 android.hardware.radio@1.2::IRadio
 
 # HALs released in Android S
-# NOTE: waiting to freeze HALs until later in the release
-# NOTE: new HALs are recommended to be in AIDL
+59fa68432e374c8d3b7ec098a91a1e023a2c728110bb733237c551afa5929725 android.hardware.audio@7.0::IDevice
+2207948ca127b801c94f667c99dfd139f150b50671e1408d3e855d03efbf631d android.hardware.audio@7.0::IDevicesFactory
+1d201e15c553cd44c62864ac8d7039351ddf048a7ee61e380f6efb0904442eb8 android.hardware.audio@7.0::IPrimaryDevice
+38afa920e6d36013b5a800e8c82eefeebd24602de24441e2f8ce5b3bdf62d3af android.hardware.audio@7.0::IStream
+77d84330418abba5a92b0cdc4e27fa7c85c27344eaf7eeef441b8e88829ee475 android.hardware.audio@7.0::IStreamIn
+40c5c43e923da23497c0e3d5a1c2d699b423a0268e1e971f918e4854e1c39951 android.hardware.audio@7.0::IStreamOut
+54cbc3c637fe8d4b889ccb5690e5e3069ca8efd9c6607ce1d021a3f47576c67e android.hardware.audio@7.0::IStreamOutCallback
+8036ae0a68a698a79207218018de5f41aed344723f644112ffc99e20e5e2e9ff android.hardware.audio@7.0::IStreamOutEventCallback
+84978dbd15d4fa8be6073d0974755f7718ee0cde519ce71449fb734f53cee46b android.hardware.audio@7.0::types
+6a03a9d8cc917da00e8b88f4abc42db2f741e2d50901e8ab6dea32084a238fbd android.hardware.audio.common@7.0::types
+842b4485a00005fb938f674b12445cb592cd1636f56c7cc447966119070811bd android.hardware.audio.effect@7.0::IAcousticEchoCancelerEffect
+b62a85e5d745dc35b5a60464c6b33a5bb7a2b8b95863a1374aee77ea29cf8f49 android.hardware.audio.effect@7.0::IAutomaticGainControlEffect
+c8d5e30848191713db7cffccc482e4427816f33c98a24734c8769962f79f855b android.hardware.audio.effect@7.0::IBassBoostEffect
+7d021ecdf5bb6a61eb9ad193585d4986d1a64cb7fb4b52f219d7380145f2c6f1 android.hardware.audio.effect@7.0::IDownmixEffect
+7fee1e7c7bb3d513a524c8963d1f8f7c2ad856f26c745b4ebc286b40d503264a android.hardware.audio.effect@7.0::IEffect
+7596050ccc00234458dcb4e692056ed3c16f3618c11d7b17cb749cfd5713705d android.hardware.audio.effect@7.0::IEffectBufferProviderCallback
+f2e41467bcf1140a11b219c2e8f77981b955c2941befe66e1cc685b7863ae4c9 android.hardware.audio.effect@7.0::IEffectsFactory
+af66fb4addbc477f9fea65fb63475203122a9189624ca8d14e757bc7826d60a4 android.hardware.audio.effect@7.0::IEnvironmentalReverbEffect
+2878d007ed55e1a4149ddcd29606962c948d8610642276f91dffd5ed32281824 android.hardware.audio.effect@7.0::IEqualizerEffect
+0260ef9e2a3e077de366ebebc0c117c7ee13f46a1eabd4abd66cc6245d0bed98 android.hardware.audio.effect@7.0::ILoudnessEnhancerEffect
+3586bbc3a7cbe30f9aff0a522524eea9b78eea78280f09c35d43dbab48a1193e android.hardware.audio.effect@7.0::INoiseSuppressionEffect
+a7d74d7e7e0b1e3b739f233b7776bf01e868856a536f5cdac0f307e9c2850e64 android.hardware.audio.effect@7.0::IPresetReverbEffect
+b4cbc1f2d38787f2ad069a8e4d10c0896287531a2596f0de0283e390b0ecf05d android.hardware.audio.effect@7.0::IVirtualizerEffect
+2b5681e1ea6a2db0dc1e84edb96d3de2f7daf306046543e7956be76dcb8f20fb android.hardware.audio.effect@7.0::IVisualizerEffect
+fa1e2d78e66fd662de93cb479ffd55947fe54f51cb53915814b3d3e3036c86a5 android.hardware.audio.effect@7.0::types
+b525e91d886379c13588f4975bb04d625d46e1f41b4453792c4b2db1e7ff4340 android.hardware.biometrics.fingerprint@2.3::IBiometricsFingerprint
+4baf8e0eca4aa896cc9ceb7bb676aaf4fa21372ef8b49eed68eced1221c3dc0d android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvider
+d417a9212c8f96e3a06a2f221c8c5756c765355b2b81de2b2a65d4c9eee85401 android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvidersFactory
+c17d9e27abd37ae5a8ff8da08fc5c9b13a264670feef6bbbc9d3ab1915216130 android.hardware.bluetooth.audio@2.1::types
+6763dd2273b1b47f3ac68af9b66870287eba33fb5b4d66e8fe1d30ae18ce24cb android.hardware.boot@1.2::IBootControl
+1a1dff6e8d25dbc02a69fed3c077dd0782b30331ca3f345848ec52fc67744224 android.hardware.camera.device@3.7::ICameraDevice
+3be6faa3d11ad9c7ec01a1a0a009cf11cb65d701d109dab37613ce9cfb3cdd60 android.hardware.camera.device@3.7::ICameraDeviceSession
+3740ec773b2eb8fa6bd8c6e879eedb56c4e4306b88f1c20fa51103d791d871b1 android.hardware.camera.device@3.7::ICameraInjectionSession
+21f023685571daf46148097d98b89cea353f07e3ed83b2ed5685b23bd136c3ee android.hardware.camera.device@3.7::types
+f655c93132d223369ff6ddc621cb721f82dde6cc85ab9df2cbde6cb24cf2c885 android.hardware.camera.metadata@3.6::types
+98ff825a7d37e5ab983502d13cec1f2e5a9cac9b674b6ff1a52bcf540f4e315e android.hardware.camera.provider@2.7::ICameraProvider
+51fd14005859b16be55872660c34f5d423c77a2abcc5d4bdd5a537c40f32516b android.hardware.camera.provider@2.7::types
+3500d3c4e2d49eeed2f3239330a166beb2db2d5071b84d9c738b048c2d54a3d9 android.hardware.contexthub@1.2::IContexthub
+5ec58b1f9283d47857e3e70fbd39d9a4ff030c12d6fa3113af2b59fa3c77046e android.hardware.contexthub@1.2::IContexthubCallback
+afe69d8a66fcbd87aa1d5c033e91017237d5466110538a57e8287d7efa114538 android.hardware.contexthub@1.2::types
+c38a42661a90a0cef538b1757e29268d3a91867fcadf3c44185c992566da0b81 android.hardware.drm@1.4::ICryptoFactory
+0e96e7699ee74d65432a8712e6b2474b425a3a079c55f4e3122aa65ff2b778a4 android.hardware.drm@1.4::ICryptoPlugin
+4ebbe2b8ff3a6c8729619fe9ce62429f4b561d2e26e32eb54675babddfec9e59 android.hardware.drm@1.4::IDrmFactory
+a71fa24841f1398620f9baf738863dbaf29bde62cd171ac7b4608797ca55e35d android.hardware.drm@1.4::IDrmPlugin
+48127234347525a9d0fad9b7b79be5762d14e51cc87b1a09329a2452e06f3307 android.hardware.drm@1.4::types
+0c0657fad2239c2c7ec363d3b13f2e002d1c267ca89d2cc96d2b1de0475386cb android.hardware.fastboot@1.1::IFastboot
+61e5d73d12d558a3115e5b1e2e740fbe149235c36c607881f053f2f443cafdf5 android.hardware.media.c2@1.2::IComponent
+fcd17fb86c1b5570c8b55d855fd2ab93e4a2c6629d9ba9374372928469aad961 android.hardware.media.c2@1.2::IComponentStore
+8b149e213e77f4be50dcaf68ab55b8220ca62abd9443969e7f46a197b5adddfc android.hardware.media.c2@1.2::types
+3e8866987de4ecb48807c09d4c88ec38365930a22415f1b74edf8b14da17846b android.hardware.radio@1.6::IRadio
+715789427a44cc78f9d123b0ceb9e035e4ac2b1049501337c23a512e85b87850 android.hardware.radio@1.6::IRadioIndication
+4443f5c5f789d77ab491aad4cf4673d131bba45014d6bb4816888cbac37aa8c1 android.hardware.radio@1.6::IRadioResponse
+d06226cd3e916bb866b016b22f35c89c1d9286f62a4aa82a549daf8d52805183 android.hardware.radio@1.6::types
+f22813615be1445ddd817655c054fc69dc9efea56c9035cd0757f3cbed190641 android.hardware.radio.config@1.3::IRadioConfig
+c9ad18729268593d14681d88ffad1c97e707444a45e1b4ed804dab949edbd84f android.hardware.radio.config@1.3::IRadioConfigResponse
+fd43298c43f70130c747a642ee43b0c242ac0cebffb377faa24f2725f0aa6caf android.hardware.tetheroffload.control@1.1::IOffloadControl
+ead4ec8713a2cb40906fe31ba793d21a6b1190143c446690d16a6ea686aa2fea android.hardware.tetheroffload.control@1.1::ITetheringOffloadCallback
+e34b4c7bec5e032c14804707ca924dd6b99ed5ba139da7505fe7d698d0fe178f android.hardware.tetheroffload.control@1.1::types
+63dfdb433ac73fb2bf4a44d2ade7b7e289e155835206d1939640d6c88d208994 android.hardware.tv.cec@1.1::IHdmiCec
+b9682587677ce9c872e04f0e9fd6c9c78a56ae795c07cbf8c50100e0351d4c44 android.hardware.tv.cec@1.1::IHdmiCecCallback
+4c4243db0fad48931622b07c5ada346ed7b8e49ace30da88fde754c976e430c2 android.hardware.tv.cec@1.1::types
+b8305da72fb09d5c8ae9f89bebdc30a4c1908a62253b705f9cb6df4dc28c9094 android.hardware.tv.tuner@1.1::IFilter
+3e29c02ccc3f0c22414eb05604a0cfe93af4521c49fc4165f0fdcaa3c975dc4f android.hardware.tv.tuner@1.1::IFilterCallback
+0f2deed38165f154f27a01c3e70e6ea770520d67f7dbf3630ee45992f7a644be android.hardware.tv.tuner@1.1::IFrontend
+695213b5c7ccd13cc0227a7aaca652f77b5a5a2352483575f2905d1361562b5d android.hardware.tv.tuner@1.1::IFrontendCallback
+6fd387a7ff5916def0d1c9010aa501b30ef2fe34f161164531c9631f67ff098a android.hardware.tv.tuner@1.1::ITuner
+ea2453fa7563783fa0cd7c09aa86e6cd7ac799a1d0fc439b34c416d1c55062d1 android.hardware.tv.tuner@1.1::types
+bcebdb0b3b59bd144eefc1b17aa96609c1b23bc9a5f7e0d7060dabd7f06ef9c0 android.hardware.usb@1.3::IUsb
+f2a87e56164145160b3b3a097dba2ccc1b0ffc2fb87e8c984adfe59f47b5ecf7 android.hardware.usb.gadget@1.2::IUsbGadget
+c539325b22bce686876f062b16292038555ae2857e8d34aa63a975df18e113b1 android.hardware.usb.gadget@1.2::IUsbGadgetCallback
+95e23dbfcfb38d390f236e5fff77dd518ba272a85551df6ecb4707fe466e60bd android.hardware.usb.gadget@1.2::types
+bb7b8f653452f2044e1db587ceb415edf72201ab9c1d547564c3f75bfab65a83 android.hardware.wifi@1.5::IWifi
+c66c3ea1b375e69d7ad820d67950305a463b5a50908c4fddcbbdbab8bbac3951 android.hardware.wifi@1.5::IWifiApIface
+a1193d37f7e13222bf79ef4b73dbad15b0f0a330d03356d12226dcda20de44a7 android.hardware.wifi@1.5::IWifiChip
+d78f1c169cf7e92ddf49e0b6a2b6d5566730e8085a56aefd9140c76b107bb35d android.hardware.wifi@1.5::IWifiEventCallback
+45ab93378f02a8bc3a658853ce4d3fdc1e9fa2c1d1c545821d490bf0f4d96cec android.hardware.wifi@1.5::IWifiNanIface
+b20261b97359a8b8784700d4278aa00a8666fcd9fc259c3a8386ba34bad5ec83 android.hardware.wifi@1.5::IWifiNanIfaceEventCallback
+b154b9fe8b94cf64392766bfd932c023edf658ba8ecc4b952f8424058120e5bd android.hardware.wifi@1.5::IWifiStaIface
+ee214c95481f61aefc1255987c92be6c3b4519be327467b1692630572060b489 android.hardware.wifi@1.5::types
+c972b342dd527a3952b5d2377bd6483dec48f6b85292ca537a97705381c7ea3c android.hardware.wifi.hostapd@1.3::IHostapd
+6a3152de16dfa71b065c686e84ce8b85317745c0b9dd8f4790009ec6950d96c8 android.hardware.wifi.hostapd@1.3::IHostapdCallback
+6d361bfd0ff1ecb6d2dddb72636ddce4d706453391de401852a6a53effb7b599 android.hardware.wifi.hostapd@1.3::types
+17818b6b1952a75e4364ae82c534b9d2f5c0a9765a56256b16faa5a5cf45d3a8 android.hardware.wifi.supplicant@1.4::ISupplicant
+d29fe62973c49b6fa40eb56c28567e37c4bbec015ff59111edfae446301dc06d android.hardware.wifi.supplicant@1.4::ISupplicantP2pIface
+ea465970e96d9605ee6f6706b3b512726c66d2644738added9128c739f8f8b0c android.hardware.wifi.supplicant@1.4::ISupplicantP2pIfaceCallback
+026b9a46658ee82b154ee624adc30a454f6401230db5eb446ead42e7ef51fc80 android.hardware.wifi.supplicant@1.4::ISupplicantStaIface
+0d5adbc7ca369ca30c64a3e3f021335f991055787bc80ab44bbbbaa056d5515b android.hardware.wifi.supplicant@1.4::ISupplicantStaIfaceCallback
+c8a57364f6ad20842be14f4db284df5304f7521ca8eac6bcc1fa6c5b466fb8a6 android.hardware.wifi.supplicant@1.4::ISupplicantStaNetwork
+2123482b69f3b531c88023aa2a007110e130efbf4ed68ac9ce0bc55d5e82bc8b android.hardware.wifi.supplicant@1.4::ISupplicantStaNetworkCallback
+0821f516e4d428bc15251969f7e19411c94d8f2ccbd99e1fc8168d8e49e38b0f android.hardware.wifi.supplicant@1.4::types
+
+# There should be no more HIDL HALs - please use AIDL instead.
diff --git a/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp b/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
index e74cca9..7d32ced 100644
--- a/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
+++ b/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
@@ -306,6 +306,8 @@
   if (first != nullptr && second != nullptr) {
     EXPECT_NE(first->user_id, second->user_id);
   }
+  // the old enrollment should be invalid now
+  verifyPassword(password, enrollRsp.data, 0, verifyRsp, false);
   ALOGI("Testing Untrusted Reenroll done");
 }
 
diff --git a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
index 237e8ec..699ce9a 100644
--- a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
+++ b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
@@ -135,12 +135,29 @@
   }
 
   /*
+   * SetPositionMode:
+   * Helper function to set positioning mode and verify output
+   */
+  void SetPositionMode(const int min_interval_msec) {
+      const int kPreferredAccuracy = 0;  // Ideally perfect (matches GnssLocationProvider)
+      const int kPreferredTimeMsec = 0;  // Ideally immediate
+
+      auto result = gnss_hal_->setPositionMode(
+              IGnss::GnssPositionMode::MS_BASED, IGnss::GnssPositionRecurrence::RECURRENCE_PERIODIC,
+              min_interval_msec, kPreferredAccuracy, kPreferredTimeMsec);
+
+      ASSERT_TRUE(result.isOk());
+      EXPECT_TRUE(result);
+  }
+
+  /*
    * StartAndGetSingleLocation:
    * Helper function to get one Location and check fields
    *
    * returns  true if a location was successfully generated
    */
-  bool StartAndGetSingleLocation(bool checkAccuracies) {
+  bool StartAndGetSingleLocation(const bool checkAccuracies, const int min_interval_msec) {
+      SetPositionMode(min_interval_msec);
       auto result = gnss_hal_->start();
 
       EXPECT_TRUE(result.isOk());
@@ -349,37 +366,24 @@
  * and checks them for reasonable validity.
  */
 TEST_P(GnssHalTest, GetLocation) {
-#define MIN_INTERVAL_MSEC 500
-#define PREFERRED_ACCURACY 0   // Ideally perfect (matches GnssLocationProvider)
-#define PREFERRED_TIME_MSEC 0  // Ideally immediate
+    const int kMinIntervalMsec = 500;
+    const int kLocationTimeoutSubsequentSec = 3;
+    const int kLocationsToCheck = 5;
 
-#define LOCATION_TIMEOUT_SUBSEQUENT_SEC 3
-#define LOCATIONS_TO_CHECK 5
+    bool checkMoreAccuracies = (info_called_count_ > 0 && last_info_.yearOfHw >= 2017);
 
-  bool checkMoreAccuracies =
-      (info_called_count_ > 0 && last_info_.yearOfHw >= 2017);
+    /*
+     * GPS signals initially optional for this test, so don't expect timeout yet.
+     */
+    bool gotLocation = StartAndGetSingleLocation(checkMoreAccuracies, kMinIntervalMsec);
 
-  auto result = gnss_hal_->setPositionMode(
-      IGnss::GnssPositionMode::MS_BASED,
-      IGnss::GnssPositionRecurrence::RECURRENCE_PERIODIC, MIN_INTERVAL_MSEC,
-      PREFERRED_ACCURACY, PREFERRED_TIME_MSEC);
-
-  ASSERT_TRUE(result.isOk());
-  EXPECT_TRUE(result);
-
-  /*
-   * GPS signals initially optional for this test, so don't expect no timeout
-   * yet
-   */
-  bool gotLocation = StartAndGetSingleLocation(checkMoreAccuracies);
-
-  if (gotLocation) {
-    for (int i = 1; i < LOCATIONS_TO_CHECK; i++) {
-        EXPECT_EQ(std::cv_status::no_timeout, wait(LOCATION_TIMEOUT_SUBSEQUENT_SEC));
-        EXPECT_EQ(location_called_count_, i + 1);
-        CheckLocation(last_location_, checkMoreAccuracies, true);
+    if (gotLocation) {
+        for (int i = 1; i < kLocationsToCheck; i++) {
+            EXPECT_EQ(std::cv_status::no_timeout, wait(kLocationTimeoutSubsequentSec));
+            EXPECT_EQ(location_called_count_, i + 1);
+            CheckLocation(last_location_, checkMoreAccuracies, true);
+        }
     }
-  }
 
   StopAndClearLocations();
 }
@@ -410,7 +414,7 @@
   ASSERT_TRUE(resultVoid.isOk());
 
   // Ensure we can get a good location after a bad injection has been deleted
-  StartAndGetSingleLocation(false);
+  StartAndGetSingleLocation(false, /* min_interval_sec= */ 1000);
 
   StopAndClearLocations();
 }
@@ -430,7 +434,7 @@
     ASSERT_TRUE(result.isOk());
     EXPECT_TRUE(result);
 
-    StartAndGetSingleLocation(false);
+    StartAndGetSingleLocation(false, /* min_interval_msec= */ 1000);
 
     // Ensure we don't get a location anywhere within 111km (1 degree of lat or lng) of the seed
     // location.
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.cpp b/gnss/1.1/vts/functional/gnss_hal_test.cpp
index 52aaa69..6663a19 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test.cpp
@@ -99,7 +99,9 @@
     EXPECT_TRUE(result);
 }
 
-bool GnssHalTest::StartAndCheckFirstLocation(bool strict) {
+bool GnssHalTest::StartAndCheckFirstLocation(const bool strict, const int min_interval_msec,
+                                             const bool low_power_mode) {
+    SetPositionMode(min_interval_msec, low_power_mode);
     auto result = gnss_hal_->start();
 
     EXPECT_TRUE(result.isOk());
@@ -141,7 +143,9 @@
 
     SetPositionMode(kMinIntervalMsec, kLowPowerMode);
 
-    EXPECT_TRUE(StartAndCheckFirstLocation(/* strict= */ true));
+    EXPECT_TRUE(StartAndCheckFirstLocation(/* strict= */ true,
+                                           /* min_interval_msec= */ 1000,
+                                           /* low_power_mode= */ false));
 
     for (int i = 1; i < count; i++) {
         EXPECT_TRUE(gnss_cb_->location_cbq_.retrieve(gnss_cb_->last_location_,
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.h b/gnss/1.1/vts/functional/gnss_hal_test.h
index 75c4216..c642028 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.h
+++ b/gnss/1.1/vts/functional/gnss_hal_test.h
@@ -106,7 +106,8 @@
      *
      * returns  true if a location was successfully generated
      */
-    bool StartAndCheckFirstLocation(bool strict);
+    bool StartAndCheckFirstLocation(const bool strict, const int min_interval_msec,
+                                    const bool low_power_mode);
 
     /*
      * CheckLocation:
diff --git a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
index e6a51eb..ef64324 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
@@ -90,10 +90,8 @@
     gnss_cb_->location_cbq_.reset();
 
     // Start of Low Power Mode test
-    SetPositionMode(kMinIntervalMsec, kLowPowerMode);
-
     // Don't expect true - as without AGPS access
-    if (!StartAndCheckFirstLocation(/* strict= */ false)) {
+    if (!StartAndCheckFirstLocation(/* strict= */ false, kMinIntervalMsec, kLowPowerMode)) {
         ALOGW("GetLocationLowPower test - no first low power location received.");
     }
 
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.cpp b/gnss/2.0/vts/functional/gnss_hal_test.cpp
index 1cb44c5..5227693 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test.cpp
@@ -97,7 +97,9 @@
     EXPECT_TRUE(result);
 }
 
-bool GnssHalTest::StartAndCheckFirstLocation(bool strict) {
+bool GnssHalTest::StartAndCheckFirstLocation(const bool strict, const int min_interval_msec,
+                                             const bool low_power_mode) {
+    SetPositionMode(min_interval_msec, low_power_mode);
     const auto result = gnss_hal_->start();
 
     EXPECT_TRUE(result.isOk());
@@ -137,7 +139,9 @@
 
     SetPositionMode(kMinIntervalMsec, kLowPowerMode);
 
-    EXPECT_TRUE(StartAndCheckFirstLocation(/* strict= */ true));
+    EXPECT_TRUE(StartAndCheckFirstLocation(/* strict= */ true,
+                                           /* min_interval_msec= */ 1000,
+                                           /* low_power_mode= */ false));
 
     for (int i = 1; i < count; i++) {
         EXPECT_TRUE(gnss_cb_->location_cbq_.retrieve(gnss_cb_->last_location_,
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.h b/gnss/2.0/vts/functional/gnss_hal_test.h
index 7fbd735..28a1979 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.h
+++ b/gnss/2.0/vts/functional/gnss_hal_test.h
@@ -159,7 +159,8 @@
      *
      * returns  true if a location was successfully generated
      */
-    bool StartAndCheckFirstLocation(bool strict);
+    bool StartAndCheckFirstLocation(const bool strict, const int min_interval_msec,
+                                    const bool low_power_mode);
 
     /*
      * CheckLocation:
diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
index 3e0058f..f57c599 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
@@ -403,7 +403,9 @@
 }
 
 TEST_P(GnssHalTest, TestGnssLocationElapsedRealtime) {
-    StartAndCheckFirstLocation(/* strict= */ true);
+    StartAndCheckFirstLocation(/* strict= */ true,
+                               /* min_interval_msec= */ 1000,
+                               /* low_power_mode= */ false);
 
     ASSERT_TRUE((int)gnss_cb_->last_location_.elapsedRealtime.flags <=
                 (int)(ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
@@ -419,7 +421,9 @@
 
 // This test only verify that injectBestLocation_2_0 does not crash.
 TEST_P(GnssHalTest, TestInjectBestLocation_2_0) {
-    StartAndCheckFirstLocation(/* strict= */ true);
+    StartAndCheckFirstLocation(/* strict= */ true,
+                               /* min_interval_msec= */ 1000,
+                               /* low_power_mode= */ false);
     gnss_hal_->injectBestLocation_2_0(gnss_cb_->last_location_);
     StopAndClearLocations();
 }
@@ -460,10 +464,10 @@
     gnss_cb_->location_cbq_.reset();
 
     // Start of Low Power Mode test
-    SetPositionMode(kMinIntervalMsec, kLowPowerMode);
-
     // Don't expect true - as without AGPS access
-    if (!StartAndCheckFirstLocation(/* strict= */ false)) {
+    if (!StartAndCheckFirstLocation(/* strict= */ false,
+                                    /* min_interval_msec= */ kMinIntervalMsec,
+                                    /* low_power_mode= */ kLowPowerMode)) {
         ALOGW("GetLocationLowPower test - no first low power location received.");
     }
 
diff --git a/gnss/2.1/IGnssMeasurementCallback.hal b/gnss/2.1/IGnssMeasurementCallback.hal
index 60a5423..beef4fd 100644
--- a/gnss/2.1/IGnssMeasurementCallback.hal
+++ b/gnss/2.1/IGnssMeasurementCallback.hal
@@ -103,6 +103,9 @@
          *
          * The value does not include the inter-frequency Ionospheric bias.
          *
+         * The sign of the value is defined by the following equation:
+         *      corrected pseudorange = raw pseudorange - fullInterSignalBias
+         *
          * The full ISB of GnssClock.referenceSignalTypeForIsb is defined to be 0.0 nanoseconds.
          */
         double fullInterSignalBiasNs;
@@ -127,6 +130,9 @@
          * - Satellite inter-code bias (e.g., Differential Code Bias (DCB)) (with respect to the
          *   code type in GnssClock.referenceSignalTypeForIsb)
          *
+         * The sign of the value is defined by the following equation:
+         *      corrected pseudorange = raw pseudorange - satelliteInterSignalBias
+         *
          * The satellite ISB of GnssClock.referenceSignalTypeForIsb is defined to be 0.0
          * nanoseconds.
          */
diff --git a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
index deb80e8..fcab8c4 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
@@ -254,7 +254,7 @@
  */
 TEST_P(GnssHalTest, TestGnssSvInfoFields) {
     gnss_cb_->location_cbq_.reset();
-    StartAndCheckFirstLocation();
+    StartAndCheckFirstLocation(/* min_interval_msec= */ 1000, /* low_power_mode= */ false);
     int location_called_count = gnss_cb_->location_cbq_.calledCount();
 
     // Tolerate 1 less sv status to handle edge cases in reporting.
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePvt.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePvt.aidl
index 747ee90..8c17841 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePvt.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/SatellitePvt.aidl
@@ -34,9 +34,13 @@
 package android.hardware.gnss;
 @VintfStability
 parcelable SatellitePvt {
+  int flags;
   android.hardware.gnss.SatellitePositionEcef satPosEcef;
   android.hardware.gnss.SatelliteVelocityEcef satVelEcef;
   android.hardware.gnss.SatelliteClockInfo satClockInfo;
   double ionoDelayMeters;
   double tropoDelayMeters;
+  const int HAS_POSITION_VELOCITY_CLOCK_INFO = 1;
+  const int HAS_IONO = 2;
+  const int HAS_TROPO = 4;
 }
diff --git a/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl b/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
index 336e927..58f29c5 100644
--- a/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
+++ b/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
@@ -625,6 +625,17 @@
      * The position and velocity must be in ECEF coordinates.
      *
      * If the data is available, gnssMeasurementFlags must contain HAS_SATELLITE_PVT.
+     *
+     * If SatellitePvt is derived from Broadcast ephemeris, then the position is already w.r.t.
+     * the antenna phase center. However, if SatellitePvt is derived from other modeled orbits,
+     * such as long-term-orbits, or precise orbits, then the orbits may have been computed w.r.t.
+     * the satellite center of mass, and then GNSS vendors are expected to correct for the effect
+     * on different phase centers (can differ by meters) of different GNSS signals (e.g. L1, L5)
+     * on the reported satellite position. Accordingly, we might observe a different satellite
+     * position reported for L1 GnssMeasurement struct compared to L5 GnssMeasurement struct.
+     *
+     * If receivedSvTimeNs is not fully decoded, Satellite PVT could still be reported and
+     * receivedSvTimeNs uncertainty field would be used to provide confidence.
      */
     SatellitePvt satellitePvt;
 
diff --git a/gnss/aidl/android/hardware/gnss/SatelliteClockInfo.aidl b/gnss/aidl/android/hardware/gnss/SatelliteClockInfo.aidl
index 844fd1c..4b7d5d6 100644
--- a/gnss/aidl/android/hardware/gnss/SatelliteClockInfo.aidl
+++ b/gnss/aidl/android/hardware/gnss/SatelliteClockInfo.aidl
@@ -24,6 +24,14 @@
     /**
      * Satellite hardware code bias of the reported code type w.r.t
      * ionosphere-free measurement in meters.
+     *
+     * When broadcast ephemeris is used, this is the offset caused
+     * by the satellite hardware delays at different frequencies;
+     * e.g. in IS-GPS-705D, this term is described in Section
+     * 20.3.3.3.1.2.1.
+     *
+     * For GPS this term is ~10ns, and affects the satellite position
+     * computation by less than a millimeter.
      */
     double satHardwareCodeBiasMeters;
 
@@ -31,9 +39,20 @@
      * Satellite time correction for ionospheric-free signal measurement
      * (meters). The satellite clock correction for the given signal type
      * = satTimeCorrectionMeters - satHardwareCodeBiasMeters.
+     *
+     * When broadcast ephemeris is used, this is the offset modeled in the
+     * clock terms broadcast over the air by the satellites;
+     * e.g. in IS-GPS-200H, Section 20.3.3.3.3.1, this term is
+     * ∆tsv = af0 + af1(t - toc) + af2(t - toc)^2 + ∆tr.
+     *
+     * If another source of ephemeris is used for SatellitePvt, then the
+     * equivalent value of satTimeCorrection must be provided.
+     *
+     * For GPS this term is ~1ms, and affects the satellite position
+     * computation by ~1m.
      */
     double satTimeCorrectionMeters;
 
     /** Satellite clock drift (meters per second). */
     double satClkDriftMps;
-}
\ No newline at end of file
+}
diff --git a/gnss/aidl/android/hardware/gnss/SatellitePvt.aidl b/gnss/aidl/android/hardware/gnss/SatellitePvt.aidl
index ea55f0c..a238e3f 100644
--- a/gnss/aidl/android/hardware/gnss/SatellitePvt.aidl
+++ b/gnss/aidl/android/hardware/gnss/SatellitePvt.aidl
@@ -16,9 +16,9 @@
 
 package android.hardware.gnss;
 
+import android.hardware.gnss.SatelliteClockInfo;
 import android.hardware.gnss.SatellitePositionEcef;
 import android.hardware.gnss.SatelliteVelocityEcef;
-import android.hardware.gnss.SatelliteClockInfo;
 
 /**
  * Contains estimates of the satellite position, velocity and time in the
@@ -27,6 +27,34 @@
 @VintfStability
 parcelable SatellitePvt {
     /**
+     * Bit mask indicating valid satellite position, velocity and clock info fields are
+     * stored in the SatellitePvt.
+     */
+    const int HAS_POSITION_VELOCITY_CLOCK_INFO = 1 << 0;
+
+    /**
+     * Bit mask indicating a valid iono delay field is stored in the SatellitePvt.
+     */
+    const int HAS_IONO = 1 << 1;
+
+    /**
+     * Bit mask indicating a valid tropo delay field is stored in the SatellitePvt.
+     */
+    const int HAS_TROPO = 1 << 2;
+
+    /**
+     * A bitfield of flags indicating the validity of the fields in this SatellitePvt.
+     * The bit masks are defined in the constants with prefix HAS_*
+     *
+     * Fields for which there is no corresponding flag must be filled in with a valid value.
+     * For convenience, these are marked as mandatory.
+     *
+     * Others fields may have invalid information in them, if not marked as valid by the
+     * corresponding bit in flags.
+     */
+    int flags;
+
+    /**
      * Satellite position in WGS84 ECEF. See comments of
      * SatellitePositionEcef for units.
      */
@@ -46,4 +74,4 @@
 
     /** Tropospheric delay in meters. */
     double tropoDelayMeters;
-}
\ No newline at end of file
+}
diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp
index 435afa3..6061eec 100644
--- a/gnss/aidl/default/Gnss.cpp
+++ b/gnss/aidl/default/Gnss.cpp
@@ -20,7 +20,6 @@
 #include <log/log.h>
 #include "GnssConfiguration.h"
 #include "GnssMeasurementInterface.h"
-#include "GnssPowerIndication.h"
 #include "GnssPsds.h"
 
 namespace aidl::android::hardware::gnss {
@@ -73,8 +72,11 @@
 ndk::ScopedAStatus Gnss::getExtensionGnssPowerIndication(
         std::shared_ptr<IGnssPowerIndication>* iGnssPowerIndication) {
     ALOGD("Gnss::getExtensionGnssPowerIndication");
+    if (mGnssPowerIndication == nullptr) {
+        mGnssPowerIndication = SharedRefBase::make<GnssPowerIndication>();
+    }
 
-    *iGnssPowerIndication = SharedRefBase::make<GnssPowerIndication>();
+    *iGnssPowerIndication = mGnssPowerIndication;
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/gnss/aidl/default/Gnss.h b/gnss/aidl/default/Gnss.h
index bccc7f2..76ebe4d 100644
--- a/gnss/aidl/default/Gnss.h
+++ b/gnss/aidl/default/Gnss.h
@@ -22,6 +22,7 @@
 #include <aidl/android/hardware/gnss/BnGnssPowerIndication.h>
 #include <aidl/android/hardware/gnss/BnGnssPsds.h>
 #include "GnssConfiguration.h"
+#include "GnssPowerIndication.h"
 
 namespace aidl::android::hardware::gnss {
 
@@ -38,6 +39,7 @@
             std::shared_ptr<IGnssMeasurementInterface>* iGnssMeasurement) override;
 
     std::shared_ptr<GnssConfiguration> mGnssConfiguration;
+    std::shared_ptr<GnssPowerIndication> mGnssPowerIndication;
 
   private:
     static std::shared_ptr<IGnssCallback> sGnssCallback;
diff --git a/gnss/aidl/default/GnssHidlHal.cpp b/gnss/aidl/default/GnssHidlHal.cpp
index 9529ec9..263715c 100644
--- a/gnss/aidl/default/GnssHidlHal.cpp
+++ b/gnss/aidl/default/GnssHidlHal.cpp
@@ -31,11 +31,19 @@
     } else {
         mGnssConfigurationAidl = iGnss->mGnssConfiguration;
     }
+
+    std::shared_ptr<IGnssPowerIndication> iGnssPowerIndication;
+    status = iGnss->getExtensionGnssPowerIndication(&iGnssPowerIndication);
+    if (!status.isOk()) {
+        ALOGE("Failed to getExtensionGnssPowerIndication.");
+    } else {
+        mGnssPowerIndicationAidl = iGnss->mGnssPowerIndication;
+    }
 };
 
 hidl_vec<GnssSvInfo> GnssHidlHal::filterBlocklistedSatellitesV2_1(
         hidl_vec<GnssSvInfo> gnssSvInfoList) {
-    ALOGD("filterBlocklistSatellitesV2_1 - overridden by GnssHidlHal class");
+    ALOGD("GnssHidlHal::filterBlocklistSatellitesV2_1");
     if (mGnssConfigurationAidl == nullptr) {
         ALOGE("Handle to AIDL GnssConfiguration is not available.");
         return gnssSvInfoList;
@@ -51,4 +59,8 @@
     return gnssSvInfoList;
 }
 
+void GnssHidlHal::notePowerConsumption() {
+    mGnssPowerIndicationAidl->notePowerConsumption();
+}
+
 }  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/GnssHidlHal.h b/gnss/aidl/default/GnssHidlHal.h
index 93a79a1..5fb4f97 100644
--- a/gnss/aidl/default/GnssHidlHal.h
+++ b/gnss/aidl/default/GnssHidlHal.h
@@ -32,9 +32,11 @@
     filterBlocklistedSatellitesV2_1(
             hidl_vec<::android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo> gnssSvInfoList)
             override;
+    void notePowerConsumption() override;
 
     std::shared_ptr<Gnss> mGnssAidl;
     std::shared_ptr<GnssConfiguration> mGnssConfigurationAidl;
+    std::shared_ptr<GnssPowerIndication> mGnssPowerIndicationAidl;
 };
 
 }  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/GnssPowerIndication.cpp b/gnss/aidl/default/GnssPowerIndication.cpp
index 429cc8c..4dec1c6 100644
--- a/gnss/aidl/default/GnssPowerIndication.cpp
+++ b/gnss/aidl/default/GnssPowerIndication.cpp
@@ -50,13 +50,19 @@
     };
     GnssPowerStats gnssPowerStats = {
             .elapsedRealtime = elapsedRealtime,
-            .totalEnergyMilliJoule = 1.59975e+3,
-            .singlebandTrackingModeEnergyMilliJoule = 1.2342e+3,
-            .multibandTrackingModeEnergyMilliJoule = 3.653e+2,
+            .totalEnergyMilliJoule = 1.500e+3 + numLocationReported * 22.0,
+            .singlebandTrackingModeEnergyMilliJoule = 0.0,
+            .multibandTrackingModeEnergyMilliJoule = 1.28e+2 + numLocationReported * 4.0,
+            .singlebandAcquisitionModeEnergyMilliJoule = 0.0,
+            .multibandAcquisitionModeEnergyMilliJoule = 3.65e+2 + numLocationReported * 15.0,
             .otherModesEnergyMilliJoule = {1.232e+2, 3.234e+3},
     };
     sCallback->gnssPowerStatsCb(gnssPowerStats);
     return ndk::ScopedAStatus::ok();
 }
 
+void GnssPowerIndication::notePowerConsumption() {
+    numLocationReported++;
+}
+
 }  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/GnssPowerIndication.h b/gnss/aidl/default/GnssPowerIndication.h
index e32fd72..93ca0b7 100644
--- a/gnss/aidl/default/GnssPowerIndication.h
+++ b/gnss/aidl/default/GnssPowerIndication.h
@@ -26,12 +26,16 @@
             const std::shared_ptr<IGnssPowerIndicationCallback>& callback) override;
     ndk::ScopedAStatus requestGnssPowerStats() override;
 
+    void notePowerConsumption();
+
   private:
     // Guarded by mMutex
     static std::shared_ptr<IGnssPowerIndicationCallback> sCallback;
 
     // Synchronization lock for sCallback
     mutable std::mutex mMutex;
+
+    int numLocationReported;
 };
 
 }  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index ae0551d..67ccf52 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -30,6 +30,7 @@
 using android::hardware::gnss::ElapsedRealtime;
 using android::hardware::gnss::GnssClock;
 using android::hardware::gnss::GnssMeasurement;
+using android::hardware::gnss::GnssPowerStats;
 using android::hardware::gnss::IGnss;
 using android::hardware::gnss::IGnssConfiguration;
 using android::hardware::gnss::IGnssMeasurementCallback;
@@ -37,6 +38,7 @@
 using android::hardware::gnss::IGnssPowerIndication;
 using android::hardware::gnss::IGnssPsds;
 using android::hardware::gnss::PsdsType;
+using android::hardware::gnss::SatellitePvt;
 
 using GnssConstellationTypeAidl = android::hardware::gnss::GnssConstellationType;
 
@@ -127,22 +129,39 @@
                          GnssMeasurement::HAS_SATELLITE_PVT |
                          GnssMeasurement::HAS_CORRELATION_VECTOR));
 
-        if ((measurement.flags & GnssMeasurement::HAS_SATELLITE_PVT) &&
-            (has_capability_satpvt == true)) {
-            ASSERT_TRUE(measurement.satellitePvt.satPosEcef.posXMeters >= -43000000 &&
-                        measurement.satellitePvt.satPosEcef.posXMeters <= 43000000);
-            ASSERT_TRUE(measurement.satellitePvt.satPosEcef.posYMeters >= -43000000 &&
-                        measurement.satellitePvt.satPosEcef.posYMeters <= 43000000);
-            ASSERT_TRUE(measurement.satellitePvt.satPosEcef.posZMeters >= -43000000 &&
-                        measurement.satellitePvt.satPosEcef.posZMeters <= 43000000);
-            ASSERT_TRUE(measurement.satellitePvt.satPosEcef.ureMeters > 0);
-            ASSERT_TRUE(measurement.satellitePvt.satVelEcef.velXMps >= -4000 &&
-                        measurement.satellitePvt.satVelEcef.velXMps <= 4000);
-            ASSERT_TRUE(measurement.satellitePvt.satVelEcef.velYMps >= -4000 &&
-                        measurement.satellitePvt.satVelEcef.velYMps <= 4000);
-            ASSERT_TRUE(measurement.satellitePvt.satVelEcef.velZMps >= -4000 &&
-                        measurement.satellitePvt.satVelEcef.velZMps <= 4000);
-            ASSERT_TRUE(measurement.satellitePvt.satVelEcef.ureRateMps > 0);
+        if (measurement.flags & GnssMeasurement::HAS_SATELLITE_PVT &&
+            has_capability_satpvt == true) {
+            if (measurement.satellitePvt.flags & SatellitePvt::HAS_POSITION_VELOCITY_CLOCK_INFO) {
+                ASSERT_TRUE(measurement.satellitePvt.satPosEcef.posXMeters >= -43000000 &&
+                            measurement.satellitePvt.satPosEcef.posXMeters <= 43000000);
+                ASSERT_TRUE(measurement.satellitePvt.satPosEcef.posYMeters >= -43000000 &&
+                            measurement.satellitePvt.satPosEcef.posYMeters <= 43000000);
+                ASSERT_TRUE(measurement.satellitePvt.satPosEcef.posZMeters >= -43000000 &&
+                            measurement.satellitePvt.satPosEcef.posZMeters <= 43000000);
+                ASSERT_TRUE(measurement.satellitePvt.satPosEcef.ureMeters > 0);
+                ASSERT_TRUE(measurement.satellitePvt.satVelEcef.velXMps >= -4000 &&
+                            measurement.satellitePvt.satVelEcef.velXMps <= 4000);
+                ASSERT_TRUE(measurement.satellitePvt.satVelEcef.velYMps >= -4000 &&
+                            measurement.satellitePvt.satVelEcef.velYMps <= 4000);
+                ASSERT_TRUE(measurement.satellitePvt.satVelEcef.velZMps >= -4000 &&
+                            measurement.satellitePvt.satVelEcef.velZMps <= 4000);
+                ASSERT_TRUE(measurement.satellitePvt.satVelEcef.ureRateMps > 0);
+                ASSERT_TRUE(
+                        measurement.satellitePvt.satClockInfo.satHardwareCodeBiasMeters > -17.869 &&
+                        measurement.satellitePvt.satClockInfo.satHardwareCodeBiasMeters < 17.729);
+                ASSERT_TRUE(measurement.satellitePvt.satClockInfo.satTimeCorrectionMeters > -3e6 &&
+                            measurement.satellitePvt.satClockInfo.satTimeCorrectionMeters < 3e6);
+                ASSERT_TRUE(measurement.satellitePvt.satClockInfo.satClkDriftMps > -1.117 &&
+                            measurement.satellitePvt.satClockInfo.satClkDriftMps < 1.117);
+            }
+            if (measurement.satellitePvt.flags & SatellitePvt::HAS_IONO) {
+                ASSERT_TRUE(measurement.satellitePvt.ionoDelayMeters > 0 &&
+                            measurement.satellitePvt.ionoDelayMeters < 100);
+            }
+            if (measurement.satellitePvt.flags & SatellitePvt::HAS_TROPO) {
+                ASSERT_TRUE(measurement.satellitePvt.tropoDelayMeters > 0 &&
+                            measurement.satellitePvt.tropoDelayMeters < 100);
+            }
         }
 
         if (kIsCorrelationVectorSupported &&
@@ -168,9 +187,12 @@
  * TestGnssPowerIndication
  * 1. Gets the GnssPowerIndicationExtension.
  * 2. Sets a GnssPowerIndicationCallback.
- * 3.
+ * 3. Requests and verifies the 1st GnssPowerStats is received.
+ * 4. Gets a location.
+ * 5. Requests the 2nd GnssPowerStats, and verifies it has larger values than the 1st one.
  */
 TEST_P(GnssHalTest, TestGnssPowerIndication) {
+    // Set up gnssPowerIndication and callback
     sp<IGnssPowerIndication> iGnssPowerIndication;
     auto status = aidl_gnss_hal_->getExtensionGnssPowerIndication(&iGnssPowerIndication);
     ASSERT_TRUE(status.isOk());
@@ -186,10 +208,49 @@
 
     EXPECT_EQ(gnssPowerIndicationCallback->capabilities_cbq_.calledCount(), 1);
 
+    // Request and verify a GnssPowerStats is received
+    gnssPowerIndicationCallback->gnss_power_stats_cbq_.reset();
     iGnssPowerIndication->requestGnssPowerStats();
+
     EXPECT_TRUE(gnssPowerIndicationCallback->gnss_power_stats_cbq_.retrieve(
             gnssPowerIndicationCallback->last_gnss_power_stats_, kTimeoutSec));
     EXPECT_EQ(gnssPowerIndicationCallback->gnss_power_stats_cbq_.calledCount(), 1);
+    auto powerStats1 = gnssPowerIndicationCallback->last_gnss_power_stats_;
+
+    // Get a location and request another GnssPowerStats
+    gnss_cb_->location_cbq_.reset();
+    StartAndCheckFirstLocation(/* min_interval_msec= */ 1000, /* low_power_mode= */ false);
+
+    // Request and verify the 2nd GnssPowerStats has larger values than the 1st one
+    iGnssPowerIndication->requestGnssPowerStats();
+
+    EXPECT_TRUE(gnssPowerIndicationCallback->gnss_power_stats_cbq_.retrieve(
+            gnssPowerIndicationCallback->last_gnss_power_stats_, kTimeoutSec));
+    EXPECT_EQ(gnssPowerIndicationCallback->gnss_power_stats_cbq_.calledCount(), 2);
+    auto powerStats2 = gnssPowerIndicationCallback->last_gnss_power_stats_;
+
+    // Elapsed realtime must increase
+    EXPECT_GT(powerStats2.elapsedRealtime.timestampNs, powerStats1.elapsedRealtime.timestampNs);
+
+    // Total energy must increase
+    EXPECT_GT(powerStats2.totalEnergyMilliJoule, powerStats1.totalEnergyMilliJoule);
+
+    // At least oone of singleband and multiband acquisition energy must increase
+    bool singlebandAcqEnergyIncreased = powerStats2.singlebandAcquisitionModeEnergyMilliJoule >
+                                        powerStats1.singlebandAcquisitionModeEnergyMilliJoule;
+    bool multibandAcqEnergyIncreased = powerStats2.multibandAcquisitionModeEnergyMilliJoule >
+                                       powerStats1.multibandAcquisitionModeEnergyMilliJoule;
+    EXPECT_TRUE(singlebandAcqEnergyIncreased || multibandAcqEnergyIncreased);
+
+    // At least one of singleband and multiband tracking energy must increase
+    bool singlebandTrackingEnergyIncreased = powerStats2.singlebandTrackingModeEnergyMilliJoule >
+                                             powerStats1.singlebandTrackingModeEnergyMilliJoule;
+    bool multibandTrackingEnergyIncreased = powerStats2.multibandTrackingModeEnergyMilliJoule >
+                                            powerStats1.multibandTrackingModeEnergyMilliJoule;
+    EXPECT_TRUE(singlebandTrackingEnergyIncreased || multibandTrackingEnergyIncreased);
+
+    // Clean up
+    StopAndClearLocations();
 }
 
 /*
diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp
index 9bc6786..569dac4 100644
--- a/gnss/common/utils/default/Utils.cpp
+++ b/gnss/common/utils/default/Utils.cpp
@@ -31,6 +31,7 @@
 using aidl::android::hardware::gnss::GnssMeasurement;
 using aidl::android::hardware::gnss::IGnss;
 using aidl::android::hardware::gnss::IGnssMeasurementCallback;
+using aidl::android::hardware::gnss::SatellitePvt;
 
 using GnssSvFlags = V1_0::IGnssCallback::GnssSvFlags;
 using GnssMeasurementFlagsV1_0 = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags;
@@ -175,7 +176,9 @@
             .fullInterSignalBiasUncertaintyNs = 792.0,
             .satelliteInterSignalBiasNs = 233.9,
             .satelliteInterSignalBiasUncertaintyNs = 921.2,
-            .satellitePvt = {.satPosEcef = {.posXMeters = 10442993.1153328,
+            .satellitePvt = {.flags = SatellitePvt::HAS_POSITION_VELOCITY_CLOCK_INFO |
+                                      SatellitePvt::HAS_IONO | SatellitePvt::HAS_TROPO,
+                             .satPosEcef = {.posXMeters = 10442993.1153328,
                                             .posYMeters = -19926932.8051666,
                                             .posZMeters = -12034295.0216203,
                                             .ureMeters = 1000.2345678},
diff --git a/gnss/common/utils/default/include/v2_1/GnssTemplate.h b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
index a6e8f58..a1d6981 100644
--- a/gnss/common/utils/default/include/v2_1/GnssTemplate.h
+++ b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
@@ -132,6 +132,7 @@
     mutable std::mutex mMutex;
     virtual hidl_vec<V2_1::IGnssCallback::GnssSvInfo> filterBlocklistedSatellitesV2_1(
             hidl_vec<V2_1::IGnssCallback::GnssSvInfo> gnssSvInfoList);
+    virtual void notePowerConsumption();
 };
 
 template <class T_IGnss>
@@ -219,6 +220,7 @@
             auto svStatus = filterBlocklistedSatellitesV2_1(Utils::getMockSvInfoListV2_1());
             this->reportSvStatus(svStatus);
             auto currentLocation = getLocationFromHW();
+            notePowerConsumption();
             if (mGnssFd != -1) {
                 // Only report location if the return from hardware is valid
                 // note that we can not merge these two "if" together, if didn't
@@ -245,7 +247,7 @@
 template <class T_IGnss>
 hidl_vec<V2_1::IGnssCallback::GnssSvInfo> GnssTemplate<T_IGnss>::filterBlocklistedSatellitesV2_1(
         hidl_vec<V2_1::IGnssCallback::GnssSvInfo> gnssSvInfoList) {
-    ALOGD("filterBlocklistedSatellitesV2_1");
+    ALOGD("GnssTemplate::filterBlocklistedSatellitesV2_1");
     for (uint32_t i = 0; i < gnssSvInfoList.size(); i++) {
         if (mGnssConfiguration->isBlacklistedV2_1(gnssSvInfoList[i])) {
             gnssSvInfoList[i].v2_0.v1_0.svFlag &=
@@ -256,6 +258,11 @@
 }
 
 template <class T_IGnss>
+void GnssTemplate<T_IGnss>::notePowerConsumption() {
+    ALOGD("GnssTemplate::notePowerConsumption");
+}
+
+template <class T_IGnss>
 Return<bool> GnssTemplate<T_IGnss>::stop() {
     ALOGD("stop");
     mIsActive = false;
diff --git a/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h b/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h
index fec3503..03166be 100644
--- a/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h
+++ b/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h
@@ -107,7 +107,7 @@
      *
      * returns  true if a location was successfully generated
      */
-    bool StartAndCheckFirstLocation();
+    bool StartAndCheckFirstLocation(const int min_interval_msec, const bool low_power_mode);
 
     /*
      * CheckLocation:
@@ -234,7 +234,9 @@
 }
 
 template <class T_IGnss>
-bool GnssHalTestTemplate<T_IGnss>::StartAndCheckFirstLocation() {
+bool GnssHalTestTemplate<T_IGnss>::StartAndCheckFirstLocation(const int min_interval_msec,
+                                                              const bool low_power_mode) {
+    SetPositionMode(min_interval_msec, low_power_mode);
     const auto result = gnss_hal_->start();
 
     EXPECT_TRUE(result.isOk());
@@ -274,9 +276,7 @@
     const int kLocationTimeoutSubsequentSec = 2;
     const bool kLowPowerMode = false;
 
-    SetPositionMode(kMinIntervalMsec, kLowPowerMode);
-
-    EXPECT_TRUE(StartAndCheckFirstLocation());
+    EXPECT_TRUE(StartAndCheckFirstLocation(kMinIntervalMsec, kLowPowerMode));
 
     for (int i = 1; i < count; i++) {
         EXPECT_TRUE(gnss_cb_->location_cbq_.retrieve(gnss_cb_->last_location_,
diff --git a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl
index 8b12169..232e023 100644
--- a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl
+++ b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl
@@ -21,7 +21,7 @@
   int width;
   int height;
   int layers;
-  android.hardware.graphics.common.PixelFormat format;
-  android.hardware.graphics.common.BufferUsage usage;
+  android.hardware.graphics.common.PixelFormat format = android.hardware.graphics.common.PixelFormat.UNSPECIFIED;
+  android.hardware.graphics.common.BufferUsage usage = android.hardware.graphics.common.BufferUsage.CPU_READ_NEVER;
   int stride;
 }
diff --git a/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl b/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl
index e1e3492..078c512 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl
@@ -29,7 +29,7 @@
     int width;
     int height;
     int layers;
-    PixelFormat format;
-    BufferUsage usage;
+    PixelFormat format = PixelFormat.UNSPECIFIED;
+    BufferUsage usage = BufferUsage.CPU_READ_NEVER;
     int stride;
 }
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 0df2b8d..7d0a83b 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -73,15 +73,15 @@
 
     IComposerClient::Rect getFrameRect() const { return {0, 0, mDisplayWidth, mDisplayHeight}; }
 
-    void setDimensions(int32_t displayWidth, int32_t displayHeight) const {
+    void setDimensions(int32_t displayWidth, int32_t displayHeight) {
         mDisplayWidth = displayWidth;
         mDisplayHeight = displayHeight;
     }
 
   private:
     const Display mDisplay;
-    mutable int32_t mDisplayWidth;
-    mutable int32_t mDisplayHeight;
+    int32_t mDisplayWidth;
+    int32_t mDisplayHeight;
 };
 
 class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> {
@@ -200,7 +200,7 @@
                                        const ContentType& contentType, const char* contentTypeStr);
 
     Error setActiveConfigWithConstraints(
-            const VtsDisplay& display, Config config,
+            VtsDisplay& display, Config config,
             const IComposerClient::VsyncPeriodChangeConstraints& constraints,
             VsyncPeriodChangeTimeline* timeline) {
         const auto error = mComposerClient->setActiveConfigWithConstraints(display.get(), config,
@@ -215,7 +215,7 @@
         return error;
     }
 
-    void setActiveConfig(const VtsDisplay& display, Config config) {
+    void setActiveConfig(VtsDisplay& display, Config config) {
         mComposerClient->setActiveConfig(display.get(), config);
         const int32_t displayWidth = mComposerClient->getDisplayAttribute_2_4(
                 display.get(), config, IComposerClient::Attribute::WIDTH);
@@ -377,7 +377,7 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod) {
-    for (const auto& display : mDisplays) {
+    for (VtsDisplay& display : mDisplays) {
         for (Config config : mComposerClient->getDisplayConfigs(display.get())) {
             VsyncPeriodNanos expectedVsyncPeriodNanos = mComposerClient->getDisplayAttribute_2_4(
                     display.get(), config,
@@ -441,7 +441,7 @@
     constraints.seamlessRequired = false;
     constraints.desiredTimeNanos = systemTime();
 
-    for (const auto& display : mDisplays) {
+    for (VtsDisplay& display : mDisplays) {
         Config invalidConfigId = GetInvalidConfigId(display.get());
         EXPECT_EQ(Error::BAD_CONFIG,
                   setActiveConfigWithConstraints(display, invalidConfigId, constraints, &timeline));
@@ -455,7 +455,7 @@
     constraints.seamlessRequired = true;
     constraints.desiredTimeNanos = systemTime();
 
-    for (const auto& display : mDisplays) {
+    for (VtsDisplay& display : mDisplays) {
         forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
             const auto configGroup1 = mComposerClient->getDisplayAttribute_2_4(
                     display.get(), config1,
@@ -556,7 +556,7 @@
 }
 
 void GraphicsComposerHidlTest::Test_setActiveConfigWithConstraints(const TestParameters& params) {
-    for (const auto& display : mDisplays) {
+    for (VtsDisplay& display : mDisplays) {
         forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
             setActiveConfig(display, config1);
             sendRefreshFrame(display, nullptr);
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl
index a097895..3224e4b 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl
@@ -43,8 +43,8 @@
   void startRetrieval(in android.hardware.identity.SecureAccessControlProfile[] accessControlProfiles, in android.hardware.keymaster.HardwareAuthToken authToken, in byte[] itemsRequest, in byte[] signingKeyBlob, in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts);
   void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize, in int[] accessControlProfileIds);
   byte[] retrieveEntryValue(in byte[] encryptedContent);
-  void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
-  android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
+  @SuppressWarnings(value={"out-array"}) void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
+  @SuppressWarnings(value={"out-array"}) android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
   void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces);
   void setVerificationToken(in android.hardware.keymaster.VerificationToken verificationToken);
   byte[] deleteCredentialWithChallenge(in byte[] challenge);
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl
index a713462..19a29ec 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -38,6 +38,6 @@
   android.hardware.identity.SecureAccessControlProfile addAccessControlProfile(in int id, in android.hardware.identity.Certificate readerCertificate, in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
   void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize);
   byte[] addEntryValue(in byte[] content);
-  void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
+  @SuppressWarnings(value={"out-array"}) void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
   void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
 }
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index d23f88c..8ae293b 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -324,6 +324,7 @@
      *
      * @param out deviceNameSpaces the bytes of DeviceNameSpaces.
      */
+    @SuppressWarnings(value={"out-array"})
     void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
 
     /**
@@ -376,6 +377,7 @@
      *
      * @return an X.509 certificate for the new signing key, signed by the credential key.
      */
+    @SuppressWarnings(value={"out-array"})
     Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
 
     /**
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index 5f878ee..22bcf61 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -320,6 +320,7 @@
      *              "accessControlProfiles" : [ * uint ],
      *          }
      */
+    @SuppressWarnings(value={"out-array"})
     void finishAddingEntries(out byte[] credentialData,
         out byte[] proofOfProvisioningSignature);
 
diff --git a/identity/aidl/default/libeic/EicCbor.c b/identity/aidl/default/libeic/EicCbor.c
index fe131eb..0e2684f 100644
--- a/identity/aidl/default/libeic/EicCbor.c
+++ b/identity/aidl/default/libeic/EicCbor.c
@@ -114,7 +114,7 @@
         data[4] = size & 0xff;
         eicCborAppend(cbor, data, 5);
     } else {
-        data[0] = (majorType << 5) | 24;
+        data[0] = (majorType << 5) | 27;
         data[1] = (((uint64_t)size) >> 56) & 0xff;
         data[2] = (((uint64_t)size) >> 48) & 0xff;
         data[3] = (((uint64_t)size) >> 40) & 0xff;
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index e0d60fc..9e37ed0 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -921,6 +921,23 @@
                               .Authorization(TAG_MIN_MAC_LENGTH, 128)));
 }
 
+/**
+ * NewKeyGenerationTest.AesInvalidKeySize
+ *
+ * Verifies that specifying an invalid key size for AES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, AesInvalidKeySize) {
+    for (auto key_size : InvalidKeySizes(Algorithm::AES)) {
+        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AesEncryptionKey(key_size)
+                                      .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                      .Padding(PaddingMode::NONE)));
+    }
+}
+
 INSTANTIATE_KEYMASTER_HIDL_TEST(NewKeyGenerationTest);
 
 typedef KeymasterHidlTest SigningOperationsTest;
diff --git a/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
index e46cb48..0639da8 100644
--- a/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -243,7 +243,9 @@
 
     EXPECT_EQ(ErrorCode::OK, result);
     EXPECT_EQ(2U, cert_chain.size());
-    if (dumpAttestations) dumpContent(bin2hex(cert_chain[0]));
+    if (dumpAttestations) {
+      for (auto cert_ : cert_chain) dumpContent(bin2hex(cert_));
+    }
     auto [err, attestation] = parse_attestation_record(cert_chain[0]);
     ASSERT_EQ(ErrorCode::OK, err);
 
@@ -287,7 +289,9 @@
 
     EXPECT_EQ(ErrorCode::OK, result);
     EXPECT_EQ(2U, cert_chain.size());
-    if (dumpAttestations) dumpContent(bin2hex(cert_chain[0]));
+    if (dumpAttestations) {
+      for (auto cert_ : cert_chain) dumpContent(bin2hex(cert_));
+    }
     auto [err, attestation] = parse_attestation_record(cert_chain[0]);
     ASSERT_EQ(ErrorCode::OK, err);
 
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
index db1df2b..4f21cba 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -21,7 +21,7 @@
   long challenge;
   long userId;
   long authenticatorId;
-  android.hardware.keymaster.HardwareAuthenticatorType authenticatorType;
+  android.hardware.keymaster.HardwareAuthenticatorType authenticatorType = android.hardware.keymaster.HardwareAuthenticatorType.NONE;
   android.hardware.keymaster.Timestamp timestamp;
   byte[] mac;
 }
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
index 0633765..b116dac 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
@@ -20,6 +20,6 @@
 parcelable VerificationToken {
   long challenge;
   android.hardware.keymaster.Timestamp timestamp;
-  android.hardware.keymaster.SecurityLevel securityLevel;
+  android.hardware.keymaster.SecurityLevel securityLevel = android.hardware.keymaster.SecurityLevel.SOFTWARE;
   byte[] mac;
 }
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
index 58602aa..99b036a 100644
--- a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -55,7 +55,7 @@
      * authenticatorType describes the type of authentication that took place, e.g. password or
      * fingerprint.
      */
-    HardwareAuthenticatorType authenticatorType;
+    HardwareAuthenticatorType authenticatorType = HardwareAuthenticatorType.NONE;
 
     /**
      * timestamp indicates when the user authentication took place, in milliseconds since some
diff --git a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
index f053254..5efd937 100644
--- a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
@@ -43,7 +43,7 @@
     /**
      * SecurityLevel of the secure environment that generated the token.
      */
-    SecurityLevel securityLevel;
+    SecurityLevel securityLevel = SecurityLevel.SOFTWARE;
 
     /**
      * 32-byte HMAC-SHA256 of the above values, computed as:
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
index 7849ca7..8bd2fbe 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
@@ -48,6 +48,10 @@
             const nn::OptionalTimePoint& deadline,
             const nn::OptionalDuration& loopTimeoutDuration) const override;
 
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
   private:
     const nn::SharedPreparedModel kPreparedModel;
 };
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
new file mode 100644
index 0000000..e201e25
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_EXECUTION_H
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+            std::shared_ptr<const PreparedModel> preparedModel, Request request,
+            hal::utils::RequestRelocation relocation);
+
+    Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+              Request request, hal::utils::RequestRelocation relocation);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const PreparedModel> kPreparedModel;
+    const Request kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_EXECUTION_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
index 8853eea..48be595 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
@@ -57,10 +57,17 @@
             const nn::OptionalDuration& loopTimeoutDuration,
             const nn::OptionalDuration& timeoutDurationAfterFence) const override;
 
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
     nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
 
     std::any getUnderlyingResource() const override;
 
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const V1_0::Request& request, const hal::utils::RequestRelocation& relocation) const;
+
   private:
     const sp<V1_0::IPreparedModel> kPreparedModel;
     const hal::utils::DeathHandler kDeathHandler;
diff --git a/neuralnetworks/1.0/utils/src/Burst.cpp b/neuralnetworks/1.0/utils/src/Burst.cpp
index e3a9757..1284721 100644
--- a/neuralnetworks/1.0/utils/src/Burst.cpp
+++ b/neuralnetworks/1.0/utils/src/Burst.cpp
@@ -55,4 +55,10 @@
     return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
 }
 
+nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration);
+}
+
 }  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Execution.cpp b/neuralnetworks/1.0/utils/src/Execution.cpp
new file mode 100644
index 0000000..7a3216b
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Execution.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Execution.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+        std::shared_ptr<const PreparedModel> preparedModel, Request request,
+        hal::utils::RequestRelocation relocation) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "V1_0::utils::Execution::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+                                             std::move(request), std::move(relocation));
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+                     std::shared_ptr<const PreparedModel> preparedModel, Request request,
+                     hal::utils::RequestRelocation relocation)
+    : kPreparedModel(std::move(preparedModel)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+        const nn::OptionalTimePoint& /*deadline*/) const {
+    return kPreparedModel->executeInternal(kRequest, kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+        const std::vector<nn::SyncFence>& /*waitFor*/, const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IExecution::computeFenced is not supported on 1.0 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
index 858571d..00970c0 100644
--- a/neuralnetworks/1.0/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
@@ -19,6 +19,7 @@
 #include "Burst.h"
 #include "Callbacks.h"
 #include "Conversions.h"
+#include "Execution.h"
 #include "Utils.h"
 
 #include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
@@ -61,22 +62,35 @@
         const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
-    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
 
     const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
 
+    return executeInternal(hidlRequest, relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const V1_0::Request& request,
+                               const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
     const auto cb = sp<ExecutionCallback>::make();
     const auto scoped = kDeathHandler.protectCallback(cb.get());
 
-    const auto ret = kPreparedModel->execute(hidlRequest, cb);
+    const auto ret = kPreparedModel->execute(request, cb);
     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
     HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
 
     auto result = NN_TRY(cb->get());
-    NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+    if (relocation.output) {
+        relocation.output->flush();
+    }
     return result;
 }
 
@@ -91,6 +105,20 @@
            << "IPreparedModel::executeFenced is not supported on 1.0 HAL service";
 }
 
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming /*measure*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto hidlRequest = NN_TRY(convert(requestInShared));
+    return Execution::create(shared_from_this(), std::move(hidlRequest), std::move(relocation));
+}
+
 nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
     return Burst::create(shared_from_this());
 }
diff --git a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
index f19ed77..7820c06 100644
--- a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
@@ -19,6 +19,7 @@
 #include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
@@ -224,6 +225,150 @@
     EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
 }
 
+TEST(PreparedModelTest, reusableExecute) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteLaunchError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecute(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                         V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteReturnError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteCrash) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockPreparedModel->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedNotSupported) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
 TEST(PreparedModelTest, configureExecutionBurst) {
     // setup test
     const auto mockPreparedModel = MockPreparedModel::create();
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 03aed86..f5b6ead 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -3618,7 +3618,7 @@
      *      front of dimension i.
      *      padding[i, 1] specifies the number of elements to be padded after
      *      the end of dimension i.
-     * * 2: An scalar specifying the value to use for padding input0.
+     * * 2: A scalar specifying the value to use for padding input0.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
      *      pad value must be of {@link OperandType::FLOAT16}.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
new file mode 100644
index 0000000..9c66446
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_H
+
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+            std::shared_ptr<const PreparedModel> preparedModel, V1_0::Request request,
+            hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure);
+
+    Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+              V1_0::Request request, hal::utils::RequestRelocation relocation,
+              V1_2::MeasureTiming measure);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const PreparedModel> kPreparedModel;
+    const V1_0::Request kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+    const MeasureTiming kMeasure;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
index 9669d8c0..dae1ff3 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
@@ -28,9 +28,11 @@
 #include <fmq/MessageQueue.h>
 #include <hidl/MQDescriptor.h>
 #include <nnapi/IBurst.h>
+#include <nnapi/IExecution.h>
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
 #include <nnapi/hal/ProtectCallback.h>
 
 #include <atomic>
@@ -51,14 +53,14 @@
  * across FMQ, making it appear to the runtime as a regular synchronous inference. Additionally,
  * this class manages the burst's memory cache.
  */
-class ExecutionBurstController final : public nn::IBurst {
+class ExecutionBurstController final
+    : public nn::IBurst,
+      public std::enable_shared_from_this<ExecutionBurstController> {
     struct PrivateConstructorTag {};
 
   public:
-    using FallbackFunction =
-            std::function<nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>(
-                    const nn::Request&, nn::MeasureTiming, const nn::OptionalTimePoint&,
-                    const nn::OptionalDuration&)>;
+    using FallbackFunction = std::function<
+            nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>()>;
 
     /**
      * NN runtime memory cache.
@@ -154,10 +156,10 @@
      * @return ExecutionBurstController Execution burst controller object.
      */
     static nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> create(
-            const sp<IPreparedModel>& preparedModel, FallbackFunction fallback,
+            nn::SharedPreparedModel preparedModel, const sp<IPreparedModel>& hidlPreparedModel,
             std::chrono::microseconds pollingTimeWindow);
 
-    ExecutionBurstController(PrivateConstructorTag tag, FallbackFunction fallback,
+    ExecutionBurstController(PrivateConstructorTag tag, nn::SharedPreparedModel preparedModel,
                              std::unique_ptr<RequestChannelSender> requestChannelSender,
                              std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
                              sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
@@ -173,9 +175,21 @@
             const nn::OptionalTimePoint& deadline,
             const nn::OptionalDuration& loopTimeoutDuration) const override;
 
+    // See IBurst::createReusableExecution for information on this method.
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    // If fallback is not nullptr, this method will invoke the fallback function to try another
+    // execution path if the packet could not be sent. Otherwise, failing to send the packet will
+    // result in an error.
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const std::vector<FmqRequestDatum>& requestPacket,
+            const hal::utils::RequestRelocation& relocation, FallbackFunction fallback) const;
+
   private:
     mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
-    const FallbackFunction kFallback;
+    const nn::SharedPreparedModel kPreparedModel;
     const std::unique_ptr<RequestChannelSender> mRequestChannelSender;
     const std::unique_ptr<ResultChannelReceiver> mResultChannelReceiver;
     const sp<ExecutionBurstCallback> mBurstCallback;
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
index fb11130..35abd79 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
@@ -58,10 +58,18 @@
             const nn::OptionalDuration& loopTimeoutDuration,
             const nn::OptionalDuration& timeoutDurationAfterFence) const override;
 
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
     nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
 
     std::any getUnderlyingResource() const override;
 
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const V1_0::Request& request, MeasureTiming measure,
+            const hal::utils::RequestRelocation& relocation) const;
+
   private:
     nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeSynchronously(
             const V1_0::Request& request, MeasureTiming measure) const;
diff --git a/neuralnetworks/1.2/utils/src/Execution.cpp b/neuralnetworks/1.2/utils/src/Execution.cpp
new file mode 100644
index 0000000..18d1c90
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Execution.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Execution.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+        std::shared_ptr<const PreparedModel> preparedModel, V1_0::Request request,
+        hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "V1_2::utils::Execution::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+                                             std::move(request), std::move(relocation), measure);
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+                     std::shared_ptr<const PreparedModel> preparedModel, V1_0::Request request,
+                     hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure)
+    : kPreparedModel(std::move(preparedModel)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)),
+      kMeasure(measure) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+        const nn::OptionalTimePoint& /*deadline*/) const {
+    return kPreparedModel->executeInternal(kRequest, kMeasure, kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+        const std::vector<nn::SyncFence>& /*waitFor*/, const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IExecution::computeFenced is not supported on 1.2 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
index 7a17f25..b4b6f68 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
@@ -28,6 +28,7 @@
 #include <nnapi/Types.h>
 #include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
 #include <nnapi/hal/HandleError.h>
 #include <nnapi/hal/ProtectCallback.h>
 #include <nnapi/hal/TransferValue.h>
@@ -50,6 +51,35 @@
 namespace android::hardware::neuralnetworks::V1_2::utils {
 namespace {
 
+class BurstExecution final : public nn::IExecution,
+                             public std::enable_shared_from_this<BurstExecution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const BurstExecution>> create(
+            std::shared_ptr<const ExecutionBurstController> controller,
+            std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
+            std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds);
+
+    BurstExecution(PrivateConstructorTag tag,
+                   std::shared_ptr<const ExecutionBurstController> controller,
+                   std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
+                   std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const ExecutionBurstController> kController;
+    const std::vector<FmqRequestDatum> kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+    const std::vector<ExecutionBurstController::OptionalCacheHold> kCacheHolds;
+};
+
 nn::GeneralResult<sp<IBurstContext>> executionBurstResultCallback(
         V1_0::ErrorStatus status, const sp<IBurstContext>& burstContext) {
     HANDLE_HAL_STATUS(status) << "IPreparedModel::configureExecutionBurst failed with status "
@@ -209,10 +239,10 @@
 // ExecutionBurstController methods
 
 nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> ExecutionBurstController::create(
-        const sp<V1_2::IPreparedModel>& preparedModel, FallbackFunction fallback,
+        nn::SharedPreparedModel preparedModel, const sp<V1_2::IPreparedModel>& hidlPreparedModel,
         std::chrono::microseconds pollingTimeWindow) {
     // check inputs
-    if (preparedModel == nullptr) {
+    if (preparedModel == nullptr || hidlPreparedModel == nullptr) {
         return NN_ERROR() << "ExecutionBurstController::create passed a nullptr";
     }
 
@@ -236,7 +266,7 @@
     auto cb = hal::utils::CallbackValue(executionBurstResultCallback);
 
     // configure burst
-    const Return<void> ret = preparedModel->configureExecutionBurst(
+    const Return<void> ret = hidlPreparedModel->configureExecutionBurst(
             burstCallback, *requestChannelDescriptor, *resultChannelDescriptor, cb);
     HANDLE_TRANSPORT_FAILURE(ret);
 
@@ -250,18 +280,18 @@
 
     // make and return controller
     return std::make_shared<const ExecutionBurstController>(
-            PrivateConstructorTag{}, std::move(fallback), std::move(requestChannelSender),
+            PrivateConstructorTag{}, std::move(preparedModel), std::move(requestChannelSender),
             std::move(resultChannelReceiver), std::move(burstCallback), std::move(burstContext),
             std::move(memoryCache), std::move(deathHandler));
 }
 
 ExecutionBurstController::ExecutionBurstController(
-        PrivateConstructorTag /*tag*/, FallbackFunction fallback,
+        PrivateConstructorTag /*tag*/, nn::SharedPreparedModel preparedModel,
         std::unique_ptr<RequestChannelSender> requestChannelSender,
         std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
         sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
         std::shared_ptr<MemoryCache> memoryCache, neuralnetworks::utils::DeathHandler deathHandler)
-    : kFallback(std::move(fallback)),
+    : kPreparedModel(std::move(preparedModel)),
       mRequestChannelSender(std::move(requestChannelSender)),
       mResultChannelReceiver(std::move(resultChannelReceiver)),
       mBurstCallback(std::move(callback)),
@@ -283,26 +313,98 @@
     // systraces. Note that the first point we can begin collecting systraces in
     // ExecutionBurstServer is when the RequestChannelReceiver realizes there is data in the FMQ, so
     // ExecutionBurstServer collects systraces at different points in the code.
-    NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::execute");
+    NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::execute");
 
     // if the request is valid but of a higher version than what's supported in burst execution,
     // fall back to another execution path
     if (const auto version = NN_TRY(hal::utils::makeExecutionFailure(nn::validate(request)));
         version > nn::Version::ANDROID_Q) {
         // fallback to another execution path if the packet could not be sent
-        if (kFallback) {
-            return kFallback(request, measure, deadline, loopTimeoutDuration);
-        }
-        return NN_ERROR() << "Request object has features not supported by IBurst::execute";
+        return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
     }
 
+    // ensure that request is ready for IPC
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
+
     // clear pools field of request, as they will be provided via slots
-    const auto requestWithoutPools =
-            nn::Request{.inputs = request.inputs, .outputs = request.outputs, .pools = {}};
+    const auto requestWithoutPools = nn::Request{
+            .inputs = requestInShared.inputs, .outputs = requestInShared.outputs, .pools = {}};
     auto hidlRequest = NN_TRY(
             hal::utils::makeExecutionFailure(V1_0::utils::unvalidatedConvert(requestWithoutPools)));
     const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
 
+    std::vector<int32_t> slots;
+    std::vector<OptionalCacheHold> holds;
+    slots.reserve(requestInShared.pools.size());
+    holds.reserve(requestInShared.pools.size());
+    for (const auto& memoryPool : requestInShared.pools) {
+        auto [slot, hold] = mMemoryCache->cacheMemory(std::get<nn::SharedMemory>(memoryPool));
+        slots.push_back(slot);
+        holds.push_back(std::move(hold));
+    }
+
+    // send request packet
+    const auto requestPacket = serialize(hidlRequest, hidlMeasure, slots);
+    const auto fallback = [this, &request, measure, &deadline, &loopTimeoutDuration] {
+        return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
+    };
+    return executeInternal(requestPacket, relocation, fallback);
+}
+
+// See IBurst::createReusableExecution for information on this method.
+nn::GeneralResult<nn::SharedExecution> ExecutionBurstController::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::createReusableExecution");
+
+    // if the request is valid but of a higher version than what's supported in burst execution,
+    // fall back to another execution path
+    if (const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(request)));
+        version > nn::Version::ANDROID_Q) {
+        // fallback to another execution path if the packet could not be sent
+        return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration);
+    }
+
+    // ensure that request is ready for IPC
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    // clear pools field of request, as they will be provided via slots
+    const auto requestWithoutPools = nn::Request{
+            .inputs = requestInShared.inputs, .outputs = requestInShared.outputs, .pools = {}};
+    auto hidlRequest = NN_TRY(V1_0::utils::unvalidatedConvert(requestWithoutPools));
+    const auto hidlMeasure = NN_TRY(convert(measure));
+
+    std::vector<int32_t> slots;
+    std::vector<OptionalCacheHold> holds;
+    slots.reserve(requestInShared.pools.size());
+    holds.reserve(requestInShared.pools.size());
+    for (const auto& memoryPool : requestInShared.pools) {
+        auto [slot, hold] = mMemoryCache->cacheMemory(std::get<nn::SharedMemory>(memoryPool));
+        slots.push_back(slot);
+        holds.push_back(std::move(hold));
+    }
+
+    const auto requestPacket = serialize(hidlRequest, hidlMeasure, slots);
+    return BurstExecution::create(shared_from_this(), std::move(requestPacket),
+                                  std::move(relocation), std::move(holds));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ExecutionBurstController::executeInternal(const std::vector<FmqRequestDatum>& requestPacket,
+                                          const hal::utils::RequestRelocation& relocation,
+                                          FallbackFunction fallback) const {
+    NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
+                 "ExecutionBurstController::executeInternal");
+
     // Ensure that at most one execution is in flight at any given time.
     const bool alreadyInFlight = mExecutionInFlight.test_and_set();
     if (alreadyInFlight) {
@@ -310,22 +412,16 @@
     }
     const auto guard = base::make_scope_guard([this] { mExecutionInFlight.clear(); });
 
-    std::vector<int32_t> slots;
-    std::vector<OptionalCacheHold> holds;
-    slots.reserve(request.pools.size());
-    holds.reserve(request.pools.size());
-    for (const auto& memoryPool : request.pools) {
-        auto [slot, hold] = mMemoryCache->cacheMemory(std::get<nn::SharedMemory>(memoryPool));
-        slots.push_back(slot);
-        holds.push_back(std::move(hold));
+    if (relocation.input) {
+        relocation.input->flush();
     }
 
     // send request packet
-    const auto sendStatus = mRequestChannelSender->send(hidlRequest, hidlMeasure, slots);
+    const auto sendStatus = mRequestChannelSender->sendPacket(requestPacket);
     if (!sendStatus.ok()) {
         // fallback to another execution path if the packet could not be sent
-        if (kFallback) {
-            return kFallback(request, measure, deadline, loopTimeoutDuration);
+        if (fallback) {
+            return fallback();
         }
         return NN_ERROR() << "Error sending FMQ packet: " << sendStatus.error();
     }
@@ -333,7 +429,47 @@
     // get result packet
     const auto [status, outputShapes, timing] =
             NN_TRY(hal::utils::makeExecutionFailure(mResultChannelReceiver->getBlocking()));
+
+    if (relocation.output) {
+        relocation.output->flush();
+    }
     return executionCallback(status, outputShapes, timing);
 }
 
+nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create(
+        std::shared_ptr<const ExecutionBurstController> controller,
+        std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
+        std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds) {
+    if (controller == nullptr) {
+        return NN_ERROR() << "V1_2::utils::BurstExecution::create must have non-null controller";
+    }
+
+    return std::make_shared<const BurstExecution>(PrivateConstructorTag{}, std::move(controller),
+                                                  std::move(request), std::move(relocation),
+                                                  std::move(cacheHolds));
+}
+
+BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/,
+                               std::shared_ptr<const ExecutionBurstController> controller,
+                               std::vector<FmqRequestDatum> request,
+                               hal::utils::RequestRelocation relocation,
+                               std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds)
+    : kController(std::move(controller)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)),
+      kCacheHolds(std::move(cacheHolds)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> BurstExecution::compute(
+        const nn::OptionalTimePoint& /*deadline*/) const {
+    return kController->executeInternal(kRequest, kRelocation, /*fallback=*/nullptr);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+BurstExecution::computeFenced(const std::vector<nn::SyncFence>& /*waitFor*/,
+                              const nn::OptionalTimePoint& /*deadline*/,
+                              const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IExecution::computeFenced is not supported on burst object";
+}
+
 }  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index b209a44..d0ef36e 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -18,6 +18,7 @@
 
 #include "Callbacks.h"
 #include "Conversions.h"
+#include "Execution.h"
 #include "ExecutionBurstController.h"
 #include "ExecutionBurstUtils.h"
 #include "Utils.h"
@@ -93,19 +94,32 @@
         const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
-    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
 
     const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
     const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
 
-    auto result = kExecuteSynchronously ? executeSynchronously(hidlRequest, hidlMeasure)
-                                        : executeAsynchronously(hidlRequest, hidlMeasure);
+    return executeInternal(hidlRequest, hidlMeasure, relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const V1_0::Request& request, MeasureTiming measure,
+                               const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
+    auto result = kExecuteSynchronously ? executeSynchronously(request, measure)
+                                        : executeAsynchronously(request, measure);
     auto [outputShapes, timing] = NN_TRY(std::move(result));
 
-    NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+    if (relocation.output) {
+        relocation.output->flush();
+    }
     return std::make_pair(std::move(outputShapes), timing);
 }
 
@@ -120,6 +134,22 @@
            << "IPreparedModel::executeFenced is not supported on 1.2 HAL service";
 }
 
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto hidlRequest = NN_TRY(convert(requestInShared));
+    auto hidlMeasure = NN_TRY(convert(measure));
+    return Execution::create(shared_from_this(), std::move(hidlRequest), std::move(relocation),
+                             hidlMeasure);
+}
+
 nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
     auto self = shared_from_this();
     auto fallback = [preparedModel = std::move(self)](
@@ -130,7 +160,7 @@
         return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
     };
     const auto pollingTimeWindow = getBurstControllerPollingTimeWindow();
-    return ExecutionBurstController::create(kPreparedModel, std::move(fallback), pollingTimeWindow);
+    return ExecutionBurstController::create(shared_from_this(), kPreparedModel, pollingTimeWindow);
 }
 
 std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
index d297b1a..5e2ad79 100644
--- a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
@@ -21,6 +21,7 @@
 #include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
@@ -334,6 +335,248 @@
     EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
 }
 
+TEST(PreparedModelTest, reusableExecuteSync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(
+                    Invoke(makeExecuteSynchronously(V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecuteSynchronously(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeExecuteAsynchronously(
+                    V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncLaunchError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                                       V1_0::ErrorStatus::GENERAL_FAILURE, {},
+                                                       kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncReturnError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(
+                    V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncCrash) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockPreparedModel->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedNotSupported) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
 TEST(PreparedModelTest, configureExecutionBurst) {
     // setup test
     const auto mockPreparedModel = MockPreparedModel::create();
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index a5dbd5e..a26b858 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -3834,7 +3834,7 @@
      *      front of dimension i.
      *      padding[i, 1] specifies the number of elements to be padded after
      *      the end of dimension i.
-     * * 2: An scalar specifying the value to use for padding input0.
+     * * 2: A scalar specifying the value to use for padding input0.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
      *      pad value must be of {@link OperandType::FLOAT16}.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Execution.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Execution.h
new file mode 100644
index 0000000..06c33d4
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Execution.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_EXECUTION_H
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+            std::shared_ptr<const PreparedModel> preparedModel, Request request,
+            hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure,
+            OptionalTimeoutDuration loopTimeoutDuration);
+
+    Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+              Request request, hal::utils::RequestRelocation relocation,
+              V1_2::MeasureTiming measure, OptionalTimeoutDuration loopTimeoutDuration);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const PreparedModel> kPreparedModel;
+    const Request kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+    const V1_2::MeasureTiming kMeasure;
+    const OptionalTimeoutDuration kLoopTimeoutDuration;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_EXECUTION_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
index 690fecc..5acba71 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
@@ -57,10 +57,26 @@
             const nn::OptionalDuration& loopTimeoutDuration,
             const nn::OptionalDuration& timeoutDurationAfterFence) const override;
 
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
     nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
 
     std::any getUnderlyingResource() const override;
 
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+            const OptionalTimeoutDuration& loopTimeoutDuration,
+            const hal::utils::RequestRelocation& relocation) const;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+    executeFencedInternal(const Request& request, const hidl_vec<hidl_handle>& waitFor,
+                          V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+                          const OptionalTimeoutDuration& loopTimeoutDuration,
+                          const OptionalTimeoutDuration& timeoutDurationAfterFence,
+                          const hal::utils::RequestRelocation& relocation) const;
+
   private:
     nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeSynchronously(
             const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
diff --git a/neuralnetworks/1.3/utils/src/Execution.cpp b/neuralnetworks/1.3/utils/src/Execution.cpp
new file mode 100644
index 0000000..3d17cc3
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Execution.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Execution.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+        std::shared_ptr<const PreparedModel> preparedModel, Request request,
+        hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure,
+        OptionalTimeoutDuration loopTimeoutDuration) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "V1_3::utils::Execution::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+                                             std::move(request), std::move(relocation), measure,
+                                             std::move(loopTimeoutDuration));
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+                     std::shared_ptr<const PreparedModel> preparedModel, Request request,
+                     hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure,
+                     OptionalTimeoutDuration loopTimeoutDuration)
+    : kPreparedModel(std::move(preparedModel)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)),
+      kMeasure(measure),
+      kLoopTimeoutDuration(std::move(loopTimeoutDuration)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+        const nn::OptionalTimePoint& deadline) const {
+    const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    return kPreparedModel->executeInternal(kRequest, kMeasure, hidlDeadline, kLoopTimeoutDuration,
+                                           kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+        const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& timeoutDurationAfterFence) const {
+    const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
+    const auto hidlDeadline = NN_TRY(convert(deadline));
+    const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+    return kPreparedModel->executeFencedInternal(kRequest, hidlWaitFor, kMeasure, hidlDeadline,
+                                                 kLoopTimeoutDuration,
+                                                 hidlTimeoutDurationAfterFence, kRelocation);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index fd7f8f2..1623de5 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -18,6 +18,7 @@
 
 #include "Callbacks.h"
 #include "Conversions.h"
+#include "Execution.h"
 #include "Utils.h"
 
 #include <android/hardware/neuralnetworks/1.0/types.h>
@@ -139,8 +140,11 @@
         const nn::OptionalDuration& loopTimeoutDuration) const {
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
-    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
 
     const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
     const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
@@ -148,16 +152,27 @@
     const auto hidlLoopTimeoutDuration =
             NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
 
+    return executeInternal(hidlRequest, hidlMeasure, hidlDeadline, hidlLoopTimeoutDuration,
+                           relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const Request& request, V1_2::MeasureTiming measure,
+                               const OptionalTimePoint& deadline,
+                               const OptionalTimeoutDuration& loopTimeoutDuration,
+                               const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
     auto result = kExecuteSynchronously
-                          ? executeSynchronously(hidlRequest, hidlMeasure, hidlDeadline,
-                                                 hidlLoopTimeoutDuration)
-                          : executeAsynchronously(hidlRequest, hidlMeasure, hidlDeadline,
-                                                  hidlLoopTimeoutDuration);
+                          ? executeSynchronously(request, measure, deadline, loopTimeoutDuration)
+                          : executeAsynchronously(request, measure, deadline, loopTimeoutDuration);
     auto [outputShapes, timing] = NN_TRY(std::move(result));
 
-    NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+    if (relocation.output) {
+        relocation.output->flush();
+    }
     return std::make_pair(std::move(outputShapes), timing);
 }
 
@@ -168,8 +183,10 @@
                              const nn::OptionalDuration& timeoutDurationAfterFence) const {
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
-    const nn::Request& requestInShared =
-            NN_TRY(hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared));
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
 
     const auto hidlRequest = NN_TRY(convert(requestInShared));
     const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
@@ -178,27 +195,59 @@
     const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
     const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
 
+    return executeFencedInternal(hidlRequest, hidlWaitFor, hidlMeasure, hidlDeadline,
+                                 hidlLoopTimeoutDuration, hidlTimeoutDurationAfterFence,
+                                 relocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFencedInternal(const Request& request, const hidl_vec<hidl_handle>& waitFor,
+                                     V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+                                     const OptionalTimeoutDuration& loopTimeoutDuration,
+                                     const OptionalTimeoutDuration& timeoutDurationAfterFence,
+                                     const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
     auto cb = hal::utils::CallbackValue(fencedExecutionCallback);
 
-    const auto ret = kPreparedModel->executeFenced(hidlRequest, hidlWaitFor, hidlMeasure,
-                                                   hidlDeadline, hidlLoopTimeoutDuration,
-                                                   hidlTimeoutDurationAfterFence, cb);
+    const auto ret =
+            kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
+                                          timeoutDurationAfterFence, cb);
     HANDLE_TRANSPORT_FAILURE(ret);
     auto [syncFence, callback] = NN_TRY(cb.take());
 
     // If executeFenced required the request memory to be moved into shared memory, block here until
     // the fenced execution has completed and flush the memory back.
-    if (maybeRequestInShared.has_value()) {
+    if (relocation.output) {
         const auto state = syncFence.syncWait({});
         if (state != nn::SyncFence::FenceState::SIGNALED) {
             return NN_ERROR() << "syncWait failed with " << state;
         }
-        NN_TRY(hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared));
+        relocation.output->flush();
     }
 
     return std::make_pair(std::move(syncFence), std::move(callback));
 }
 
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto hidlRequest = NN_TRY(convert(requestInShared));
+    auto hidlMeasure = NN_TRY(convert(measure));
+    auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+    return Execution::create(shared_from_this(), std::move(hidlRequest), std::move(relocation),
+                             hidlMeasure, std::move(hidlLoopTimeoutDuration));
+}
+
 nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
     auto self = shared_from_this();
     auto fallback = [preparedModel = std::move(self)](
@@ -209,7 +258,7 @@
         return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
     };
     const auto pollingTimeWindow = V1_2::utils::getBurstControllerPollingTimeWindow();
-    return V1_2::utils::ExecutionBurstController::create(kPreparedModel, std::move(fallback),
+    return V1_2::utils::ExecutionBurstController::create(shared_from_this(), kPreparedModel,
                                                          pollingTimeWindow);
 }
 
diff --git a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
index 5303c2a..6dbbd6b 100644
--- a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
@@ -22,6 +22,7 @@
 #include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
@@ -462,6 +463,363 @@
     EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
 }
 
+TEST(PreparedModelTest, reusableExecuteSync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(
+                    Invoke(makeExecuteSynchronously(V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecuteSynchronously(V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeExecuteAsynchronously(
+                    V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncLaunchError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(V1_3::ErrorStatus::GENERAL_FAILURE,
+                                                       V1_3::ErrorStatus::GENERAL_FAILURE, {},
+                                                       kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncReturnError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(
+                    V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncCrash) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_3::ErrorStatus> {
+        mockPreparedModel->simulateCrash();
+        return V1_3::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(ret));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFenced) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::NONE,
+                                                                   kNoTiming, kNoTiming)));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(
+                    Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+        ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+        const auto& [syncFence, callback] = computeResult.value();
+        EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
+        ASSERT_NE(callback, nullptr);
+
+        // get results from callback
+        const auto callbackResult = callback();
+        ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code
+                                                << ": " << callbackResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedCallbackError) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::GENERAL_FAILURE,
+                                                             kNoTiming, kNoTiming)));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code << ": "
+                                           << computeResult.error().message;
+    const auto& [syncFence, callback] = computeResult.value();
+    EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
+    ASSERT_NE(callback, nullptr);
+
+    // verify callback failure
+    const auto callbackResult = callback();
+    ASSERT_FALSE(callbackResult.has_value());
+    EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecuteFencedReturn(V1_3::ErrorStatus::GENERAL_FAILURE, {}, nullptr)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
 TEST(PreparedModelTest, configureExecutionBurst) {
     // setup test
     const auto mockPreparedModel = MockPreparedModel::create();
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
index 5a9f4ff..1d9bdd8 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
@@ -34,11 +34,11 @@
 package android.hardware.neuralnetworks;
 @VintfStability
 parcelable Operand {
-  android.hardware.neuralnetworks.OperandType type;
+  android.hardware.neuralnetworks.OperandType type = android.hardware.neuralnetworks.OperandType.FLOAT32;
   int[] dimensions;
   float scale;
   int zeroPoint;
-  android.hardware.neuralnetworks.OperandLifeTime lifetime;
+  android.hardware.neuralnetworks.OperandLifeTime lifetime = android.hardware.neuralnetworks.OperandLifeTime.TEMPORARY_VARIABLE;
   android.hardware.neuralnetworks.DataLocation location;
   @nullable android.hardware.neuralnetworks.OperandExtraParams extraParams;
 }
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
index de93d8b..ebb361b 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
@@ -34,6 +34,6 @@
 package android.hardware.neuralnetworks;
 @VintfStability
 parcelable OperandPerformance {
-  android.hardware.neuralnetworks.OperandType type;
+  android.hardware.neuralnetworks.OperandType type = android.hardware.neuralnetworks.OperandType.FLOAT32;
   android.hardware.neuralnetworks.PerformanceInfo info;
 }
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
index 33fcd60..a4a3fbe 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
@@ -34,7 +34,7 @@
 package android.hardware.neuralnetworks;
 @VintfStability
 parcelable Operation {
-  android.hardware.neuralnetworks.OperationType type;
+  android.hardware.neuralnetworks.OperationType type = android.hardware.neuralnetworks.OperationType.ADD;
   int[] inputs;
   int[] outputs;
 }
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
index 4d2260f..998e06d 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
@@ -33,7 +33,7 @@
      * {@link IDevice::OPERAND_TYPE_BASE_MAX} is possible and should be interpreted as an extension
      * type according to {@link Model::extensionNameToPrefix}.
      */
-    OperandType type;
+    OperandType type = OperandType.FLOAT32;
     /**
      * Dimensions of the operand.
      *
@@ -86,7 +86,7 @@
     /**
      * How the operand is used.
      */
-    OperandLifeTime lifetime;
+    OperandLifeTime lifetime = OperandLifeTime.TEMPORARY_VARIABLE;
     /**
      * Where to find the data for this operand.
      * If the lifetime is TEMPORARY_VARIABLE, SUBGRAPH_INPUT, SUBGRAPH_OUTPUT, or NO_VALUE:
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
index 7fd86f9..7f53967 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
@@ -25,6 +25,6 @@
  */
 @VintfStability
 parcelable OperandPerformance {
-    OperandType type;
+    OperandType type = OperandType.FLOAT32;
     PerformanceInfo info;
 }
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
index 0c6032f..366d9a4 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
@@ -30,7 +30,7 @@
      * {@link IDevice::OPERATION_TYPE_BASE_MAX} is possible and should be interpreted as an
      * extension type according to {@link Model::extensionNameToPrefix}.
      */
-    OperationType type;
+    OperationType type = OperationType.ADD;
     /**
      * Describes the table that contains the indexes of the inputs of the operation. The offset is
      * the index in the operandIndexes table.
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
index 3f49154..e7fb90d 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
@@ -3693,7 +3693,7 @@
      *      front of dimension i.
      *      padding[i, 1] specifies the number of elements to be padded after
      *      the end of dimension i.
-     * * 2: An scalar specifying the value to use for padding input0.
+     * * 2: A scalar specifying the value to use for padding input0.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
      *      pad value must be of {@link OperandType::FLOAT16}.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
index 008e4e4..0cc78d4 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
@@ -38,7 +38,7 @@
 namespace aidl::android::hardware::neuralnetworks::utils {
 
 // Class that adapts aidl_hal::IBurst to nn::IBurst.
-class Burst final : public nn::IBurst {
+class Burst final : public nn::IBurst, public std::enable_shared_from_this<Burst> {
     struct PrivateConstructorTag {};
 
   public:
@@ -100,6 +100,16 @@
             const nn::OptionalTimePoint& deadline,
             const nn::OptionalDuration& loopTimeoutDuration) const override;
 
+    // See IBurst::createReusableExecution for information.
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const aidl_hal::Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
+            bool measure, int64_t deadline, int64_t loopTimeoutDuration,
+            const hal::utils::RequestRelocation& relocation) const;
+
   private:
     mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
     const std::shared_ptr<aidl_hal::IBurst> kBurst;
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Execution.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Execution.h
new file mode 100644
index 0000000..a77ea98
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Execution.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+            std::shared_ptr<const PreparedModel> preparedModel, Request request,
+            hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration);
+
+    Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+              Request request, hal::utils::RequestRelocation relocation, bool measure,
+              int64_t loopTimeoutDuration);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const PreparedModel> kPreparedModel;
+    const Request kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+    const bool kMeasure;
+    const int64_t kLoopTimeoutDuration;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
index abce6cc..4035764 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
@@ -18,6 +18,7 @@
 #define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PREPARED_MODEL_H
 
 #include <aidl/android/hardware/neuralnetworks/IPreparedModel.h>
+#include <aidl/android/hardware/neuralnetworks/Request.h>
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
@@ -34,7 +35,8 @@
 namespace aidl::android::hardware::neuralnetworks::utils {
 
 // Class that adapts aidl_hal::IPreparedModel to nn::IPreparedModel.
-class PreparedModel final : public nn::IPreparedModel {
+class PreparedModel final : public nn::IPreparedModel,
+                            public std::enable_shared_from_this<PreparedModel> {
     struct PrivateConstructorTag {};
 
   public:
@@ -55,10 +57,25 @@
             const nn::OptionalDuration& loopTimeoutDuration,
             const nn::OptionalDuration& timeoutDurationAfterFence) const override;
 
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
     nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
 
     std::any getUnderlyingResource() const override;
 
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const Request& request, bool measure, int64_t deadline, int64_t loopTimeoutDuration,
+            const hal::utils::RequestRelocation& relocation) const;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+    executeFencedInternal(const Request& request,
+                          const std::vector<ndk::ScopedFileDescriptor>& waitFor, bool measure,
+                          int64_t deadline, int64_t loopTimeoutDuration,
+                          int64_t timeoutDurationAfterFence,
+                          const hal::utils::RequestRelocation& relocation) const;
+
   private:
     const std::shared_ptr<aidl_hal::IPreparedModel> kPreparedModel;
 };
diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp
index 0b475bc..87cd0e4 100644
--- a/neuralnetworks/aidl/utils/src/Burst.cpp
+++ b/neuralnetworks/aidl/utils/src/Burst.cpp
@@ -22,6 +22,7 @@
 #include <android-base/logging.h>
 #include <android/binder_auto_utils.h>
 #include <nnapi/IBurst.h>
+#include <nnapi/IExecution.h>
 #include <nnapi/Result.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
@@ -35,6 +36,39 @@
 namespace aidl::android::hardware::neuralnetworks::utils {
 namespace {
 
+class BurstExecution final : public nn::IExecution,
+                             public std::enable_shared_from_this<BurstExecution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const BurstExecution>> create(
+            std::shared_ptr<const Burst> burst, Request request,
+            std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration,
+            hal::utils::RequestRelocation relocation,
+            std::vector<Burst::OptionalCacheHold> cacheHolds);
+
+    BurstExecution(PrivateConstructorTag tag, std::shared_ptr<const Burst> burst, Request request,
+                   std::vector<int64_t> memoryIdentifierTokens, bool measure,
+                   int64_t loopTimeoutDuration, hal::utils::RequestRelocation relocation,
+                   std::vector<Burst::OptionalCacheHold> cacheHolds);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const Burst> kBurst;
+    const Request kRequest;
+    const std::vector<int64_t>& kMemoryIdentifierTokens;
+    const bool kMeasure;
+    const int64_t kLoopTimeoutDuration;
+    const hal::utils::RequestRelocation kRelocation;
+    const std::vector<Burst::OptionalCacheHold> kCacheHolds;
+};
+
 nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
         const std::vector<OutputShape>& outputShapes, const Timing& timing) {
     return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
@@ -139,17 +173,13 @@
         const nn::Request& request, nn::MeasureTiming measure,
         const nn::OptionalTimePoint& deadline,
         const nn::OptionalDuration& loopTimeoutDuration) const {
-    // Ensure that at most one execution is in flight at any given time.
-    const bool alreadyInFlight = mExecutionInFlight.test_and_set();
-    if (alreadyInFlight) {
-        return NN_ERROR() << "IBurst already has an execution in flight";
-    }
-    const auto guard = ::android::base::make_scope_guard([this] { mExecutionInFlight.clear(); });
-
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
-    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
 
     const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
     const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
@@ -159,9 +189,9 @@
 
     std::vector<int64_t> memoryIdentifierTokens;
     std::vector<OptionalCacheHold> holds;
-    memoryIdentifierTokens.reserve(request.pools.size());
-    holds.reserve(request.pools.size());
-    for (const auto& memoryPool : request.pools) {
+    memoryIdentifierTokens.reserve(requestInShared.pools.size());
+    holds.reserve(requestInShared.pools.size());
+    for (const auto& memoryPool : requestInShared.pools) {
         if (const auto* memory = std::get_if<nn::SharedMemory>(&memoryPool)) {
             if (auto cached = kMemoryCache->getMemoryIfAvailable(*memory)) {
                 auto& [identifier, hold] = *cached;
@@ -172,12 +202,30 @@
         }
         memoryIdentifierTokens.push_back(-1);
     }
-    CHECK_EQ(request.pools.size(), memoryIdentifierTokens.size());
+    CHECK_EQ(requestInShared.pools.size(), memoryIdentifierTokens.size());
+
+    return executeInternal(aidlRequest, memoryIdentifierTokens, aidlMeasure, aidlDeadline,
+                           aidlLoopTimeoutDuration, relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::executeInternal(
+        const Request& request, const std::vector<int64_t>& memoryIdentifierTokens, bool measure,
+        int64_t deadline, int64_t loopTimeoutDuration,
+        const hal::utils::RequestRelocation& relocation) const {
+    // Ensure that at most one execution is in flight at any given time.
+    const bool alreadyInFlight = mExecutionInFlight.test_and_set();
+    if (alreadyInFlight) {
+        return NN_ERROR() << "IBurst already has an execution in flight";
+    }
+    const auto guard = ::android::base::make_scope_guard([this] { mExecutionInFlight.clear(); });
+
+    if (relocation.input) {
+        relocation.input->flush();
+    }
 
     ExecutionResult executionResult;
-    const auto ret =
-            kBurst->executeSynchronously(aidlRequest, memoryIdentifierTokens, aidlMeasure,
-                                         aidlDeadline, aidlLoopTimeoutDuration, &executionResult);
+    const auto ret = kBurst->executeSynchronously(request, memoryIdentifierTokens, measure,
+                                                  deadline, loopTimeoutDuration, &executionResult);
     HANDLE_ASTATUS(ret) << "execute failed";
     if (!executionResult.outputSufficientSize) {
         auto canonicalOutputShapes =
@@ -188,10 +236,89 @@
     auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
             convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
 
-    NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+    if (relocation.output) {
+        relocation.output->flush();
+    }
     return std::make_pair(std::move(outputShapes), timing);
 }
 
+nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto aidlRequest = NN_TRY(convert(requestInShared));
+    const auto aidlMeasure = NN_TRY(convert(measure));
+    const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+
+    std::vector<int64_t> memoryIdentifierTokens;
+    std::vector<OptionalCacheHold> holds;
+    memoryIdentifierTokens.reserve(requestInShared.pools.size());
+    holds.reserve(requestInShared.pools.size());
+    for (const auto& memoryPool : requestInShared.pools) {
+        if (const auto* memory = std::get_if<nn::SharedMemory>(&memoryPool)) {
+            if (auto cached = kMemoryCache->getMemoryIfAvailable(*memory)) {
+                auto& [identifier, hold] = *cached;
+                memoryIdentifierTokens.push_back(identifier);
+                holds.push_back(std::move(hold));
+                continue;
+            }
+        }
+        memoryIdentifierTokens.push_back(-1);
+    }
+    CHECK_EQ(requestInShared.pools.size(), memoryIdentifierTokens.size());
+
+    return BurstExecution::create(shared_from_this(), std::move(aidlRequest),
+                                  std::move(memoryIdentifierTokens), aidlMeasure,
+                                  aidlLoopTimeoutDuration, std::move(relocation), std::move(holds));
+}
+
+nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create(
+        std::shared_ptr<const Burst> burst, Request request,
+        std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration,
+        hal::utils::RequestRelocation relocation,
+        std::vector<Burst::OptionalCacheHold> cacheHolds) {
+    if (burst == nullptr) {
+        return NN_ERROR() << "aidl::utils::BurstExecution::create must have non-null burst";
+    }
+
+    return std::make_shared<const BurstExecution>(
+            PrivateConstructorTag{}, std::move(burst), std::move(request),
+            std::move(memoryIdentifierTokens), measure, loopTimeoutDuration, std::move(relocation),
+            std::move(cacheHolds));
+}
+
+BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/, std::shared_ptr<const Burst> burst,
+                               Request request, std::vector<int64_t> memoryIdentifierTokens,
+                               bool measure, int64_t loopTimeoutDuration,
+                               hal::utils::RequestRelocation relocation,
+                               std::vector<Burst::OptionalCacheHold> cacheHolds)
+    : kBurst(std::move(burst)),
+      kRequest(std::move(request)),
+      kMemoryIdentifierTokens(std::move(memoryIdentifierTokens)),
+      kMeasure(measure),
+      kLoopTimeoutDuration(loopTimeoutDuration),
+      kRelocation(std::move(relocation)),
+      kCacheHolds(std::move(cacheHolds)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> BurstExecution::compute(
+        const nn::OptionalTimePoint& deadline) const {
+    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    return kBurst->executeInternal(kRequest, kMemoryIdentifierTokens, kMeasure, aidlDeadline,
+                                   kLoopTimeoutDuration, kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+BurstExecution::computeFenced(const std::vector<nn::SyncFence>& /*waitFor*/,
+                              const nn::OptionalTimePoint& /*deadline*/,
+                              const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IExecution::computeFenced is not supported on burst object";
+}
+
 }  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Execution.cpp b/neuralnetworks/aidl/utils/src/Execution.cpp
new file mode 100644
index 0000000..2aee8a6
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Execution.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Execution.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <aidl/android/hardware/neuralnetworks/Request.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+        std::shared_ptr<const PreparedModel> preparedModel, Request request,
+        hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "aidl::utils::Execution::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+                                             std::move(request), std::move(relocation), measure,
+                                             loopTimeoutDuration);
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+                     std::shared_ptr<const PreparedModel> preparedModel, Request request,
+                     hal::utils::RequestRelocation relocation, bool measure,
+                     int64_t loopTimeoutDuration)
+    : kPreparedModel(std::move(preparedModel)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)),
+      kMeasure(measure),
+      kLoopTimeoutDuration(loopTimeoutDuration) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+        const nn::OptionalTimePoint& deadline) const {
+    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    return kPreparedModel->executeInternal(kRequest, kMeasure, aidlDeadline, kLoopTimeoutDuration,
+                                           kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+        const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& timeoutDurationAfterFence) const {
+    const auto aidlWaitFor = NN_TRY(convert(waitFor));
+    const auto aidlDeadline = NN_TRY(convert(deadline));
+    const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+    return kPreparedModel->executeFencedInternal(kRequest, aidlWaitFor, kMeasure, aidlDeadline,
+                                                 kLoopTimeoutDuration,
+                                                 aidlTimeoutDurationAfterFence, kRelocation);
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
index 003965b..18e7636 100644
--- a/neuralnetworks/aidl/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
@@ -19,8 +19,11 @@
 #include "Burst.h"
 #include "Callbacks.h"
 #include "Conversions.h"
+#include "Execution.h"
+#include "ProtectCallback.h"
 #include "Utils.h"
 
+#include <aidl/android/hardware/neuralnetworks/Request.h>
 #include <android/binder_auto_utils.h>
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
@@ -74,18 +77,32 @@
         const nn::OptionalDuration& loopTimeoutDuration) const {
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
-    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
 
     const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
     const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
     const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
     const auto aidlLoopTimeoutDuration =
             NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+    return executeInternal(aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration,
+                           relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const Request& request, bool measure, int64_t deadline,
+                               int64_t loopTimeoutDuration,
+                               const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
 
     ExecutionResult executionResult;
-    const auto ret = kPreparedModel->executeSynchronously(
-            aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration, &executionResult);
+    const auto ret = kPreparedModel->executeSynchronously(request, measure, deadline,
+                                                          loopTimeoutDuration, &executionResult);
     HANDLE_ASTATUS(ret) << "executeSynchronously failed";
     if (!executionResult.outputSufficientSize) {
         auto canonicalOutputShapes =
@@ -96,9 +113,9 @@
     auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
             convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
 
-    NN_TRY(hal::utils::makeExecutionFailure(
-            hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
-
+    if (relocation.output) {
+        relocation.output->flush();
+    }
     return std::make_pair(std::move(outputShapes), timing);
 }
 
@@ -109,8 +126,10 @@
                              const nn::OptionalDuration& timeoutDurationAfterFence) const {
     // Ensure that request is ready for IPC.
     std::optional<nn::Request> maybeRequestInShared;
-    const nn::Request& requestInShared =
-            NN_TRY(hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared));
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+            &maybeRequestInShared, &relocation));
 
     const auto aidlRequest = NN_TRY(convert(requestInShared));
     const auto aidlWaitFor = NN_TRY(convert(waitFor));
@@ -118,11 +137,25 @@
     const auto aidlDeadline = NN_TRY(convert(deadline));
     const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
     const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+    return executeFencedInternal(aidlRequest, aidlWaitFor, aidlMeasure, aidlDeadline,
+                                 aidlLoopTimeoutDuration, aidlTimeoutDurationAfterFence,
+                                 relocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFencedInternal(const Request& request,
+                                     const std::vector<ndk::ScopedFileDescriptor>& waitFor,
+                                     bool measure, int64_t deadline, int64_t loopTimeoutDuration,
+                                     int64_t timeoutDurationAfterFence,
+                                     const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
 
     FencedExecutionResult result;
-    const auto ret = kPreparedModel->executeFenced(aidlRequest, aidlWaitFor, aidlMeasure,
-                                                   aidlDeadline, aidlLoopTimeoutDuration,
-                                                   aidlTimeoutDurationAfterFence, &result);
+    const auto ret =
+            kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
+                                          timeoutDurationAfterFence, &result);
     HANDLE_ASTATUS(ret) << "executeFenced failed";
 
     auto resultSyncFence = nn::SyncFence::createAsSignaled();
@@ -137,12 +170,12 @@
 
     // If executeFenced required the request memory to be moved into shared memory, block here until
     // the fenced execution has completed and flush the memory back.
-    if (maybeRequestInShared.has_value()) {
+    if (relocation.output) {
         const auto state = resultSyncFence.syncWait({});
         if (state != nn::SyncFence::FenceState::SIGNALED) {
             return NN_ERROR() << "syncWait failed with " << state;
         }
-        NN_TRY(hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared));
+        relocation.output->flush();
     }
 
     // Create callback which can be used to retrieve the execution error status and timings.
@@ -159,6 +192,23 @@
     return std::make_pair(std::move(resultSyncFence), std::move(resultCallback));
 }
 
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto aidlRequest = NN_TRY(convert(requestInShared));
+    auto aidlMeasure = NN_TRY(convert(measure));
+    auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+    return Execution::create(shared_from_this(), std::move(aidlRequest), std::move(relocation),
+                             aidlMeasure, aidlLoopTimeoutDuration);
+}
+
 nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
     std::shared_ptr<IBurst> burst;
     const auto ret = kPreparedModel->configureExecutionBurst(&burst);
diff --git a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
index ff98a7d..8bb5c90 100644
--- a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
@@ -21,6 +21,7 @@
 #include <aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
 #include <nnapi/IPreparedModel.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
@@ -253,6 +254,225 @@
     EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
 }
 
+TEST(PreparedModelTest, reusableExecuteSync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto mockExecutionResult = ExecutionResult{
+            .outputSufficientSize = true,
+            .outputShapes = {},
+            .timing = kNoTiming,
+    };
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(
+                    DoAll(SetArgPointee<4>(mockExecutionResult), InvokeWithoutArgs(makeStatusOk)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncError) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeGeneralFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFenced) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+                                  SetArgPointee<2>(ErrorStatus::NONE), Invoke(makeStatusOk)));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeFencedExecutionResult(mockCallback)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+        ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+        const auto& [syncFence, callback] = computeResult.value();
+        EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
+        ASSERT_NE(callback, nullptr);
+
+        // get results from callback
+        const auto callbackResult = callback();
+        ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code
+                                                << ": " << callbackResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedCallbackError) {
+    // setup call
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+                                   SetArgPointee<2>(ErrorStatus::GENERAL_FAILURE),
+                                   Invoke(makeStatusOk))));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeFencedExecutionResult(mockCallback)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code << ": "
+                                           << computeResult.error().message;
+    const auto& [syncFence, callback] = computeResult.value();
+    EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
+    ASSERT_NE(callback, nullptr);
+
+    // verify callback failure
+    const auto callbackResult = callback();
+    ASSERT_FALSE(callbackResult.has_value());
+    EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedError) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedDeadObject) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
 TEST(PreparedModelTest, configureExecutionBurst) {
     // setup test
     const auto mockPreparedModel = MockPreparedModel::create();
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index 8fe6b90..702ee92 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -20,6 +20,7 @@
 #include <cutils/native_handle.h>
 #include <hidl/HidlSupport.h>
 #include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
 #include <nnapi/Types.h>
 #include <functional>
 #include <vector>
@@ -59,19 +60,70 @@
 nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
         const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut);
 
+// Record a relocation mapping between pointer-based data and shared memory.
+// Only two specializations of this template may exist:
+// - RelocationInfo<const void*> for request inputs
+// - RelocationInfo<void*> for request outputs
+template <typename PointerType>
+struct RelocationInfo {
+    PointerType data;
+    size_t length;
+    size_t offset;
+};
+using InputRelocationInfo = RelocationInfo<const void*>;
+using OutputRelocationInfo = RelocationInfo<void*>;
+
+// Keep track of the relocation mapping between pointer-based data and shared memory pool,
+// and provide method to copy the data between pointers and the shared memory pool.
+// Only two specializations of this template may exist:
+// - RelocationTracker<InputRelocationInfo> for request inputs
+// - RelocationTracker<OutputRelocationInfo> for request outputs
+template <typename RelocationInfoType>
+class RelocationTracker {
+  public:
+    static nn::GeneralResult<std::unique_ptr<RelocationTracker>> create(
+            std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory) {
+        auto mapping = NN_TRY(map(memory));
+        return std::make_unique<RelocationTracker<RelocationInfoType>>(
+                std::move(relocationInfos), std::move(memory), std::move(mapping));
+    }
+
+    RelocationTracker(std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory,
+                      nn::Mapping mapping)
+        : kRelocationInfos(std::move(relocationInfos)),
+          kMemory(std::move(memory)),
+          kMapping(std::move(mapping)) {}
+
+    // Specializations defined in CommonUtils.cpp.
+    // For InputRelocationTracker, this method will copy pointer data to the shared memory pool.
+    // For OutputRelocationTracker, this method will copy shared memory data to the pointers.
+    void flush() const;
+
+  private:
+    const std::vector<RelocationInfoType> kRelocationInfos;
+    const nn::SharedMemory kMemory;
+    const nn::Mapping kMapping;
+};
+using InputRelocationTracker = RelocationTracker<InputRelocationInfo>;
+using OutputRelocationTracker = RelocationTracker<OutputRelocationInfo>;
+
+struct RequestRelocation {
+    std::unique_ptr<InputRelocationTracker> input;
+    std::unique_ptr<OutputRelocationTracker> output;
+};
+
 // Relocate pointer-based data to shared memory. If `request` has no
 // Request::Argument::LifeTime::POINTER data, the function returns with a reference to `request`. If
 // `request` has Request::Argument::LifeTime::POINTER data, the request is copied to
 // `maybeRequestInSharedOut` with the POINTER data relocated to a memory pool, and the function
-// returns with a reference to `*maybeRequestInSharedOut`.
-nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
-        const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut);
-
-// Undoes `flushDataFromPointerToShared` on a Request object. More specifically,
-// `unflushDataFromSharedToPointer` copies the output shared memory data from the transformed
-// Request object back to the output pointer-based memory in the original Request object.
-nn::GeneralResult<void> unflushDataFromSharedToPointer(
-        const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared);
+// returns with a reference to `*maybeRequestInSharedOut`. The `relocationOut` will be set to track
+// the input and output relocations.
+//
+// Unlike `flushDataFromPointerToShared`, this method will not copy the input pointer data to the
+// shared memory pool. Use `relocationOut` to flush the input or output data after the call.
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
+        const nn::Request* request, uint32_t alignment, uint32_t padding,
+        std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut);
 
 nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
         size_t numberOfOperands, const std::vector<nn::Operation>& operations);
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
index 17b3fd9..e86edda 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
@@ -35,6 +35,10 @@
             const nn::Request& request, nn::MeasureTiming measure,
             const nn::OptionalTimePoint& deadline,
             const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
 };
 
 }  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidExecution.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidExecution.h
new file mode 100644
index 0000000..5b00221
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidExecution.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_EXECUTION_H
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidExecution final : public nn::IExecution {
+  public:
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_EXECUTION_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
index 3e1dca7..de30aae 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
@@ -40,6 +40,10 @@
             const nn::OptionalDuration& loopTimeoutDuration,
             const nn::OptionalDuration& timeoutDurationAfterFence) const override;
 
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
     nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
 
     std::any getUnderlyingResource() const override;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
index c92cc41..fde2486 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
@@ -51,7 +51,16 @@
             const nn::OptionalTimePoint& deadline,
             const nn::OptionalDuration& loopTimeoutDuration) const override;
 
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
   private:
+    bool isValidInternal() const EXCLUDES(mMutex);
+    nn::GeneralResult<nn::SharedExecution> createReusableExecutionInternal(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const;
+
     const Factory kMakeBurst;
     mutable std::mutex mMutex;
     mutable nn::SharedBurst mBurst GUARDED_BY(mMutex);
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientExecution.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientExecution.h
new file mode 100644
index 0000000..d0084e8
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientExecution.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_EXECUTION_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientExecution final : public nn::IExecution,
+                                 public std::enable_shared_from_this<ResilientExecution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedExecution>()>;
+
+    static nn::GeneralResult<std::shared_ptr<const ResilientExecution>> create(
+            Factory makeExecution);
+
+    ResilientExecution(PrivateConstructorTag tag, Factory makeExecution,
+                       nn::SharedExecution execution);
+
+    nn::SharedExecution getExecution() const;
+    nn::GeneralResult<nn::SharedExecution> recover(const nn::IExecution* failingExecution) const;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    bool isValidInternal() const EXCLUDES(mMutex);
+
+    const Factory kMakeExecution;
+    mutable std::mutex mMutex;
+    mutable nn::SharedExecution mExecution GUARDED_BY(mMutex);
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_EXECUTION_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
index a6c1b19..86533ed 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
@@ -58,12 +58,19 @@
             const nn::OptionalDuration& loopTimeoutDuration,
             const nn::OptionalDuration& timeoutDurationAfterFence) const override;
 
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
     nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
 
     std::any getUnderlyingResource() const override;
 
   private:
     bool isValidInternal() const EXCLUDES(mMutex);
+    nn::GeneralResult<nn::SharedExecution> createReusableExecutionInternal(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const;
     nn::GeneralResult<nn::SharedBurst> configureExecutionBurstInternal() const;
 
     const Factory kMakePreparedModel;
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index 4d26795..8e55bf0 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -200,10 +200,31 @@
     return **maybeModelInSharedOut;
 }
 
-nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
-        const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut) {
+template <>
+void InputRelocationTracker::flush() const {
+    // Copy from pointers to shared memory.
+    uint8_t* memoryPtr = static_cast<uint8_t*>(std::get<void*>(kMapping.pointer));
+    for (const auto& [data, length, offset] : kRelocationInfos) {
+        std::memcpy(memoryPtr + offset, data, length);
+    }
+}
+
+template <>
+void OutputRelocationTracker::flush() const {
+    // Copy from shared memory to pointers.
+    const uint8_t* memoryPtr = static_cast<const uint8_t*>(
+            std::visit([](auto ptr) { return static_cast<const void*>(ptr); }, kMapping.pointer));
+    for (const auto& [data, length, offset] : kRelocationInfos) {
+        std::memcpy(data, memoryPtr + offset, length);
+    }
+}
+
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
+        const nn::Request* request, uint32_t alignment, uint32_t padding,
+        std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut) {
     CHECK(request != nullptr);
     CHECK(maybeRequestInSharedOut != nullptr);
+    CHECK(relocationOut != nullptr);
 
     if (hasNoPointerData(*request)) {
         return *request;
@@ -213,8 +234,11 @@
     // to the caller through `maybeRequestInSharedOut` if the function succeeds.
     nn::Request requestInShared = *request;
 
+    RequestRelocation relocation;
+
     // Change input pointers to shared memory.
-    nn::ConstantMemoryBuilder inputBuilder(requestInShared.pools.size());
+    nn::MutableMemoryBuilder inputBuilder(requestInShared.pools.size());
+    std::vector<InputRelocationInfo> inputRelocationInfos;
     for (auto& input : requestInShared.inputs) {
         const auto& location = input.location;
         if (input.lifetime != nn::Request::Argument::LifeTime::POINTER) {
@@ -225,17 +249,21 @@
         const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
                                       location.pointer);
         CHECK(data != nullptr);
-        input.location = inputBuilder.append(data, location.length);
+        input.location = inputBuilder.append(location.length, alignment, padding);
+        inputRelocationInfos.push_back({data, input.location.length, input.location.offset});
     }
 
     // Allocate input memory.
     if (!inputBuilder.empty()) {
         auto memory = NN_TRY(inputBuilder.finish());
-        requestInShared.pools.push_back(std::move(memory));
+        requestInShared.pools.push_back(memory);
+        relocation.input = NN_TRY(
+                InputRelocationTracker::create(std::move(inputRelocationInfos), std::move(memory)));
     }
 
     // Change output pointers to shared memory.
     nn::MutableMemoryBuilder outputBuilder(requestInShared.pools.size());
+    std::vector<OutputRelocationInfo> outputRelocationInfos;
     for (auto& output : requestInShared.outputs) {
         const auto& location = output.location;
         if (output.lifetime != nn::Request::Argument::LifeTime::POINTER) {
@@ -243,62 +271,25 @@
         }
 
         output.lifetime = nn::Request::Argument::LifeTime::POOL;
-        output.location = outputBuilder.append(location.length);
+        void* data = std::get<void*>(location.pointer);
+        CHECK(data != nullptr);
+        output.location = outputBuilder.append(location.length, alignment, padding);
+        outputRelocationInfos.push_back({data, output.location.length, output.location.offset});
     }
 
     // Allocate output memory.
     if (!outputBuilder.empty()) {
         auto memory = NN_TRY(outputBuilder.finish());
-        requestInShared.pools.push_back(std::move(memory));
+        requestInShared.pools.push_back(memory);
+        relocation.output = NN_TRY(OutputRelocationTracker::create(std::move(outputRelocationInfos),
+                                                                   std::move(memory)));
     }
 
     *maybeRequestInSharedOut = requestInShared;
+    *relocationOut = std::move(relocation);
     return **maybeRequestInSharedOut;
 }
 
-nn::GeneralResult<void> unflushDataFromSharedToPointer(
-        const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared) {
-    if (!maybeRequestInShared.has_value() || maybeRequestInShared->pools.empty() ||
-        !std::holds_alternative<nn::SharedMemory>(maybeRequestInShared->pools.back())) {
-        return {};
-    }
-    const auto& requestInShared = *maybeRequestInShared;
-
-    // Map the memory.
-    const auto& outputMemory = std::get<nn::SharedMemory>(requestInShared.pools.back());
-    const auto [pointer, size, context] = NN_TRY(map(outputMemory));
-    const uint8_t* constantPointer =
-            std::visit([](const auto& o) { return static_cast<const uint8_t*>(o); }, pointer);
-
-    // Flush each output pointer.
-    CHECK_EQ(request.outputs.size(), requestInShared.outputs.size());
-    for (size_t i = 0; i < request.outputs.size(); ++i) {
-        const auto& location = request.outputs[i].location;
-        const auto& locationInShared = requestInShared.outputs[i].location;
-        if (!std::holds_alternative<void*>(location.pointer)) {
-            continue;
-        }
-
-        // Get output pointer and size.
-        void* data = std::get<void*>(location.pointer);
-        CHECK(data != nullptr);
-        const size_t length = location.length;
-
-        // Get output pool location.
-        CHECK(requestInShared.outputs[i].lifetime == nn::Request::Argument::LifeTime::POOL);
-        const size_t index = locationInShared.poolIndex;
-        const size_t offset = locationInShared.offset;
-        const size_t outputPoolIndex = requestInShared.pools.size() - 1;
-        CHECK(locationInShared.length == length);
-        CHECK(index == outputPoolIndex);
-
-        // Flush memory.
-        std::memcpy(data, constantPointer + offset, length);
-    }
-
-    return {};
-}
-
 nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
         size_t numberOfOperands, const std::vector<nn::Operation>& operations) {
     return makeGeneralFailure(nn::countNumberOfConsumers(numberOfOperands, operations));
diff --git a/neuralnetworks/utils/common/src/InvalidBurst.cpp b/neuralnetworks/utils/common/src/InvalidBurst.cpp
index 0c34f05..0191533 100644
--- a/neuralnetworks/utils/common/src/InvalidBurst.cpp
+++ b/neuralnetworks/utils/common/src/InvalidBurst.cpp
@@ -38,4 +38,10 @@
     return NN_ERROR() << "InvalidBurst";
 }
 
+nn::GeneralResult<nn::SharedExecution> InvalidBurst::createReusableExecution(
+        const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    return NN_ERROR() << "InvalidBurst";
+}
+
 }  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidExecution.cpp b/neuralnetworks/utils/common/src/InvalidExecution.cpp
new file mode 100644
index 0000000..c4edd25
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidExecution.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InvalidExecution.h"
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> InvalidExecution::compute(
+        const nn::OptionalTimePoint& /*deadline*/) const {
+    return NN_ERROR() << "InvalidExecution";
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+InvalidExecution::computeFenced(const std::vector<nn::SyncFence>& /*waitFor*/,
+                                const nn::OptionalTimePoint& /*deadline*/,
+                                const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR() << "InvalidExecution";
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
index 9081e1f..8195462 100644
--- a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
+++ b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
@@ -42,6 +42,12 @@
     return NN_ERROR() << "InvalidPreparedModel";
 }
 
+nn::GeneralResult<nn::SharedExecution> InvalidPreparedModel::createReusableExecution(
+        const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    return NN_ERROR() << "InvalidPreparedModel";
+}
+
 nn::GeneralResult<nn::SharedBurst> InvalidPreparedModel::configureExecutionBurst() const {
     return NN_ERROR() << "InvalidPreparedModel";
 }
diff --git a/neuralnetworks/utils/common/src/ResilientBurst.cpp b/neuralnetworks/utils/common/src/ResilientBurst.cpp
index 38ccc62..79cbe39 100644
--- a/neuralnetworks/utils/common/src/ResilientBurst.cpp
+++ b/neuralnetworks/utils/common/src/ResilientBurst.cpp
@@ -19,6 +19,7 @@
 #include <android-base/logging.h>
 #include <android-base/thread_annotations.h>
 #include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.h>
 #include <nnapi/Result.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
@@ -29,6 +30,9 @@
 #include <optional>
 #include <utility>
 
+#include "InvalidExecution.h"
+#include "ResilientExecution.h"
+
 namespace android::hardware::neuralnetworks::utils {
 namespace {
 
@@ -46,11 +50,11 @@
     // Attempt recovery and return if it fails.
     auto maybeBurst = resilientBurst.recover(burst.get());
     if (!maybeBurst.has_value()) {
-        auto [resultErrorMessage, resultErrorCode, resultOutputShapes] = std::move(result).error();
-        const auto& [recoveryErrorMessage, recoveryErrorCode] = maybeBurst.error();
-        return nn::error(resultErrorCode, std::move(resultOutputShapes))
-               << resultErrorMessage << ", and failed to recover dead burst object with error "
-               << recoveryErrorCode << ": " << recoveryErrorMessage;
+        const auto& [message, code] = maybeBurst.error();
+        std::ostringstream oss;
+        oss << ", and failed to recover dead burst object with error " << code << ": " << message;
+        result.error().message += oss.str();
+        return result;
     }
     burst = std::move(maybeBurst).value();
 
@@ -109,4 +113,35 @@
     return protect(*this, fn);
 }
 
+nn::GeneralResult<nn::SharedExecution> ResilientBurst::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+#if 0
+    auto self = shared_from_this();
+    ResilientExecution::Factory makeExecution =
+            [burst = std::move(self), request, measure, loopTimeoutDuration] {
+        return burst->createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+    };
+    return ResilientExecution::create(std::move(makeExecution));
+#else
+    return createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+#endif
+}
+
+nn::GeneralResult<nn::SharedExecution> ResilientBurst::createReusableExecutionInternal(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidExecution>();
+    }
+    const auto fn = [&request, measure, &loopTimeoutDuration](const nn::IBurst& burst) {
+        return burst.createReusableExecution(request, measure, loopTimeoutDuration);
+    };
+    return protect(*this, fn);
+}
+
+bool ResilientBurst::isValidInternal() const {
+    return true;
+}
+
 }  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientExecution.cpp b/neuralnetworks/utils/common/src/ResilientExecution.cpp
new file mode 100644
index 0000000..46b404a
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientExecution.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResilientExecution.h"
+
+#include "InvalidBurst.h"
+#include "ResilientBurst.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientExecution& resilientExecution, const FnType& fn)
+        -> decltype(fn(*resilientExecution.getExecution())) {
+    auto execution = resilientExecution.getExecution();
+    auto result = fn(*execution);
+
+    // Immediately return if prepared model is not dead.
+    if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+        return result;
+    }
+
+    // Attempt recovery and return if it fails.
+    auto maybeExecution = resilientExecution.recover(execution.get());
+    if (!maybeExecution.has_value()) {
+        const auto& [message, code] = maybeExecution.error();
+        std::ostringstream oss;
+        oss << ", and failed to recover dead prepared model with error " << code << ": " << message;
+        result.error().message += oss.str();
+        return result;
+    }
+    execution = std::move(maybeExecution).value();
+
+    return fn(*execution);
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const ResilientExecution>> ResilientExecution::create(
+        Factory makeExecution) {
+    if (makeExecution == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::ResilientExecution::create must have non-empty makeExecution";
+    }
+    auto execution = NN_TRY(makeExecution());
+    CHECK(execution != nullptr);
+    return std::make_shared<ResilientExecution>(PrivateConstructorTag{}, std::move(makeExecution),
+                                                std::move(execution));
+}
+
+ResilientExecution::ResilientExecution(PrivateConstructorTag /*tag*/, Factory makeExecution,
+                                       nn::SharedExecution execution)
+    : kMakeExecution(std::move(makeExecution)), mExecution(std::move(execution)) {
+    CHECK(kMakeExecution != nullptr);
+    CHECK(mExecution != nullptr);
+}
+
+nn::SharedExecution ResilientExecution::getExecution() const {
+    std::lock_guard guard(mMutex);
+    return mExecution;
+}
+
+nn::GeneralResult<nn::SharedExecution> ResilientExecution::recover(
+        const nn::IExecution* failingExecution) const {
+    std::lock_guard guard(mMutex);
+
+    // Another caller updated the failing prepared model.
+    if (mExecution.get() != failingExecution) {
+        return mExecution;
+    }
+
+    mExecution = NN_TRY(kMakeExecution());
+    return mExecution;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ResilientExecution::compute(const nn::OptionalTimePoint& deadline) const {
+    const auto fn = [&deadline](const nn::IExecution& execution) {
+        return execution.compute(deadline);
+    };
+    return protect(*this, fn);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+ResilientExecution::computeFenced(const std::vector<nn::SyncFence>& waitFor,
+                                  const nn::OptionalTimePoint& deadline,
+                                  const nn::OptionalDuration& timeoutDurationAfterFence) const {
+    const auto fn = [&waitFor, &deadline,
+                     &timeoutDurationAfterFence](const nn::IExecution& execution) {
+        return execution.computeFenced(waitFor, deadline, timeoutDurationAfterFence);
+    };
+    return protect(*this, fn);
+}
+
+bool ResilientExecution::isValidInternal() const {
+    return true;
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
index 5dd5f99..1ae19bc 100644
--- a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
+++ b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
@@ -17,7 +17,9 @@
 #include "ResilientPreparedModel.h"
 
 #include "InvalidBurst.h"
+#include "InvalidExecution.h"
 #include "ResilientBurst.h"
+#include "ResilientExecution.h"
 
 #include <android-base/logging.h>
 #include <android-base/thread_annotations.h>
@@ -127,6 +129,21 @@
     return protect(*this, fn);
 }
 
+nn::GeneralResult<nn::SharedExecution> ResilientPreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+#if 0
+    auto self = shared_from_this();
+    ResilientExecution::Factory makeExecution =
+            [preparedModel = std::move(self), request, measure, loopTimeoutDuration] {
+        return preparedModel->createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+    };
+    return ResilientExecution::create(std::move(makeExecution));
+#else
+    return createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+#endif
+}
+
 nn::GeneralResult<nn::SharedBurst> ResilientPreparedModel::configureExecutionBurst() const {
 #if 0
     auto self = shared_from_this();
@@ -140,6 +157,19 @@
 #endif
 }
 
+nn::GeneralResult<nn::SharedExecution> ResilientPreparedModel::createReusableExecutionInternal(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidExecution>();
+    }
+    const auto fn = [&request, measure,
+                     &loopTimeoutDuration](const nn::IPreparedModel& preparedModel) {
+        return preparedModel.createReusableExecution(request, measure, loopTimeoutDuration);
+    };
+    return protect(*this, fn);
+}
+
 std::any ResilientPreparedModel::getUnderlyingResource() const {
     return getPreparedModel()->getUnderlyingResource();
 }
diff --git a/neuralnetworks/utils/common/test/MockExecution.h b/neuralnetworks/utils/common/test/MockExecution.h
new file mode 100644
index 0000000..91e3428
--- /dev/null
+++ b/neuralnetworks/utils/common/test/MockExecution.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_EXECUTION
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_EXECUTION
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
+
+namespace android::nn {
+
+class MockExecution final : public IExecution {
+  public:
+    MOCK_METHOD((ExecutionResult<std::pair<std::vector<OutputShape>, Timing>>), compute,
+                (const OptionalTimePoint& deadline), (const, override));
+    MOCK_METHOD((GeneralResult<std::pair<SyncFence, ExecuteFencedInfoCallback>>), computeFenced,
+                (const std::vector<SyncFence>& waitFor, const OptionalTimePoint& deadline,
+                 const OptionalDuration& timeoutDurationAfterFence),
+                (const, override));
+};
+
+}  // namespace android::nn
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_EXECUTION
diff --git a/neuralnetworks/utils/common/test/MockPreparedModel.h b/neuralnetworks/utils/common/test/MockPreparedModel.h
index c004861..c8ce006 100644
--- a/neuralnetworks/utils/common/test/MockPreparedModel.h
+++ b/neuralnetworks/utils/common/test/MockPreparedModel.h
@@ -35,6 +35,10 @@
                  const OptionalDuration& loopTimeoutDuration,
                  const OptionalDuration& timeoutDurationAfterFence),
                 (const, override));
+    MOCK_METHOD((GeneralResult<SharedExecution>), createReusableExecution,
+                (const nn::Request& request, nn::MeasureTiming measure,
+                 const nn::OptionalDuration& loopTimeoutDuration),
+                (const, override));
     MOCK_METHOD(GeneralResult<SharedBurst>, configureExecutionBurst, (), (const, override));
     MOCK_METHOD(std::any, getUnderlyingResource, (), (const, override));
 };
diff --git a/neuralnetworks/utils/common/test/ResilientExecution.cpp b/neuralnetworks/utils/common/test/ResilientExecution.cpp
new file mode 100644
index 0000000..c0737fb
--- /dev/null
+++ b/neuralnetworks/utils/common/test/ResilientExecution.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientExecution.h>
+#include <utility>
+#include "MockExecution.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+using SharedMockExecution = std::shared_ptr<const nn::MockExecution>;
+using MockExecutionFactory = ::testing::MockFunction<nn::GeneralResult<nn::SharedExecution>()>;
+
+SharedMockExecution createMockExecution() {
+    return std::make_shared<const nn::MockExecution>();
+}
+
+std::tuple<SharedMockExecution, std::unique_ptr<MockExecutionFactory>,
+           std::shared_ptr<const ResilientExecution>>
+setup() {
+    auto mockExecution = std::make_shared<const nn::MockExecution>();
+
+    auto mockExecutionFactory = std::make_unique<MockExecutionFactory>();
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(mockExecution));
+
+    auto buffer = ResilientExecution::create(mockExecutionFactory->AsStdFunction()).value();
+    return std::make_tuple(std::move(mockExecution), std::move(mockExecutionFactory),
+                           std::move(buffer));
+}
+
+constexpr auto makeError = [](nn::ErrorStatus status) {
+    return [status](const auto&... /*args*/) { return nn::error(status); };
+};
+const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
+const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+
+const auto kNoExecutionError =
+        nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>{};
+const auto kNoFencedExecutionError =
+        nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>(
+                std::make_pair(nn::SyncFence::createAsSignaled(), nullptr));
+
+}  // namespace
+
+TEST(ResilientExecutionTest, invalidExecutionFactory) {
+    // setup call
+    const auto invalidExecutionFactory = ResilientExecution::Factory{};
+
+    // run test
+    const auto result = ResilientExecution::create(invalidExecutionFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(ResilientExecutionTest, executionFactoryFailure) {
+    // setup call
+    const auto invalidExecutionFactory = kReturnGeneralFailure;
+
+    // run test
+    const auto result = ResilientExecution::create(invalidExecutionFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientExecutionTest, getExecution) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+
+    // run test
+    const auto result = execution->getExecution();
+
+    // verify result
+    EXPECT_TRUE(result == mockExecution);
+}
+
+TEST(ResilientExecutionTest, compute) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(Return(kNoExecutionError));
+
+    // run test
+    const auto result = execution->compute({});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, computeError) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->compute({});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientExecutionTest, computeDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->compute({});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientExecutionTest, computeDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*recoveredMockExecution, compute(_)).Times(1).WillOnce(Return(kNoExecutionError));
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+
+    // run test
+    const auto result = execution->compute({});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, computeFenced) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, computeFenced(_, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoFencedExecutionError));
+
+    // run test
+    const auto result = execution->computeFenced({}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, computeFencedError) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, computeFenced(_, _, _)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->computeFenced({}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientExecutionTest, computeFencedDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, computeFenced(_, _, _)).Times(1).WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->computeFenced({}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientExecutionTest, computeFencedDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, computeFenced(_, _, _)).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*recoveredMockExecution, computeFenced(_, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoFencedExecutionError));
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+
+    // run test
+    const auto result = execution->computeFenced({}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, recover) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+
+    // run test
+    const auto result = execution->recover(mockExecution.get());
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockExecution);
+}
+
+TEST(ResilientExecutionTest, recoverFailure) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->recover(mockExecution.get());
+
+    // verify result
+    EXPECT_FALSE(result.has_value());
+}
+
+TEST(ResilientExecutionTest, someoneElseRecovered) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+    execution->recover(mockExecution.get());
+
+    // run test
+    const auto result = execution->recover(mockExecution.get());
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockExecution);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
index 6d86e10..d396ca8 100644
--- a/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
+++ b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
@@ -55,6 +55,7 @@
 const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
 const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
 
+const auto kNoCreateReusableExecutionError = nn::GeneralResult<nn::SharedExecution>{};
 const auto kNoExecutionError =
         nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>{};
 const auto kNoFencedExecutionError =
@@ -231,6 +232,36 @@
             << "Failed with " << result.error().code << ": " << result.error().message;
 }
 
+TEST(ResilientPreparedModelTest, createReusableExecution) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoCreateReusableExecutionError));
+
+    // run test
+    const auto result = preparedModel->createReusableExecution({}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, createReusableExecutionError) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _))
+            .Times(1)
+            .WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = preparedModel->createReusableExecution({}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
 TEST(ResilientPreparedModelTest, getUnderlyingResource) {
     // setup call
     const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
diff --git a/nfc/1.0/default/Nfc.cpp b/nfc/1.0/default/Nfc.cpp
index fcdcbbc..a1e50f0 100644
--- a/nfc/1.0/default/Nfc.cpp
+++ b/nfc/1.0/default/Nfc.cpp
@@ -38,7 +38,7 @@
 ::android::hardware::Return<NfcStatus> Nfc::coreInitialized(const hidl_vec<uint8_t>& data)  {
     hidl_vec<uint8_t> copy = data;
 
-    if (mDevice == nullptr) {
+    if (mDevice == nullptr || copy.size() == 0) {
         return NfcStatus::FAILED;
     }
     int ret = mDevice->core_initialized(mDevice, &copy[0]);
diff --git a/power/stats/aidl/android/hardware/power/stats/EnergyConsumerResult.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerResult.aidl
index 12d2042..66c8c8c 100644
--- a/power/stats/aidl/android/hardware/power/stats/EnergyConsumerResult.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerResult.aidl
@@ -25,7 +25,7 @@
      */
     int id;
     /**
-     * Time since boot in milliseconds
+     * Time of data capture in milliseconds since boot (CLOCK_BOOTTIME clock)
      */
     long timestampMs;
     /**
@@ -38,4 +38,3 @@
      */
     EnergyConsumerAttribution[] attribution;
 }
-
diff --git a/power/stats/aidl/android/hardware/power/stats/EnergyMeasurement.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyMeasurement.aidl
index d3e8f46..31fbaa8 100644
--- a/power/stats/aidl/android/hardware/power/stats/EnergyMeasurement.aidl
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyMeasurement.aidl
@@ -23,7 +23,7 @@
      */
     int id;
     /**
-     * Approximate time of data capture in millseconds since boot
+     * Time of data capture in milliseconds since boot (CLOCK_BOOTTIME clock)
      */
     long timestampMs;
     /**
@@ -35,4 +35,3 @@
      */
     long energyUWs;
 }
-
diff --git a/radio/1.0/vts/OWNERS b/radio/1.0/vts/OWNERS
index 2384317..9310f8e 100644
--- a/radio/1.0/vts/OWNERS
+++ b/radio/1.0/vts/OWNERS
@@ -1,8 +1,7 @@
 # Telephony team
 amitmahajan@google.com
-sanketpadawe@google.com
 shuoq@google.com
+jackyu@google.com
 
 # VTS team
-yuexima@google.com
-yim@google.com
+dshi@google.com
diff --git a/radio/1.0/vts/functional/vts_test_util.cpp b/radio/1.0/vts/functional/vts_test_util.cpp
index fc37201..5b31acc 100644
--- a/radio/1.0/vts/functional/vts_test_util.cpp
+++ b/radio/1.0/vts/functional/vts_test_util.cpp
@@ -83,6 +83,13 @@
     return hasFeature;
 }
 
+bool isSsSsEnabled() {
+    // Do not use checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "")
+    // until b/148904287 is fixed. We need exact matching instead of partial matching. (i.e.
+    // by definition the empty string "" is a substring of any string).
+    return !isDsDsEnabled() && !isTsTsEnabled();
+}
+
 bool isDsDsEnabled() {
     return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "dsds");
 }
diff --git a/radio/1.0/vts/functional/vts_test_util.h b/radio/1.0/vts/functional/vts_test_util.h
index eeb1d29..fa338a3 100644
--- a/radio/1.0/vts/functional/vts_test_util.h
+++ b/radio/1.0/vts/functional/vts_test_util.h
@@ -80,12 +80,17 @@
 bool deviceSupportsFeature(const char* feature);
 
 /*
- * Check if device is in DSDS.
+ * Check if device is in SsSs (Single SIM Single Standby).
+ */
+bool isSsSsEnabled();
+
+/*
+ * Check if device is in DSDS (Dual SIM Dual Standby).
  */
 bool isDsDsEnabled();
 
 /*
- * Check if device is in TSTS.
+ * Check if device is in TSTS (Triple SIM Triple Standby).
  */
 bool isTsTsEnabled();
 
diff --git a/radio/1.1/vts/OWNERS b/radio/1.1/vts/OWNERS
index 2384317..a07c917 100644
--- a/radio/1.1/vts/OWNERS
+++ b/radio/1.1/vts/OWNERS
@@ -1,8 +1 @@
-# Telephony team
-amitmahajan@google.com
-sanketpadawe@google.com
-shuoq@google.com
-
-# VTS team
-yuexima@google.com
-yim@google.com
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.2/vts/OWNERS b/radio/1.2/vts/OWNERS
index 245d9d4..a07c917 100644
--- a/radio/1.2/vts/OWNERS
+++ b/radio/1.2/vts/OWNERS
@@ -1,9 +1 @@
-# Telephony team
-amitmahajan@google.com
-sanketpadawe@google.com
-shuoq@google.com
-sasindran@google.com
-
-# VTS team
-yuexima@google.com
-yim@google.com
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.3/vts/OWNERS b/radio/1.3/vts/OWNERS
index d642064..a07c917 100644
--- a/radio/1.3/vts/OWNERS
+++ b/radio/1.3/vts/OWNERS
@@ -1,10 +1 @@
-# Telephony team
-amitmahajan@google.com
-sanketpadawe@google.com
-shuoq@google.com
-sasindran@google.com
-nazaninb@google.com
-
-# VTS team
-yuexima@google.com
-yim@google.com
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.4/vts/OWNERS b/radio/1.4/vts/OWNERS
index fd69f36..a07c917 100644
--- a/radio/1.4/vts/OWNERS
+++ b/radio/1.4/vts/OWNERS
@@ -1,8 +1 @@
-# Telephony team
-amitmahajan@google.com
-shuoq@google.com
-sasindran@google.com
-
-# VTS team
-yuexima@google.com
-yim@google.com
\ No newline at end of file
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.5/vts/OWNERS b/radio/1.5/vts/OWNERS
index 3629a6c..a07c917 100644
--- a/radio/1.5/vts/OWNERS
+++ b/radio/1.5/vts/OWNERS
@@ -1,10 +1 @@
-# Telephony team
-refuhoo@google.com
-amitmahajan@google.com
-jackyu@google.com
-fionaxu@google.com
-# more to add
-
-# VTS team
-yuexima@google.com
-dshi@google.com
\ No newline at end of file
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.6/vts/OWNERS b/radio/1.6/vts/OWNERS
index 3629a6c..a07c917 100644
--- a/radio/1.6/vts/OWNERS
+++ b/radio/1.6/vts/OWNERS
@@ -1,10 +1 @@
-# Telephony team
-refuhoo@google.com
-amitmahajan@google.com
-jackyu@google.com
-fionaxu@google.com
-# more to add
-
-# VTS team
-yuexima@google.com
-dshi@google.com
\ No newline at end of file
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
index f09b74e..5af007e 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -447,6 +447,8 @@
                  ::android::hardware::radio::V1_6::RadioError::NONE,
                  ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
     }
+
+    sleep(1);
     serial = GetRandomSerialNumber();
 
     res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::THROTTLE_ANCHOR_CARRIER,
@@ -467,6 +469,8 @@
                  ::android::hardware::radio::V1_6::RadioError::NONE,
                  ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
     }
+
+    sleep(1);
     serial = GetRandomSerialNumber();
 
     res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::HOLD, 60000);
@@ -487,6 +491,8 @@
                  ::android::hardware::radio::V1_6::RadioError::NONE,
                  ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
     }
+
+    sleep(1);
     serial = GetRandomSerialNumber();
 
     res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::NO_DATA_THROTTLING, 60000);
@@ -506,6 +512,8 @@
                  ::android::hardware::radio::V1_6::RadioError::NONE,
                  ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
     }
+
+    sleep(1);
 }
 
 /*
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp b/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp
index 4222441..8c99d92 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp
@@ -45,10 +45,8 @@
                ::android::hardware::radio::V1_0::RadioError::NONE) {
         static const std::regex kOperatorNumericRe("^[0-9]{5,6}$");
         for (OperatorInfo info : radioRsp_v1_6->networkInfos) {
-            if (info.operatorNumeric != nullptr) {
-                ASSERT_TRUE(
-                        std::regex_match(std::string(info.operatorNumeric), kOperatorNumericRe));
-            }
+            ASSERT_TRUE(
+                    std::regex_match(std::string(info.operatorNumeric), kOperatorNumericRe));
         }
     }
 
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_test.cpp b/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
index 5d514a0..00f4468 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
@@ -16,8 +16,39 @@
 
 #include <radio_hidl_hal_utils_v1_6.h>
 
+bool isServiceValidForDeviceConfiguration(hidl_string& serviceName) {
+    if (isSsSsEnabled()) {
+        // Device is configured as SSSS.
+        if (serviceName != RADIO_SERVICE_SLOT1_NAME) {
+            ALOGI("%s instance is not valid for SSSS device.", serviceName.c_str());
+            return false;
+        }
+    } else if (isDsDsEnabled()) {
+        // Device is configured as DSDS.
+        if (serviceName != RADIO_SERVICE_SLOT1_NAME && serviceName != RADIO_SERVICE_SLOT2_NAME) {
+            ALOGI("%s instance is not valid for DSDS device.", serviceName.c_str());
+            return false;
+        }
+    } else if (isTsTsEnabled()) {
+        // Device is configured as TSTS.
+        if (serviceName != RADIO_SERVICE_SLOT1_NAME && serviceName != RADIO_SERVICE_SLOT2_NAME &&
+            serviceName != RADIO_SERVICE_SLOT3_NAME) {
+            ALOGI("%s instance is not valid for TSTS device.", serviceName.c_str());
+            return false;
+        }
+    }
+    return true;
+}
+
 void RadioHidlTest_v1_6::SetUp() {
-    radio_v1_6 = android::hardware::radio::V1_6::IRadio::getService(GetParam());
+    hidl_string serviceName = GetParam();
+
+    if (!isServiceValidForDeviceConfiguration(serviceName)) {
+        ALOGI("Skipped the test due to device configuration.");
+        GTEST_SKIP();
+    }
+
+    radio_v1_6 = android::hardware::radio::V1_6::IRadio::getService(serviceName);
     ASSERT_NE(nullptr, radio_v1_6.get());
 
     radioRsp_v1_6 = new (std::nothrow) RadioResponse_v1_6(*this);
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
index 3185f98..54c2977 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
+++ b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
@@ -48,7 +48,9 @@
 #define MODEM_EMERGENCY_CALL_ESTABLISH_TIME 3
 #define MODEM_EMERGENCY_CALL_DISCONNECT_TIME 3
 
-#define RADIO_SERVICE_NAME "slot1"
+#define RADIO_SERVICE_SLOT1_NAME "slot1"  // HAL instance name for SIM slot 1 or single SIM device
+#define RADIO_SERVICE_SLOT2_NAME "slot2"  // HAL instance name for SIM slot 2 on dual SIM device
+#define RADIO_SERVICE_SLOT3_NAME "slot3"  // HAL instance name for SIM slot 3 on triple SIM device
 
 class RadioHidlTest_v1_6;
 extern ::android::hardware::radio::V1_5::CardStatus cardStatus;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
index bf30999..fa643fc 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -44,10 +44,10 @@
   void deleteKey(in byte[] keyBlob);
   void deleteAllKeys();
   void destroyAttestationIds();
-  android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose purpose, in byte[] keyBlob, in android.hardware.security.keymint.KeyParameter[] params, in android.hardware.security.keymint.HardwareAuthToken authToken);
+  android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose purpose, in byte[] keyBlob, in android.hardware.security.keymint.KeyParameter[] params, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken);
   void deviceLocked(in boolean passwordOnly, in @nullable android.hardware.security.secureclock.TimeStampToken timestampToken);
   void earlyBootEnded();
   byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
-  byte[] performOperation(in byte[] request);
+  android.hardware.security.keymint.KeyCharacteristics[] getKeyCharacteristics(in byte[] keyBlob, in byte[] appId, in byte[] appData);
   const int AUTH_TOKEN_MAC_LENGTH = 32;
 }
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
index 7591318..e310b44 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
@@ -48,7 +48,6 @@
   RSA_PUBLIC_EXPONENT = 1342177480,
   INCLUDE_UNIQUE_ID = 1879048394,
   RSA_OAEP_MGF_DIGEST = 536871115,
-  BLOB_USAGE_REQUIREMENTS = 268435757,
   BOOTLOADER_ONLY = 1879048494,
   ROLLBACK_RESISTANCE = 1879048495,
   HARDWARE_TYPE = 268435760,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
index b4bc60c..4e3008f 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
@@ -27,7 +27,19 @@
 @VintfStability
 @RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
 parcelable AttestationKey {
+    /**
+     * Key blob containing a key pair with KeyPurpose::ATTEST_KEY
+     */
     byte[] keyBlob;
+
+    /**
+     * Key parameters needed to use the key in keyBlob, notably Tag::APPLICATION_ID and
+     * Tag::APPLICATION_DATA, if they were provided during generation of the key in keyBlob.
+     */
     KeyParameter[] attestKeyParams;
+
+    /**
+     * The issuerSubjectName to use in the generated attestation.
+     */
     byte[] issuerSubjectName;
 }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
index 2304a58..b5336b9 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
@@ -25,7 +25,10 @@
  */
 @VintfStability
 parcelable BeginResult {
-    /* This is the challenge used in verifyAuthorization.  It must be a nonce. */
+    /**
+     * This is the challenge used to verify authorization of an operation.
+     * See IKeyMintOperation.aidl entrypoints updateAad() and update().
+     */
     long challenge;
 
     /**
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 1c503c2..b6af813 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -20,6 +20,7 @@
 import android.hardware.security.keymint.BeginResult;
 import android.hardware.security.keymint.HardwareAuthToken;
 import android.hardware.security.keymint.IKeyMintOperation;
+import android.hardware.security.keymint.KeyCharacteristics;
 import android.hardware.security.keymint.KeyCreationResult;
 import android.hardware.security.keymint.KeyFormat;
 import android.hardware.security.keymint.KeyMintHardwareInfo;
@@ -107,7 +108,6 @@
  *
  *      - 168-bit keys.
  *      - CBC and ECB mode.
-
  *      - CBC and ECB modes must support unpadded and PKCS7 padding modes.  With no padding CBC and
  *        ECB-mode operations must fail with ErrorCode::INVALID_INPUT_LENGTH if the input isn't a
  *        multiple of the DES block size.
@@ -150,8 +150,8 @@
  *
  * The IKeyMintDevice must ignore unknown tags.
  *
- * The caller must always provide the current date time in the keyParameter CREATION_DATETIME
- * tags.
+ * The caller may provide the current date time in the keyParameter CREATION_DATETIME tag, but
+ * this is optional and informational only.
  *
  * All authorization tags and their values enforced by an IKeyMintDevice must be cryptographically
  * bound to the private/secret key material such that any modification of the portion of the key
@@ -185,7 +185,7 @@
  * startup, preferably by the bootloader.  This bitstring must be cryptographically bound to every
  * key managed by the IKeyMintDevice.  As above, the recommended mechanism for this cryptographic
  * binding is to include the Root of Trust data in the input to the key derivation function used to
- * derive a key that is used to encryp the private/secret key material.
+ * derive a key that is used to encrypt the private/secret key material.
  *
  * The root of trust consists of a bitstring that must be derived from the public key used by
  * Verified Boot to verify the signature on the boot image and from the lock state of the
@@ -247,7 +247,7 @@
      * Generates a new cryptographic key, specifying associated parameters, which must be
      * cryptographically bound to the key.  IKeyMintDevice implementations must disallow any use
      * of a key in any way inconsistent with the authorizations specified at generation time.  With
-     * respect to parameters that the secure environment cannot enforce, the secure envionment's
+     * respect to parameters that the secure environment cannot enforce, the secure environment's
      * obligation is limited to ensuring that the unenforceable parameters associated with the key
      * cannot be modified.  In addition, the characteristics returned by generateKey places
      * parameters correctly in the tee-enforced and strongbox-enforced lists.
@@ -257,9 +257,6 @@
      *
      * o Tag::ORIGIN with the value KeyOrigin::GENERATED.
      *
-     * o Tag::BLOB_USAGE_REQUIREMENTS with the appropriate value (see KeyBlobUsageRequirements in
-     *   Tag.aidl).
-     *
      * o Tag::OS_VERSION, Tag::OS_PATCHLEVEL, Tag::VENDOR_PATCHLEVEL and Tag::BOOT_PATCHLEVEL with
      *   appropriate values.
      *
@@ -271,15 +268,14 @@
      *
      * The following parameters are required to generate an RSA key:
      *
-     * o Tag::Key_SIZE specifies the size of the public modulus, in bits.  If omitted, generateKey
+     * o Tag::KEY_SIZE specifies the size of the public modulus, in bits.  If omitted, generateKey
      *   must return ErrorCode::UNSUPPORTED_KEY_SIZE.  Required values for TEE IKeyMintDevice
      *   implementations are 1024, 2048, 3072 and 4096.  StrongBox IKeyMintDevice implementations
      *   must support 2048.
      *
      * o Tag::RSA_PUBLIC_EXPONENT specifies the RSA public exponent value.  If omitted, generateKey
      *   must return ErrorCode::INVALID_ARGUMENT.  The values 3 and 65537 must be supported.  It is
-     *   recommended to support all prime values up to 2^64.  If provided with a non-prime value,
-     *   generateKey must return ErrorCode::INVALID_ARGUMENT.
+     *   recommended to support all prime values up to 2^64.
      *
      * The following parameters are not necessary to generate a usable RSA key, but generateKey must
      * not return an error if they are omitted:
@@ -288,7 +284,7 @@
      *   except AGREE_KEY must be supported for RSA keys.
      *
      * o Tag::DIGEST specifies digest algorithms that may be used with the new key.  TEE
-     *   IKeyMintDevice implementatiosn must support all Digest values (see digest.aidl) for RSA
+     *   IKeyMintDevice implementations must support all Digest values (see digest.aidl) for RSA
      *   keys.  StrongBox IKeyMintDevice implementations must support SHA_2_256.
      *
      * o Tag::PADDING specifies the padding modes that may be used with the new
@@ -298,11 +294,9 @@
      *
      * == ECDSA Keys ==
      *
-     * Either Tag::KEY_SIZE or Tag::EC_CURVE must be provided to generate an ECDSA key.  If neither
-     * is provided, generateKey must return ErrorCode::UNSUPPORTED_KEY_SIZE.  If Tag::KEY_SIZE is
-     * provided, the possible values are 224, 256, 384 and 521, and must be mapped to Tag::EC_CURVE
-     * values P_224, P_256, P_384 and P_521, respectively.  TEE IKeyMintDevice implementations
-     * must support all curves.  StrongBox implementations must support P_256.
+     * Tag::EC_CURVE must be provided to generate an ECDSA key.  If it is not provided, generateKey
+     * must return ErrorCode::UNSUPPORTED_KEY_SIZE. TEE IKeyMintDevice implementations must support
+     * all curves.  StrongBox implementations must support P_256.
      *
      * == AES Keys ==
      *
@@ -312,6 +306,10 @@
      * If Tag::BLOCK_MODE is specified with value BlockMode::GCM, then the caller must also provide
      * Tag::MIN_MAC_LENGTH.  If omitted, generateKey must return ErrorCode::MISSING_MIN_MAC_LENGTH.
      *
+     * == 3DES Keys ==
+     *
+     * Only Tag::KEY_SIZE is required to generate an 3DES key, and its value must be 168.  If
+     * omitted, generateKey must return ErrorCode::UNSUPPORTED_KEY_SIZE.
      *
      * @param keyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
      *        provided in params.  See above for detailed specifications of which tags are required
@@ -325,6 +323,10 @@
      *        return ErrorCode::INCOMPATIBLE_PURPOSE.  If the provided AttestationKey has an empty
      *        issuer subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
      *
+     *        If `attestationKey` is null and `keyParams` contains Tag::ATTESTATION_CHALLENGE but
+     *        the KeyMint implementation does not have factory-provisioned attestation keys, it must
+     *        return ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED.
+     *
      * @return The result of key creation.  See KeyCreationResult.aidl.
      */
     KeyCreationResult generateKey(
@@ -348,13 +350,12 @@
      *
      * o Tag::ORIGIN (returned in keyCharacteristics) must have the value KeyOrigin::IMPORTED.
      *
-     * @param inKeyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
+     * @param keyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
      *        provided in params.
      *
-     * @param inKeyFormat The format of the key material to import.  See KeyFormat in
-     *        keyformat.aidl.
+     * @param keyFormat The format of the key material to import.  See KeyFormat in keyformat.aidl.
      *
-     * @param inKeyData The key material to import, in the format specified in keyFormat.
+     * @param keyData The key material to import, in the format specified in keyFormat.
      *
      * @param attestationKey, if provided, specifies the key that must be used to sign the
      *        attestation certificate.  If `keyParams` does not contain a Tag::ATTESTATION_CHALLENGE
@@ -364,6 +365,10 @@
      *        return ErrorCode::INCOMPATIBLE_PURPOSE.  If the provided AttestationKey has an empty
      *        issuer subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
      *
+     *        If `attestationKey` is null and `keyParams` contains Tag::ATTESTATION_CHALLENGE but
+     *        the KeyMint implementation does not have factory-provisioned attestation keys, it must
+     *        return ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED.
+     *
      * @return The result of key creation.  See KeyCreationResult.aidl.
      */
     KeyCreationResult importKey(in KeyParameter[] keyParams, in KeyFormat keyFormat,
@@ -373,9 +378,8 @@
      * Securely imports a key, or key pair, returning a key blob and a description of the imported
      * key.
      *
-     * @param inWrappedKeyData The wrapped key material to import.
-     *     TODO(seleneh) Decide if we want the wrapped key in DER-encoded ASN.1 format or CBOR
-     *     format or both.  And specify the standarized format.
+     * @param wrappedKeyData The wrapped key material to import, as ASN.1 DER-encoded data
+     *        corresponding to the following schema.
      *
      *     KeyDescription ::= SEQUENCE(
      *         keyFormat INTEGER,                   # Values from KeyFormat enum.
@@ -393,20 +397,20 @@
      *
      *     Where:
      *
-     *     o keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext
+     *     - keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext
      *       key material.
-     *     o keyParams is the characteristics of the key to be imported (as with generateKey or
+     *     - keyParams is the characteristics of the key to be imported (as with generateKey or
      *       importKey).  If the secure import is successful, these characteristics must be
      *       associated with the key exactly as if the key material had been insecurely imported
-     *       with the IKeyMintDevice::importKey.  See attestKey() for documentation of the
-     *       AuthorizationList schema.
-     *     o encryptedTransportKey is a 256-bit AES key, XORed with a masking key and then encrypted
+     *       with the IKeyMintDevice::importKey.  See KeyCreationResult.aidl for documentation of
+     *       the AuthorizationList schema.
+     *     - encryptedTransportKey is a 256-bit AES key, XORed with a masking key and then encrypted
      *       with the wrapping key specified by wrappingKeyBlob.
-     *     o keyDescription is a KeyDescription, above.
-     *     o encryptedKey is the key material of the key to be imported, in format keyFormat, and
+     *     - keyDescription is a KeyDescription, above.
+     *     - encryptedKey is the key material of the key to be imported, in format keyFormat, and
      *       encrypted with encryptedEphemeralKey in AES-GCM mode, with the DER-encoded
      *       representation of keyDescription provided as additional authenticated data.
-     *     o tag is the tag produced by the AES-GCM encryption of encryptedKey.
+     *     - tag is the tag produced by the AES-GCM encryption of encryptedKey.
      *
      * So, importWrappedKey does the following:
      *
@@ -435,7 +439,7 @@
      *
      * @param passwordSid specifies the password secure ID (SID) of the user that owns the key being
      *        installed.  If the authorization list in wrappedKeyData contains a
-     *        Tag::USER_SECURE_IDwith a value that has the HardwareAuthenticatorType::PASSWORD bit
+     *        Tag::USER_SECURE_ID with a value that has the HardwareAuthenticatorType::PASSWORD bit
      *        set, the constructed key must be bound to the SID value provided by this argument.  If
      *        the wrappedKeyData does not contain such a tag and value, this argument must be
      *        ignored.
@@ -478,9 +482,9 @@
      * patch level and OS version.  This requirement is relaxed for 4.0::IKeymasterDevice and
      * IKeyMintDevice, and the OS version in the boot image footer is no longer used.
      *
-     * @param inKeyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey();
+     * @param keyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey().
      *
-     * @param inUpgradeParams A parameter list containing any parameters needed to complete the
+     * @param upgradeParams A parameter list containing any parameters needed to complete the
      *        upgrade, including Tag::APPLICATION_ID and Tag::APPLICATION_DATA.
      *
      * @return A new key blob that references the same key as keyBlobToUpgrade, but is in the new
@@ -494,7 +498,9 @@
      * render the key permanently unusable.  Keys without Tag::ROLLBACK_RESISTANCE may or
      * may not be rendered unusable.
      *
-     * @param inKeyBlob The opaque descriptor returned by generateKey() or importKey();
+     * @param keyBlob The opaque descriptor returned by generateKey() or importKey();
+     *
+     * @return error See the ErrorCode enum.
      */
     void deleteKey(in byte[] keyBlob);
 
@@ -503,8 +509,6 @@
      * this function is called all keys with Tag::ROLLBACK_RESISTANCE in their hardware-enforced
      * authorization lists must be rendered permanently unusable.  Keys without
      * Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
-     *
-     * @return error See the ErrorCode enum.
      */
     void deleteAllKeys();
 
@@ -523,28 +527,27 @@
 
     /**
      * Begins a cryptographic operation using the specified key.  If all is well, begin() must
-     * return ErrorCode::OK and create an operation handle which must be passed to subsequent calls
-     * to update(), finish() or abort().
+     * return ErrorCode::OK and create an IKeyMintOperation handle which will be used to perform
+     * the cryptographic operation.
      *
-     * It is critical that each call to begin() be paired with a subsequent call to finish() or
-     * abort(), to allow the IKeyMintDevice implementation to clean up any internal operation
-     * state.  The caller's failure to do this may leak internal state space or other internal
-     * resources and may eventually cause begin() to return ErrorCode::TOO_MANY_OPERATIONS when it
-     * runs out of space for operations.  Any result other than ErrorCode::OK from begin(), update()
-     * or finish() implicitly aborts the operation, in which case abort() need not be called (and
-     * must return ErrorCode::INVALID_OPERATION_HANDLE if called).  IKeyMintDevice implementations
-     * must support 32 concurrent operations.
+     * It is critical that each successful call to begin() be paired with a subsequent call to
+     * finish() or abort() on the resulting IKeyMintOperation, to allow the IKeyMintDevice
+     * implementation to clean up any internal operation state.  The caller's failure to do this may
+     * leak internal state space or other internal resources and may eventually cause begin() to
+     * return ErrorCode::TOO_MANY_OPERATIONS when it runs out of space for operations.  Any result
+     * other than ErrorCode::OK from begin() will not return an IKeyMintOperation (in which case
+     * calling finish() or abort() is neither possible nor necessary). IKeyMintDevice
+     * implementations must support 32 concurrent operations.
      *
      * If Tag::APPLICATION_ID or Tag::APPLICATION_DATA were specified during key generation or
      * import, calls to begin must include those tags with the originally-specified values in the
-     * inParams argument to this method.  If not, begin() must return ErrorCode::INVALID_KEY_BLOB.
+     * params argument to this method.  If not, begin() must return ErrorCode::INVALID_KEY_BLOB.
      *
      * == Authorization Enforcement ==
      *
      * The following key authorization parameters must be enforced by the IKeyMintDevice secure
      * environment if the tags were returned in the "hardwareEnforced" list in the
-     * KeyCharacteristics.  Public key operations, meaning KeyPurpose::ENCRYPT and
-     * KeyPurpose::VERIFY must be allowed to succeed even if authorization requirements are not met.
+     * KeyCharacteristics.
      *
      * -- All Key Types --
      *
@@ -573,9 +576,9 @@
      *
      * o Tag::USER_SECURE_ID must be enforced by this method if and only if the key also has
      *   Tag::AUTH_TIMEOUT (if it does not have Tag::AUTH_TIMEOUT, the Tag::USER_SECURE_ID
-     *   requirement must be enforced by update() and finish()).  If the key has both, then this
-     *   method must receive a non-empty HardwareAuthToken in the authToken argument.  For the auth
-     *   token to be valid, all of the following have to be true:
+     *   requirement must be enforced by updateAad(), update() and finish()).  If the key has both,
+     *   then this method must receive a non-empty HardwareAuthToken in the authToken argument.  For
+     *   the auth token to be valid, all of the following have to be true:
      *
      *   o The HMAC field must validate correctly.
      *
@@ -602,32 +605,30 @@
      *
      * -- RSA Keys --
      *
-     * All RSA key operations must specify exactly one padding mode in inParams.  If unspecified or
+     * All RSA key operations must specify exactly one padding mode in params.  If unspecified or
      * specified more than once, the begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE.
      *
-     * RSA signing and verification operations need a digest, as do RSA encryption and decryption
-     * operations with OAEP padding mode.  For those cases, the caller must specify exactly one
-     * digest in inParams.  If unspecified or specified more than once, begin() must return
+     * RSA signing operations need a digest, as do RSA encryption and decryption operations with
+     * OAEP padding mode.  For those cases, the caller must specify exactly one digest in params.
+     * If unspecified or specified more than once, begin() must return
      * ErrorCode::UNSUPPORTED_DIGEST.
      *
      * Private key operations (KeyPurpose::DECRYPT and KeyPurpose::SIGN) need authorization of
      * digest and padding, which means that the key authorizations need to contain the specified
      * values.  If not, begin() must return ErrorCode::INCOMPATIBLE_DIGEST or
-     * ErrorCode::INCOMPATIBLE_PADDING, as appropriate.  Public key operations (KeyPurpose::ENCRYPT
-     * and KeyPurpose::VERIFY) are permitted with unauthorized digest or padding modes.
+     * ErrorCode::INCOMPATIBLE_PADDING_MODE, as appropriate.
      *
      * With the exception of PaddingMode::NONE, all RSA padding modes are applicable only to certain
      * purposes.  Specifically, PaddingMode::RSA_PKCS1_1_5_SIGN and PaddingMode::RSA_PSS only
-     * support signing and verification, while PaddingMode::RSA_PKCS1_1_5_ENCRYPT and
-     * PaddingMode::RSA_OAEP only support encryption and decryption.  begin() must return
-     * ErrorCode::UNSUPPORTED_PADDING_MODE if the specified mode does not support the specified
-     * purpose.
+     * support signing, while PaddingMode::RSA_PKCS1_1_5_ENCRYPT and PaddingMode::RSA_OAEP only
+     * support encryption and decryption.  begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE
+     * if the specified mode does not support the specified purpose.
      *
      * There are some important interactions between padding modes and digests:
      *
-     * o PaddingMode::NONE indicates that a "raw" RSA operation is performed.  If signing or
-     *   verifying, Digest::NONE is specified for the digest.  No digest is necessary for unpadded
-     *   encryption or decryption.
+     * o PaddingMode::NONE indicates that a "raw" RSA operation is performed.  If signing,
+     *   Digest::NONE is specified for the digest.  No digest is necessary for unpadded encryption
+     *   or decryption.
      *
      * o PaddingMode::RSA_PKCS1_1_5_SIGN padding requires a digest.  The digest may be Digest::NONE,
      *   in which case the KeyMint implementation cannot build a proper PKCS#1 v1.5 signature
@@ -639,37 +640,37 @@
      *
      * o PaddingMode::RSA_PKCS1_1_1_5_ENCRYPT padding does not require a digest.
      *
-     * o PaddingMode::RSA_PSS padding requires a digest, which may not be Digest::NONE.  If
-     *   Digest::NONE is specified, the begin() must return ErrorCode::INCOMPATIBLE_DIGEST.  In
-     *   addition, the size of the RSA key must be at least 2 + D bytes larger than the output size
-     *   of the digest, where D is the size of the digest, in bytes.  Otherwise begin() must
-     *   return ErrorCode::INCOMPATIBLE_DIGEST.  The salt size must be D.
+     * o PaddingMode::RSA_PSS padding requires a digest, which must match one of the padding values
+     *   in the key authorizations, and which may not be Digest::NONE.  begin() must return
+     *   ErrorCode::INCOMPATIBLE_DIGEST if this is not the case.  In addition, the size of the RSA
+     *   key must be at least 2 + D bytes larger than the output size of the digest, where D is the
+     *   size of the digest, in bytes.  Otherwise begin() must return
+     *   ErrorCode::INCOMPATIBLE_DIGEST.  The salt size must be D.
      *
-     * o PaddingMode::RSA_OAEP padding requires a digest, which may not be Digest::NONE.  If
-     *   Digest::NONE is specified, begin() must return ErrorCode::INCOMPATIBLE_DIGEST.  The OAEP
-     *   mask generation function must be MGF1 and the MGF1 digest must be SHA1, regardless of the
-     *   OAEP digest specified.
+     * o PaddingMode::RSA_OAEP padding requires a digest, which must match one of the padding values
+     *   in the key authorizations, and which may not be Digest::NONE.  begin() must return
+     *   ErrorCode::INCOMPATIBLE_DIGEST if this is not the case.  RSA_OAEP padding also requires an
+     *   MGF1 digest, specified with Tag::RSA_OAEP_MGF_DIGEST, which must match one of the MGF1
+     *   padding values in the key authorizations and which may not be Digest::NONE.  begin() must
+     *   return ErrorCode::INCOMPATIBLE_MGF_DIGEST if this is not the case. The OAEP mask generation
+     *   function must be MGF1.
      *
      * -- EC Keys --
      *
-     * EC key operations must specify exactly one padding mode in inParams.  If unspecified or
-     * specified more than once, begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE.
-     *
      * Private key operations (KeyPurpose::SIGN) need authorization of digest and padding, which
      * means that the key authorizations must contain the specified values.  If not, begin() must
-     * return ErrorCode::INCOMPATIBLE_DIGEST.  Public key operations (KeyPurpose::VERIFY) are
-     * permitted with unauthorized digest or padding.
+     * return ErrorCode::INCOMPATIBLE_DIGEST.
      *
      * -- AES Keys --
      *
      * AES key operations must specify exactly one block mode (Tag::BLOCK_MODE) and one padding mode
-     * (Tag::PADDING) in inParams.  If either value is unspecified or specified more than once,
+     * (Tag::PADDING) in params.  If either value is unspecified or specified more than once,
      * begin() must return ErrorCode::UNSUPPORTED_BLOCK_MODE or
      * ErrorCode::UNSUPPORTED_PADDING_MODE.  The specified modes must be authorized by the key,
      * otherwise begin() must return ErrorCode::INCOMPATIBLE_BLOCK_MODE or
      * ErrorCode::INCOMPATIBLE_PADDING_MODE.
      *
-     * If the block mode is BlockMode::GCM, inParams must specify Tag::MAC_LENGTH, and the specified
+     * If the block mode is BlockMode::GCM, params must specify Tag::MAC_LENGTH, and the specified
      * value must be a multiple of 8 that is not greater than 128 or less than the value of
      * Tag::MIN_MAC_LENGTH in the key authorizations.  For MAC lengths greater than 128 or
      * non-multiples of 8, begin() must return ErrorCode::UNSUPPORTED_MAC_LENGTH.  For values less
@@ -682,48 +683,66 @@
      *
      * If the block mode is BlockMode::CBC, BlockMode::CTR, or BlockMode::GCM, an initialization
      * vector or nonce is required.  In most cases, callers shouldn't provide an IV or nonce and the
-     * IKeyMintDevice implementation must generate a random IV or nonce and return it via
-     * Tag::NONCE in outParams.  CBC and CTR IVs are 16 bytes.  GCM nonces are 12 bytes.  If the key
+     * IKeyMintDevice implementation must generate a random IV or nonce and return it via Tag::NONCE
+     * in outParams.  CBC and CTR IVs are 16 bytes.  GCM nonces are 12 bytes.  If the key
      * authorizations contain Tag::CALLER_NONCE, then the caller may provide an IV/nonce with
-     * Tag::NONCE in inParams.  If a nonce is provided when Tag::CALLER_NONCE is not authorized,
+     * Tag::NONCE in params, which must be of the correct size (if not, return
+     * ErrorCode::INVALID_NONCE).  If a nonce is provided when Tag::CALLER_NONCE is not authorized,
      * begin() must return ErrorCode::CALLER_NONCE_PROHIBITED.  If a nonce is not provided when
-     * Tag::CALLER_NONCE is authorized, IKeyMintDevice msut generate a random IV/nonce.
+     * Tag::CALLER_NONCE is authorized, IKeyMintDevice must generate a random IV/nonce.
+     *
+     * -- 3DES Keys --
+     *
+     * 3DES key operations must specify exactly one block mode (Tag::BLOCK_MODE) and one padding
+     * mode (Tag::PADDING) in params.  If either value is unspecified or specified more than once,
+     * begin() must return ErrorCode::UNSUPPORTED_BLOCK_MODE or
+     * ErrorCode::UNSUPPORTED_PADDING_MODE.  The specified modes must be authorized by the key,
+     * otherwise begin() must return ErrorCode::INCOMPATIBLE_BLOCK_MODE or
+     * ErrorCode::INCOMPATIBLE_PADDING_MODE.
+     *
+     * If the block mode is BlockMode::CBC, an initialization vector or nonce is required.  In most
+     * cases, callers shouldn't provide an IV or nonce and the IKeyMintDevice implementation must
+     * generate a random IV or nonce and return it via Tag::NONCE in outParams.  CBC IVs are 8
+     * bytes.  If the key authorizations contain Tag::CALLER_NONCE, then the caller may provide an
+     * IV/nonce with Tag::NONCE in params, which must be of the correct size (if not, return
+     * ErrorCode::INVALID_NONCE).  If a nonce is provided when Tag::CALLER_NONCE is not authorized,
+     * begin() must return ErrorCode::CALLER_NONCE_PROHIBITED.  If a nonce is not provided when
+     * Tag::CALLER_NONCE is authorized, IKeyMintDevice must generate a random IV/nonce.
+     *
      *
      * -- HMAC keys --
      *
-     * HMAC key operations must specify Tag::MAC_LENGTH in inParams.  The specified value must be a
+     * HMAC key operations must specify Tag::MAC_LENGTH in params.  The specified value must be a
      * multiple of 8 that is not greater than the digest length or less than the value of
      * Tag::MIN_MAC_LENGTH in the key authorizations.  For MAC lengths greater than the digest
      * length or non-multiples of 8, begin() must return ErrorCode::UNSUPPORTED_MAC_LENGTH.  For
      * values less than the key's minimum length, begin() must return ErrorCode::INVALID_MAC_LENGTH.
      *
-     * @param inPurpose The purpose of the operation, one of KeyPurpose::ENCRYPT,
-     *        KeyPurpose::DECRYPT, KeyPurpose::SIGN, KeyPurpose::VERIFY, or KeyPurpose::AGREE_KEY.
-     *        Note that for AEAD modes, encryption and decryption imply signing and verification,
-     *        respectively, but must be specified as KeyPurpose::ENCRYPT and KeyPurpose::DECRYPT.
+     * @param purpose The purpose of the operation, one of KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT,
+     *        KeyPurpose::SIGN, KeyPurpose::VERIFY, or KeyPurpose::AGREE_KEY.  Note that for AEAD
+     *        modes, encryption and decryption imply signing and verification, respectively, but
+     *        must be specified as KeyPurpose::ENCRYPT and KeyPurpose::DECRYPT.
      *
-     * @param inKeyBlob The opaque key descriptor returned by generateKey() or importKey().  The key
+     * @param keyBlob The opaque key descriptor returned by generateKey() or importKey().  The key
      *        must have a purpose compatible with purpose and all of its usage requirements must be
      *        satisfied, or begin() must return an appropriate error code (see above).
      *
-     * @param inParams Additional parameters for the operation.  If Tag::APPLICATION_ID or
+     * @param params Additional parameters for the operation.  If Tag::APPLICATION_ID or
      *        Tag::APPLICATION_DATA were provided during generation, they must be provided here, or
      *        the operation must fail with ErrorCode::INVALID_KEY_BLOB.  For operations that require
-     *        a nonce or IV, on keys that were generated with Tag::CALLER_NONCE, inParams may
+     *        a nonce or IV, on keys that were generated with Tag::CALLER_NONCE, params may
      *        contain a tag Tag::NONCE.  If Tag::NONCE is provided for a key without
      *        Tag:CALLER_NONCE, ErrorCode::CALLER_NONCE_PROHIBITED must be returned.
      *
-     * @param inAuthToken Authentication token.  Callers that provide no token must set all numeric
-     *        fields to zero and the MAC must be an empty vector.  TODO: make this field nullable.
-     *        b/173483024.
+     * @param authToken Authentication token.
      *
      * @return BeginResult as output, which contains the challenge, KeyParameters which haves
      *         additional data from the operation initialization, notably to return the IV or nonce
      *         from operations that generate an IV or nonce, and IKeyMintOperation object pointer
-     *         which is used to perform update(), finish() or abort() operations.
+     *         which is used to perform updateAad(), update(), finish() or abort() operations.
      */
     BeginResult begin(in KeyPurpose purpose, in byte[] keyBlob, in KeyParameter[] params,
-            in HardwareAuthToken authToken);
+            in @nullable HardwareAuthToken authToken);
 
     /**
      * Called by client to notify the IKeyMintDevice that the device is now locked, and keys with
@@ -737,7 +756,7 @@
      * Note that the IKeyMintDevice UNLOCKED_DEVICE_REQUIRED semantics are slightly different from
      * the UNLOCKED_DEVICE_REQUIRED semantics enforced by keystore.  Keystore handles device locking
      * on a per-user basis.  Because auth tokens do not contain an Android user ID, it's not
-     * possible to replicate the keystore enformcement logic in IKeyMintDevice.  So from the
+     * possible to replicate the keystore enforcement logic in IKeyMintDevice.  So from the
      * IKeyMintDevice perspective, any user unlock unlocks all UNLOCKED_DEVICE_REQUIRED keys.
      * Keystore will continue enforcing the per-user device locking.
      *
@@ -763,7 +782,7 @@
      */
     void earlyBootEnded();
 
-    /*
+    /**
      * Called by the client to get a wrapped per-boot ephemeral key from a wrapped storage key.
      * Clients will then use the returned per-boot ephemeral key in place of the wrapped storage
      * key. Whenever the hardware is presented with a per-boot ephemeral key for an operation, it
@@ -785,16 +804,24 @@
     byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
 
     /**
-     * Called by the client to perform a KeyMint operation.
+     * Returns parameters associated with the provided key. This should match the
+     * KeyCharacteristics present in the KeyCreationResult returned by generateKey(),
+     * importKey(), or importWrappedKey().
      *
-     *  This method is added primarily as a placeholder.  Details will be fleshed before the KeyMint
-     *  V1 interface is frozen.  Until then, implementations must return ErrorCode::UNIMPLEMENTED.
+     * @param keyBlob The opaque descriptor returned by generateKey, importKey or importWrappedKey.
      *
-     * @param request is an encrypted buffer containing a description of the operation the client
-     *        wishes to perform.  Structure, content and encryption are TBD.
+     * @param appId An opaque byte string identifying the client.  This value must match the
+     *        Tag::APPLICATION_ID data provided during key generation/import.  Without the correct
+     *        value, it must be computationally infeasible for the secure hardware to obtain the
+     *        key material.
      *
-     * @return an encrypted buffer containing the result of the operation.  Structure, content and
-     *         encryption are TBD.
+     * @param appData An opaque byte string provided by the application.  This value must match the
+     *        Tag::APPLICATION_DATA data provided during key generation/import.  Without the
+     *        correct value, it must be computationally infeasible for the secure hardware to
+     *        obtain the key material.
+     *
+     * @return Characteristics of the generated key. See KeyCreationResult for details.
      */
-    byte[] performOperation(in byte[] request);
+    KeyCharacteristics[] getKeyCharacteristics(
+            in byte[] keyBlob, in byte[] appId, in byte[] appData);
 }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
index d2a993f..aa7b492 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -31,12 +31,12 @@
      * update() is called.  If updateAad() is called after update(), it must return
      * ErrorCode::INVALID_TAG.
      *
-     * If operation is in an invalid state (was aborted or had an error) update() must return
+     * If the operation is in an invalid state (was aborted or had an error) update() must return
      * ErrorCode::INVALID_OPERATION_HANDLE.
      *
      * If this method returns an error code other than ErrorCode::OK, the operation is aborted and
-     * the operation handle must be invalidated.  Any future use of the handle, with this method,
-     * finish, or abort, must return ErrorCode::INVALID_OPERATION_HANDLE.
+     * the operation handle must be invalidated.  Any future use of this object must return
+     * ErrorCode::INVALID_OPERATION_HANDLE.
      *
      * == Authorization Enforcement ==
      *
@@ -58,9 +58,10 @@
      *
      *   o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
      *
-     *   o The challenge field in the auth token must contain the operationHandle
+     *   o The challenge field in the auth token must contain the value returned from
+     *     IKeyMintDevice::begin(), given by the challenge field of the BeginResult structure.
      *
-     *   If any of these conditions are not met, update() must return
+     *   If any of these conditions are not met, updateAad() must return
      *   ErrorCode::KEY_USER_NOT_AUTHENTICATED.
      *
      * The caller must provide the auth token on every call to updateAad(), update() and finish().
@@ -74,7 +75,7 @@
      *
      * @param input Additional Authentication Data to be processed.
      *
-     * @param authToken Authentication token. Can be nullable if not provided.
+     * @param authToken Authentication token, if provided.
      *
      * @param timeStampToken timestamp token, certifies the freshness of an auth token in case
      *        the security domain of this KeyMint instance has a different clock than the
@@ -93,18 +94,9 @@
      * If operation is in an invalid state (was aborted or had an error) update() must return
      * ErrorCode::INVALID_OPERATION_HANDLE.
      *
-     * To provide more flexibility for buffer handling, implementations of this method have the
-     * option of consuming less data than was provided.  The caller is responsible for looping to
-     * feed the rest of the data in subsequent calls.  The amount of input consumed must be returned
-     * in the inputConsumed parameter.  Implementations must always consume at least one byte,
-     * unless the operation cannot accept any more; if more than zero bytes are provided and zero
-     * bytes are consumed, callers must consider this an error and abort the operation.
-     * TODO(seleneh) update the code to always consume alll the input data. b/168665179.
-     *
-     * Implementations may also choose how much data to return, as a result of the update.  This is
-     * only relevant for encryption and decryption operations, because signing and verification
-     * return no data until finish.  It is recommended to return data as early as possible, rather
-     * than buffer it.
+     * Implementations may choose how much data to return as a result of the update.  This is
+     * only relevant for encryption and decryption operations, because signing returns no data
+     * until finish.  It is recommended to return data as early as possible, rather than buffer it.
      *
      * If this method returns an error code other than ErrorCode::OK, the operation is aborted and
      * the operation handle must be invalidated.  Any future use of the handle, with this method,
@@ -112,8 +104,8 @@
      *
      * == Authorization Enforcement ==
      *
-     * Key authorization enforcement is performed primarily in begin().  The one exception is the
-     * case where the key has:
+     * Key authorization enforcement is performed primarily in IKeyMintDevice::begin().  The one
+     * exception is the case where the key has:
      *
      * o One or more Tag::USER_SECURE_IDs, and
      *
@@ -130,7 +122,8 @@
      *
      *   o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
      *
-     *   o The challenge field in the auth token must contain the operationHandle
+     *   o The challenge field in the auth token must contain the challenge value contained in the
+     *     BeginResult returned from IKeyMintDevice::begin().
      *
      *   If any of these conditions are not met, update() must return
      *   ErrorCode::KEY_USER_NOT_AUTHENTICATED.
@@ -139,22 +132,20 @@
      *
      * -- RSA keys --
      *
-     * For signing and verification operations with Digest::NONE, this method must accept the entire
-     * block to be signed or verified in a single update.  It may not consume only a portion of the
-     * block in these cases.  However, the caller may choose to provide the data in multiple
-     * updates, and update() must accept the data this way as well.  If the caller provides more
-     * data to sign than can be used (length of data exceeds RSA key size), update() must return
-     * ErrorCode::INVALID_INPUT_LENGTH.
+     * For signing operations with Digest::NONE, this method must accept the entire block to be
+     * signed in a single update.  It may not consume only a portion of the block in these cases.
+     * However, the caller may choose to provide the data in multiple updates, and update() must
+     * accept the data this way as well.  If the caller provides more data to sign than can be used
+     * (length of data exceeds RSA key size), update() must return ErrorCode::INVALID_INPUT_LENGTH.
      *
      * -- ECDSA keys --
      *
-     * For signing and verification operations with Digest::NONE, this method must accept the entire
-     * block to be signed or verified in a single update.  This method may not consume only a
-     * portion of the block.  However, the caller may choose to provide the data in multiple updates
-     * and update() must accept the data this way as well.  If the caller provides more data to sign
-     * than can be used, the data is silently truncated.  (This differs from the handling of excess
-     * data provided in similar RSA operations.  The reason for this is compatibility with legacy
-     * clients.)
+     * For signing operations with Digest::NONE, this method must accept the entire block to be
+     * signed in a single update.  This method may not consume only a portion of the block.
+     * However, the caller may choose to provide the data in multiple updates and update() must
+     * accept the data this way as well.  If the caller provides more data to sign than can be used,
+     * the data is silently truncated.  (This differs from the handling of excess data provided in
+     * similar RSA operations.  The reason for this is compatibility with legacy clients.)
      *
      * -- AES keys --
      *
@@ -182,7 +173,7 @@
             in @nullable TimeStampToken timeStampToken);
 
     /**
-     * Finalizes a cryptographic operation begun with begin() and invalidates operation.
+     * Finalizes a cryptographic operation begun with begin() and invalidates the operation.
      *
      * This method is the last one called in an operation, so all processed data must be returned.
      *
@@ -190,8 +181,7 @@
      * Any future use of the operation, with finish(), update(), or abort(), must return
      * ErrorCode::INVALID_OPERATION_HANDLE.
      *
-     * Signing operations return the signature as the output.  Verification operations accept the
-     * signature in the signature parameter, and return no output.
+     * Signing operations return the signature as the output.
      *
      * == Authorization enforcement ==
      *
@@ -230,44 +220,35 @@
      * Some additional requirements, depending on the padding mode:
      *
      * o PaddingMode::NONE.  For unpadded signing and encryption operations, if the provided data is
-     *   shorter than the key, the data must be zero-padded on the left before
-     *   signing/encryption.  If the data is the same length as the key, but numerically larger,
-     *   finish() must return ErrorCode::INVALID_ARGUMENT.  For verification and decryption
-     *   operations, the data must be exactly as long as the key.  Otherwise, return
-     *   ErrorCode::INVALID_INPUT_LENGTH.
+     *   shorter than the key, the data must be zero-padded on the left before signing/encryption.
+     *   If the data is the same length as the key, but numerically larger, finish() must return
+     *   ErrorCode::INVALID_ARGUMENT.  For decryption operations, the data must be exactly as long
+     *   as the key.  Otherwise, return ErrorCode::INVALID_INPUT_LENGTH.
      *
      * o PaddingMode::RSA_PSS.  For PSS-padded signature operations, the PSS salt length must match
-     *   the size of the PSS digest selected.  The digest specified with Tag::DIGEST in inputParams
+     *   the size of the PSS digest selected.  The digest specified with Tag::DIGEST in params
      *   on begin() must be used as the PSS digest algorithm, MGF1 must be used as the mask
      *   generation function and SHA1 must be used as the MGF1 digest algorithm.
      *
-     * o PaddingMode::RSA_OAEP.  The digest specified with Tag::DIGEST in inputParams on begin is
-     *   used as the OAEP digest algorithm, MGF1 must be used as the mask generation function and
-     *   and SHA1 must be used as the MGF1 digest algorithm.
-     *
      * -- ECDSA keys --
      *
-     * If the data provided for unpadded signing or verification is too long, truncate it.
+     * If the data provided for undigested signing is too long, truncate it.
      *
      * -- AES keys --
      *
      * Some additional conditions, depending on block mode:
      *
      * o BlockMode::ECB or BlockMode::CBC.  If padding is PaddingMode::NONE and the data length is
-     *  not a multiple of the AES block size, finish() must return
-     *  ErrorCode::INVALID_INPUT_LENGTH.  If padding is PaddingMode::PKCS7, pad the data per the
-     *  PKCS#7 specification, including adding an additional padding block if the data is a multiple
-     *  of the block length.
+     *   not a multiple of the AES block size, finish() must return
+     *   ErrorCode::INVALID_INPUT_LENGTH.  If padding is PaddingMode::PKCS7, pad the data per the
+     *   PKCS#7 specification, including adding an additional padding block if the data is a
+     *   multiple of the block length.
      *
      * o BlockMode::GCM.  During encryption, after processing all plaintext, compute the tag
      *   (Tag::MAC_LENGTH bytes) and append it to the returned ciphertext.  During decryption,
      *   process the last Tag::MAC_LENGTH bytes as the tag.  If tag verification fails, finish()
      *   must return ErrorCode::VERIFICATION_FAILED.
      *
-     * TODO: update() will need to be refactored into 2 function. b/168665179.
-     *
-     * @param inParams Additional parameters for the operation.
-     *
      * @param input Data to be processed, per the parameters established in the call to begin().
      *        finish() must consume all provided data or return ErrorCode::INVALID_INPUT_LENGTH.
      *
@@ -281,11 +262,9 @@
      *        token.
      *
      * @param confirmationToken is the confirmation token required by keys with
-     * Tag::TRUSTED_CONFIRMATION_REQUIRED.
+     *        Tag::TRUSTED_CONFIRMATION_REQUIRED.
      *
      * @return The output data, if any.
-     *
-     * @return outParams Any output parameters generated by finish().
      */
     byte[] finish(in @nullable byte[] input, in @nullable byte[] signature,
             in @nullable HardwareAuthToken authToken,
@@ -293,13 +272,10 @@
             in @nullable byte[] confirmationToken);
 
     /**
-     * Aborts a cryptographic operation begun with begin(), freeing all internal resources. If an
-     * operation was finalized, calling update, finish, or abort yields
-     * ErrorCode::INVALID_OPERATION_HANDLE. An operation is finalized if finish or abort was
-     * called on it, or if update returned an ErrorCode.
-     *
-     * @param operationHandle The operation handle returned by begin().  This handle must be
-     *        invalid when abort() returns.
+     * Aborts a cryptographic operation begun with IKeyMintDevice::begin(), freeing all internal
+     * resources.  If an operation was finalized, calling updateAad, update, finish, or abort yields
+     * ErrorCode::INVALID_OPERATION_HANDLE. An operation is finalized if finish or abort was called
+     * on it, or if updateAad or update returned an ErrorCode.
      *
      * @return error See the ErrorCode enum in ErrorCode.aidl.
      */
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index b6285d9..04d91d0 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -78,7 +78,7 @@
  * While a proper BCC, as described above, reflects the complete boot sequence from boot ROM to the
  * secure area image of the IRemotelyProvisionedComponent, it's also possible to use a "degenerate"
  * BCC which consists only of a single, self-signed certificate containing the public key of a
- * hardware-bound key pair. This is an appopriate solution for devices which haven't implemented
+ * hardware-bound key pair. This is an appropriate solution for devices which haven't implemented
  * everything necessary to produce a proper BCC, but can derive a unique key pair in the secure
  * area.  In this degenerate case, DK_pub is the same as KM_pub.
  *
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
index c2e21b6..f93dbba 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -58,7 +58,7 @@
      * There are a few variations in what is contained in `certificateChain`, depending on whether
      * the caller requested attestation, whether they provided an attestation key (via the
      * `attestationKey` parameter of `generateKey()`, `importKey()` or `importWrappedKey()`), and in
-     * the non-attestaion case, whether the key can self-sign.
+     * the non-attestation case, whether the key can self-sign.
      *
      * 1.  Asymmetric key attestation with factory key.  If Tag::ATTESTATION_CHALLENGE is provided
      *     and the `attestationKey` parameter on the generate/import call is null, the returned
@@ -66,10 +66,11 @@
      *     provisioned attestation key, and the full certificate chain for that factory-provisioned
      *     attestation key.  Tag::ATTESTATION_APPLICATION_ID must also be provided when the
      *     ATTESTATION_CHALLENGE is provided, otherwise ATTESTATION_APPLICATION_ID_MISSING will be
-     *     returned.
+     *     returned.  KeyMint implementations are not required to support factory-provisioned
+     *     attestation keys.
      *
      * 2.  Asymmetric key attestation with caller-provided key.  If Tag::ATTESTATION_CHALLENGE is
-     *     provided and the `attestationKey` parameter on the generat/import call is non-null and
+     *     provided and the `attestationKey` parameter on the generate/import call is non-null and
      *     contains the key blob of a key with KeyPurpose::ATTEST_KEY, the returned certificate
      *     chain must contain only an attestation certificate signed with the specified key.  The
      *     caller must know the certificate chain for the provided key.  Tag::
@@ -90,6 +91,110 @@
      * 5.  Symmetric key.  If the generated/imported key is symmetric, the certificate chain must
      *     return empty, any Tag::ATTESTATION_CHALLENGE or Tag::ATTESTATION_APPLICATION_ID inputs,
      *     if provided, are ignored.
+     *
+     * In all cases except the symmetric key, the contents of certificate chain must be DER-encoded
+     * X.509 certificates ordered such that each certificate is signed by the subsequent one, up to
+     * the root which must be self-signed (or contain a fake signature in the case of case 4 above).
+     * The first certificate in the chain signs the public key info of the newly-generated or
+     * newly-imported key pair.  In the attestation cases (1 and 2 above), the first certificate
+     * must also satisfy some other requirements:
+     *
+     * o It must have the serial number provided in Tag::CERTIFICATE_SERIAL, or default to 1 if the
+     *   tag is not provided.
+     *
+     * o It must have the subject provided in Tag::CERTIFICATE_SUBJECT, or default to CN="Android
+     *   Keystore Key", if the tag is not provided.
+     *
+     * o It must contain the notBefore and notAfter date-times specified in
+     *   Tag::CERTIFICATE_NOT_BEFORE and Tag::CERTIFICATE_NOT_AFTER, respectively.
+     *
+     * o It must contain a Key Usage extension with:
+     *
+     *    - the digitalSignature bit set iff the attested key has KeyPurpose::SIGN,
+     *    - the dataEncipherment bit set iff the attested key has KeyPurpose::DECRYPT,
+     *    - the keyEncipherment bit set iff the attested key has KeyPurpose::WRAP_KEY,
+     *    - the keyAgreement bit set iff the attested key has KeyPurpose::AGREE_KEY, and
+     *    - the keyCertSignBit set iff the attested key has KeyPurpose::ATTEST_KEY.
+     *
+     * o it must contain a KeyDescription attestation extension with OID 1.3.6.1.4.1.11129.2.1.17.
+     *
+     * The KeyDescription content is defined by the following ASN.1 schema, which is mostly a
+     * straightforward translation of the KeyMint tag/value parameter lists to ASN.1.
+     *
+     * KeyDescription ::= SEQUENCE {
+     *     attestationVersion         INTEGER, # Value 100
+     *     attestationSecurityLevel   SecurityLevel, # See below
+     *     keyMintVersion             INTEGER, # Value 100
+     *     keymintSecurityLevel       SecurityLevel, # See below
+     *     attestationChallenge       OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams
+     *     uniqueId                   OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID
+     *     softwareEnforced           AuthorizationList, # See below
+     *     hardwareEnforced           AuthorizationList, # See below
+     * }
+     *
+     * SecurityLevel ::= ENUMERATED {
+     *     Software                   (0),
+     *     TrustedEnvironment         (1),
+     *     StrongBox                  (2),
+     * }
+     *
+     * RootOfTrust ::= SEQUENCE {
+     *     verifiedBootKey            OCTET_STRING,
+     *     deviceLocked               BOOLEAN,
+     *     verifiedBootState          VerifiedBootState,
+     *     # verifiedBootHash must contain 32-byte value that represents the state of all binaries
+     *     # or other components validated by verified boot.  Updating any verified binary or
+     *     # component must cause this value to change.
+     *     verifiedBootHash           OCTET_STRING,
+     * }
+     *
+     * VerifiedBootState ::= ENUMERATED {
+     *     Verified                   (0),
+     *     SelfSigned                 (1),
+     *     Unverified                 (2),
+     *     Failed                     (3),
+     * }
+     *
+     * AuthorizationList ::= SEQUENCE {
+     *     purpose                    [1] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     algorithm                  [2] EXPLICIT INTEGER OPTIONAL,
+     *     keySize                    [3] EXPLICIT INTEGER OPTIONAL,
+     *     blockMode                  [4] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     digest                     [5] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     padding                    [6] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     callerNonce                [7] EXPLICIT NULL OPTIONAL,
+     *     minMacLength               [8] EXPLICIT INTEGER OPTIONAL,
+     *     ecCurve                    [10] EXPLICIT INTEGER OPTIONAL,
+     *     rsaPublicExponent          [200] EXPLICIT INTEGER OPTIONAL,
+     *     rollbackResistance         [303] EXPLICIT NULL OPTIONAL,
+     *     activeDateTime             [400] EXPLICIT INTEGER OPTIONAL,
+     *     originationExpireDateTime  [401] EXPLICIT INTEGER OPTIONAL,
+     *     usageExpireDateTime        [402] EXPLICIT INTEGER OPTIONAL,
+     *     userSecureId               [502] EXPLICIT INTEGER OPTIONAL,
+     *     noAuthRequired             [503] EXPLICIT NULL OPTIONAL,
+     *     userAuthType               [504] EXPLICIT INTEGER OPTIONAL,
+     *     authTimeout                [505] EXPLICIT INTEGER OPTIONAL,
+     *     allowWhileOnBody           [506] EXPLICIT NULL OPTIONAL,
+     *     trustedUserPresenceReq     [507] EXPLICIT NULL OPTIONAL,
+     *     trustedConfirmationReq     [508] EXPLICIT NULL OPTIONAL,
+     *     unlockedDeviceReq          [509] EXPLICIT NULL OPTIONAL,
+     *     creationDateTime           [701] EXPLICIT INTEGER OPTIONAL,
+     *     origin                     [702] EXPLICIT INTEGER OPTIONAL,
+     *     rootOfTrust                [704] EXPLICIT RootOfTrust OPTIONAL,
+     *     osVersion                  [705] EXPLICIT INTEGER OPTIONAL,
+     *     osPatchLevel               [706] EXPLICIT INTEGER OPTIONAL,
+     *     attestationApplicationId   [709] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdBrand         [710] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdDevice        [711] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdProduct       [712] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdSerial        [713] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdImei          [714] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdMeid          [715] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdManufacturer  [716] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdModel         [717] EXPLICIT OCTET_STRING OPTIONAL,
+     *     vendorPatchLevel           [718] EXPLICIT INTEGER OPTIONAL,
+     *     bootPatchLevel             [719] EXPLICIT INTEGER OPTIONAL,
+     * }
      */
     Certificate[] certificateChain;
 }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
index f896125..5840c6b 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
@@ -20,7 +20,7 @@
  * The origin of a key (or pair), i.e. where it was generated.  Note that ORIGIN can be found in
  * either the hardware-enforced or software-enforced list for a key, indicating whether the key is
  * hardware or software-based.  Specifically, a key with GENERATED in the hardware-enforced list
- * must be guaranteed never to have existed outide the secure hardware.
+ * must be guaranteed never to have existed outside the secure hardware.
  * @hide
  */
 @VintfStability
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
index c874fc3..e141e55 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
@@ -23,10 +23,10 @@
 @VintfStability
 @Backing(type="int")
 enum KeyPurpose {
-    /* Usable with RSA, EC and AES keys. */
+    /* Usable with RSA, 3DES and AES keys. */
     ENCRYPT = 0,
 
-    /* Usable with RSA, EC and AES keys. */
+    /* Usable with RSA, 3DES and AES keys. */
     DECRYPT = 1,
 
     /* Usable with RSA, EC and HMAC keys. */
@@ -36,6 +36,7 @@
     VERIFY = 3,
 
     /* 4 is reserved */
+
     /* Usable with wrapping keys. */
     WRAP_KEY = 5,
 
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index cde1fc0..8fbc91a 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -19,7 +19,7 @@
 import android.hardware.security.keymint.TagType;
 
 // TODO(seleneh) : note aidl currently does not support double nested enum definitions such as
-// ROOT_OF_TRUST = TagType:BYTES | 704.  So we are forced to write definations as
+// ROOT_OF_TRUST = TagType:BYTES | 704.  So we are forced to write definitions as
 // ROOT_OF_TRUST = (9 << 28) for now.  Will need to flip this back later when aidl support is added.
 
 /**
@@ -59,7 +59,7 @@
     ALGORITHM = (1 << 28) /* TagType:ENUM */ | 2,
 
     /**
-     * Tag::KEY_SIZE pecifies the size, in bits, of the key, measuring in the normal way for the
+     * Tag::KEY_SIZE specifies the size, in bits, of the key, measuring in the normal way for the
      * key's algorithm.  For example, for RSA keys, Tag::KEY_SIZE specifies the size of the public
      * modulus.  For AES keys it specifies the length of the secret key material.  For 3DES keys it
      * specifies the length of the key material, not counting parity bits (though parity bits must
@@ -75,9 +75,9 @@
      * is only relevant to AES and 3DES keys.  Possible values are defined by the BlockMode enum.
      *
      * This tag is repeatable for key generation/import.  For AES and 3DES operations the caller
-     * must specify a Tag::BLOCK_MODE in the additionalParams argument of begin().  If the mode is
-     * missing or the specified mode is not in the modes specified for the key during
-     * generation/import, the operation must fail with ErrorCode::INCOMPATIBLE_BLOCK_MODE.
+     * must specify a Tag::BLOCK_MODE in the params argument of begin().  If the mode is missing or
+     * the specified mode is not in the modes specified for the key during generation/import, the
+     * operation must fail with ErrorCode::INCOMPATIBLE_BLOCK_MODE.
      *
      * Must be hardware-enforced.
      */
@@ -89,9 +89,9 @@
      * values are defined by the Digest enum.
      *
      * This tag is repeatable for key generation/import.  For signing and verification operations,
-     * the caller must specify a digest in the additionalParams argument of begin().  If the digest
-     * is missing or the specified digest is not in the digests associated with the key, the
-     * operation must fail with ErrorCode::INCOMPATIBLE_DIGEST.
+     * the caller must specify a digest in the params argument of begin().  If the digest is missing
+     * or the specified digest is not in the digests associated with the key, the operation must
+     * fail with ErrorCode::INCOMPATIBLE_DIGEST.
      *
      * Must be hardware-enforced.
      */
@@ -145,7 +145,7 @@
      * This value is the minimum MAC length, in bits.  It must be a multiple of 8 bits.  For HMAC
      * keys, the value must be least 64 and no more than 512.  For GCM keys, the value must be at
      * least 96 and no more than 128.  If the provided value violates these requirements,
-     * generateKey() or importKey() must return ErrorCode::UNSUPPORTED_KEY_SIZE.
+     * generateKey() or importKey() must return ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH.
      *
      * Must be hardware-enforced.
      */
@@ -154,9 +154,8 @@
     // Tag 9 reserved
 
     /**
-     * Tag::EC_CURVE specifies the elliptic curve.  EC key generation requests may have
-     * Tag:EC_CURVE, Tag::KEY_SIZE, or both.  If both are provided and the size and curve do not
-     * match, IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+     * Tag::EC_CURVE specifies the elliptic curve.  Possible values are defined in the EcCurve
+     * enumeration.
      *
      * Must be hardware-enforced.
      */
@@ -188,37 +187,19 @@
     INCLUDE_UNIQUE_ID = (7 << 28) /* TagType:BOOL */ | 202,
 
     /**
-     * Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with
-     * RSA encryption/decryption with OAEP padding. If the key characteristics supports OAEP
-     * and this tag is absent then SHA1 digest is selected by default for MGF1.
+     * Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with RSA
+     * encryption/decryption with OAEP padding.  Possible values are defined by the Digest enum.
      *
-     * This tag is repeatable for key generation/import.  If this tag is present in the key
-     * characteristics with one or more values from @4.0::Digest, then for RSA cipher
-     * operations with OAEP Padding, the caller must specify a digest in the additionalParams
-     * argument of begin operation. If this tag is missing or the specified digest is not in
-     * the digests associated with the key then begin operation must fail with
-     * ErrorCode::INCOMPATIBLE_MGF_DIGEST.
+     * This tag is repeatable for key generation/import.  RSA cipher operations with OAEP padding
+     * must specify an MGF1 digest in the params argument of begin(). If this tag is missing or the
+     * specified digest is not in the MGF1 digests associated with the key then begin operation must
+     * fail with ErrorCode::INCOMPATIBLE_MGF_DIGEST.
      *
      * Must be hardware-enforced.
      */
     RSA_OAEP_MGF_DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 203,
 
-    /**
-     * TODO(seleneh) this tag needs to be deleted from all codes.
-     *
-     * Tag::BLOB_USAGE_REQUIREMENTS specifies the necessary system environment conditions for the
-     * generated key to be used.  Possible values are defined by the KeyBlobUsageRequirements enum.
-     *
-     * This tag is specified by the caller during key generation or import to require that the key
-     * is usable in the specified condition.  If the caller specifies Tag::BLOB_USAGE_REQUIREMENTS
-     * with value KeyBlobUsageRequirements::STANDALONE the IKeyMintDevice must return a key blob
-     * that can be used without file system support.  This is critical for devices with encrypted
-     * disks, where the file system may not be available until after a KeyMint key is used to
-     * decrypt the disk.
-     *
-     * Must be hardware-enforced.
-     */
-    BLOB_USAGE_REQUIREMENTS = (1 << 28) /* TagType:ENUM */ | 301,
+    // Tag 301 reserved
 
     /**
      * Tag::BOOTLOADER_ONLY specifies only the bootloader can use the key.
@@ -241,7 +222,7 @@
      * ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE.  IKeyMintDevice implementations are not
      * required to support rollback resistance.
      *
-     * Must be hardwared-enforced.
+     * Must be hardware-enforced.
      */
     ROLLBACK_RESISTANCE = (7 << 28) /* TagType:BOOL */ | 303,
 
@@ -251,7 +232,7 @@
     /**
      * Keys tagged with EARLY_BOOT_ONLY may only be used during early boot, until
      * IKeyMintDevice::earlyBootEnded() is called.  Early boot keys may be created after
-     * early boot.  Early boot keys may not be imprted at all, if Tag::EARLY_BOOT_ONLY is
+     * early boot.  Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is
      * provided to IKeyMintDevice::importKey, the import must fail with
      * ErrorCode::INVALID_ARGUMENT.
      */
@@ -307,7 +288,7 @@
      * with ErrorCode::KEY_RATE_LIMIT_EXCEEDED.  This implies that the IKeyMintDevice must keep a
      * table of use counters for keys with this tag.  Because memory is often limited, this table
      * may have a fixed maximum size and KeyMint may fail operations that attempt to use keys with
-     * this tag when the table is full.  The table must acommodate at least 8 in-use keys and
+     * this tag when the table is full.  The table must accommodate at least 8 in-use keys and
      * aggressively reuse table slots when key minimum-usage intervals expire.  If an operation
      * fails because the table is full, KeyMint returns ErrorCode::TOO_MANY_OPERATIONS.
      *
@@ -327,7 +308,7 @@
      * device is restarted.  This implies that the IKeyMintDevice must keep a table of use
      * counters for keys with this tag.  Because KeyMint memory is often limited, this table can
      * have a fixed maximum size and KeyMint can fail operations that attempt to use keys with
-     * this tag when the table is full.  The table needs to acommodate at least 8 keys.  If an
+     * this tag when the table is full.  The table needs to accommodate at least 8 keys.  If an
      * operation fails because the table is full, IKeyMintDevice must
      * ErrorCode::TOO_MANY_OPERATIONS.
      *
@@ -386,14 +367,14 @@
      * key, and may only be used if the difference between the current time when begin() is called
      * and the timestamp in the HardwareAuthToken is less than the value in Tag::AUTH_TIMEOUT * 1000
      * (the multiplier is because Tag::AUTH_TIMEOUT is in seconds, but the HardwareAuthToken
-     * timestamp is in milliseconds).  Otherwise the IKeyMintDevice must returrn
+     * timestamp is in milliseconds).  Otherwise the IKeyMintDevice must return
      * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
      *
      * If Tag::AUTH_TIMEOUT is not present, then the key is an "auth-per-operation" key.  In this
      * case, begin() must not require a HardwareAuthToken with appropriate contents.  Instead,
      * update() and finish() must receive a HardwareAuthToken with Tag::USER_SECURE_ID value in
      * userId or authenticatorId fields, and the current operation's operation handle in the
-     * challenge field.  Otherwise the IKeyMintDevice must returrn
+     * challenge field.  Otherwise the IKeyMintDevice must return
      * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
      *
      * This tag is repeatable.  If repeated, and any one of the values matches the HardwareAuthToken
@@ -434,7 +415,7 @@
     /**
      * Tag::AUTH_TIMEOUT specifies the time in seconds for which the key is authorized for use,
      * after user authentication.  If
-     * Tag::USER_SECURE_ID is present and this tag is not, then the key requies authentication for
+     * Tag::USER_SECURE_ID is present and this tag is not, then the key requires authentication for
      * every usage (see begin() for the details of the authentication-per-operation flow).
      *
      * The value is a 32-bit integer specifying the time in seconds after a successful
@@ -504,7 +485,7 @@
      * Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
      *  specifies that this key must not be usable unless the user provides confirmation of the data
      *  to be signed.  Confirmation is proven to keyMint via an approval token.  See
-     *  CONFIRMATION_TOKEN, as well as the ConfirmatinUI HAL.
+     *  CONFIRMATION_TOKEN, as well as the ConfirmationUI HAL.
      *
      * If an attempt to use a key with this tag does not have a cryptographically valid
      * CONFIRMATION_TOKEN provided to finish() or if the data provided to update()/finish() does not
@@ -526,7 +507,7 @@
      * Tag::APPLICATION_ID.  When provided to generateKey or importKey, this tag specifies data
      * that is necessary during all uses of the key.  In particular, calls to exportKey() and
      * getKeyCharacteristics() must provide the same value to the clientId parameter, and calls to
-     * begin must provide this tag and the same associated data as part of the inParams set.  If
+     * begin() must provide this tag and the same associated data as part of the inParams set.  If
      * the correct data is not provided, the method must return ErrorCode::INVALID_KEY_BLOB.
      *
      * The content of this tag must be bound to the key cryptographically, meaning it must not be
@@ -550,7 +531,7 @@
      * provide this tag and the same associated data as part of the inParams set.  If the correct
      * data is not provided, the method must return ErrorCode::INVALID_KEY_BLOB.
      *
-     * The content of this tag msut be bound to the key cryptographically, meaning it must not be
+     * The content of this tag must be bound to the key cryptographically, meaning it must not be
      * possible for an adversary who has access to all of the secure world secrets but does not have
      * access to the tag content to decrypt the key without brute-forcing the tag content, which
      * applications can prevent by specifying sufficiently high-entropy content.
@@ -561,10 +542,9 @@
 
     /**
      * Tag::CREATION_DATETIME specifies the date and time the key was created, in milliseconds since
-     * January 1, 1970.  This tag is optional and informational only.
+     * January 1, 1970.  This tag is optional and informational only, and not enforced by anything.
      *
-     * Tag::CREATED is informational only, and not enforced by anything.  Must be in the
-     * software-enforced list, if provided.
+     * Must be in the software-enforced list, if provided.
      */
     CREATION_DATETIME = (6 << 28) /* TagType:DATE */ | 701,
 
@@ -650,10 +630,11 @@
      *      Tag::CREATION_DATETIME by 2592000000, dropping any remainder.  T changes every 30 days
      *      (2592000000 = 30 * 24 * 60 * 60 * 1000).
      *
-     *    C is the value of Tag::ATTESTATION_APPLICATION_ID that is provided to attestKey().
+     *    C is the value of Tag::ATTESTATION_APPLICATION_ID that is provided to attested key
+     *      generation/import operations.
      *
-     *    R is 1 if Tag::RESET_SINCE_ID_ROTATION was provided to attestKey or 0 if the tag was not
-     *      provided.
+     *    R is 1 if Tag::RESET_SINCE_ID_ROTATION was provided to attested key generation/import or 0
+     *      if the tag was not provided.
      *
      *    HBK is a unique hardware-bound secret known to the secure environment and never revealed
      *    by it.  The secret must contain at least 128 bits of entropy and be unique to the
@@ -668,9 +649,9 @@
     UNIQUE_ID = (9 << 28) /* TagType:BYTES */ | 707,
 
     /**
-     * Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attestKey() method,
-     * which must place the value in the KeyDescription SEQUENCE of the attestation extension.  See
-     * attestKey().
+     * Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attested key
+     * generation/import methods, which must place the value in the KeyDescription SEQUENCE of the
+     * attestation extension.
      *
      * Must never appear in KeyCharacteristics.
      */
@@ -678,7 +659,7 @@
 
     /**
      * Tag::ATTESTATION_APPLICATION_ID identifies the set of applications which may use a key, used
-     * only with attestKey().
+     * only with attested key generation/import operations.
      *
      * The content of Tag::ATTESTATION_APPLICATION_ID is a DER-encoded ASN.1 structure, with the
      * following schema:
@@ -704,8 +685,8 @@
 
     /**
      * Tag::ATTESTATION_ID_BRAND provides the device's brand name, as returned by Build.BRAND in
-     * Android, to attestKey().  This field must be set only when requesting attestation of the
-     * device's identifiers.
+     * Android, to attested key generation/import operations.  This field must be set only when
+     * requesting attestation of the device's identifiers.
      *
      * If the device does not support ID attestation (or destroyAttestationIds() was previously
      * called and the device can no longer attest its IDs), any key attestation request that
@@ -717,8 +698,8 @@
 
     /**
      * Tag::ATTESTATION_ID_DEVICE provides the device's device name, as returned by Build.DEVICE in
-     * Android, to attestKey().  This field must be set only when requesting attestation of the
-     * device's identifiers.
+     * Android, to attested key generation/import operations.  This field must be set only when
+     * requesting attestation of the device's identifiers.
      *
      * If the device does not support ID attestation (or destroyAttestationIds() was previously
      * called and the device can no longer attest its IDs), any key attestation request that
@@ -730,8 +711,8 @@
 
     /**
      * Tag::ATTESTATION_ID_PRODUCT provides the device's product name, as returned by Build.PRODUCT
-     * in Android, to attestKey().  This field must be set only when requesting attestation of the
-     * device's identifiers.
+     * in Android, to attested key generation/import operations.  This field must be set only when
+     * requesting attestation of the device's identifiers.
      *
      * If the device does not support ID attestation (or destroyAttestationIds() was previously
      * called and the device can no longer attest its IDs), any key attestation request that
@@ -754,8 +735,9 @@
     ATTESTATION_ID_SERIAL = (9 << 28) /* TagType:BYTES */ | 713,
 
     /**
-     * Tag::ATTESTATION_ID_IMEI provides the IMEIs for all radios on the device to attestKey().
-     * This field must be set only when requesting attestation of the device's identifiers.
+     * Tag::ATTESTATION_ID_IMEI provides the IMEIs for all radios on the device to attested key
+     * generation/import operations.  This field must be set only when requesting attestation of the
+     * device's identifiers.
      *
      * If the device does not support ID attestation (or destroyAttestationIds() was previously
      * called and the device can no longer attest its IDs), any key attestation request that
@@ -766,8 +748,9 @@
     ATTESTATION_ID_IMEI = (9 << 28) /* TagType:BYTES */ | 714,
 
     /**
-     * Tag::ATTESTATION_ID_MEID provides the MEIDs for all radios on the device to attestKey().
-     * This field must be set only when requesting attestation of the device's identifiers.
+     * Tag::ATTESTATION_ID_MEID provides the MEIDs for all radios on the device to attested key
+     * generation/import operations.  This field must be set only when requesting attestation of the
+     * device's identifiers.
      *
      * If the device does not support ID attestation (or destroyAttestationIds() was previously
      * called and the device can no longer attest its IDs), any key attestation request that
@@ -779,8 +762,8 @@
 
     /**
      * Tag::ATTESTATION_ID_MANUFACTURER provides the device's manufacturer name, as returned by
-     * Build.MANUFACTURER in Android, to attstKey().  This field must be set only when requesting
-     * attestation of the device's identifiers.
+     * Build.MANUFACTURER in Android, to attested key generation/import operations.  This field must
+     * be set only when requesting attestation of the device's identifiers.
      *
      * If the device does not support ID attestation (or destroyAttestationIds() was previously
      * called and the device can no longer attest its IDs), any key attestation request that
@@ -792,8 +775,8 @@
 
     /**
      * Tag::ATTESTATION_ID_MODEL provides the device's model name, as returned by Build.MODEL in
-     * Android, to attestKey().  This field must be set only when requesting attestation of the
-     * device's identifiers.
+     * Android, to attested key generation/import operations.  This field must be set only when
+     * requesting attestation of the device's identifiers.
      *
      * If the device does not support ID attestation (or destroyAttestationIds() was previously
      * called and the device can no longer attest its IDs), any key attestation request that
@@ -839,20 +822,20 @@
      * the value would be 20180605.  If the day is not known, 00 may be substituted.
      *
      * During each boot, the bootloader must provide the patch level of the boot image to the secure
-     * envirionment (mechanism is implementation-defined).
+     * environment (mechanism is implementation-defined).
      *
      * Must be hardware-enforced.
      */
     BOOT_PATCHLEVEL = (3 << 28) /* TagType:UINT */ | 719,
 
     /**
-     * DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attestKey().  It indicates that
-     * attestation using a device-unique key is requested, rather than a batch key.  When a
-     * device-unique key is used, only the attestation certificate is returned; no additional
-     * chained certificates are provided.  It's up to the caller to recognize the device-unique
-     * signing key.  Only SecurityLevel::STRONGBOX IKeyMintDevices may support device-unique
-     * attestations.  SecurityLevel::TRUSTED_ENVIRONMENT IKeyMintDevices must return
-     * ErrorCode::INVALID_ARGUMENT if they receive DEVICE_UNIQUE_ATTESTATION.
+     * DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attested key generation/import
+     * operations.  It indicates that attestation using a device-unique key is requested, rather
+     * than a batch key.  When a device-unique key is used, only the attestation certificate is
+     * returned; no additional chained certificates are provided.  It's up to the caller to
+     * recognize the device-unique signing key.  Only SecurityLevel::STRONGBOX IKeyMintDevices may
+     * support device-unique attestations.  SecurityLevel::TRUSTED_ENVIRONMENT IKeyMintDevices must
+     * return ErrorCode::INVALID_ARGUMENT if they receive DEVICE_UNIQUE_ATTESTATION.
      * SecurityLevel::STRONGBOX IKeyMintDevices need not support DEVICE_UNIQUE_ATTESTATION, and
      * return ErrorCode::CANNOT_ATTEST_IDS if they do not support it.
      *
@@ -935,33 +918,34 @@
     CONFIRMATION_TOKEN = (9 << 28) /* TagType:BYTES */ | 1005,
 
     /**
-     * Tag::CERTIFICATE_SERIAL specifies the serial number to be assigned to the
-     * attestation certificate to be generated for the given key.  This parameter should only
-     * be passed to keyMint in the attestation parameters during generateKey() and importKey().
+     * Tag::CERTIFICATE_SERIAL specifies the serial number to be assigned to the attestation
+     * certificate to be generated for the given key.  This parameter should only be passed to
+     * keyMint in the attestation parameters during generateKey() and importKey().  If not provided,
+     * the serial shall default to 1.
      */
     CERTIFICATE_SERIAL = (8 << 28) /* TagType:BIGNUM */ | 1006,
 
     /**
-     * Tag::CERTIFICATE_SUBJECT the certificate subject. The value is a DER encoded X509 NAME.
-     * This value is used when generating a self signed certificates. This tag may be specified
+     * Tag::CERTIFICATE_SUBJECT the certificate subject.  The value is a DER encoded X509 NAME.
+     * This value is used when generating a self signed certificates.  This tag may be specified
      * during generateKey and importKey. If not provided the subject name shall default to
-     * <TODO default subject here>.
+     * CN="Android Keystore Key".
      */
     CERTIFICATE_SUBJECT = (9 << 28) /* TagType:BYTES */ | 1007,
 
     /**
      * Tag::CERTIFICATE_NOT_BEFORE the beginning of the validity of the certificate in UNIX epoch
-     * time in seconds. This value is used when generating attestation or self signed certificates.
-     * ErrorCode::MISSING_NOT_BEFORE must be returned if this tag is not provided if this tag is
-     * not provided to generateKey or importKey.
+     * time in seconds.  This value is used when generating attestation or self signed certificates.
+     * ErrorCode::MISSING_NOT_BEFORE must be returned if this tag is not provided if this tag is not
+     * provided to generateKey or importKey.
      */
     CERTIFICATE_NOT_BEFORE = (6 << 28) /* TagType:DATE */ | 1008,
 
     /**
-     * Tag::CERTIFICATE_NOT_AFTER the end of the validity of the certificate in UNIX epoch
-     * time in seconds. This value is used when generating attestation or self signed certificates.
-     * ErrorCode::MISSING_NOT_AFTER must be returned if this tag is not provided to generateKey
-     * or importKey.
+     * Tag::CERTIFICATE_NOT_AFTER the end of the validity of the certificate in UNIX epoch time in
+     * seconds.  This value is used when generating attestation or self signed certificates.
+     * ErrorCode::MISSING_NOT_AFTER must be returned if this tag is not provided to generateKey or
+     * importKey.
      */
     CERTIFICATE_NOT_AFTER = (6 << 28) /* TagType:DATE */ | 1009,
 
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index daa3e18..881354d 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -35,10 +35,16 @@
 
 using AttestKeyTest = KeyMintAidlTestBase;
 
+/*
+ * AttestKeyTest.AllRsaSizes
+ *
+ * This test creates self signed RSA attestation keys of various sizes, and verify they can be
+ * used to sign other RSA and EC keys.
+ */
 TEST_P(AttestKeyTest, AllRsaSizes) {
     for (auto size : ValidKeySizes(Algorithm::RSA)) {
         /*
-         * Create attestaton key.
+         * Create attestation key.
          */
         AttestationKey attest_key;
         vector<KeyCharacteristics> attest_key_characteristics;
@@ -50,11 +56,12 @@
                                              {} /* attestation signing key */, &attest_key.keyBlob,
                                              &attest_key_characteristics, &attest_key_cert_chain));
 
+        ASSERT_GT(attest_key_cert_chain.size(), 0);
         EXPECT_EQ(attest_key_cert_chain.size(), 1);
         EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain)) << "Failed on size " << size;
 
         /*
-         * Use attestation key to sign RSA key
+         * Use attestation key to sign RSA signing key
          */
         attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
         vector<uint8_t> attested_key_blob;
@@ -81,20 +88,55 @@
         EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
 
         // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
-        if (attest_key_cert_chain.size() > 0) {
-            attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
-        }
+        attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
         EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+        EXPECT_EQ(attested_key_cert_chain.size(), 2);
 
         /*
-         * Use attestation key to sign EC key
+         * Use attestation key to sign RSA decryption key
          */
+        attested_key_characteristics.resize(0);
+        attested_key_cert_chain.resize(0);
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaEncryptionKey(2048, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationChallenge("foo2")
+                                      .AttestationApplicationId("bar2")
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        CheckedDeleteKey(&attested_key_blob);
+
+        hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo2", "bar2", sw_enforced, hw_enforced, SecLevel(),
+                                              attested_key_cert_chain[0].encodedCertificate));
+
+        // Attestation by itself is not valid (last entry is not self-signed).
+        EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+        // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+        attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+        EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+        EXPECT_EQ(attested_key_cert_chain.size(), 2);
+
+        /*
+         * Use attestation key to sign EC key. Specify a CREATION_DATETIME for this one.
+         */
+        attested_key_characteristics.resize(0);
+        attested_key_cert_chain.resize(0);
+        uint64_t timestamp = 1619621648000;
         EXPECT_EQ(ErrorCode::OK,
                   GenerateKey(AuthorizationSetBuilder()
                                       .EcdsaSigningKey(EcCurve::P_256)
                                       .Authorization(TAG_NO_AUTH_REQUIRED)
                                       .AttestationChallenge("foo")
                                       .AttestationApplicationId("bar")
+                                      .Authorization(TAG_CREATION_DATETIME, timestamp)
                                       .SetDefaultValidity(),
                               attest_key, &attested_key_blob, &attested_key_characteristics,
                               &attested_key_cert_chain));
@@ -104,6 +146,12 @@
 
         hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
         sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        // The client-specified CREATION_DATETIME should be in sw_enforced.
+        // Its presence will also trigger verify_attestation_record() to check that it
+        // is in the attestation extension with a matching value.
+        EXPECT_TRUE(sw_enforced.Contains(TAG_CREATION_DATETIME, timestamp))
+                << "expected CREATION_TIMESTAMP in sw_enforced:" << sw_enforced
+                << " not in hw_enforced:" << hw_enforced;
         EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
                                               attested_key_cert_chain[0].encodedCertificate));
 
@@ -111,9 +159,7 @@
         EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
 
         // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
-        if (attest_key_cert_chain.size() > 0) {
-            attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
-        }
+        attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
         EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
 
         // Bail early if anything failed.
@@ -121,10 +167,378 @@
     }
 }
 
+/*
+ * AttestKeyTest.RsaAttestedAttestKeys
+ *
+ * This test creates an RSA attestation key signed by factory keys, and varifies it can be
+ * used to sign other RSA and EC keys.
+ */
+TEST_P(AttestKeyTest, RsaAttestedAttestKeys) {
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 66;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    /*
+     * Create attestation key.
+     */
+    AttestationKey attest_key;
+    vector<KeyCharacteristics> attest_key_characteristics;
+    vector<Certificate> attest_key_cert_chain;
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaSigningKey(2048, 65537)
+                                  .AttestKey()
+                                  .AttestationChallenge(challenge)
+                                  .AttestationApplicationId(app_id)
+                                  .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                  .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .SetDefaultValidity(),
+                          {} /* attestation signing key */, &attest_key.keyBlob,
+                          &attest_key_characteristics, &attest_key_cert_chain));
+
+    EXPECT_GT(attest_key_cert_chain.size(), 1);
+    verify_subject_and_serial(attest_key_cert_chain[0], serial_int, subject, false);
+    EXPECT_TRUE(ChainSignaturesAreValid(attest_key_cert_chain));
+
+    AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attest_key_characteristics);
+    AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attest_key_characteristics);
+    EXPECT_TRUE(verify_attestation_record(challenge, app_id,  //
+                                          sw_enforced, hw_enforced, SecLevel(),
+                                          attest_key_cert_chain[0].encodedCertificate));
+
+    /*
+     * Use attestation key to sign RSA key
+     */
+    attest_key.issuerSubjectName = subject_der;
+    vector<uint8_t> attested_key_blob;
+    vector<KeyCharacteristics> attested_key_characteristics;
+    vector<Certificate> attested_key_cert_chain;
+
+    auto subject2 = "cert subject";
+    vector<uint8_t> subject_der2(make_name_from_str(subject2));
+
+    uint64_t serial_int2 = 987;
+    vector<uint8_t> serial_blob2(build_serial_blob(serial_int2));
+
+    EXPECT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaSigningKey(2048, 65537)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .AttestationChallenge("foo")
+                                  .AttestationApplicationId("bar")
+                                  .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob2)
+                                  .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der2)
+                                  .SetDefaultValidity(),
+                          attest_key, &attested_key_blob, &attested_key_characteristics,
+                          &attested_key_cert_chain));
+
+    CheckedDeleteKey(&attested_key_blob);
+    CheckedDeleteKey(&attest_key.keyBlob);
+
+    AuthorizationSet hw_enforced2 = HwEnforcedAuthorizations(attested_key_characteristics);
+    AuthorizationSet sw_enforced2 = SwEnforcedAuthorizations(attested_key_characteristics);
+    EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced2, hw_enforced2, SecLevel(),
+                                          attested_key_cert_chain[0].encodedCertificate));
+
+    // Attestation by itself is not valid (last entry is not self-signed).
+    EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+    // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+    attested_key_cert_chain.insert(attested_key_cert_chain.end(), attest_key_cert_chain.begin(),
+                                   attest_key_cert_chain.end());
+
+    EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+    EXPECT_GT(attested_key_cert_chain.size(), 2);
+    verify_subject_and_serial(attested_key_cert_chain[0], serial_int2, subject2, false);
+}
+
+/*
+ * AttestKeyTest.RsaAttestKeyChaining
+ *
+ * This test creates a chain of multiple RSA attest keys, each used to sign the next attest key,
+ * with the last attest key signed by the factory chain.
+ */
+TEST_P(AttestKeyTest, RsaAttestKeyChaining) {
+    const int chain_size = 6;
+    vector<vector<uint8_t>> key_blob_list(chain_size);
+    vector<vector<Certificate>> cert_chain_list(chain_size);
+
+    for (int i = 0; i < chain_size; i++) {
+        string sub = "attest key chaining ";
+        char index = '1' + i;
+        string subject = sub + index;
+        vector<uint8_t> subject_der(make_name_from_str(subject));
+
+        uint64_t serial_int = 7000 + i;
+        vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+        vector<KeyCharacteristics> attested_key_characteristics;
+        AttestationKey attest_key;
+        optional<AttestationKey> attest_key_opt;
+
+        if (i > 0) {
+            attest_key.issuerSubjectName = make_name_from_str(sub + (char)(index - 1));
+            attest_key.keyBlob = key_blob_list[i - 1];
+            attest_key_opt = attest_key;
+        }
+
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(2048, 65537)
+                                      .AttestKey()
+                                      .AttestationChallenge("foo")
+                                      .AttestationApplicationId("bar")
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+                              &cert_chain_list[i]));
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_list[i][0].encodedCertificate));
+
+        if (i > 0) {
+            /*
+             * The first key is attestated with factory chain, but all the rest of the keys are
+             * not supposed to be returned in attestation certificate chains.
+             */
+            EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_list[i]));
+
+            // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+            cert_chain_list[i].insert(cert_chain_list[i].end(),        //
+                                      cert_chain_list[i - 1].begin(),  //
+                                      cert_chain_list[i - 1].end());
+        }
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_list[i]));
+        EXPECT_GT(cert_chain_list[i].size(), i + 1);
+        verify_subject_and_serial(cert_chain_list[i][0], serial_int, subject, false);
+    }
+
+    for (int i = 0; i < chain_size; i++) {
+        CheckedDeleteKey(&key_blob_list[i]);
+    }
+}
+
+/*
+ * AttestKeyTest.EcAttestKeyChaining
+ *
+ * This test creates a chain of multiple Ec attest keys, each used to sign the next attest key,
+ * with the last attest key signed by the factory chain.
+ */
+TEST_P(AttestKeyTest, EcAttestKeyChaining) {
+    const int chain_size = 6;
+    vector<vector<uint8_t>> key_blob_list(chain_size);
+    vector<vector<Certificate>> cert_chain_list(chain_size);
+
+    for (int i = 0; i < chain_size; i++) {
+        string sub = "Ec attest key chaining ";
+        char index = '1' + i;
+        string subject = sub + index;
+        vector<uint8_t> subject_der(make_name_from_str(subject));
+
+        uint64_t serial_int = 800000 + i;
+        vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+        vector<KeyCharacteristics> attested_key_characteristics;
+        AttestationKey attest_key;
+        optional<AttestationKey> attest_key_opt;
+
+        if (i > 0) {
+            attest_key.issuerSubjectName = make_name_from_str(sub + (char)(index - 1));
+            attest_key.keyBlob = key_blob_list[i - 1];
+            attest_key_opt = attest_key;
+        }
+
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .EcdsaSigningKey(224)
+                                      .AttestKey()
+                                      .AttestationChallenge("foo")
+                                      .AttestationApplicationId("bar")
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .SetDefaultValidity(),
+                              attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+                              &cert_chain_list[i]));
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_list[i][0].encodedCertificate));
+
+        if (i > 0) {
+            /*
+             * The first key is attestated with factory chain, but all the rest of the keys are
+             * not supposed to be returned in attestation certificate chains.
+             */
+            EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_list[i]));
+
+            // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+            cert_chain_list[i].insert(cert_chain_list[i].end(),        //
+                                      cert_chain_list[i - 1].begin(),  //
+                                      cert_chain_list[i - 1].end());
+        }
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_list[i]));
+        EXPECT_GT(cert_chain_list[i].size(), i + 1);
+        verify_subject_and_serial(cert_chain_list[i][0], serial_int, subject, false);
+    }
+
+    for (int i = 0; i < chain_size; i++) {
+        CheckedDeleteKey(&key_blob_list[i]);
+    }
+}
+
+/*
+ * AttestKeyTest.AlternateAttestKeyChaining
+ *
+ * This test creates a chain of multiple attest keys, in the order Ec - RSA - Ec - RSA ....
+ * Each attest key is used to sign the next attest key, with the last attest key signed by
+ * the factory chain. This is to verify different algorithms of attest keys can
+ * cross sign each other and be chained together.
+ */
+TEST_P(AttestKeyTest, AlternateAttestKeyChaining) {
+    const int chain_size = 6;
+    vector<vector<uint8_t>> key_blob_list(chain_size);
+    vector<vector<Certificate>> cert_chain_list(chain_size);
+
+    for (int i = 0; i < chain_size; i++) {
+        string sub = "Alt attest key chaining ";
+        char index = '1' + i;
+        string subject = sub + index;
+        vector<uint8_t> subject_der(make_name_from_str(subject));
+
+        uint64_t serial_int = 90000000 + i;
+        vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+        vector<KeyCharacteristics> attested_key_characteristics;
+        AttestationKey attest_key;
+        optional<AttestationKey> attest_key_opt;
+
+        if (i > 0) {
+            attest_key.issuerSubjectName = make_name_from_str(sub + (char)(index - 1));
+            attest_key.keyBlob = key_blob_list[i - 1];
+            attest_key_opt = attest_key;
+        }
+
+        if ((i & 0x1) == 1) {
+            EXPECT_EQ(ErrorCode::OK,
+                      GenerateKey(AuthorizationSetBuilder()
+                                          .EcdsaSigningKey(224)
+                                          .AttestKey()
+                                          .AttestationChallenge("foo")
+                                          .AttestationApplicationId("bar")
+                                          .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                          .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                                          .SetDefaultValidity(),
+                                  attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+                                  &cert_chain_list[i]));
+        } else {
+            EXPECT_EQ(ErrorCode::OK,
+                      GenerateKey(AuthorizationSetBuilder()
+                                          .RsaSigningKey(2048, 65537)
+                                          .AttestKey()
+                                          .AttestationChallenge("foo")
+                                          .AttestationApplicationId("bar")
+                                          .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                          .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                                          .SetDefaultValidity(),
+                                  attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+                                  &cert_chain_list[i]));
+        }
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_list[i][0].encodedCertificate));
+
+        if (i > 0) {
+            /*
+             * The first key is attestated with factory chain, but all the rest of the keys are
+             * not supposed to be returned in attestation certificate chains.
+             */
+            EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_list[i]));
+
+            // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+            cert_chain_list[i].insert(cert_chain_list[i].end(),        //
+                                      cert_chain_list[i - 1].begin(),  //
+                                      cert_chain_list[i - 1].end());
+        }
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_list[i]));
+        EXPECT_GT(cert_chain_list[i].size(), i + 1);
+        verify_subject_and_serial(cert_chain_list[i][0], serial_int, subject, false);
+    }
+
+    for (int i = 0; i < chain_size; i++) {
+        CheckedDeleteKey(&key_blob_list[i]);
+    }
+}
+
+TEST_P(AttestKeyTest, MissingChallenge) {
+    for (auto size : ValidKeySizes(Algorithm::RSA)) {
+        /*
+         * Create attestation key.
+         */
+        AttestationKey attest_key;
+        vector<KeyCharacteristics> attest_key_characteristics;
+        vector<Certificate> attest_key_cert_chain;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .RsaSigningKey(size, 65537)
+                                                     .AttestKey()
+                                                     .SetDefaultValidity(),
+                                             {} /* attestation signing key */, &attest_key.keyBlob,
+                                             &attest_key_characteristics, &attest_key_cert_chain));
+
+        EXPECT_EQ(attest_key_cert_chain.size(), 1);
+        EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain)) << "Failed on size " << size;
+
+        /*
+         * Use attestation key to sign RSA / ECDSA key but forget to provide a challenge
+         */
+        attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+        vector<uint8_t> attested_key_blob;
+        vector<KeyCharacteristics> attested_key_characteristics;
+        vector<Certificate> attested_key_cert_chain;
+        EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(2048, 65537)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationApplicationId("bar")
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .EcdsaSigningKey(EcCurve::P_256)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationApplicationId("bar")
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        CheckedDeleteKey(&attest_key.keyBlob);
+    }
+}
+
 TEST_P(AttestKeyTest, AllEcCurves) {
     for (auto curve : ValidCurves()) {
         /*
-         * Create attestaton key.
+         * Create attestation key.
          */
         AttestationKey attest_key;
         vector<KeyCharacteristics> attest_key_characteristics;
@@ -136,6 +550,7 @@
                                              {} /* attestation siging key */, &attest_key.keyBlob,
                                              &attest_key_characteristics, &attest_key_cert_chain));
 
+        ASSERT_GT(attest_key_cert_chain.size(), 0);
         EXPECT_EQ(attest_key_cert_chain.size(), 1);
         EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain)) << "Failed on curve " << curve;
 
@@ -208,7 +623,7 @@
 }
 
 TEST_P(AttestKeyTest, AttestWithNonAttestKey) {
-    // Create non-attestaton key.
+    // Create non-attestation key.
     AttestationKey non_attest_key;
     vector<KeyCharacteristics> non_attest_key_characteristics;
     vector<Certificate> non_attest_key_cert_chain;
@@ -219,6 +634,7 @@
                     {} /* attestation siging key */, &non_attest_key.keyBlob,
                     &non_attest_key_characteristics, &non_attest_key_cert_chain));
 
+    ASSERT_GT(non_attest_key_cert_chain.size(), 0);
     EXPECT_EQ(non_attest_key_cert_chain.size(), 1);
     EXPECT_TRUE(IsSelfSigned(non_attest_key_cert_chain));
 
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
index 7009c6e..6f0ee4e 100644
--- a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -70,13 +70,12 @@
                                       .Digest(Digest::SHA_2_256)
                                       .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
                                       .Authorization(TAG_INCLUDE_UNIQUE_ID)
-                                      .Authorization(TAG_NO_AUTH_REQUIRED)
                                       .AttestationChallenge("challenge")
                                       .AttestationApplicationId("foo")
                                       .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
                               &key_blob, &key_characteristics);
 
-    ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_TAG);
+    ASSERT_EQ(result, ErrorCode::INVALID_ARGUMENT);
 }
 
 /*
@@ -102,7 +101,7 @@
                                       .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
                               &key_blob, &key_characteristics);
 
-    ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_TAG);
+    ASSERT_EQ(result, ErrorCode::INVALID_ARGUMENT);
 }
 
 /*
@@ -124,7 +123,6 @@
                                       .Digest(Digest::SHA_2_256)
                                       .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
                                       .Authorization(TAG_INCLUDE_UNIQUE_ID)
-                                      .Authorization(TAG_NO_AUTH_REQUIRED)
                                       .AttestationChallenge("challenge")
                                       .AttestationApplicationId("foo")
                                       .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 59cb57b..4789204 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -119,7 +119,6 @@
 // Attestations don't contain everything in key authorization lists, so we need to filter the key
 // lists to produce the lists that we expect to match the attestations.
 auto kTagsToFilter = {
-        Tag::BLOB_USAGE_REQUIREMENTS,  //
         Tag::CREATION_DATETIME,        //
         Tag::EC_CURVE,
         Tag::HARDWARE_TYPE,
@@ -168,9 +167,11 @@
     securityLevel_ = info.securityLevel;
     name_.assign(info.keyMintName.begin(), info.keyMintName.end());
     author_.assign(info.keyMintAuthorName.begin(), info.keyMintAuthorName.end());
+    timestamp_token_required_ = info.timestampTokenRequired;
 
     os_version_ = getOsVersion();
     os_patch_level_ = getOsPatchlevel();
+    vendor_patch_level_ = getVendorPatchlevel();
 }
 
 void KeyMintAidlTestBase::SetUp() {
@@ -273,7 +274,8 @@
 ErrorCode KeyMintAidlTestBase::ImportWrappedKey(string wrapped_key, string wrapping_key,
                                                 const AuthorizationSet& wrapping_key_desc,
                                                 string masking_key,
-                                                const AuthorizationSet& unwrapping_params) {
+                                                const AuthorizationSet& unwrapping_params,
+                                                int64_t password_sid, int64_t biometric_sid) {
     EXPECT_EQ(ErrorCode::OK, ImportKey(wrapping_key_desc, KeyFormat::PKCS8, wrapping_key));
 
     key_characteristics_.clear();
@@ -282,8 +284,7 @@
     Status result = keymint_->importWrappedKey(
             vector<uint8_t>(wrapped_key.begin(), wrapped_key.end()), key_blob_,
             vector<uint8_t>(masking_key.begin(), masking_key.end()),
-            unwrapping_params.vector_data(), 0 /* passwordSid */, 0 /* biometricSid */,
-            &creationResult);
+            unwrapping_params.vector_data(), password_sid, biometric_sid, &creationResult);
 
     if (result.isOk()) {
         EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
@@ -332,6 +333,11 @@
     return GetReturnErrorCode(result);
 }
 
+ErrorCode KeyMintAidlTestBase::DestroyAttestationIds() {
+    Status result = keymint_->destroyAttestationIds();
+    return GetReturnErrorCode(result);
+}
+
 void KeyMintAidlTestBase::CheckedDeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob) {
     ErrorCode result = DeleteKey(key_blob, keep_key_blob);
     EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED) << result << endl;
@@ -348,7 +354,7 @@
     SCOPED_TRACE("Begin");
     Status result;
     BeginResult out;
-    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), HardwareAuthToken(), &out);
+    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out);
 
     if (result.isOk()) {
         *out_params = out.params;
@@ -366,7 +372,7 @@
     Status result;
     BeginResult out;
 
-    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), HardwareAuthToken(), &out);
+    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out);
 
     if (result.isOk()) {
         *out_params = out.params;
@@ -654,6 +660,18 @@
     return ciphertext;
 }
 
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding, uint8_t mac_length_bits) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(block_mode)
+                          .Padding(padding)
+                          .Authorization(TAG_MAC_LENGTH, mac_length_bits);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    return ciphertext;
+}
+
 string KeyMintAidlTestBase::DecryptMessage(const vector<uint8_t>& key_blob,
                                            const string& ciphertext,
                                            const AuthorizationSet& params) {
@@ -744,6 +762,15 @@
                 return {224, 384, 521};
             case Algorithm::AES:
                 return {192};
+            case Algorithm::TRIPLE_DES:
+                return {56};
+            default:
+                return {};
+        }
+    } else {
+        switch (algorithm) {
+            case Algorithm::TRIPLE_DES:
+                return {56};
             default:
                 return {};
         }
@@ -751,6 +778,68 @@
     return {};
 }
 
+vector<BlockMode> KeyMintAidlTestBase::ValidBlockModes(Algorithm algorithm) {
+    switch (algorithm) {
+        case Algorithm::AES:
+            return {
+                    BlockMode::CBC,
+                    BlockMode::CTR,
+                    BlockMode::ECB,
+                    BlockMode::GCM,
+            };
+        case Algorithm::TRIPLE_DES:
+            return {
+                    BlockMode::CBC,
+                    BlockMode::ECB,
+            };
+        default:
+            return {};
+    }
+}
+
+vector<PaddingMode> KeyMintAidlTestBase::ValidPaddingModes(Algorithm algorithm,
+                                                           BlockMode blockMode) {
+    switch (algorithm) {
+        case Algorithm::AES:
+            switch (blockMode) {
+                case BlockMode::CBC:
+                case BlockMode::ECB:
+                    return {PaddingMode::NONE, PaddingMode::PKCS7};
+                case BlockMode::CTR:
+                case BlockMode::GCM:
+                    return {PaddingMode::NONE};
+                default:
+                    return {};
+            };
+        case Algorithm::TRIPLE_DES:
+            switch (blockMode) {
+                case BlockMode::CBC:
+                case BlockMode::ECB:
+                    return {PaddingMode::NONE, PaddingMode::PKCS7};
+                default:
+                    return {};
+            };
+        default:
+            return {};
+    }
+}
+
+vector<PaddingMode> KeyMintAidlTestBase::InvalidPaddingModes(Algorithm algorithm,
+                                                             BlockMode blockMode) {
+    switch (algorithm) {
+        case Algorithm::AES:
+            switch (blockMode) {
+                case BlockMode::CTR:
+                case BlockMode::GCM:
+                    return {PaddingMode::PKCS7};
+                default:
+                    return {};
+            };
+        default:
+            return {};
+    }
+}
+
 vector<EcCurve> KeyMintAidlTestBase::ValidCurves() {
     if (securityLevel_ == SecurityLevel::STRONGBOX) {
         return {EcCurve::P_256};
@@ -845,6 +934,66 @@
     return result;
 }
 
+void verify_serial(X509* cert, const uint64_t expected_serial) {
+    BIGNUM_Ptr ser(BN_new());
+    EXPECT_TRUE(ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), ser.get()));
+
+    uint64_t serial;
+    EXPECT_TRUE(BN_get_u64(ser.get(), &serial));
+    EXPECT_EQ(serial, expected_serial);
+}
+
+// Please set self_signed to true for fake certificates or self signed
+// certificates
+void verify_subject(const X509* cert,       //
+                    const string& subject,  //
+                    bool self_signed) {
+    char* cert_issuer =  //
+            X509_NAME_oneline(X509_get_issuer_name(cert), nullptr, 0);
+
+    char* cert_subj = X509_NAME_oneline(X509_get_subject_name(cert), nullptr, 0);
+
+    string expected_subject("/CN=");
+    if (subject.empty()) {
+        expected_subject.append("Android Keystore Key");
+    } else {
+        expected_subject.append(subject);
+    }
+
+    EXPECT_STREQ(expected_subject.c_str(), cert_subj) << "Cert has wrong subject." << cert_subj;
+
+    if (self_signed) {
+        EXPECT_STREQ(cert_issuer, cert_subj)
+                << "Cert issuer and subject mismatch for self signed certificate.";
+    }
+
+    OPENSSL_free(cert_subj);
+    OPENSSL_free(cert_issuer);
+}
+
+vector<uint8_t> build_serial_blob(const uint64_t serial_int) {
+    BIGNUM_Ptr serial(BN_new());
+    EXPECT_TRUE(BN_set_u64(serial.get(), serial_int));
+
+    int len = BN_num_bytes(serial.get());
+    vector<uint8_t> serial_blob(len);
+    if (BN_bn2bin(serial.get(), serial_blob.data()) != len) {
+        return {};
+    }
+
+    return serial_blob;
+}
+
+void verify_subject_and_serial(const Certificate& certificate,  //
+                               const uint64_t expected_serial,  //
+                               const string& subject, bool self_signed) {
+    X509_Ptr cert(parse_cert_blob(certificate.encodedCertificate));
+    ASSERT_TRUE(!!cert.get());
+
+    verify_serial(cert.get(), expected_serial);
+    verify_subject(cert.get(), subject, self_signed);
+}
+
 bool verify_attestation_record(const string& challenge,                //
                                const string& app_id,                   //
                                AuthorizationSet expected_sw_enforced,  //
@@ -882,7 +1031,7 @@
     EXPECT_EQ(ErrorCode::OK, error);
     if (error != ErrorCode::OK) return false;
 
-    EXPECT_GE(att_attestation_version, 3U);
+    EXPECT_EQ(att_attestation_version, 100U);
     vector<uint8_t> appId(app_id.begin(), app_id.end());
 
     // check challenge and app id only if we expects a non-fake certificate
@@ -893,7 +1042,7 @@
         expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID, appId);
     }
 
-    EXPECT_GE(att_keymaster_version, 4U);
+    EXPECT_EQ(att_keymaster_version, 100U);
     EXPECT_EQ(security_level, att_keymaster_security_level);
     EXPECT_EQ(security_level, att_attestation_security_level);
 
@@ -1082,17 +1231,10 @@
         string cert_issuer = x509NameToStr(X509_get_issuer_name(key_cert.get()));
         string signer_subj = x509NameToStr(X509_get_subject_name(signing_cert.get()));
         if (cert_issuer != signer_subj) {
-            return AssertionFailure() << "Cert " << i << " has wrong issuer.\n" << cert_data.str();
-        }
-
-        if (i == 0) {
-            string cert_sub = x509NameToStr(X509_get_subject_name(key_cert.get()));
-            if ("/CN=Android Keystore Key" != cert_sub) {
-                return AssertionFailure()
-                       << "Leaf cert has wrong subject, should be CN=Android Keystore Key, was "
-                       << cert_sub << '\n'
-                       << cert_data.str();
-            }
+            return AssertionFailure() << "Cert " << i << " has wrong issuer.\n"
+                                      << " Signer subject is " << signer_subj
+                                      << " Issuer subject is " << cert_issuer << endl
+                                      << cert_data.str();
         }
     }
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index da174ce..cb38938 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -71,6 +71,7 @@
     IKeyMintDevice& keyMint() { return *keymint_; }
     uint32_t os_version() { return os_version_; }
     uint32_t os_patch_level() { return os_patch_level_; }
+    uint32_t vendor_patch_level() { return vendor_patch_level_; }
 
     ErrorCode GetReturnErrorCode(const Status& result);
 
@@ -94,13 +95,22 @@
 
     ErrorCode ImportWrappedKey(string wrapped_key, string wrapping_key,
                                const AuthorizationSet& wrapping_key_desc, string masking_key,
-                               const AuthorizationSet& unwrapping_params);
+                               const AuthorizationSet& unwrapping_params, int64_t password_sid,
+                               int64_t biometric_sid);
+    ErrorCode ImportWrappedKey(string wrapped_key, string wrapping_key,
+                               const AuthorizationSet& wrapping_key_desc, string masking_key,
+                               const AuthorizationSet& unwrapping_params) {
+        return ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, masking_key,
+                                unwrapping_params, 0 /* password_sid */, 0 /* biometric_sid */);
+    }
 
     ErrorCode DeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob = false);
     ErrorCode DeleteKey(bool keep_key_blob = false);
 
     ErrorCode DeleteAllKeys();
 
+    ErrorCode DestroyAttestationIds();
+
     void CheckedDeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob = false);
     void CheckedDeleteKey();
 
@@ -165,6 +175,8 @@
                           const vector<uint8_t>& iv_in);
     string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
                           uint8_t mac_length_bits, const vector<uint8_t>& iv_in);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+                          uint8_t mac_length_bits);
 
     string DecryptMessage(const vector<uint8_t>& key_blob, const string& ciphertext,
                           const AuthorizationSet& params);
@@ -230,6 +242,10 @@
     vector<uint32_t> ValidKeySizes(Algorithm algorithm);
     vector<uint32_t> InvalidKeySizes(Algorithm algorithm);
 
+    vector<BlockMode> ValidBlockModes(Algorithm algorithm);
+    vector<PaddingMode> ValidPaddingModes(Algorithm algorithm, BlockMode blockMode);
+    vector<PaddingMode> InvalidPaddingModes(Algorithm algorithm, BlockMode blockMode);
+
     vector<EcCurve> ValidCurves();
     vector<EcCurve> InvalidCurves();
 
@@ -262,6 +278,8 @@
     std::shared_ptr<IKeyMintDevice> keymint_;
     uint32_t os_version_;
     uint32_t os_patch_level_;
+    uint32_t vendor_patch_level_;
+    bool timestamp_token_required_;
 
     SecurityLevel securityLevel_;
     string name_;
@@ -269,12 +287,20 @@
     long challenge_;
 };
 
+vector<uint8_t> build_serial_blob(const uint64_t serial_int);
+void verify_subject(const X509* cert, const string& subject, bool self_signed);
+void verify_serial(X509* cert, const uint64_t expected_serial);
+void verify_subject_and_serial(const Certificate& certificate,  //
+                               const uint64_t expected_serial,  //
+                               const string& subject, bool self_signed);
+
 bool verify_attestation_record(const string& challenge,                //
                                const string& app_id,                   //
                                AuthorizationSet expected_sw_enforced,  //
                                AuthorizationSet expected_hw_enforced,  //
                                SecurityLevel security_level,
                                const vector<uint8_t>& attestation_cert);
+
 string bin2hex(const vector<uint8_t>& data);
 X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
 vector<uint8_t> make_name_from_str(const string& name);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 09cdab1..bcf8b95 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -67,6 +67,8 @@
 
 namespace {
 
+bool check_patchLevels = false;
+
 template <TagType tag_type, Tag tag, typename ValueT>
 bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag,
               ValueT expected_value) {
@@ -113,109 +115,296 @@
     return b;
 }
 
-string rsa_key =
-        hex2str("30820275020100300d06092a864886f70d01010105000482025f3082025b"
-                "02010002818100c6095409047d8634812d5a218176e45c41d60a75b13901"
-                "f234226cffe776521c5a77b9e389417b71c0b6a44d13afe4e4a2805d46c9"
-                "da2935adb1ff0c1f24ea06e62b20d776430a4d435157233c6f916783c30e"
-                "310fcbd89b85c2d56771169785ac12bca244abda72bfb19fc44d27c81e1d"
-                "92de284f4061edfd99280745ea6d2502030100010281801be0f04d9cae37"
-                "18691f035338308e91564b55899ffb5084d2460e6630257e05b3ceab0297"
-                "2dfabcd6ce5f6ee2589eb67911ed0fac16e43a444b8c861e544a05933657"
-                "72f8baf6b22fc9e3c5f1024b063ac080a7b2234cf8aee8f6c47bbf4fd3ac"
-                "e7240290bef16c0b3f7f3cdd64ce3ab5912cf6e32f39ab188358afcccd80"
-                "81024100e4b49ef50f765d3b24dde01aceaaf130f2c76670a91a61ae08af"
-                "497b4a82be6dee8fcdd5e3f7ba1cfb1f0c926b88f88c92bfab137fba2285"
-                "227b83c342ff7c55024100ddabb5839c4c7f6bf3d4183231f005b31aa58a"
-                "ffdda5c79e4cce217f6bc930dbe563d480706c24e9ebfcab28a6cdefd324"
-                "b77e1bf7251b709092c24ff501fd91024023d4340eda3445d8cd26c14411"
-                "da6fdca63c1ccd4b80a98ad52b78cc8ad8beb2842c1d280405bc2f6c1bea"
-                "214a1d742ab996b35b63a82a5e470fa88dbf823cdd02401b7b57449ad30d"
-                "1518249a5f56bb98294d4b6ac12ffc86940497a5a5837a6cf946262b4945"
-                "26d328c11e1126380fde04c24f916dec250892db09a6d77cdba351024077"
-                "62cd8f4d050da56bd591adb515d24d7ccd32cca0d05f866d583514bd7324"
-                "d5f33645e8ed8b4a1cb3cc4a1d67987399f2a09f5b3fb68c88d5e5d90ac3"
-                "3492d6");
+string rsa_key = hex2str(
+        // RFC 5208 s5
+        "30820275"            // SEQUENCE length 0x275 (PrivateKeyInfo) {
+        "020100"              // INTEGER length 1 value 0x00 (version)
+        "300d"                // SEQUENCE length 0x0d (AlgorithmIdentifier) {
+        "0609"                // OBJECT IDENTIFIER length 9 (algorithm)
+        "2a864886f70d010101"  // 1.2.840.113549.1.1.1 (rsaEncryption)
+        "0500"                // NULL (parameters)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "0482025f"  // OCTET STRING length 0x25f (privateKey) holding...
+        // RFC 8017 A.1.2
+        "3082025b"  // SEQUENCE length 0x25b (RSAPrivateKey) {
+        "020100"    // INTEGER length 1 value 0x00 (version)
+        "028181"    // INTEGER length 0x81 value (modulus) ...
+        "00c6095409047d8634812d5a218176e4"
+        "5c41d60a75b13901f234226cffe77652"
+        "1c5a77b9e389417b71c0b6a44d13afe4"
+        "e4a2805d46c9da2935adb1ff0c1f24ea"
+        "06e62b20d776430a4d435157233c6f91"
+        "6783c30e310fcbd89b85c2d567711697"
+        "85ac12bca244abda72bfb19fc44d27c8"
+        "1e1d92de284f4061edfd99280745ea6d"
+        "25"
+        "0203010001"  // INTEGER length 3 value 0x10001 (publicExponent)
+        "028180"      // INTEGER length 0x80 (privateExponent) value...
+        "1be0f04d9cae3718691f035338308e91"
+        "564b55899ffb5084d2460e6630257e05"
+        "b3ceab02972dfabcd6ce5f6ee2589eb6"
+        "7911ed0fac16e43a444b8c861e544a05"
+        "93365772f8baf6b22fc9e3c5f1024b06"
+        "3ac080a7b2234cf8aee8f6c47bbf4fd3"
+        "ace7240290bef16c0b3f7f3cdd64ce3a"
+        "b5912cf6e32f39ab188358afcccd8081"
+        "0241"  // INTEGER length 0x41 (prime1)
+        "00e4b49ef50f765d3b24dde01aceaaf1"
+        "30f2c76670a91a61ae08af497b4a82be"
+        "6dee8fcdd5e3f7ba1cfb1f0c926b88f8"
+        "8c92bfab137fba2285227b83c342ff7c"
+        "55"
+        "0241"  // INTEGER length 0x41 (prime2)
+        "00ddabb5839c4c7f6bf3d4183231f005"
+        "b31aa58affdda5c79e4cce217f6bc930"
+        "dbe563d480706c24e9ebfcab28a6cdef"
+        "d324b77e1bf7251b709092c24ff501fd"
+        "91"
+        "0240"  // INTEGER length 0x40 (exponent1)
+        "23d4340eda3445d8cd26c14411da6fdc"
+        "a63c1ccd4b80a98ad52b78cc8ad8beb2"
+        "842c1d280405bc2f6c1bea214a1d742a"
+        "b996b35b63a82a5e470fa88dbf823cdd"
+        "0240"  // INTEGER length 0x40 (exponent2)
+        "1b7b57449ad30d1518249a5f56bb9829"
+        "4d4b6ac12ffc86940497a5a5837a6cf9"
+        "46262b494526d328c11e1126380fde04"
+        "c24f916dec250892db09a6d77cdba351"
+        "0240"  // INTEGER length 0x40 (coefficient)
+        "7762cd8f4d050da56bd591adb515d24d"
+        "7ccd32cca0d05f866d583514bd7324d5"
+        "f33645e8ed8b4a1cb3cc4a1d67987399"
+        "f2a09f5b3fb68c88d5e5d90ac33492d6"
+        // } end SEQUENCE (PrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
 
 /*
  * DER-encoded PKCS#8 format RSA key. Generated using:
  *
  * openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -outform der | hexdump -e '30/1  "%02X" "\n"'
  */
-string rsa_2048_key =
-        hex2str("308204BD020100300D06092A864886F70D0101010500048204A7308204A3"
-                "0201000282010100BEBC342B56D443B1299F9A6A7056E80A897E318476A5"
-                "A18029E63B2ED739A61791D339F58DC763D9D14911F2EDEC383DEE11F631"
-                "9B44510E7A3ECD9B79B97382E49500ACF8117DC89CAF0E621F77756554A2"
-                "FD4664BFE7AB8B59AB48340DBFA27B93B5A81F6ECDEB02D0759307128DF3"
-                "E3BAD4055C8B840216DFAA5700670E6C5126F0962FCB70FF308F25049164"
-                "CCF76CC2DA66A7DD9A81A714C2809D69186133D29D84568E892B6FFBF319"
-                "9BDB14383EE224407F190358F111A949552ABA6714227D1BD7F6B20DD0CB"
-                "88F9467B719339F33BFF35B3870B3F62204E4286B0948EA348B524544B5F"
-                "9838F29EE643B079EEF8A713B220D7806924CDF7295070C5020301000102"
-                "82010069F377F35F2F584EF075353CCD1CA99738DB3DBC7C7FF35F9366CE"
-                "176DFD1B135AB10030344ABF5FBECF1D4659FDEF1C0FC430834BE1BE3911"
-                "951377BB3D563A2EA9CA8F4AD9C48A8CE6FD516A735C662686C7B4B3C09A"
-                "7B8354133E6F93F790D59EAEB92E84C9A4339302CCE28FDF04CCCAFA7DE3"
-                "F3A827D4F6F7D38E68B0EC6AB706645BF074A4E4090D06FB163124365FD5"
-                "EE7A20D350E9958CC30D91326E1B292E9EF5DB408EC42DAF737D20149704"
-                "D0A678A0FB5B5446863B099228A352D604BA8091A164D01D5AB05397C71E"
-                "AD20BE2A08FC528FE442817809C787FEE4AB97F97B9130D022153EDC6EB6"
-                "CBE7B0F8E3473F2E901209B5DB10F93604DB0102818100E83C0998214941"
-                "EA4F9293F1B77E2E99E6CF305FAF358238E126124FEAF2EB9724B2EA7B78"
-                "E6032343821A80E55D1D88FB12D220C3F41A56142FEC85796D1917F1E8C7"
-                "74F142B67D3D6E7B7E6B4383E94DB5929089DBB346D5BDAB40CC2D96EE04"
-                "09475E175C63BF78CFD744136740838127EA723FF3FE7FA368C1311B4A4E"
-                "0502818100D240FCC0F5D7715CDE21CB2DC86EA146132EA3B06F61FF2AF5"
-                "4BF38473F59DADCCE32B5F4CC32DD0BA6F509347B4B5B1B58C39F95E4798"
-                "CCBB43E83D0119ACF532F359CA743C85199F0286610E200997D731291717"
-                "9AC9B67558773212EC961E8BCE7A3CC809BC5486A96E4B0E6AF394D94E06"
-                "6A0900B7B70E82A44FB30053C102818100AD15DA1CBD6A492B66851BA8C3"
-                "16D38AB700E2CFDDD926A658003513C54BAA152B30021D667D20078F500F"
-                "8AD3E7F3945D74A891ED1A28EAD0FEEAEC8C14A8E834CF46A13D1378C99D"
-                "18940823CFDD27EC5810D59339E0C34198AC638E09C87CBB1B634A9864AE"
-                "9F4D5EB2D53514F67B4CAEC048C8AB849A02E397618F3271350281801FA2"
-                "C1A5331880A92D8F3E281C617108BF38244F16E352E69ED417C7153F9EC3"
-                "18F211839C643DCF8B4DD67CE2AC312E95178D5D952F06B1BF779F491692"
-                "4B70F582A23F11304E02A5E7565AE22A35E74FECC8B6FDC93F92A1A37703"
-                "E4CF0E63783BD02EB716A7ECBBFA606B10B74D01579522E7EF84D91FC522"
-                "292108D902C1028180796FE3825F9DCC85DF22D58690065D93898ACD65C0"
-                "87BEA8DA3A63BF4549B795E2CD0E3BE08CDEBD9FCF1720D9CDC5070D74F4"
-                "0DED8E1102C52152A31B6165F83A6722AECFCC35A493D7634664B888A08D"
-                "3EB034F12EA28BFEE346E205D334827F778B16ED40872BD29FCB36536B6E"
-                "93FFB06778696B4A9D81BB0A9423E63DE5");
+string rsa_2048_key = hex2str(
+        // RFC 5208 s5
+        "308204BD"            // SEQUENCE length 0x4bd (PrivateKeyInfo) {
+        "020100"              // INTEGER length 1 value 0x00 (version)
+        "300D"                // SEQUENCE length 0x0d (AlgorithmIdentifier) {
+        "0609"                // OBJECT IDENTIFIER length 9 (algorithm)
+        "2A864886F70D010101"  // 1.2.840.113549.1.1.1 (rsaEncryption)
+        "0500"                // NULL (parameters)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "048204A7"  // OCTET STRING length 0x25f (privateKey) holding...
+        // RFC 8017 A.1.2
+        "308204A3"  // SEQUENCE length 0x4a3 (RSAPrivateKey) {
+        "020100"    // INTEGER length 1 value 0x00 (version)
+        "02820101"  // INTEGER length 0x101 value (modulus) ...
+        "00BEBC342B56D443B1299F9A6A7056E8"
+        "0A897E318476A5A18029E63B2ED739A6"
+        "1791D339F58DC763D9D14911F2EDEC38"
+        "3DEE11F6319B44510E7A3ECD9B79B973"
+        "82E49500ACF8117DC89CAF0E621F7775"
+        "6554A2FD4664BFE7AB8B59AB48340DBF"
+        "A27B93B5A81F6ECDEB02D0759307128D"
+        "F3E3BAD4055C8B840216DFAA5700670E"
+        "6C5126F0962FCB70FF308F25049164CC"
+        "F76CC2DA66A7DD9A81A714C2809D6918"
+        "6133D29D84568E892B6FFBF3199BDB14"
+        "383EE224407F190358F111A949552ABA"
+        "6714227D1BD7F6B20DD0CB88F9467B71"
+        "9339F33BFF35B3870B3F62204E4286B0"
+        "948EA348B524544B5F9838F29EE643B0"
+        "79EEF8A713B220D7806924CDF7295070"
+        "C5"
+        "0203010001"  // INTEGER length 3 value 0x10001 (publicExponent)
+        "02820100"    // INTEGER length 0x100 (privateExponent) value...
+        "69F377F35F2F584EF075353CCD1CA997"
+        "38DB3DBC7C7FF35F9366CE176DFD1B13"
+        "5AB10030344ABF5FBECF1D4659FDEF1C"
+        "0FC430834BE1BE3911951377BB3D563A"
+        "2EA9CA8F4AD9C48A8CE6FD516A735C66"
+        "2686C7B4B3C09A7B8354133E6F93F790"
+        "D59EAEB92E84C9A4339302CCE28FDF04"
+        "CCCAFA7DE3F3A827D4F6F7D38E68B0EC"
+        "6AB706645BF074A4E4090D06FB163124"
+        "365FD5EE7A20D350E9958CC30D91326E"
+        "1B292E9EF5DB408EC42DAF737D201497"
+        "04D0A678A0FB5B5446863B099228A352"
+        "D604BA8091A164D01D5AB05397C71EAD"
+        "20BE2A08FC528FE442817809C787FEE4"
+        "AB97F97B9130D022153EDC6EB6CBE7B0"
+        "F8E3473F2E901209B5DB10F93604DB01"
+        "028181"  // INTEGER length 0x81 (prime1)
+        "00E83C0998214941EA4F9293F1B77E2E"
+        "99E6CF305FAF358238E126124FEAF2EB"
+        "9724B2EA7B78E6032343821A80E55D1D"
+        "88FB12D220C3F41A56142FEC85796D19"
+        "17F1E8C774F142B67D3D6E7B7E6B4383"
+        "E94DB5929089DBB346D5BDAB40CC2D96"
+        "EE0409475E175C63BF78CFD744136740"
+        "838127EA723FF3FE7FA368C1311B4A4E"
+        "05"
+        "028181"  // INTEGER length 0x81 (prime2)
+        "00D240FCC0F5D7715CDE21CB2DC86EA1"
+        "46132EA3B06F61FF2AF54BF38473F59D"
+        "ADCCE32B5F4CC32DD0BA6F509347B4B5"
+        "B1B58C39F95E4798CCBB43E83D0119AC"
+        "F532F359CA743C85199F0286610E2009"
+        "97D7312917179AC9B67558773212EC96"
+        "1E8BCE7A3CC809BC5486A96E4B0E6AF3"
+        "94D94E066A0900B7B70E82A44FB30053"
+        "C1"
+        "028181"  // INTEGER length 0x81 (exponent1)
+        "00AD15DA1CBD6A492B66851BA8C316D3"
+        "8AB700E2CFDDD926A658003513C54BAA"
+        "152B30021D667D20078F500F8AD3E7F3"
+        "945D74A891ED1A28EAD0FEEAEC8C14A8"
+        "E834CF46A13D1378C99D18940823CFDD"
+        "27EC5810D59339E0C34198AC638E09C8"
+        "7CBB1B634A9864AE9F4D5EB2D53514F6"
+        "7B4CAEC048C8AB849A02E397618F3271"
+        "35"
+        "028180"  // INTEGER length 0x80 (exponent2)
+        "1FA2C1A5331880A92D8F3E281C617108"
+        "BF38244F16E352E69ED417C7153F9EC3"
+        "18F211839C643DCF8B4DD67CE2AC312E"
+        "95178D5D952F06B1BF779F4916924B70"
+        "F582A23F11304E02A5E7565AE22A35E7"
+        "4FECC8B6FDC93F92A1A37703E4CF0E63"
+        "783BD02EB716A7ECBBFA606B10B74D01"
+        "579522E7EF84D91FC522292108D902C1"
+        "028180"  // INTEGER length 0x80 (coefficient)
+        "796FE3825F9DCC85DF22D58690065D93"
+        "898ACD65C087BEA8DA3A63BF4549B795"
+        "E2CD0E3BE08CDEBD9FCF1720D9CDC507"
+        "0D74F40DED8E1102C52152A31B6165F8"
+        "3A6722AECFCC35A493D7634664B888A0"
+        "8D3EB034F12EA28BFEE346E205D33482"
+        "7F778B16ED40872BD29FCB36536B6E93"
+        "FFB06778696B4A9D81BB0A9423E63DE5"
+        // } end SEQUENCE (PrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
 
-string ec_256_key =
-        hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
-                "6b0201010420737c2ecd7b8d1940bf2930aa9b4ed3ff941eed09366bc032"
-                "99986481f3a4d859a14403420004bf85d7720d07c25461683bc648b4778a"
-                "9a14dd8a024e3bdd8c7ddd9ab2b528bbc7aa1b51f14ebbbb0bd0ce21bcc4"
-                "1c6eb00083cf3376d11fd44949e0b2183bfe");
+string ec_256_key = hex2str(
+        // RFC 5208 s5
+        "308187"            // SEQUENCE length 0x87 (PrivateKeyInfo) {
+        "020100"            // INTEGER length 1 value 0 (version)
+        "3013"              // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+        "0607"              // OBJECT IDENTIFIER length 7 (algorithm)
+        "2a8648ce3d0201"    // 1.2.840.10045.2.1 (ecPublicKey)
+        "0608"              // OBJECT IDENTIFIER length 8 (param)
+        "2a8648ce3d030107"  //  1.2.840.10045.3.1.7 (secp256r1)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "046d"    // OCTET STRING length 0x6d (privateKey) holding...
+        "306b"    // SEQUENCE length 0x6b (ECPrivateKey)
+        "020101"  // INTEGER length 1 value 1 (version)
+        "0420"    // OCTET STRING length 0x20 (privateKey)
+        "737c2ecd7b8d1940bf2930aa9b4ed3ff"
+        "941eed09366bc03299986481f3a4d859"
+        "a144"  // TAG [1] len 0x44 (publicKey) {
+        "03420004bf85d7720d07c25461683bc6"
+        "48b4778a9a14dd8a024e3bdd8c7ddd9a"
+        "b2b528bbc7aa1b51f14ebbbb0bd0ce21"
+        "bcc41c6eb00083cf3376d11fd44949e0"
+        "b2183bfe"
+        // } end SEQUENCE (ECPrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
 
-string ec_521_key =
-        hex2str("3081EE020100301006072A8648CE3D020106052B810400230481D63081D3"
-                "02010104420011458C586DB5DAA92AFAB03F4FE46AA9D9C3CE9A9B7A006A"
-                "8384BEC4C78E8E9D18D7D08B5BCFA0E53C75B064AD51C449BAE0258D54B9"
-                "4B1E885DED08ED4FB25CE9A1818903818600040149EC11C6DF0FA122C6A9"
-                "AFD9754A4FA9513A627CA329E349535A5629875A8ADFBE27DCB932C05198"
-                "6377108D054C28C6F39B6F2C9AF81802F9F326B842FF2E5F3C00AB7635CF"
-                "B36157FC0882D574A10D839C1A0C049DC5E0D775E2EE50671A208431BB45"
-                "E78E70BEFE930DB34818EE4D5C26259F5C6B8E28A652950F9F88D7B4B2C9"
-                "D9");
+string ec_521_key = hex2str(
+        // RFC 5208 s5
+        "3081EE"          // SEQUENCE length 0xee (PrivateKeyInfo) {
+        "020100"          // INTEGER length 1 value 0 (version)
+        "3010"            // SEQUENCE length 0x10 (AlgorithmIdentifier) {
+        "0607"            // OBJECT IDENTIFIER length 7 (algorithm)
+        "2A8648CE3D0201"  // 1.2.840.10045.2.1 (ecPublicKey)
+        "0605"            // OBJECT IDENTIFIER length 5 (param)
+        "2B81040023"      //  1.3.132.0.35 (secp521r1)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "0481D6"  // OCTET STRING length 0xd6 (privateKey) holding...
+        "3081D3"  // SEQUENCE length 0xd3 (ECPrivateKey)
+        "020101"  // INTEGER length 1 value 1 (version)
+        "0442"    // OCTET STRING length 0x42 (privateKey)
+        "0011458C586DB5DAA92AFAB03F4FE46A"
+        "A9D9C3CE9A9B7A006A8384BEC4C78E8E"
+        "9D18D7D08B5BCFA0E53C75B064AD51C4"
+        "49BAE0258D54B94B1E885DED08ED4FB2"
+        "5CE9"
+        "A18189"  // TAG [1] len 0x89 (publicKey) {
+        "03818600040149EC11C6DF0FA122C6A9"
+        "AFD9754A4FA9513A627CA329E349535A"
+        "5629875A8ADFBE27DCB932C051986377"
+        "108D054C28C6F39B6F2C9AF81802F9F3"
+        "26B842FF2E5F3C00AB7635CFB36157FC"
+        "0882D574A10D839C1A0C049DC5E0D775"
+        "E2EE50671A208431BB45E78E70BEFE93"
+        "0DB34818EE4D5C26259F5C6B8E28A652"
+        "950F9F88D7B4B2C9D9"
+        // } end SEQUENCE (ECPrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
 
-string ec_256_key_rfc5915 =
-        hex2str("308193020100301306072a8648ce3d020106082a8648ce3d030107047930"
-                "770201010420782370a8c8ce5537baadd04dcff079c8158cfa9c67b818b3"
-                "8e8d21c9fa750c1da00a06082a8648ce3d030107a14403420004e2cc561e"
-                "e701da0ad0ef0d176bb0c919d42e79c393fdc1bd6c4010d85cf2cf8e68c9"
-                "05464666f98dad4f01573ba81078b3428570a439ba3229fbc026c550682f");
+string ec_256_key_rfc5915 = hex2str(
+        // RFC 5208 s5
+        "308193"            // SEQUENCE length 0x93 (PrivateKeyInfo) {
+        "020100"            // INTEGER length 1 value 0 (version)
+        "3013"              // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+        "0607"              // OBJECT IDENTIFIER length 7 (algorithm)
+        "2a8648ce3d0201"    // 1.2.840.10045.2.1 (ecPublicKey)
+        "0608"              // OBJECT IDENTIFIER length 8 (param)
+        "2a8648ce3d030107"  //  1.2.840.10045.3.1.7 (secp256r1)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "0479"  // OCTET STRING length 0x79 (privateKey) holding...
+        // RFC 5915 s3
+        "3077"    // SEQUENCE length 0x77 (ECPrivateKey)
+        "020101"  // INTEGER length 1 value 1 (version)
+        "0420"    // OCTET STRING length 0x42 (privateKey)
+        "782370a8c8ce5537baadd04dcff079c8"
+        "158cfa9c67b818b38e8d21c9fa750c1d"
+        "a00a"              // TAG [0] length 0xa (parameters)
+        "0608"              // OBJECT IDENTIFIER length 8
+        "2a8648ce3d030107"  // 1.2.840.10045.3.1.7 (secp256r1)
+        // } end TAG [0]
+        "a144"  // TAG [1] length 0x44 (publicKey) {
+        "0342"  // BIT STRING length 0x42
+        "00"    // no pad bits
+        "04e2cc561ee701da0ad0ef0d176bb0c9"
+        "19d42e79c393fdc1bd6c4010d85cf2cf"
+        "8e68c905464666f98dad4f01573ba810"
+        "78b3428570a439ba3229fbc026c55068"
+        "2f"
+        // } end SEQUENCE (ECPrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
 
-string ec_256_key_sec1 =
-        hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
-                "6b0201010420782370a8c8ce5537baadd04dcff079c8158cfa9c67b818b3"
-                "8e8d21c9fa750c1da14403420004e2cc561ee701da0ad0ef0d176bb0c919"
-                "d42e79c393fdc1bd6c4010d85cf2cf8e68c905464666f98dad4f01573ba8"
-                "1078b3428570a439ba3229fbc026c550682f");
+string ec_256_key_sec1 = hex2str(
+        // RFC 5208 s5
+        "308187"            // SEQUENCE length 0x87 (PrivateKeyInfo) {
+        "020100"            // INTEGER length 1 value 0 (version)
+        "3013"              // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+        "0607"              // OBJECT IDENTIFIER length 7 (algorithm)
+        "2a8648ce3d0201"    // 1.2.840.10045.2.1 (ecPublicKey)
+        "0608"              // OBJECT IDENTIFIER length 8 (param)
+        "2a8648ce3d030107"  // 1.2.840.10045.3.1.7 (secp256r1)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "046d"  // OCTET STRING length 0x6d (privateKey) holding...
+        // SEC1-v2 C.4
+        "306b"    // SEQUENCE length 0x6b (ECPrivateKey)
+        "020101"  // INTEGER length 1 value 0x01 (version)
+        "0420"    // OCTET STRING length 0x20 (privateKey)
+        "782370a8c8ce5537baadd04dcff079c8"
+        "158cfa9c67b818b38e8d21c9fa750c1d"
+        "a144"  // TAG [1] length 0x44 (publicKey) {
+        "0342"  // BIT STRING length 0x42
+        "00"    // no pad bits
+        "04e2cc561ee701da0ad0ef0d176bb0c9"
+        "19d42e79c393fdc1bd6c4010d85cf2cf"
+        "8e68c905464666f98dad4f01573ba810"
+        "78b3428570a439ba3229fbc026c55068"
+        "2f"
+        // } end TAG [1] (publicKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
 
 struct RSA_Delete {
     void operator()(RSA* p) { RSA_free(p); }
@@ -291,37 +480,375 @@
 class NewKeyGenerationTest : public KeyMintAidlTestBase {
   protected:
     void CheckBaseParams(const vector<KeyCharacteristics>& keyCharacteristics) {
-        // TODO(swillden): Distinguish which params should be in which auth list.
-
-        AuthorizationSet auths;
-        for (auto& entry : keyCharacteristics) {
-            auths.push_back(AuthorizationSet(entry.authorizations));
-        }
-
-        EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
+        AuthorizationSet auths = CheckCommonParams(keyCharacteristics);
         EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
         EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
 
-        // Verify that App data and ROT are NOT included.
-        EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
-        EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
-
         // Check that some unexpected tags/values are NOT present.
         EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
         EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+    }
+
+    void CheckSymmetricParams(const vector<KeyCharacteristics>& keyCharacteristics) {
+        AuthorizationSet auths = CheckCommonParams(keyCharacteristics);
+        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
+        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+
+        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
+        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
+    }
+
+    AuthorizationSet CheckCommonParams(const vector<KeyCharacteristics>& keyCharacteristics) {
+        // TODO(swillden): Distinguish which params should be in which auth list.
+        AuthorizationSet auths;
+        for (auto& entry : keyCharacteristics) {
+            auths.push_back(AuthorizationSet(entry.authorizations));
+        }
+        EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
+
+        // Verify that App data, ROT and auth timeout are NOT included.
+        EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
+        EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
         EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301U));
 
-        auto os_ver = auths.GetTagValue(TAG_OS_VERSION);
-        ASSERT_TRUE(os_ver);
-        EXPECT_EQ(*os_ver, os_version());
+        // None of the tests specify CREATION_DATETIME so check that the KeyMint implementation
+        // never adds it.
+        EXPECT_FALSE(auths.Contains(TAG_CREATION_DATETIME));
 
+        // Check OS details match the original hardware info.
+        auto os_ver = auths.GetTagValue(TAG_OS_VERSION);
+        EXPECT_TRUE(os_ver);
+        EXPECT_EQ(*os_ver, os_version());
         auto os_pl = auths.GetTagValue(TAG_OS_PATCHLEVEL);
-        ASSERT_TRUE(os_pl);
+        EXPECT_TRUE(os_pl);
         EXPECT_EQ(*os_pl, os_patch_level());
+
+        if (check_patchLevels) {
+            // Should include vendor and boot patchlevels.
+            auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
+            EXPECT_TRUE(vendor_pl);
+            EXPECT_EQ(*vendor_pl, vendor_patch_level());
+            auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
+            EXPECT_TRUE(boot_pl);
+        }
+
+        return auths;
     }
 };
 
 /*
+ * NewKeyGenerationTest.Aes
+ *
+ * Verifies that keymint can generate all required AES key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, Aes) {
+    for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                auto builder = AuthorizationSetBuilder()
+                                       .AesEncryptionKey(key_size)
+                                       .BlockMode(block_mode)
+                                       .Padding(padding_mode)
+                                       .SetDefaultValidity();
+                if (block_mode == BlockMode::GCM) {
+                    builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+                }
+                ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, &key_blob, &key_characteristics));
+
+                EXPECT_GT(key_blob.size(), 0U);
+                CheckSymmetricParams(key_characteristics);
+
+                AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+                EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::AES));
+                EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                        << "Key size " << key_size << "missing";
+
+                CheckedDeleteKey(&key_blob);
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.AesInvalidSize
+ *
+ * Verifies that specifying an invalid key size for AES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, AesInvalidSize) {
+    for (auto key_size : InvalidKeySizes(Algorithm::AES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                auto builder = AuthorizationSetBuilder()
+                                       .AesEncryptionKey(key_size)
+                                       .BlockMode(block_mode)
+                                       .Padding(padding_mode)
+                                       .SetDefaultValidity();
+                if (block_mode == BlockMode::GCM) {
+                    builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+                }
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                          GenerateKey(builder, &key_blob, &key_characteristics));
+            }
+        }
+    }
+
+    for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+        for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+            vector<uint8_t> key_blob;
+            vector<KeyCharacteristics> key_characteristics;
+            // No key size specified
+            auto builder = AuthorizationSetBuilder()
+                                   .Authorization(TAG_ALGORITHM, Algorithm::AES)
+                                   .BlockMode(block_mode)
+                                   .Padding(padding_mode)
+                                   .SetDefaultValidity();
+            if (block_mode == BlockMode::GCM) {
+                builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+            }
+            EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                      GenerateKey(builder, &key_blob, &key_characteristics));
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.AesInvalidPadding
+ *
+ * Verifies that specifying an invalid padding on AES keys gives a failure
+ * somewhere along the way.
+ */
+TEST_P(NewKeyGenerationTest, AesInvalidPadding) {
+    for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+            for (auto padding_mode : InvalidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                auto builder = AuthorizationSetBuilder()
+                                       .Authorization(TAG_NO_AUTH_REQUIRED)
+                                       .AesEncryptionKey(key_size)
+                                       .BlockMode(block_mode)
+                                       .Padding(padding_mode)
+                                       .SetDefaultValidity();
+                if (block_mode == BlockMode::GCM) {
+                    builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+                }
+
+                auto result = GenerateKey(builder);
+                if (result == ErrorCode::OK) {
+                    // Key creation was OK but has generated a key that cannot be used.
+                    auto params =
+                            AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding_mode);
+                    if (block_mode == BlockMode::GCM) {
+                        params.Authorization(TAG_MAC_LENGTH, 128);
+                    }
+                    auto result = Begin(KeyPurpose::ENCRYPT, params);
+                    EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
+                                result == ErrorCode::INVALID_KEY_BLOB)
+                            << "unexpected result: " << result;
+                } else {
+                    // The KeyMint implementation detected that the generated key
+                    // is unusable.
+                    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, result);
+                }
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.AesGcmMissingMinMac
+ *
+ * Verifies that specifying an invalid key size for AES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, AesGcmMissingMinMac) {
+    for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+        BlockMode block_mode = BlockMode::GCM;
+        for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+            SCOPED_TRACE(testing::Message()
+                         << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+            vector<uint8_t> key_blob;
+            vector<KeyCharacteristics> key_characteristics;
+            // No MIN_MAC_LENGTH provided.
+            auto builder = AuthorizationSetBuilder()
+                                   .AesEncryptionKey(key_size)
+                                   .BlockMode(block_mode)
+                                   .Padding(padding_mode)
+                                   .SetDefaultValidity();
+            EXPECT_EQ(ErrorCode::MISSING_MIN_MAC_LENGTH,
+                      GenerateKey(builder, &key_blob, &key_characteristics));
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.AesGcmMinMacOutOfRange
+ *
+ * Verifies that specifying an invalid min MAC size for AES key generation returns
+ * UNSUPPORTED_MIN_MAC_LENGTH.
+ */
+TEST_P(NewKeyGenerationTest, AesGcmMinMacOutOfRange) {
+    for (size_t min_mac_len : {88, 136}) {
+        for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+            BlockMode block_mode = BlockMode::GCM;
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                auto builder = AuthorizationSetBuilder()
+                                       .AesEncryptionKey(key_size)
+                                       .BlockMode(block_mode)
+                                       .Padding(padding_mode)
+                                       .Authorization(TAG_MIN_MAC_LENGTH, min_mac_len)
+                                       .SetDefaultValidity();
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
+                          GenerateKey(builder, &key_blob, &key_characteristics));
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.TripleDes
+ *
+ * Verifies that keymint can generate all required 3DES key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, TripleDes) {
+    for (auto key_size : ValidKeySizes(Algorithm::TRIPLE_DES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                             .TripleDesEncryptionKey(key_size)
+                                                             .BlockMode(block_mode)
+                                                             .Padding(padding_mode)
+                                                             .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                             .SetDefaultValidity(),
+                                                     &key_blob, &key_characteristics));
+
+                EXPECT_GT(key_blob.size(), 0U);
+                CheckSymmetricParams(key_characteristics);
+
+                AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+                EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::TRIPLE_DES));
+                EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                        << "Key size " << key_size << "missing";
+
+                CheckedDeleteKey(&key_blob);
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.TripleDesWithAttestation
+ *
+ * Verifies that keymint can generate all required 3DES key sizes, and that the resulting keys
+ * have correct characteristics.
+ *
+ * Request attestation, which doesn't help for symmetric keys (as there is no public key to
+ * put in a certificate) but which isn't an error.
+ */
+TEST_P(NewKeyGenerationTest, TripleDesWithAttestation) {
+    for (auto key_size : ValidKeySizes(Algorithm::TRIPLE_DES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
+
+                auto challenge = "hello";
+                auto app_id = "foo";
+
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                             .TripleDesEncryptionKey(key_size)
+                                                             .BlockMode(block_mode)
+                                                             .Padding(padding_mode)
+                                                             .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                             .AttestationChallenge(challenge)
+                                                             .AttestationApplicationId(app_id)
+                                                             .SetDefaultValidity(),
+                                                     &key_blob, &key_characteristics));
+
+                EXPECT_GT(key_blob.size(), 0U);
+                CheckSymmetricParams(key_characteristics);
+
+                AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+                EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::TRIPLE_DES));
+                EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                        << "Key size " << key_size << "missing";
+
+                CheckedDeleteKey(&key_blob);
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.TripleDesInvalidSize
+ *
+ * Verifies that specifying an invalid key size for 3-DES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, TripleDesInvalidSize) {
+    for (auto key_size : InvalidKeySizes(Algorithm::TRIPLE_DES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                          GenerateKey(AuthorizationSetBuilder()
+                                              .TripleDesEncryptionKey(key_size)
+                                              .BlockMode(block_mode)
+                                              .Padding(padding_mode)
+                                              .Authorization(TAG_NO_AUTH_REQUIRED)
+                                              .SetDefaultValidity(),
+                                      &key_blob, &key_characteristics));
+            }
+        }
+    }
+
+    // Omitting the key size fails.
+    for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+        for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+            SCOPED_TRACE(testing::Message()
+                         << "3DES-default-" << block_mode << "-" << padding_mode);
+            vector<uint8_t> key_blob;
+            vector<KeyCharacteristics> key_characteristics;
+            ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                      GenerateKey(AuthorizationSetBuilder()
+                                          .Authorization(TAG_ALGORITHM, Algorithm::TRIPLE_DES)
+                                          .BlockMode(block_mode)
+                                          .Padding(padding_mode)
+                                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                                          .SetDefaultValidity(),
+                                  &key_blob, &key_characteristics));
+        }
+    }
+}
+
+/*
  * NewKeyGenerationTest.Rsa
  *
  * Verifies that keymint can generate all required RSA key sizes, and that the resulting keys
@@ -355,25 +882,34 @@
 /*
  * NewKeyGenerationTest.RsaWithAttestation
  *
- * Verifies that keymint can generate all required RSA key sizes, and that the resulting keys
- * have correct characteristics.
+ * Verifies that keymint can generate all required RSA key sizes with attestation, and that the
+ * resulting keys have correct characteristics.
  */
 TEST_P(NewKeyGenerationTest, RsaWithAttestation) {
     auto challenge = "hello";
     auto app_id = "foo";
 
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 66;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
-        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                     .RsaSigningKey(key_size, 65537)
-                                                     .Digest(Digest::NONE)
-                                                     .Padding(PaddingMode::NONE)
-                                                     .AttestationChallenge(challenge)
-                                                     .AttestationApplicationId(app_id)
-                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                     .SetDefaultValidity(),
-                                             &key_blob, &key_characteristics));
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .AttestationChallenge(challenge)
+                                      .AttestationApplicationId(app_id)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
 
         ASSERT_GT(key_blob.size(), 0U);
         CheckBaseParams(key_characteristics);
@@ -385,6 +921,7 @@
                 << "Key size " << key_size << "missing";
         EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
 
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
         EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
         ASSERT_GT(cert_chain_.size(), 0);
 
@@ -480,16 +1017,25 @@
     auto challenge = "hello";
     auto app_id = "foo";
 
+    auto subject = "subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 111166;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
     vector<uint8_t> key_blob;
     vector<KeyCharacteristics> key_characteristics;
-    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .RsaEncryptionKey(key_size, 65537)
-                                                 .Padding(PaddingMode::NONE)
-                                                 .AttestationChallenge(challenge)
-                                                 .AttestationApplicationId(app_id)
-                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .SetDefaultValidity(),
-                                         &key_blob, &key_characteristics));
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaEncryptionKey(key_size, 65537)
+                                  .Padding(PaddingMode::NONE)
+                                  .AttestationChallenge(challenge)
+                                  .AttestationApplicationId(app_id)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                  .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                  .SetDefaultValidity(),
+                          &key_blob, &key_characteristics));
 
     ASSERT_GT(key_blob.size(), 0U);
     AuthorizationSet auths;
@@ -521,6 +1067,7 @@
             << "Key size " << key_size << "missing";
     EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
 
+    verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
     EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
     ASSERT_GT(cert_chain_.size(), 0);
 
@@ -541,16 +1088,25 @@
  * works as expected.
  */
 TEST_P(NewKeyGenerationTest, RsaWithSelfSign) {
+    auto subject = "cert subj subj subj subj subj subj 22222222222222222222";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 0;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
-        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                     .RsaSigningKey(key_size, 65537)
-                                                     .Digest(Digest::NONE)
-                                                     .Padding(PaddingMode::NONE)
-                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                     .SetDefaultValidity(),
-                                             &key_blob, &key_characteristics));
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
 
         ASSERT_GT(key_blob.size(), 0U);
         CheckBaseParams(key_characteristics);
@@ -562,6 +1118,7 @@
                 << "Key size " << key_size << "missing";
         EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
 
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
         EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
         ASSERT_EQ(cert_chain_.size(), 1);
 
@@ -599,16 +1156,25 @@
     auto key_size = 2048;
     auto app_id = "foo";
 
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 1;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
     vector<uint8_t> key_blob;
     vector<KeyCharacteristics> key_characteristics;
-    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .RsaSigningKey(key_size, 65537)
-                                                 .Digest(Digest::NONE)
-                                                 .Padding(PaddingMode::NONE)
-                                                 .AttestationApplicationId(app_id)
-                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .SetDefaultValidity(),
-                                         &key_blob, &key_characteristics));
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaSigningKey(key_size, 65537)
+                                  .Digest(Digest::NONE)
+                                  .Padding(PaddingMode::NONE)
+                                  .AttestationApplicationId(app_id)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                  .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                  .SetDefaultValidity(),
+                          &key_blob, &key_characteristics));
 
     ASSERT_GT(key_blob.size(), 0U);
     CheckBaseParams(key_characteristics);
@@ -620,6 +1186,7 @@
             << "Key size " << key_size << "missing";
     EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
 
+    verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
     EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
     ASSERT_EQ(cert_chain_.size(), 1);
 
@@ -676,19 +1243,28 @@
     auto challenge = "hello";
     auto app_id = "foo";
 
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 66;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
     for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
-        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                     .RsaSigningKey(key_size, 65537)
-                                                     .Digest(Digest::NONE)
-                                                     .Padding(PaddingMode::NONE)
-                                                     .AttestationChallenge(challenge)
-                                                     .AttestationApplicationId(app_id)
-                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                     .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
-                                                     .SetDefaultValidity(),
-                                             &key_blob, &key_characteristics));
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .AttestationChallenge(challenge)
+                                      .AttestationApplicationId(app_id)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
 
         ASSERT_GT(key_blob.size(), 0U);
         CheckBaseParams(key_characteristics);
@@ -711,6 +1287,7 @@
         // Check the usage count limit tag also appears in the attestation.
         EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
         ASSERT_GT(cert_chain_.size(), 0);
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
 
         AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
         AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
@@ -757,6 +1334,20 @@
 }
 
 /*
+ * NewKeyGenerationTest.RsaMissingParams
+ *
+ * Verifies that omitting optional tags works.
+ */
+TEST_P(NewKeyGenerationTest, RsaMissingParams) {
+    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(
+                          AuthorizationSetBuilder().RsaKey(key_size, 65537).SetDefaultValidity()));
+        CheckedDeleteKey();
+    }
+}
+
+/*
  * NewKeyGenerationTest.Ecdsa
  *
  * Verifies that keymint can generate all required EC key sizes, and that the resulting keys
@@ -794,17 +1385,26 @@
     auto challenge = "hello";
     auto app_id = "foo";
 
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 0xFFFFFFFFFFFFFFFF;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
     for (auto key_size : ValidKeySizes(Algorithm::EC)) {
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
-        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                     .EcdsaSigningKey(key_size)
-                                                     .Digest(Digest::NONE)
-                                                     .AttestationChallenge(challenge)
-                                                     .AttestationApplicationId(app_id)
-                                                     .SetDefaultValidity(),
-                                             &key_blob, &key_characteristics));
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .EcdsaSigningKey(key_size)
+                                      .Digest(Digest::NONE)
+                                      .AttestationChallenge(challenge)
+                                      .AttestationApplicationId(app_id)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
         ASSERT_GT(key_blob.size(), 0U);
         CheckBaseParams(key_characteristics);
 
@@ -816,6 +1416,7 @@
 
         EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
         ASSERT_GT(cert_chain_.size(), 0);
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
 
         AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
         AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
@@ -834,14 +1435,23 @@
  * the key will generate a self signed attestation.
  */
 TEST_P(NewKeyGenerationTest, EcdsaSelfSignAttestation) {
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 0x123456FFF1234;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
     for (auto key_size : ValidKeySizes(Algorithm::EC)) {
         vector<uint8_t> key_blob;
         vector<KeyCharacteristics> key_characteristics;
-        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                     .EcdsaSigningKey(key_size)
-                                                     .Digest(Digest::NONE)
-                                                     .SetDefaultValidity(),
-                                             &key_blob, &key_characteristics));
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .EcdsaSigningKey(key_size)
+                                      .Digest(Digest::NONE)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
         ASSERT_GT(key_blob.size(), 0U);
         CheckBaseParams(key_characteristics);
 
@@ -852,6 +1462,7 @@
                 << "Key size " << key_size << "missing";
 
         EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
         ASSERT_EQ(cert_chain_.size(), 1);
 
         AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
@@ -1077,7 +1688,7 @@
 }
 
 /*
- * NewKeyGenerationTest.EcdsaInvalidCurves
+ * NewKeyGenerationTest.EcdsaAllValidCurves
  *
  * Verifies that keymint does not support any curve designated as unsupported.
  */
@@ -1228,6 +1839,16 @@
             CheckedDeleteKey();
         }
     }
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        // STRONGBOX devices must not support keys larger than 512 bits.
+        size_t key_size = 520;
+        EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .HmacKey(key_size)
+                                      .Digest(Digest::SHA_2_256)
+                                      .Authorization(TAG_MIN_MAC_LENGTH, 256)))
+                << "HMAC key size " << key_size << " unexpectedly valid";
+    }
 }
 
 /*
@@ -1261,6 +1882,15 @@
             CheckedDeleteKey();
         }
     }
+
+    // Minimum MAC length must be no more than 512 bits.
+    size_t min_mac_length = 520;
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .HmacKey(128)
+                                  .Digest(Digest::SHA_2_256)
+                                  .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
+            << "HMAC min mac length " << min_mac_length << " invalid.";
 }
 
 /*
@@ -1611,6 +2241,38 @@
 }
 
 /*
+ * SigningOperationsTest.RsaNonUniqueParams
+ *
+ * Verifies that an operation with multiple padding modes is rejected.
+ */
+TEST_P(SigningOperationsTest, RsaNonUniqueParams) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Digest(Digest::SHA1)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Digest(Digest::SHA1)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+}
+
+/*
  * SigningOperationsTest.RsaUnsupportedPadding
  *
  * Verifies that RSA operations fail with the correct error (but key gen succeeds) when used
@@ -1627,6 +2289,20 @@
             ErrorCode::UNSUPPORTED_PADDING_MODE,
             Begin(KeyPurpose::SIGN,
                   AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::PKCS7)));
+    CheckedDeleteKey();
+
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(
+                      AuthorizationSetBuilder()
+                              .RsaSigningKey(2048, 65537)
+                              .Authorization(TAG_NO_AUTH_REQUIRED)
+                              .Digest(Digest::SHA_2_256 /* supported digest */)
+                              .Padding(PaddingMode::RSA_OAEP) /* padding mode for encryption only */
+                              .SetDefaultValidity()));
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::SHA_2_256)
+                                              .Padding(PaddingMode::RSA_OAEP)));
 }
 
 /*
@@ -1650,7 +2326,7 @@
 }
 
 /*
- * SigningOperationsTest.RsaPssNoDigest
+ * SigningOperationsTest.RsaPssNoPadding
  *
  * Verifies that RSA operations fail when no padding mode is specified.  PaddingMode::NONE is
  * supported in some cases (as validated in other tests), but a mode must be specified.
@@ -1829,6 +2505,23 @@
 }
 
 /*
+ * SigningOperationsTest.EcdsaIncompatibleDigest
+ *
+ * Verifies that using an EC key requires compatible digest.
+ */
+TEST_P(SigningOperationsTest, EcdsaIncompatibleDigest) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(256)
+                                                 .Digest(Digest::NONE)
+                                                 .Digest(Digest::SHA1)
+                                                 .SetDefaultValidity()));
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::SHA_2_256)));
+    AbortIfNeeded();
+}
+
+/*
  * SigningOperationsTest.AesEcbSign
  *
  * Verifies that attempts to use AES keys to sign fail in the correct way.
@@ -1889,6 +2582,26 @@
 }
 
 /*
+ * SigningOperationsTest.HmacSha256InvalidMacLength
+ *
+ * Verifies that HMAC fails in the correct way when asked to generate a MAC whose length is
+ * not a multiple of 8.
+ */
+TEST_P(SigningOperationsTest, HmacSha256InvalidMacLength) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .HmacKey(128)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 160)));
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
+                                                       AuthorizationSetBuilder()
+                                                               .Digest(Digest::SHA_2_256)
+                                                               .Authorization(TAG_MAC_LENGTH, 161),
+                                                       &output_params));
+}
+
+/*
  * SigningOperationsTest.HmacSha256TooSmallMacLength
  *
  * Verifies that HMAC fails in the correct way when asked to generate a MAC smaller than the
@@ -2005,7 +2718,7 @@
 }
 
 /*
- * VerificationOperationsTest.RsaSuccess
+ * VerificationOperationsTest.RsaAllPaddingsAndDigests
  *
  * Verifies RSA signature/verification for all padding modes and digests.
  */
@@ -2101,7 +2814,7 @@
 }
 
 /*
- * VerificationOperationsTest.RsaSuccess
+ * VerificationOperationsTest.RsaAllDigestsAndCurves
  *
  * Verifies ECDSA signature/verification for all digests and curves.
  */
@@ -2307,6 +3020,48 @@
 }
 
 /*
+ * ImportKeyTest.RsaSuccessWithoutParams
+ *
+ * Verifies that importing and using an RSA key pair without specifying parameters
+ * works correctly.
+ */
+TEST_P(ImportKeyTest, RsaSuccessWithoutParams) {
+    uint32_t key_size;
+    string key;
+
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        key_size = 2048;
+        key = rsa_2048_key;
+    } else {
+        key_size = 1024;
+        key = rsa_key;
+    }
+
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .SigningKey()
+                                               .Authorization(TAG_ALGORITHM, Algorithm::RSA)
+                                               .Digest(Digest::SHA_2_256)
+                                               .Padding(PaddingMode::RSA_PSS)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, key));
+
+    // Key size and public exponent are determined from the imported key material.
+    CheckCryptoParam(TAG_KEY_SIZE, key_size);
+    CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
+    CheckOrigin();
+
+    string message(1024 / 8, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
+    string signature = SignMessage(message, params);
+    VerifyMessage(message, signature, params);
+}
+
+/*
  * ImportKeyTest.RsaKeySizeMismatch
  *
  * Verifies that importing an RSA key pair with a size that doesn't match the key fails in the
@@ -2501,7 +3256,113 @@
 }
 
 /*
- * ImportKeyTest.AesSuccess
+ * ImportKeyTest.AesFailure
+ *
+ * Verifies that importing an invalid AES key fails.
+ */
+TEST_P(ImportKeyTest, AesFailure) {
+    string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    uint32_t bitlen = key.size() * 8;
+    for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
+        // Explicit key size doesn't match that of the provided key.
+        auto result = ImportKey(AuthorizationSetBuilder()
+                                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                                    .AesEncryptionKey(key_size)
+                                    .EcbMode()
+                                    .Padding(PaddingMode::PKCS7),
+                                KeyFormat::RAW, key);
+        ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH ||
+                    result == ErrorCode::UNSUPPORTED_KEY_SIZE)
+                << "unexpected result: " << result;
+    }
+
+    // Explicit key size matches that of the provided key, but it's not a valid size.
+    string long_key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .AesEncryptionKey(long_key.size() * 8)
+                                .EcbMode()
+                                .Padding(PaddingMode::PKCS7),
+                        KeyFormat::RAW, long_key));
+    string short_key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .AesEncryptionKey(short_key.size() * 8)
+                                .EcbMode()
+                                .Padding(PaddingMode::PKCS7),
+                        KeyFormat::RAW, short_key));
+}
+
+/*
+ * ImportKeyTest.TripleDesSuccess
+ *
+ * Verifies that importing and using a 3DES key works.
+ */
+TEST_P(ImportKeyTest, TripleDesSuccess) {
+    string key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .TripleDesEncryptionKey(168)
+                                               .EcbMode()
+                                               .Padding(PaddingMode::PKCS7),
+                                       KeyFormat::RAW, key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::TRIPLE_DES);
+    CheckCryptoParam(TAG_KEY_SIZE, 168U);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::PKCS7);
+    CheckCryptoParam(TAG_BLOCK_MODE, BlockMode::ECB);
+    CheckOrigin();
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    string ciphertext = EncryptMessage(message, params);
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * ImportKeyTest.TripleDesFailure
+ *
+ * Verifies that importing an invalid 3DES key fails.
+ */
+TEST_P(ImportKeyTest, TripleDesFailure) {
+    string key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
+    uint32_t bitlen = key.size() * 7;
+    for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
+        // Explicit key size doesn't match that of the provided key.
+        auto result = ImportKey(AuthorizationSetBuilder()
+                                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                                    .TripleDesEncryptionKey(key_size)
+                                    .EcbMode()
+                                    .Padding(PaddingMode::PKCS7),
+                                KeyFormat::RAW, key);
+        ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH ||
+                    result == ErrorCode::UNSUPPORTED_KEY_SIZE)
+                << "unexpected result: " << result;
+    }
+    // Explicit key size matches that of the provided key, but it's not a valid size.
+    string long_key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f735800");
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .TripleDesEncryptionKey(long_key.size() * 7)
+                                .EcbMode()
+                                .Padding(PaddingMode::PKCS7),
+                        KeyFormat::RAW, long_key));
+    string short_key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f73");
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .TripleDesEncryptionKey(short_key.size() * 7)
+                                .EcbMode()
+                                .Padding(PaddingMode::PKCS7),
+                        KeyFormat::RAW, short_key));
+}
+
+/*
+ * ImportKeyTest.HmacKeySuccess
  *
  * Verifies that importing and using an HMAC key works.
  */
@@ -2527,57 +3388,230 @@
 INSTANTIATE_KEYMINT_AIDL_TEST(ImportKeyTest);
 
 auto wrapped_key = hex2str(
-        "3082017902010004820100934bf94e2aa28a3f83c9f79297250262fbe3276b5a1c91159bbfa3ef8957aac8"
-        "4b59b30b455a79c2973480823d8b3863c3deef4a8e243590268d80e18751a0e130f67ce6a1ace9f79b95e0"
-        "97474febc981195b1d13a69086c0863f66a7b7fdb48792227b1ac5e2489febdf087ab5486483033a6f001c"
-        "a5d1ec1e27f5c30f4cec2642074a39ae68aee552e196627a8e3d867e67a8c01b11e75f13cca0a97ab668b5"
-        "0cda07a8ecb7cd8e3dd7009c9636534f6f239cffe1fc8daa466f78b676c7119efb96bce4e69ca2a25d0b34"
-        "ed9c3ff999b801597d5220e307eaa5bee507fb94d1fa69f9e519b2de315bac92c36f2ea1fa1df4478c0dde"
-        "deae8c70e0233cd098040cd796b02c370f1fa4cc0124f1302e0201033029a1083106020100020101a20302"
-        "0120a30402020100a4053103020101a6053103020140bf83770205000420ccd540855f833a5e1480bfd2d3"
-        "6faf3aeee15df5beabe2691bc82dde2a7aa910041064c9f689c60ff6223ab6e6999e0eb6e5");
+        // IKeyMintDevice.aidl
+        "30820179"  // SEQUENCE length 0x179 (SecureKeyWrapper) {
+        "020100"    // INTEGER length 1 value 0x00 (version)
+        "04820100"  // OCTET STRING length 0x100 (encryptedTransportKey)
+        "934bf94e2aa28a3f83c9f79297250262"
+        "fbe3276b5a1c91159bbfa3ef8957aac8"
+        "4b59b30b455a79c2973480823d8b3863"
+        "c3deef4a8e243590268d80e18751a0e1"
+        "30f67ce6a1ace9f79b95e097474febc9"
+        "81195b1d13a69086c0863f66a7b7fdb4"
+        "8792227b1ac5e2489febdf087ab54864"
+        "83033a6f001ca5d1ec1e27f5c30f4cec"
+        "2642074a39ae68aee552e196627a8e3d"
+        "867e67a8c01b11e75f13cca0a97ab668"
+        "b50cda07a8ecb7cd8e3dd7009c963653"
+        "4f6f239cffe1fc8daa466f78b676c711"
+        "9efb96bce4e69ca2a25d0b34ed9c3ff9"
+        "99b801597d5220e307eaa5bee507fb94"
+        "d1fa69f9e519b2de315bac92c36f2ea1"
+        "fa1df4478c0ddedeae8c70e0233cd098"
+        "040c"  // OCTET STRING length 0x0c (initializationVector)
+        "d796b02c370f1fa4cc0124f1"
+        "302e"    // SEQUENCE length 0x2e (KeyDescription) {
+        "020103"  // INTEGER length 1 value 0x03 (keyFormat = RAW)
+        "3029"    // SEQUENCE length 0x29 (AuthorizationList) {
+        "a108"    // [1] context-specific constructed tag=1 length 0x08 { (purpose)
+        "3106"    // SET length 0x06
+        "020100"  // INTEGER length 1 value 0x00 (Encrypt)
+        "020101"  // INTEGER length 1 value 0x01 (Decrypt)
+        // } end SET
+        // } end [1]
+        "a203"    // [2] context-specific constructed tag=2 length 0x02 { (algorithm)
+        "020120"  // INTEGER length 1 value 0x20 (AES)
+        // } end [2]
+        "a304"      // [3] context-specific constructed tag=3 length 0x04 { (keySize)
+        "02020100"  // INTEGER length 2 value 0x100
+        // } end [3]
+        "a405"    // [4] context-specific constructed tag=4 length 0x05 { (blockMode)
+        "3103"    // SET length 0x03 {
+        "020101"  // INTEGER length 1 value 0x01 (ECB)
+        // } end SET
+        // } end [4]
+        "a605"    // [6] context-specific constructed tag=6 length 0x05 { (padding)
+        "3103"    // SET length 0x03 {
+        "020140"  // INTEGER length 1 value 0x40 (PKCS7)
+        // } end SET
+        // } end [5]
+        "bf837702"  // [503] context-specific constructed tag=503=0x1F7 length 0x02 {
+                    // (noAuthRequired)
+        "0500"      // NULL
+        // } end [503]
+        // } end SEQUENCE (AuthorizationList)
+        // } end SEQUENCE (KeyDescription)
+        "0420"  // OCTET STRING length 0x20 (encryptedKey)
+        "ccd540855f833a5e1480bfd2d36faf3a"
+        "eee15df5beabe2691bc82dde2a7aa910"
+        "0410"  // OCTET STRING length 0x10 (tag)
+        "64c9f689c60ff6223ab6e6999e0eb6e5"
+        // } SEQUENCE (SecureKeyWrapper)
+);
 
 auto wrapped_key_masked = hex2str(
-        "3082017902010004820100aad93ed5924f283b4bb5526fbe7a1412f9d9749ec30db9062b29e574a8546f33"
-        "c88732452f5b8e6a391ee76c39ed1712c61d8df6213dec1cffbc17a8c6d04c7b30893d8daa9b2015213e21"
-        "946821553207f8f9931c4caba23ed3bee28b36947e47f10e0a5c3dc51c988a628daad3e5e1f4005e79c2d5"
-        "a96c284b4b8d7e4948f331e5b85dd5a236f85579f3ea1d1b848487470bdb0ab4f81a12bee42c99fe0df4be"
-        "e3759453e69ad1d68a809ce06b949f7694a990429b2fe81e066ff43e56a21602db70757922a4bcc23ab89f"
-        "1e35da77586775f423e519c2ea394caf48a28d0c8020f1dcf6b3a68ec246f615ae96dae9a079b1f6eb9590"
-        "33c1af5c125fd94168040c6d9721d08589581ab49204a3302e0201033029a1083106020100020101a20302"
-        "0120a30402020100a4053103020101a6053103020140bf83770205000420a61c6e247e25b3e6e69aa78eb0"
-        "3c2d4ac20d1f99a9a024a76f35c8e2cab9b68d04102560c70109ae67c030f00b98b512a670");
+        // IKeyMintDevice.aidl
+        "30820179"  // SEQUENCE length 0x179 (SecureKeyWrapper) {
+        "020100"    // INTEGER length 1 value 0x00 (version)
+        "04820100"  // OCTET STRING length 0x100 (encryptedTransportKey)
+        "aad93ed5924f283b4bb5526fbe7a1412"
+        "f9d9749ec30db9062b29e574a8546f33"
+        "c88732452f5b8e6a391ee76c39ed1712"
+        "c61d8df6213dec1cffbc17a8c6d04c7b"
+        "30893d8daa9b2015213e219468215532"
+        "07f8f9931c4caba23ed3bee28b36947e"
+        "47f10e0a5c3dc51c988a628daad3e5e1"
+        "f4005e79c2d5a96c284b4b8d7e4948f3"
+        "31e5b85dd5a236f85579f3ea1d1b8484"
+        "87470bdb0ab4f81a12bee42c99fe0df4"
+        "bee3759453e69ad1d68a809ce06b949f"
+        "7694a990429b2fe81e066ff43e56a216"
+        "02db70757922a4bcc23ab89f1e35da77"
+        "586775f423e519c2ea394caf48a28d0c"
+        "8020f1dcf6b3a68ec246f615ae96dae9"
+        "a079b1f6eb959033c1af5c125fd94168"
+        "040c"  // OCTET STRING length 0x0c (initializationVector)
+        "6d9721d08589581ab49204a3"
+        "302e"    // SEQUENCE length 0x2e (KeyDescription) {
+        "020103"  // INTEGER length 1 value 0x03 (keyFormat = RAW)
+        "3029"    // SEQUENCE length 0x29 (AuthorizationList) {
+        "a108"    // [1] context-specific constructed tag=1 length 0x08 { (purpose)
+        "3106"    // SET length 0x06
+        "020100"  // INTEGER length 1 value 0x00 (Encrypt)
+        "020101"  // INTEGER length 1 value 0x01 (Decrypt)
+        // } end SET
+        // } end [1]
+        "a203"    // [2] context-specific constructed tag=2 length 0x02 { (algorithm)
+        "020120"  // INTEGER length 1 value 0x20 (AES)
+        // } end [2]
+        "a304"      // [3] context-specific constructed tag=3 length 0x04 { (keySize)
+        "02020100"  // INTEGER length 2 value 0x100
+        // } end [3]
+        "a405"    // [4] context-specific constructed tag=4 length 0x05 { (blockMode
+        "3103"    // SET length 0x03 {
+        "020101"  // INTEGER length 1 value 0x01 (ECB)
+        // } end SET
+        // } end [4]
+        "a605"    // [6] context-specific constructed tag=6 length 0x05 { (padding)
+        "3103"    // SET length 0x03 {
+        "020140"  // INTEGER length 1 value 0x40 (PKCS7)
+        // } end SET
+        // } end [5]
+        "bf837702"  // [503] context-specific constructed tag=503=0x1F7 length 0x02 {
+                    // (noAuthRequired)
+        "0500"      // NULL
+        // } end [503]
+        // } end SEQUENCE (AuthorizationList)
+        // } end SEQUENCE (KeyDescription)
+        "0420"  // OCTET STRING length 0x20 (encryptedKey)
+        "a61c6e247e25b3e6e69aa78eb03c2d4a"
+        "c20d1f99a9a024a76f35c8e2cab9b68d"
+        "0410"  // OCTET STRING length 0x10 (tag)
+        "2560c70109ae67c030f00b98b512a670"
+        // } SEQUENCE (SecureKeyWrapper)
+);
 
 auto wrapping_key = hex2str(
-        "308204be020100300d06092a864886f70d0101010500048204a8308204a40201000282010100aec367931d"
-        "8900ce56b0067f7d70e1fc653f3f34d194c1fed50018fb43db937b06e673a837313d56b1c725150a3fef86"
-        "acbddc41bb759c2854eae32d35841efb5c18d82bc90a1cb5c1d55adf245b02911f0b7cda88c421ff0ebafe"
-        "7c0d23be312d7bd5921ffaea1347c157406fef718f682643e4e5d33c6703d61c0cf7ac0bf4645c11f5c137"
-        "4c3886427411c449796792e0bef75dec858a2123c36753e02a95a96d7c454b504de385a642e0dfc3e60ac3"
-        "a7ee4991d0d48b0172a95f9536f02ba13cecccb92b727db5c27e5b2f5cec09600b286af5cf14c42024c61d"
-        "dfe71c2a8d7458f185234cb00e01d282f10f8fc6721d2aed3f4833cca2bd8fa62821dd5502030100010282"
-        "0100431447b6251908112b1ee76f99f3711a52b6630960046c2de70de188d833f8b8b91e4d785caeeeaf4f"
-        "0f74414e2cda40641f7fe24f14c67a88959bdb27766df9e710b630a03adc683b5d2c43080e52bee71e9eae"
-        "b6de297a5fea1072070d181c822bccff087d63c940ba8a45f670feb29fb4484d1c95e6d2579ba02aae0a00"
-        "900c3ebf490e3d2cd7ee8d0e20c536e4dc5a5097272888cddd7e91f228b1c4d7474c55b8fcd618c4a957bb"
-        "ddd5ad7407cc312d8d98a5caf7e08f4a0d6b45bb41c652659d5a5ba05b663737a8696281865ba20fbdd7f8"
-        "51e6c56e8cbe0ddbbf24dc03b2d2cb4c3d540fb0af52e034a2d06698b128e5f101e3b51a34f8d8b4f86181"
-        "02818100de392e18d682c829266cc3454e1d6166242f32d9a1d10577753e904ea7d08bff841be5bac82a16"
-        "4c5970007047b8c517db8f8f84e37bd5988561bdf503d4dc2bdb38f885434ae42c355f725c9a60f91f0788"
-        "e1f1a97223b524b5357fdf72e2f696bab7d78e32bf92ba8e1864eab1229e91346130748a6e3c124f9149d7"
-        "1c743502818100c95387c0f9d35f137b57d0d65c397c5e21cc251e47008ed62a542409c8b6b6ac7f8967b3"
-        "863ca645fcce49582a9aa17349db6c4a95affdae0dae612e1afac99ed39a2d934c880440aed8832f984316"
-        "3a47f27f392199dc1202f9a0f9bd08308007cb1e4e7f58309366a7de25f7c3c9b880677c068e1be936e812"
-        "88815252a8a102818057ff8ca1895080b2cae486ef0adfd791fb0235c0b8b36cd6c136e52e4085f4ea5a06"
-        "3212a4f105a3764743e53281988aba073f6e0027298e1c4378556e0efca0e14ece1af76ad0b030f27af6f0"
-        "ab35fb73a060d8b1a0e142fa2647e93b32e36d8282ae0a4de50ab7afe85500a16f43a64719d6e2b9439823"
-        "719cd08bcd03178102818100ba73b0bb28e3f81e9bd1c568713b101241acc607976c4ddccc90e65b6556ca"
-        "31516058f92b6e09f3b160ff0e374ec40d78ae4d4979fde6ac06a1a400c61dd31254186af30b22c10582a8"
-        "a43e34fe949c5f3b9755bae7baa7b7b7a6bd03b38cef55c86885fc6c1978b9cee7ef33da507c9df6b9277c"
-        "ff1e6aaa5d57aca528466102818100c931617c77829dfb1270502be9195c8f2830885f57dba869536811e6"
-        "864236d0c4736a0008a145af36b8357a7c3d139966d04c4e00934ea1aede3bb6b8ec841dc95e3f579751e2"
-        "bfdfe27ae778983f959356210723287b0affcc9f727044d48c373f1babde0724fa17a4fd4da0902c7c9b9b"
-        "f27ba61be6ad02dfddda8f4e6822");
+        // RFC 5208 s5
+        "308204be"            // SEQUENCE length 0x4be (PrivateKeyInfo) {
+        "020100"              // INTEGER length 1 value 0x00 (version)
+        "300d"                // SEQUENCE length 0x0d (AlgorithmIdentifier) {
+        "0609"                // OBJECT IDENTIFIER length 0x09 (algorithm)
+        "2a864886f70d010101"  // 1.2.840.113549.1.1.1 (RSAES-PKCS1-v1_5 encryption scheme)
+        "0500"                // NULL (parameters)
+        // } SEQUENCE (AlgorithmIdentifier)
+        "048204a8"  // OCTET STRING len 0x4a8 (privateKey), which contains...
+        // RFC 8017 A.1.2
+        "308204a4"                          // SEQUENCE len 0x4a4 (RSAPrivateKey) {
+        "020100"                            // INTEGER length 1 value 0x00 (version)
+        "02820101"                          // INTEGER length 0x0101 (modulus) value...
+        "00aec367931d8900ce56b0067f7d70e1"  // 0x10
+        "fc653f3f34d194c1fed50018fb43db93"  // 0x20
+        "7b06e673a837313d56b1c725150a3fef"  // 0x30
+        "86acbddc41bb759c2854eae32d35841e"  // 0x40
+        "fb5c18d82bc90a1cb5c1d55adf245b02"  // 0x50
+        "911f0b7cda88c421ff0ebafe7c0d23be"  // 0x60
+        "312d7bd5921ffaea1347c157406fef71"  // 0x70
+        "8f682643e4e5d33c6703d61c0cf7ac0b"  // 0x80
+        "f4645c11f5c1374c3886427411c44979"  // 0x90
+        "6792e0bef75dec858a2123c36753e02a"  // 0xa0
+        "95a96d7c454b504de385a642e0dfc3e6"  // 0xb0
+        "0ac3a7ee4991d0d48b0172a95f9536f0"  // 0xc0
+        "2ba13cecccb92b727db5c27e5b2f5cec"  // 0xd0
+        "09600b286af5cf14c42024c61ddfe71c"  // 0xe0
+        "2a8d7458f185234cb00e01d282f10f8f"  // 0xf0
+        "c6721d2aed3f4833cca2bd8fa62821dd"  // 0x100
+        "55"                                // 0x101
+        "0203010001"                        // INTEGER length 3 value 0x10001 (publicExponent)
+        "02820100"                          // INTEGER length 0x100 (privateExponent) value...
+        "431447b6251908112b1ee76f99f3711a"  // 0x10
+        "52b6630960046c2de70de188d833f8b8"  // 0x20
+        "b91e4d785caeeeaf4f0f74414e2cda40"  // 0x30
+        "641f7fe24f14c67a88959bdb27766df9"  // 0x40
+        "e710b630a03adc683b5d2c43080e52be"  // 0x50
+        "e71e9eaeb6de297a5fea1072070d181c"  // 0x60
+        "822bccff087d63c940ba8a45f670feb2"  // 0x70
+        "9fb4484d1c95e6d2579ba02aae0a0090"  // 0x80
+        "0c3ebf490e3d2cd7ee8d0e20c536e4dc"  // 0x90
+        "5a5097272888cddd7e91f228b1c4d747"  // 0xa0
+        "4c55b8fcd618c4a957bbddd5ad7407cc"  // 0xb0
+        "312d8d98a5caf7e08f4a0d6b45bb41c6"  // 0xc0
+        "52659d5a5ba05b663737a8696281865b"  // 0xd0
+        "a20fbdd7f851e6c56e8cbe0ddbbf24dc"  // 0xe0
+        "03b2d2cb4c3d540fb0af52e034a2d066"  // 0xf0
+        "98b128e5f101e3b51a34f8d8b4f86181"  // 0x100
+        "028181"                            // INTEGER length 0x81 (prime1) value...
+        "00de392e18d682c829266cc3454e1d61"  // 0x10
+        "66242f32d9a1d10577753e904ea7d08b"  // 0x20
+        "ff841be5bac82a164c5970007047b8c5"  // 0x30
+        "17db8f8f84e37bd5988561bdf503d4dc"  // 0x40
+        "2bdb38f885434ae42c355f725c9a60f9"  // 0x50
+        "1f0788e1f1a97223b524b5357fdf72e2"  // 0x60
+        "f696bab7d78e32bf92ba8e1864eab122"  // 0x70
+        "9e91346130748a6e3c124f9149d71c74"  // 0x80
+        "35"
+        "028181"                            // INTEGER length 0x81 (prime2) value...
+        "00c95387c0f9d35f137b57d0d65c397c"  // 0x10
+        "5e21cc251e47008ed62a542409c8b6b6"  // 0x20
+        "ac7f8967b3863ca645fcce49582a9aa1"  // 0x30
+        "7349db6c4a95affdae0dae612e1afac9"  // 0x40
+        "9ed39a2d934c880440aed8832f984316"  // 0x50
+        "3a47f27f392199dc1202f9a0f9bd0830"  // 0x60
+        "8007cb1e4e7f58309366a7de25f7c3c9"  // 0x70
+        "b880677c068e1be936e81288815252a8"  // 0x80
+        "a1"
+        "028180"                            // INTEGER length 0x80 (exponent1) value...
+        "57ff8ca1895080b2cae486ef0adfd791"  // 0x10
+        "fb0235c0b8b36cd6c136e52e4085f4ea"  // 0x20
+        "5a063212a4f105a3764743e53281988a"  // 0x30
+        "ba073f6e0027298e1c4378556e0efca0"  // 0x40
+        "e14ece1af76ad0b030f27af6f0ab35fb"  // 0x50
+        "73a060d8b1a0e142fa2647e93b32e36d"  // 0x60
+        "8282ae0a4de50ab7afe85500a16f43a6"  // 0x70
+        "4719d6e2b9439823719cd08bcd031781"  // 0x80
+        "028181"                            // INTEGER length 0x81 (exponent2) value...
+        "00ba73b0bb28e3f81e9bd1c568713b10"  // 0x10
+        "1241acc607976c4ddccc90e65b6556ca"  // 0x20
+        "31516058f92b6e09f3b160ff0e374ec4"  // 0x30
+        "0d78ae4d4979fde6ac06a1a400c61dd3"  // 0x40
+        "1254186af30b22c10582a8a43e34fe94"  // 0x50
+        "9c5f3b9755bae7baa7b7b7a6bd03b38c"  // 0x60
+        "ef55c86885fc6c1978b9cee7ef33da50"  // 0x70
+        "7c9df6b9277cff1e6aaa5d57aca52846"  // 0x80
+        "61"
+        "028181"                            // INTEGER length 0x81 (coefficient) value...
+        "00c931617c77829dfb1270502be9195c"  // 0x10
+        "8f2830885f57dba869536811e6864236"  // 0x20
+        "d0c4736a0008a145af36b8357a7c3d13"  // 0x30
+        "9966d04c4e00934ea1aede3bb6b8ec84"  // 0x40
+        "1dc95e3f579751e2bfdfe27ae778983f"  // 0x50
+        "959356210723287b0affcc9f727044d4"  // 0x60
+        "8c373f1babde0724fa17a4fd4da0902c"  // 0x70
+        "7c9b9bf27ba61be6ad02dfddda8f4e68"  // 0x80
+        "22"
+        // } SEQUENCE
+        // } SEQUENCE ()
+);
 
 string zero_masking_key =
         hex2str("0000000000000000000000000000000000000000000000000000000000000000");
@@ -2606,6 +3640,36 @@
     EXPECT_EQ(message, plaintext);
 }
 
+/*
+ * ImportWrappedKeyTest.SuccessSidsIgnored
+ *
+ * Verifies that password_sid and biometric_sid are ignored on import if the authorizations don't
+ * include Tag:USER_SECURE_ID.
+ */
+TEST_P(ImportWrappedKeyTest, SuccessSidsIgnored) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
+
+    int64_t password_sid = 42;
+    int64_t biometric_sid = 24;
+    ASSERT_EQ(ErrorCode::OK,
+              ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP),
+                               password_sid, biometric_sid));
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    string ciphertext = EncryptMessage(message, params);
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+}
+
 TEST_P(ImportWrappedKeyTest, SuccessMasked) {
     auto wrapping_key_desc = AuthorizationSetBuilder()
                                      .RsaEncryptionKey(2048, 65537)
@@ -2652,6 +3716,36 @@
                                      .Padding(PaddingMode::RSA_OAEP)));
 }
 
+TEST_P(ImportWrappedKeyTest, WrongPaddingMode) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_PSS)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
+
+    ASSERT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE,
+              ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP)));
+}
+
+TEST_P(ImportWrappedKeyTest, WrongDigest) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_512)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
+
+    ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+              ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP)));
+}
+
 INSTANTIATE_KEYMINT_AIDL_TEST(ImportWrappedKeyTest);
 
 typedef KeyMintAidlTestBase EncryptionOperationsTest;
@@ -2662,22 +3756,26 @@
  * Verifies that raw RSA encryption works.
  */
 TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) {
-    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .RsaEncryptionKey(2048, 65537)
-                                                 .Padding(PaddingMode::NONE)
-                                                 .SetDefaultValidity()));
+    for (uint64_t exponent : {3, 65537}) {
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .RsaEncryptionKey(2048, exponent)
+                                                     .Padding(PaddingMode::NONE)
+                                                     .SetDefaultValidity()));
 
-    string message = string(2048 / 8, 'a');
-    auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
-    string ciphertext1 = EncryptMessage(message, params);
-    EXPECT_EQ(2048U / 8, ciphertext1.size());
+        string message = string(2048 / 8, 'a');
+        auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+        string ciphertext1 = EncryptMessage(message, params);
+        EXPECT_EQ(2048U / 8, ciphertext1.size());
 
-    string ciphertext2 = EncryptMessage(message, params);
-    EXPECT_EQ(2048U / 8, ciphertext2.size());
+        string ciphertext2 = EncryptMessage(message, params);
+        EXPECT_EQ(2048U / 8, ciphertext2.size());
 
-    // Unpadded RSA is deterministic
-    EXPECT_EQ(ciphertext1, ciphertext2);
+        // Unpadded RSA is deterministic
+        EXPECT_EQ(ciphertext1, ciphertext2);
+
+        CheckedDeleteKey();
+    }
 }
 
 /*
@@ -2804,14 +3902,31 @@
                                                  .Padding(PaddingMode::RSA_OAEP)
                                                  .Digest(Digest::NONE)
                                                  .SetDefaultValidity()));
-    string message = "Hello World!";
 
     auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::NONE);
     EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST, Begin(KeyPurpose::ENCRYPT, params));
 }
 
 /*
- * EncryptionOperationsTest.RsaOaepInvalidDigest
+ * EncryptionOperationsTest.RsaOaepInvalidPadding
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to operate
+ * with a padding value that is only suitable for signing/verifying.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepInvalidPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_PSS)
+                                                 .Digest(Digest::NONE)
+                                                 .SetDefaultValidity()));
+
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PSS).Digest(Digest::NONE);
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepDecryptWithWrongDigest
  *
  * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to decrypt
  * with a different digest than was used to encrypt.
@@ -3010,7 +4125,7 @@
 /*
  * EncryptionOperationsTest.RsaPkcs1TooLarge
  *
- * Verifies that RSA PKCS encryption fails in the correct way when the mssage is too large.
+ * Verifies that RSA PKCS encryption fails in the correct way when the message is too large.
  */
 TEST_P(EncryptionOperationsTest, RsaPkcs1TooLarge) {
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -3095,7 +4210,49 @@
 }
 
 /*
- * EncryptionOperationsTest.AesEcbRoundTripSuccess
+ * EncryptionOperationsTest.AesEcbUnknownTag
+ *
+ * Verifies that AES ECB operations ignore unknown tags.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbUnknownTag) {
+    int32_t unknown_tag_value = ((7 << 28) /* TagType:BOOL */ | 150);
+    Tag unknown_tag = static_cast<Tag>(unknown_tag_value);
+    KeyParameter unknown_param;
+    unknown_param.tag = unknown_tag;
+
+    vector<KeyCharacteristics> key_characteristics;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(unknown_param),
+                                         &key_blob_, &key_characteristics));
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    // Unknown tags should not be returned in key characteristics.
+    AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+    AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+    EXPECT_EQ(hw_enforced.find(unknown_tag), -1);
+    EXPECT_EQ(sw_enforced.find(unknown_tag), -1);
+
+    // Encrypt without mentioning the unknown parameter.
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+    string message = "12345678901234567890123456789012";
+    string ciphertext = EncryptMessage(message, params);
+    EXPECT_EQ(message.size(), ciphertext.size());
+
+    // Decrypt including the unknown parameter.
+    auto decrypt_params = AuthorizationSetBuilder()
+                                  .BlockMode(BlockMode::ECB)
+                                  .Padding(PaddingMode::NONE)
+                                  .Authorization(unknown_param);
+    string plaintext = DecryptMessage(ciphertext, decrypt_params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesWrongMode
  *
  * Verifies that AES encryption fails in the correct way when an unauthorized mode is specified.
  */
@@ -3105,11 +4262,8 @@
                                                  .AesEncryptionKey(128)
                                                  .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
                                                  .Padding(PaddingMode::NONE)));
-
     ASSERT_GT(key_blob_.size(), 0U);
 
-    // Two-block message.
-    string message = "12345678901234567890123456789012";
     EXPECT_EQ(
             ErrorCode::INCOMPATIBLE_BLOCK_MODE,
             Begin(KeyPurpose::ENCRYPT,
@@ -3117,6 +4271,55 @@
 }
 
 /*
+ * EncryptionOperationsTest.AesWrongPadding
+ *
+ * Verifies that AES encryption fails in the correct way when an unauthorized padding is specified.
+ */
+TEST_P(EncryptionOperationsTest, AesWrongPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Padding(PaddingMode::NONE)));
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    EXPECT_EQ(
+            ErrorCode::INCOMPATIBLE_PADDING_MODE,
+            Begin(KeyPurpose::ENCRYPT,
+                  AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::PKCS7)));
+}
+
+/*
+ * EncryptionOperationsTest.AesInvalidParams
+ *
+ * Verifies that AES encryption fails in the correct way when an duplicate parameters are specified.
+ */
+TEST_P(EncryptionOperationsTest, AesInvalidParams) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Padding(PaddingMode::PKCS7)));
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    auto result = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+                                                     .BlockMode(BlockMode::CBC)
+                                                     .BlockMode(BlockMode::ECB)
+                                                     .Padding(PaddingMode::NONE));
+    EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_BLOCK_MODE ||
+                result == ErrorCode::UNSUPPORTED_BLOCK_MODE);
+
+    result = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+                                                .BlockMode(BlockMode::ECB)
+                                                .Padding(PaddingMode::NONE)
+                                                .Padding(PaddingMode::PKCS7));
+    EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
+                result == ErrorCode::UNSUPPORTED_PADDING_MODE);
+}
+
+/*
  * EncryptionOperationsTest.AesWrongPurpose
  *
  * Verifies that AES encryption fails in the correct way when an unauthorized purpose is
@@ -3157,25 +4360,30 @@
 }
 
 /*
- * EncryptionOperationsTest.AesEcbNoPaddingWrongInputSize
+ * EncryptionOperationsTest.AesEcbCbcNoPaddingWrongInputSize
  *
  * Verifies that AES encryption fails in the correct way when provided an input that is not a
  * multiple of the block size and no padding is specified.
  */
-TEST_P(EncryptionOperationsTest, AesEcbNoPaddingWrongInputSize) {
-    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .AesEncryptionKey(128)
-                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
-                                                 .Padding(PaddingMode::NONE)));
-    // Message is slightly shorter than two blocks.
-    string message(16 * 2 - 1, 'a');
+TEST_P(EncryptionOperationsTest, AesEcbCbcNoPaddingWrongInputSize) {
+    for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .AesEncryptionKey(128)
+                                                     .Authorization(TAG_BLOCK_MODE, blockMode)
+                                                     .Padding(PaddingMode::NONE)));
+        // Message is slightly shorter than two blocks.
+        string message(16 * 2 - 1, 'a');
 
-    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
-    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
-    string ciphertext;
-    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &ciphertext));
-    EXPECT_EQ(0U, ciphertext.size());
+        auto params = AuthorizationSetBuilder().BlockMode(blockMode).Padding(PaddingMode::NONE);
+        AuthorizationSet out_params;
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &out_params));
+        string ciphertext;
+        EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &ciphertext));
+        EXPECT_EQ(0U, ciphertext.size());
+
+        CheckedDeleteKey();
+    }
 }
 
 /*
@@ -3495,7 +4703,7 @@
 }
 
 /*
- * EncryptionOperationsTest.AesCtrInvalidCallerNonce
+ * EncryptionOperationsTest.AesCbcRoundTripSuccess
  *
  * Verifies that keymint fails correctly when the user supplies an incorrect-size nonce.
  */
@@ -3735,6 +4943,31 @@
 }
 
 /*
+ * EncryptionOperationsTest.AesGcmDifferentAutoNonces
+ *
+ * Verifies that encrypting the same data with KeyMint generated nonces produces different outputs.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmDifferentAutoNonces) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+
+    string ciphertext1 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
+    string ciphertext2 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
+    string ciphertext3 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
+
+    ASSERT_NE(ciphertext1, ciphertext2);
+    ASSERT_NE(ciphertext1, ciphertext3);
+    ASSERT_NE(ciphertext2, ciphertext3);
+}
+
+/*
  * EncryptionOperationsTest.AesGcmTooShortTag
  *
  * Verifies that AES GCM mode fails correctly when a too-short tag length is specified.
@@ -3962,6 +5195,9 @@
     EXPECT_EQ(ErrorCode::OK, Update(message, &ciphertext));
     EXPECT_EQ(ErrorCode::INVALID_TAG, UpdateAad("foo"));
 
+    // The failure should have already cancelled the operation.
+    EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort());
+
     op_ = {};
 }
 
@@ -4161,11 +5397,8 @@
                                                  .BlockMode(BlockMode::ECB)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .Padding(PaddingMode::NONE)));
-    for (size_t i = 0; i < 32; ++i) {
-        auto inParams =
-                AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
-        EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
-    }
+    auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
 }
 
 /*
@@ -4331,6 +5564,25 @@
 }
 
 /*
+ * EncryptionOperationsTest.TripleDesInvalidCallerIv
+ *
+ * Validates that keymint fails correctly when the user supplies an incorrect-size IV.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesInvalidCallerIv) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Authorization(TAG_CALLER_NONCE)
+                                                 .Padding(PaddingMode::NONE)));
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::CBC)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_NONCE, AidlBuf("abcdefg"));
+    EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
  * EncryptionOperationsTest.TripleDesCallerIv
  *
  * Validates that 3DES keys can allow caller-specified IVs, and use them correctly.
@@ -4368,7 +5620,7 @@
 /*
  * EncryptionOperationsTest, TripleDesCallerNonceProhibited.
  *
- * Verifies that 3DES keys without TAG_CALLER_NONCE do not allow caller-specified IVS.
+ * Verifies that 3DES keys without TAG_CALLER_NONCE do not allow caller-specified IVs.
  */
 TEST_P(EncryptionOperationsTest, TripleDesCallerNonceProhibited) {
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -4416,25 +5668,29 @@
 }
 
 /*
- * EncryptionOperationsTest.TripleDesCbcNoPaddingWrongInputSize
+ * EncryptionOperationsTest.TripleDesEcbCbcNoPaddingWrongInputSize
  *
  * Verifies that unpadded CBC operations reject inputs that are not a multiple of block size.
  */
-TEST_P(EncryptionOperationsTest, TripleDesCbcNoPaddingWrongInputSize) {
-    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .TripleDesEncryptionKey(168)
-                                                 .BlockMode(BlockMode::CBC)
-                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .Padding(PaddingMode::NONE)));
-    // Message is slightly shorter than two blocks.
-    string message = "123456789012345";
+TEST_P(EncryptionOperationsTest, TripleDesEcbCbcNoPaddingWrongInputSize) {
+    for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .TripleDesEncryptionKey(168)
+                                                     .BlockMode(blockMode)
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .Padding(PaddingMode::NONE)));
+        // Message is slightly shorter than two blocks.
+        string message = "123456789012345";
 
-    auto begin_params =
-            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
-    AuthorizationSet output_params;
-    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &output_params));
-    string ciphertext;
-    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, "", &ciphertext));
+        auto begin_params =
+                AuthorizationSetBuilder().BlockMode(blockMode).Padding(PaddingMode::NONE);
+        AuthorizationSet output_params;
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &output_params));
+        string ciphertext;
+        EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, "", &ciphertext));
+
+        CheckedDeleteKey();
+    }
 }
 
 /*
@@ -4842,6 +6098,19 @@
 
 INSTANTIATE_KEYMINT_AIDL_TEST(UsageCountLimitTest);
 
+typedef KeyMintAidlTestBase GetHardwareInfoTest;
+
+TEST_P(GetHardwareInfoTest, GetHardwareInfo) {
+    // Retrieving hardware info should give the same result each time.
+    KeyMintHardwareInfo info;
+    ASSERT_TRUE(keyMint().getHardwareInfo(&info).isOk());
+    KeyMintHardwareInfo info2;
+    ASSERT_TRUE(keyMint().getHardwareInfo(&info2).isOk());
+    EXPECT_EQ(info, info2);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(GetHardwareInfoTest);
+
 typedef KeyMintAidlTestBase AddEntropyTest;
 
 /*
@@ -4873,6 +6142,16 @@
     EXPECT_TRUE(keyMint().addRngEntropy(AidlBuf(string(2 * 1024, 'a'))).isOk());
 }
 
+/*
+ * AddEntropyTest.AddTooLargeEntropy
+ *
+ * Verifies that the addRngEntropy method rejects more than 2KiB  of data.
+ */
+TEST_P(AddEntropyTest, AddTooLargeEntropy) {
+    ErrorCode rc = GetReturnErrorCode(keyMint().addRngEntropy(AidlBuf(string(2 * 1024 + 1, 'a'))));
+    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, rc);
+}
+
 INSTANTIATE_KEYMINT_AIDL_TEST(AddEntropyTest);
 
 typedef KeyMintAidlTestBase KeyDeletionTest;
@@ -4984,6 +6263,28 @@
 
 INSTANTIATE_KEYMINT_AIDL_TEST(KeyDeletionTest);
 
+typedef KeyMintAidlTestBase KeyUpgradeTest;
+
+/**
+ * KeyUpgradeTest.UpgradeInvalidKey
+ *
+ * This test checks that the HAL excepts invalid key blobs..
+ */
+TEST_P(KeyUpgradeTest, UpgradeInvalidKey) {
+    AidlBuf key_blob = AidlBuf("just some garbage data which is not a valid key blob");
+
+    std::vector<uint8_t> new_blob;
+    Status result = keymint_->upgradeKey(key_blob,
+                                         AuthorizationSetBuilder()
+                                                 .Authorization(TAG_APPLICATION_ID, "clientid")
+                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                                 .vector_data(),
+                                         &new_blob);
+    ASSERT_EQ(ErrorCode::INVALID_KEY_BLOB, GetReturnErrorCode(result));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(KeyUpgradeTest);
+
 using UpgradeKeyTest = KeyMintAidlTestBase;
 
 /*
@@ -5052,7 +6353,7 @@
 typedef KeyMintAidlTestBase TransportLimitTest;
 
 /*
- * TransportLimitTest.FinishInput
+ * TransportLimitTest.LargeFinishInput
  *
  * Verifies that passing input data to finish succeeds as expected.
  */
@@ -5207,6 +6508,17 @@
 
 INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest);
 
+using DestroyAttestationIdsTest = KeyMintAidlTestBase;
+
+// This is a problematic test, as it can render the device under test permanently unusable.
+// Re-enable and run at your own risk.
+TEST_P(DestroyAttestationIdsTest, DISABLED_DestroyTest) {
+    auto result = DestroyAttestationIds();
+    EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(DestroyAttestationIdsTest);
+
 using EarlyBootKeyTest = KeyMintAidlTestBase;
 
 TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) {
@@ -5219,7 +6531,7 @@
     CheckedDeleteKey(&ecdsaKeyData.blob);
 }
 
-// This is a more comprenhensive test, but it can only be run on a machine which is still in early
+// This is a more comprehensive test, but it can only be run on a machine which is still in early
 // boot stage, which no proper Android device is by the time we can run VTS.  To use this,
 // un-disable it and modify vold to remove the call to earlyBootEnded().  Running the test will end
 // early boot, so you'll have to reboot between runs.
@@ -5287,7 +6599,7 @@
     EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
 
     ErrorCode rc = GetReturnErrorCode(
-            keyMint().deviceLocked(false /* passwordOnly */, {} /* verificationToken */));
+            keyMint().deviceLocked(false /* passwordOnly */, {} /* timestampToken */));
     ASSERT_EQ(ErrorCode::OK, rc);
     EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseAesKey(aesKeyData.blob));
     EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseHmacKey(hmacKeyData.blob));
@@ -5302,16 +6614,6 @@
 
 INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest);
 
-using PerformOperationTest = KeyMintAidlTestBase;
-
-TEST_P(PerformOperationTest, RequireUnimplemented) {
-    vector<uint8_t> response;
-    auto result = keymint_->performOperation({} /* request */, &response);
-    ASSERT_EQ(GetReturnErrorCode(result), ErrorCode::UNIMPLEMENTED);
-}
-
-INSTANTIATE_KEYMINT_AIDL_TEST(PerformOperationTest);
-
 }  // namespace aidl::android::hardware::security::keymint::test
 
 int main(int argc, char** argv) {
@@ -5336,6 +6638,10 @@
             } else {
                 std::cout << "NOT dumping attestations" << std::endl;
             }
+            // TODO(drysdale): Remove this flag when available KeyMint devices comply with spec
+            if (std::string(argv[i]) == "--check_patchLevels") {
+                aidl::android::hardware::security::keymint::test::check_patchLevels = true;
+            }
         }
     }
     return RUN_ALL_TESTS();
diff --git a/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
index 6c795f5..54b6fdc 100644
--- a/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
+++ b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
@@ -228,8 +228,7 @@
                     AuthorizationSet* out_params) {
         Status result;
         BeginResult out;
-        result = keymint_->begin(purpose, key_blob_, in_params.vector_data(), HardwareAuthToken(),
-                                 &out);
+        result = keymint_->begin(purpose, key_blob_, in_params.vector_data(), std::nullopt, &out);
         if (result.isOk()) {
             *out_params = out.params;
             op_ = out.operation;
diff --git a/security/keymint/support/include/keymint_support/keymint_tags.h b/security/keymint/support/include/keymint_support/keymint_tags.h
index ae21125..5b2a6f3 100644
--- a/security/keymint/support/include/keymint_support/keymint_tags.h
+++ b/security/keymint/support/include/keymint_support/keymint_tags.h
@@ -153,7 +153,7 @@
         TAG_RESET_SINCE_ID_ROTATION_t, TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t,
         TAG_DIGEST_t, TAG_PADDING_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t,
         TAG_BOOT_PATCHLEVEL_t, TAG_VENDOR_PATCHLEVEL_t, TAG_TRUSTED_CONFIRMATION_REQUIRED_t,
-        TAG_TRUSTED_USER_PRESENCE_REQUIRED_t>;
+        TAG_TRUSTED_USER_PRESENCE_REQUIRED_t, TAG_CERTIFICATE_SERIAL_t, TAG_CERTIFICATE_SUBJECT_t>;
 
 template <typename TypedTagType>
 struct TypedTag2ValueType;
diff --git a/security/keymint/support/include/keymint_support/keymint_utils.h b/security/keymint/support/include/keymint_support/keymint_utils.h
index 53d5b96..e1ead21 100644
--- a/security/keymint/support/include/keymint_support/keymint_utils.h
+++ b/security/keymint/support/include/keymint_support/keymint_utils.h
@@ -38,5 +38,6 @@
 
 uint32_t getOsVersion();
 uint32_t getOsPatchlevel();
+uint32_t getVendorPatchlevel();
 
 }  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/keymint_utils.cpp b/security/keymint/support/keymint_utils.cpp
index e73d602..2dbdfa8 100644
--- a/security/keymint/support/keymint_utils.cpp
+++ b/security/keymint/support/keymint_utils.cpp
@@ -31,10 +31,11 @@
 constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1;
 
 constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
-constexpr char kPlatformPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-[0-9]{2}$";
+constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch";
+constexpr char kPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-[0-9]{2}$";
 constexpr size_t kYearMatch = 1;
 constexpr size_t kMonthMatch = 2;
-constexpr size_t kPlatformPatchlevelMatchCount = kMonthMatch + 1;
+constexpr size_t kPatchlevelMatchCount = kMonthMatch + 1;
 
 uint32_t match_to_uint32(const char* expression, const regmatch_t& match) {
     if (match.rm_so == -1) return 0;
@@ -80,15 +81,14 @@
     return getOsVersion(version.c_str());
 }
 
-uint32_t getOsPatchlevel(const char* patchlevel_str) {
+uint32_t getPatchlevel(const char* patchlevel_str) {
     regex_t regex;
-    if (regcomp(&regex, kPlatformPatchlevelRegex, REG_EXTENDED) != 0) {
+    if (regcomp(&regex, kPatchlevelRegex, REG_EXTENDED) != 0) {
         return 0;
     }
 
-    regmatch_t matches[kPlatformPatchlevelMatchCount];
-    int not_match =
-            regexec(&regex, patchlevel_str, kPlatformPatchlevelMatchCount, matches, 0 /* flags */);
+    regmatch_t matches[kPatchlevelMatchCount];
+    int not_match = regexec(&regex, patchlevel_str, kPatchlevelMatchCount, matches, 0 /* flags */);
     regfree(&regex);
     if (not_match) {
         return 0;
@@ -105,7 +105,12 @@
 
 uint32_t getOsPatchlevel() {
     std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
-    return getOsPatchlevel(patchlevel.c_str());
+    return getPatchlevel(patchlevel.c_str());
+}
+
+uint32_t getVendorPatchlevel() {
+    std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp);
+    return getPatchlevel(patchlevel.c_str());
 }
 
 }  // namespace aidl::android::hardware::security::keymint
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
index 71b4278..2fbd29a 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -43,7 +43,7 @@
      *
      * where:
      *
-     *   ``ISecureClock.TIME_STAMP_MAC_LABEL'' is a sting constant defined in ISecureClock.aidl.
+     *   ``ISecureClock.TIME_STAMP_MAC_LABEL'' is a string constant defined in ISecureClock.aidl.
      *
      *   ``H'' is the shared HMAC key (see computeSharedHmac() in ISharedSecret).
      *
diff --git a/sensors/1.0/ISensors.hal b/sensors/1.0/ISensors.hal
index 8d41de2..0e172ef 100644
--- a/sensors/1.0/ISensors.hal
+++ b/sensors/1.0/ISensors.hal
@@ -103,7 +103,7 @@
      * Flush adds a FLUSH_COMPLETE metadata event to the end of the "batch mode"
      * FIFO for the specified sensor and flushes the FIFO.  If the FIFO is empty
      * or if the sensor doesn't support batching (FIFO size zero), return
-     * SUCCESS and add a trivial FLUSH_COMPLETE event added to the event stream.
+     * OK and add a trivial FLUSH_COMPLETE event added to the event stream.
      * This applies to all sensors other than one-shot sensors. If the sensor
      * is a one-shot sensor, flush must return BAD_VALUE and not generate any
      * flush complete metadata.  If the sensor is not active at the time flush()
diff --git a/sensors/1.0/types.hal b/sensors/1.0/types.hal
index cbbe92f..ac7be06 100644
--- a/sensors/1.0/types.hal
+++ b/sensors/1.0/types.hal
@@ -1130,7 +1130,7 @@
 
     /**
      * High performance mode hint. Device is able to use up more power and take
-     * more reources to improve throughput and latency in high performance mode.
+     * more resources to improve throughput and latency in high performance mode.
      * One possible use case is virtual reality, when sensor latency need to be
      * carefully controlled.
      * int32_t: 1 or 0, denote if device is in/out of high performance mode,
diff --git a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
index 56bc9cf..1f579ba 100644
--- a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
+++ b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
@@ -190,7 +190,7 @@
     });
 }
 
-// Test if sensor list returned is valid
+// Test if sensor hal can switch to different operation modes
 TEST_P(SensorsHidlTest, SetOperationMode) {
     std::vector<SensorInfo> sensorList = getSensorsList();
 
@@ -208,7 +208,7 @@
     ASSERT_EQ(Result::OK, S()->setOperationMode(OperationMode::NORMAL));
 }
 
-// Test if sensor list returned is valid
+// Test if sensor hal can receive injected events in loopback mode
 TEST_P(SensorsHidlTest, InjectSensorEventData) {
     std::vector<SensorInfo> sensorList = getSensorsList();
     std::vector<SensorInfo> sensorSupportInjection;
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
index a8e1996..af14009 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
@@ -425,7 +425,10 @@
             return;
         }
 
-        batchingPeriodInNs = std::min(batchingPeriodInNs, maxBatchingTestTimeNs);
+        if (batchingPeriodInNs > maxBatchingTestTimeNs) {
+            batchingPeriodInNs = maxBatchingTestTimeNs;
+            minFifoCount = (uint32_t)(batchingPeriodInNs / minSamplingPeriodInNs);
+        }
 
         ALOGI("Test batching for %d ms", (int)(batchingPeriodInNs / 1000 / 1000));
 
@@ -448,7 +451,7 @@
                       false /*change collection*/);
 
         // 0.8 + 0.2 times the batching period
-        usleep(batchingPeriodInNs / 1000 * 8 / 10);
+        usleep(batchingPeriodInNs / 1000 * 2 / 10);
         ASSERT_EQ(flush(handle), Result::OK);
 
         // plus some time for the event to deliver
diff --git a/tetheroffload/control/1.1/ITetheringOffloadCallback.hal b/tetheroffload/control/1.1/ITetheringOffloadCallback.hal
index 7a7d56d..9c74641 100644
--- a/tetheroffload/control/1.1/ITetheringOffloadCallback.hal
+++ b/tetheroffload/control/1.1/ITetheringOffloadCallback.hal
@@ -26,8 +26,8 @@
 interface ITetheringOffloadCallback extends @1.0::ITetheringOffloadCallback {
     /**
      * Called when an asynchronous event is generated by the hardware
-     * management process. Events which are common for 1.0 and 1.1 HAL
-     * MUST be fired on both 1.0 and 1.1 callback.
+     * management process. Implementations that report events via this callback
+     * should not invoke onEvent of 1.0 HAL.
      */
     oneway onEvent_1_1(OffloadCallbackEvent event);
 };
diff --git a/tv/tuner/1.0/vts/functional/AndroidTest.xml b/tv/tuner/1.0/vts/functional/AndroidTest.xml
index 3a2db27..18c2b59 100644
--- a/tv/tuner/1.0/vts/functional/AndroidTest.xml
+++ b/tv/tuner/1.0/vts/functional/AndroidTest.xml
@@ -30,5 +30,6 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalTvTunerV1_0TargetTest" />
+        <option name="native-test-timeout" value="30m" />
     </test>
 </configuration>
diff --git a/tv/tuner/1.0/vts/functional/DescramblerTests.cpp b/tv/tuner/1.0/vts/functional/DescramblerTests.cpp
index 2e27475..67f6bae 100644
--- a/tv/tuner/1.0/vts/functional/DescramblerTests.cpp
+++ b/tv/tuner/1.0/vts/functional/DescramblerTests.cpp
@@ -53,12 +53,15 @@
         return failure();
     }
 
-    auto status = mCas->setSessionPrivateData(sessionId, hidlPvtData);
-    if (status != android::hardware::cas::V1_0::Status::OK) {
-        ALOGW("[vts] Failed to set session private data");
-        mCas->closeSession(sessionId);
-        return failure();
+    if (hidlPvtData.size() > 0) {
+        auto status = mCas->setSessionPrivateData(sessionId, hidlPvtData);
+        if (status != android::hardware::cas::V1_0::Status::OK) {
+            ALOGW("[vts] Failed to set session private data");
+            mCas->closeSession(sessionId);
+            return failure();
+        }
     }
+
     return success();
 }
 
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 4c92665..b39abe3 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -56,9 +56,6 @@
 }
 
 void TunerFilterHidlTest::testTimeFilter(TimeFilterConfig filterConf) {
-    if (!timeFilter.support) {
-        return;
-    }
     uint32_t demuxId;
     sp<IDemux> demux;
     DemuxCapabilities caps;
@@ -161,27 +158,36 @@
 
 void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
                                                  FrontendConfig frontendConf, DvrConfig dvrConf) {
-    uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    mDvrTests.setDemux(demux);
+
+    DvrConfig dvrSourceConfig;
+    if (mLnbId || record.hasFrontendConnection) {
+        uint32_t feId;
+        mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+        ASSERT_TRUE(feId != INVALID_ID);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        if (mLnbId) {
+            ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
+        }
+        if (frontendConf.isSoftwareFe) {
+            mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+        }
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+        mFrontendTests.setDvrTests(mDvrTests);
+    } else {
+        dvrSourceConfig = dvrMap[record.dvrSourceId];
+        ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+        ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+        ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+    }
+
     uint32_t filterId;
     sp<IFilter> filter;
-
-    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    if (mLnbId) {
-        ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
-    }
-    if (frontendConf.isSoftwareFe) {
-        mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
-    }
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
-    mDvrTests.setDemux(demux);
-    mFrontendTests.setDvrTests(mDvrTests);
     ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
     ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
     ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
@@ -195,17 +201,39 @@
     ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
     ASSERT_TRUE(mDvrTests.startDvrRecord());
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+
+    if (mLnbId || record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+    } else {
+        // Start DVR Source
+        mDvrTests.startPlaybackInputThread(dvrSourceConfig.playbackInputFile,
+                                           dvrSourceConfig.settings.playback());
+        ASSERT_TRUE(mDvrTests.startDvrPlayback());
+    }
+
     mDvrTests.testRecordOutput();
     mDvrTests.stopRecordThread();
-    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+
+    if (mLnbId || record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+    } else {
+        mDvrTests.stopPlaybackThread();
+        ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+    }
+
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mDvrTests.stopDvrRecord());
     ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
     mDvrTests.closeDvrRecord();
+
+    if (mLnbId || record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    } else {
+        mDvrTests.closeDvrPlayback();
+    }
+
     ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
 
 void TunerRecordHidlTest::recordSingleFilterTestWithLnb(FilterConfig filterConf,
@@ -236,23 +264,34 @@
 void TunerRecordHidlTest::attachSingleFilterToRecordDvrTest(FilterConfig filterConf,
                                                             FrontendConfig frontendConf,
                                                             DvrConfig dvrConf) {
-    uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    mDvrTests.setDemux(demux);
+
+    DvrConfig dvrSourceConfig;
+    if (record.hasFrontendConnection) {
+        uint32_t feId;
+        mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+        ASSERT_TRUE(feId != INVALID_ID);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    } else {
+        dvrSourceConfig = dvrMap[record.dvrSourceId];
+        ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+        ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+        ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+    }
+
     uint32_t filterId;
     sp<IFilter> filter;
-
-    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
-    mDvrTests.setDemux(demux);
+
     ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
     ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
     ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
+
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
@@ -268,30 +307,43 @@
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
     mDvrTests.closeDvrRecord();
     ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
+
+    if (record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    }
 }
 
 void TunerDescramblerHidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
                                                       FrontendConfig frontendConf,
                                                       DescramblerConfig descConfig) {
-    uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+
+    DvrConfig dvrSourceConfig;
+    if (descrambling.hasFrontendConnection) {
+        uint32_t feId;
+        mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        if (frontendConf.isSoftwareFe) {
+            mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+        }
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+        mFrontendTests.setDemux(demux);
+    } else {
+        dvrSourceConfig = dvrMap[descrambling.dvrSourceId];
+        mDvrTests.setDemux(demux);
+        ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+        ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+        ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+    }
+
     set<uint32_t> filterIds;
     uint32_t filterId;
     set<struct FilterConfig>::iterator config;
     set<uint32_t>::iterator id;
-
-    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    if (frontendConf.isSoftwareFe) {
-        mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
-    }
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
-    mFrontendTests.setDemux(demux);
     for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
         ASSERT_TRUE(mFilterTests.openFilterInDemux((*config).type, (*config).bufferSize));
         ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
@@ -314,10 +366,26 @@
     for (id = filterIds.begin(); id != filterIds.end(); id++) {
         ASSERT_TRUE(mFilterTests.startFilter(*id));
     }
-    // tune test
-    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+
+    if (descrambling.hasFrontendConnection) {
+        // tune test
+        ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+    } else {
+        // Start DVR Source
+        mDvrTests.startPlaybackInputThread(dvrSourceConfig.playbackInputFile,
+                                           dvrSourceConfig.settings.playback());
+        ASSERT_TRUE(mDvrTests.startDvrPlayback());
+    }
+
     ASSERT_TRUE(filterDataOutputTest());
-    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+
+    if (descrambling.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+    } else {
+        mDvrTests.stopPlaybackThread();
+        ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+    }
+
     for (id = filterIds.begin(); id != filterIds.end(); id++) {
         ASSERT_TRUE(mFilterTests.stopFilter(*id));
     }
@@ -328,27 +396,45 @@
     for (id = filterIds.begin(); id != filterIds.end(); id++) {
         ASSERT_TRUE(mFilterTests.closeFilter(*id));
     }
+
+    if (descrambling.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    } else {
+        mDvrTests.closeDvrPlayback();
+    }
+
     ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
 
 TEST_P(TunerFrontendHidlTest, TuneFrontend) {
     description("Tune one Frontend with specific setting and check Lock event");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     mFrontendTests.tuneTest(frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerFrontendHidlTest, AutoScanFrontend) {
     description("Run an auto frontend scan with specific setting and check lock scanMessage");
+    if (!scan.hasFrontendConnection) {
+        return;
+    }
     mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_AUTO);
 }
 
 TEST_P(TunerFrontendHidlTest, BlindScanFrontend) {
     description("Run an blind frontend scan with specific setting and check lock scanMessage");
+    if (!scan.hasFrontendConnection) {
+        return;
+    }
     mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
 }
 
 TEST_P(TunerLnbHidlTest, SendDiseqcMessageToLnb) {
     description("Open and configure an Lnb with specific settings then send a diseqc msg to it.");
+    if (!lnbLive.support) {
+        return;
+    }
     if (lnbMap[lnbLive.lnbId].name.compare(emptyHardwareId) == 0) {
         vector<uint32_t> ids;
         ASSERT_TRUE(mLnbTests.getLnbIds(ids));
@@ -370,6 +456,9 @@
 
 TEST_P(TunerDemuxHidlTest, openDemux) {
     description("Open and close a Demux.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
@@ -385,6 +474,9 @@
 
 TEST_P(TunerDemuxHidlTest, getAvSyncTime) {
     description("Get the A/V sync time from a PCR filter.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     if (live.pcrFilterId.compare(emptyHardwareId) == 0) {
         return;
     }
@@ -423,6 +515,9 @@
 
 TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
     description("Open and start a filter in Demux.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     // TODO use paramterized tests
     configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
 }
@@ -457,22 +552,34 @@
 
 TEST_P(TunerFilterHidlTest, testTimeFilter) {
     description("Open a timer filter in Demux and set time stamp.");
+    if (!timeFilter.support) {
+        return;
+    }
     // TODO use paramterized tests
     testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowVideoFilterTest) {
     description("Test Video Filter functionality in Broadcast use case.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowAudioFilterTest) {
     description("Test Audio Filter functionality in Broadcast use case.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     broadcastSingleFilterTest(filterMap[live.audioFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowSectionFilterTest) {
     description("Test Section Filter functionality in Broadcast use case.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     if (live.sectionFilterId.compare(emptyHardwareId) == 0) {
         return;
     }
@@ -481,6 +588,9 @@
 
 TEST_P(TunerBroadcastHidlTest, IonBufferTest) {
     description("Test the av filter data bufferring.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
 }
 
@@ -501,6 +611,22 @@
     playbackSingleFilterTest(filterMap[playback.sectionFilterId], dvrMap[playback.dvrId]);
 }
 
+TEST_P(TunerPlaybackHidlTest, PlaybackDataFlowWithTsAudioFilterTest) {
+    description("Feed ts data from playback and configure Ts audio filter to get output");
+    if (!playback.support) {
+        return;
+    }
+    playbackSingleFilterTest(filterMap[playback.audioFilterId], dvrMap[playback.dvrId]);
+}
+
+TEST_P(TunerPlaybackHidlTest, PlaybackDataFlowWithTsVideoFilterTest) {
+    description("Feed ts data from playback and configure Ts video filter to get output");
+    if (!playback.support) {
+        return;
+    }
+    playbackSingleFilterTest(filterMap[playback.videoFilterId], dvrMap[playback.dvrId]);
+}
+
 TEST_P(TunerRecordHidlTest, AttachFiltersToRecordTest) {
     description("Attach a single filter to the record dvr test.");
     // TODO use paramterized tests
@@ -522,7 +648,7 @@
 
 TEST_P(TunerRecordHidlTest, LnbRecordDataFlowWithTsRecordFilterTest) {
     description("Feed ts data from Fe with Lnb to recording and test with ts record filter");
-    if (lnbRecord.support) {
+    if (!lnbRecord.support) {
         return;
     }
     recordSingleFilterTestWithLnb(filterMap[lnbRecord.recordFilterId],
@@ -532,27 +658,34 @@
 
 TEST_P(TunerDescramblerHidlTest, CreateDescrambler) {
     description("Create Descrambler");
-    if (descrambling.support) {
+    if (!descrambling.support) {
         return;
     }
-    uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
-    mFrontendTests.getFrontendIdByType(frontendMap[descrambling.frontendId].type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+
+    if (descrambling.hasFrontendConnection) {
+        uint32_t feId;
+        mFrontendTests.getFrontendIdByType(frontendMap[descrambling.frontendId].type, feId);
+        ASSERT_TRUE(feId != INVALID_ID);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    }
+
     ASSERT_TRUE(mDescramblerTests.openDescrambler(demuxId));
     ASSERT_TRUE(mDescramblerTests.closeDescrambler());
     ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
+
+    if (descrambling.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    }
 }
 
 TEST_P(TunerDescramblerHidlTest, ScrambledBroadcastDataFlowMediaFiltersTest) {
     description("Test ts audio filter in scrambled broadcast use case");
-    if (descrambling.support) {
+    if (!descrambling.support) {
         return;
     }
     set<FilterConfig> filterConfs;
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index a1597c7..2cea181 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -150,10 +150,32 @@
 };
 
 inline bool validateConnections() {
-    bool feIsValid = frontendMap.find(live.frontendId) != frontendMap.end() &&
-                     frontendMap.find(scan.frontendId) != frontendMap.end();
-    feIsValid &= record.support ? frontendMap.find(record.frontendId) != frontendMap.end() : true;
-    feIsValid &= descrambling.support
+    if ((!live.hasFrontendConnection || !scan.hasFrontendConnection) && !playback.support) {
+        ALOGW("[vts config] VTS must support either a DVR source or a Frontend source.");
+        return false;
+    }
+
+    if (record.support && !record.hasFrontendConnection &&
+        record.dvrSourceId.compare(emptyHardwareId) == 0) {
+        ALOGW("[vts config] Record must support either a DVR source or a Frontend source.");
+        return false;
+    }
+
+    if (descrambling.support && !descrambling.hasFrontendConnection &&
+        descrambling.dvrSourceId.compare(emptyHardwareId) == 0) {
+        ALOGW("[vts config] Descrambling must support either a DVR source or a Frontend source.");
+        return false;
+    }
+
+    bool feIsValid = live.hasFrontendConnection
+                             ? frontendMap.find(live.frontendId) != frontendMap.end()
+                             : true;
+    feIsValid &= scan.hasFrontendConnection ? frontendMap.find(scan.frontendId) != frontendMap.end()
+                                            : true;
+    feIsValid &= record.support && record.hasFrontendConnection
+                         ? frontendMap.find(record.frontendId) != frontendMap.end()
+                         : true;
+    feIsValid &= (descrambling.support && descrambling.hasFrontendConnection)
                          ? frontendMap.find(descrambling.frontendId) != frontendMap.end()
                          : true;
     feIsValid &= lnbLive.support ? frontendMap.find(lnbLive.frontendId) != frontendMap.end() : true;
@@ -165,18 +187,28 @@
         return false;
     }
 
-    bool dvrIsValid = frontendMap[live.frontendId].isSoftwareFe
+    bool dvrIsValid = (live.hasFrontendConnection && frontendMap[live.frontendId].isSoftwareFe)
                               ? dvrMap.find(live.dvrSoftwareFeId) != dvrMap.end()
                               : true;
     dvrIsValid &= playback.support ? dvrMap.find(playback.dvrId) != dvrMap.end() : true;
     if (record.support) {
-        if (frontendMap[record.frontendId].isSoftwareFe) {
-            dvrIsValid &= dvrMap.find(record.dvrSoftwareFeId) != dvrMap.end();
+        if (record.hasFrontendConnection) {
+            if (frontendMap[record.frontendId].isSoftwareFe) {
+                dvrIsValid &= dvrMap.find(record.dvrSoftwareFeId) != dvrMap.end();
+            }
+        } else {
+            dvrIsValid &= dvrMap.find(record.dvrSourceId) != dvrMap.end();
         }
         dvrIsValid &= dvrMap.find(record.dvrRecordId) != dvrMap.end();
     }
-    if (descrambling.support && frontendMap[descrambling.frontendId].isSoftwareFe) {
-        dvrIsValid &= dvrMap.find(descrambling.dvrSoftwareFeId) != dvrMap.end();
+    if (descrambling.support) {
+        if (descrambling.hasFrontendConnection) {
+            if (frontendMap[descrambling.frontendId].isSoftwareFe) {
+                dvrIsValid &= dvrMap.find(descrambling.dvrSoftwareFeId) != dvrMap.end();
+            }
+        } else {
+            dvrIsValid &= dvrMap.find(descrambling.dvrSourceId) != dvrMap.end();
+        }
     }
 
     if (!dvrIsValid) {
@@ -184,8 +216,10 @@
         return false;
     }
 
-    bool filterIsValid = filterMap.find(live.audioFilterId) != filterMap.end() &&
-                         filterMap.find(live.videoFilterId) != filterMap.end();
+    bool filterIsValid = (live.hasFrontendConnection)
+                             ? filterMap.find(live.audioFilterId) != filterMap.end() &&
+                               filterMap.find(live.videoFilterId) != filterMap.end()
+                             : true;
     filterIsValid &= playback.support
                              ? (filterMap.find(playback.audioFilterId) != filterMap.end() &&
                                 filterMap.find(playback.videoFilterId) != filterMap.end())
diff --git a/tv/tuner/1.1/default/Demux.cpp b/tv/tuner/1.1/default/Demux.cpp
index db25c2e..15b8e6c 100644
--- a/tv/tuner/1.1/default/Demux.cpp
+++ b/tv/tuner/1.1/default/Demux.cpp
@@ -392,6 +392,10 @@
     mIsRecording = isRecording;
 }
 
+bool Demux::isRecording() {
+    return mIsRecording;
+}
+
 bool Demux::attachRecordFilter(uint64_t filterId) {
     if (mFilters[filterId] == nullptr || mDvrRecord == nullptr ||
         !mFilters[filterId]->isRecordFilter()) {
diff --git a/tv/tuner/1.1/default/Demux.h b/tv/tuner/1.1/default/Demux.h
index 5212eae..ce46f9c 100644
--- a/tv/tuner/1.1/default/Demux.h
+++ b/tv/tuner/1.1/default/Demux.h
@@ -85,6 +85,7 @@
     void updateMediaFilterOutput(uint64_t filterId, vector<uint8_t> data, uint64_t pts);
     uint16_t getFilterTpid(uint64_t filterId);
     void setIsRecording(bool isRecording);
+    bool isRecording();
     void startFrontendInputLoop();
 
     /**
diff --git a/tv/tuner/1.1/default/Dvr.cpp b/tv/tuner/1.1/default/Dvr.cpp
index 93bb6a8..c487d98 100644
--- a/tv/tuner/1.1/default/Dvr.cpp
+++ b/tv/tuner/1.1/default/Dvr.cpp
@@ -218,19 +218,26 @@
             continue;
         }
 
+        // If the both dvr playback and dvr record are created, the playback will be treated as
+        // the source of the record. isVirtualFrontend set to true would direct the dvr playback
+        // input to the demux record filters or live broadcast filters.
+        bool isRecording = mDemux->isRecording();
+        bool isVirtualFrontend = isRecording;
+
         if (mDvrSettings.playback().dataFormat == DataFormat::ES) {
-            if (!processEsDataOnPlayback(false /*isVirtualFrontend*/, false /*isRecording*/)) {
+            if (!processEsDataOnPlayback(isVirtualFrontend, isRecording)) {
                 ALOGE("[Dvr] playback es data failed to be filtered. Ending thread");
                 break;
             }
             maySendPlaybackStatusCallback();
             continue;
         }
+
         // Our current implementation filter the data and write it into the filter FMQ immediately
         // after the DATA_READY from the VTS/framework
         // This is for the non-ES data source, real playback use case handling.
-        if (!readPlaybackFMQ(false /*isVirtualFrontend*/, false /*isRecording*/) ||
-            !startFilterDispatcher(false /*isVirtualFrontend*/, false /*isRecording*/)) {
+        if (!readPlaybackFMQ(isVirtualFrontend, isRecording) ||
+            !startFilterDispatcher(isVirtualFrontend, isRecording)) {
             ALOGE("[Dvr] playback data failed to be filtered. Ending thread");
             break;
         }
diff --git a/tv/tuner/1.1/vts/functional/AndroidTest.xml b/tv/tuner/1.1/vts/functional/AndroidTest.xml
index 28f95db..3e6878c 100644
--- a/tv/tuner/1.1/vts/functional/AndroidTest.xml
+++ b/tv/tuner/1.1/vts/functional/AndroidTest.xml
@@ -29,5 +29,6 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalTvTunerV1_1TargetTest" />
+        <option name="native-test-timeout" value="30m" />
     </test>
 </configuration>
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.cpp b/tv/tuner/1.1/vts/functional/FilterTests.cpp
index 3bcf32a..4dff853 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.1/vts/functional/FilterTests.cpp
@@ -306,8 +306,12 @@
             android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilters[filterId]);
     if (filter_v1_1 != NULL) {
         status = filter_v1_1->configureMonitorEvent(monitorEventTypes);
-        mFilterCallbacks[filterId]->testFilterScramblingEvent();
-        mFilterCallbacks[filterId]->testFilterIpCidEvent();
+        if (monitorEventTypes & DemuxFilterMonitorEventType::SCRAMBLING_STATUS) {
+            mFilterCallbacks[filterId]->testFilterScramblingEvent();
+        }
+        if (monitorEventTypes & DemuxFilterMonitorEventType::IP_CID_CHANGE) {
+            mFilterCallbacks[filterId]->testFilterIpCidEvent();
+        }
     } else {
         ALOGW("[vts] Can't cast IFilter into v1_1.");
         return failure();
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.h b/tv/tuner/1.1/vts/functional/FilterTests.h
index 59611fa..72c8129 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.h
+++ b/tv/tuner/1.1/vts/functional/FilterTests.h
@@ -57,6 +57,7 @@
 using android::hardware::tv::tuner::V1_1::AvStreamType;
 using android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
 using android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent;
+using android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEventType;
 using android::hardware::tv::tuner::V1_1::IFilterCallback;
 using android::hardware::tv::tuner::V1_1::ITuner;
 
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
index 1080861..1a9def8 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
@@ -22,10 +22,6 @@
     return filterDataOutputTestBase(mFilterTests);
 }
 
-AssertionResult TunerRecordHidlTest::filterDataOutputTest() {
-    return filterDataOutputTestBase(mFilterTests);
-}
-
 void TunerFilterHidlTest::configSingleFilterInDemuxTest(FilterConfig1_1 filterConf,
                                                         FrontendConfig1_1 frontendConf) {
     uint32_t feId;
@@ -70,6 +66,9 @@
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+    if (frontendConf.config1_0.isSoftwareFe) {
+        mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[live.dvrSoftwareFeId]);
+    }
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFrontendTests.setDemux(demux);
@@ -103,6 +102,9 @@
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+    if (frontendConf.config1_0.isSoftwareFe) {
+        mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[live.dvrSoftwareFeId]);
+    }
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFrontendTests.setDemux(demux);
@@ -110,8 +112,8 @@
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.config1_0.type,
                                                filterConf.config1_0.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
-    ASSERT_TRUE(mFilterTests.getSharedAvMemoryHandle(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.config1_0.settings, filterId));
+    ASSERT_TRUE(mFilterTests.getSharedAvMemoryHandle(filterId));
     ASSERT_TRUE(mFilterTests.configAvFilterStreamType(filterConf.streamType, filterId));
     ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.config1_0.getMqDesc));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
@@ -129,21 +131,33 @@
 void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig1_1 filterConf,
                                                  FrontendConfig1_1 frontendConf,
                                                  DvrConfig dvrConf) {
-    uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    mDvrTests.setDemux(demux);
+
+    DvrConfig dvrSourceConfig;
+    if (record.hasFrontendConnection) {
+        uint32_t feId;
+        mFrontendTests.getFrontendIdByType(frontendConf.config1_0.type, feId);
+        ASSERT_TRUE(feId != INVALID_ID);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        if (frontendConf.config1_0.isSoftwareFe) {
+            mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+        }
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+        mFrontendTests.setDvrTests(mDvrTests);
+    } else {
+        dvrSourceConfig = dvrMap[record.dvrSourceId];
+        ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+        ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+        ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+    }
+
     uint64_t filterId;
     sp<IFilter> filter;
-
-    mFrontendTests.getFrontendIdByType(frontendConf.config1_0.type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
-    mDvrTests.setDemux(demux);
-    mFrontendTests.setDvrTests(mDvrTests);
     ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
     ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
     ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
@@ -158,22 +172,46 @@
     ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
     ASSERT_TRUE(mDvrTests.startDvrRecord());
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+
+    if (record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+    } else {
+        // Start DVR Source
+        mDvrTests.startPlaybackInputThread(dvrSourceConfig.playbackInputFile,
+                                           dvrSourceConfig.settings.playback());
+        ASSERT_TRUE(mDvrTests.startDvrPlayback());
+    }
+
     mDvrTests.testRecordOutput();
-    ASSERT_TRUE(filterDataOutputTest());
     mDvrTests.stopRecordThread();
-    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+
+    if (record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+    } else {
+        mDvrTests.stopPlaybackThread();
+        ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+    }
+
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mDvrTests.stopDvrRecord());
     ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
     mDvrTests.closeDvrRecord();
+
+    if (record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    } else {
+        mDvrTests.closeDvrPlayback();
+    }
+
     ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
 
 TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
     description("Open and start a filter in Demux.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     // TODO use parameterized tests
     configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
 }
@@ -181,6 +219,9 @@
 TEST_P(TunerFilterHidlTest, ConfigIpFilterInDemuxWithCid) {
     description("Open and configure an ip filter in Demux.");
     // TODO use parameterized tests
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     if (live.ipFilterId.compare(emptyHardwareId) == 0) {
         return;
     }
@@ -189,6 +230,9 @@
 
 TEST_P(TunerFilterHidlTest, ReconfigFilterToReceiveStartId) {
     description("Recofigure and restart a filter to test start id.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     // TODO use parameterized tests
     reconfigSingleFilterInDemuxTest(filterMap[live.videoFilterId], filterMap[live.videoFilterId],
                                     frontendMap[live.frontendId]);
@@ -205,16 +249,25 @@
 
 TEST_P(TunerFrontendHidlTest, TuneFrontendWithFrontendSettingsExt1_1) {
     description("Tune one Frontend with v1_1 extended setting and check Lock event");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     mFrontendTests.tuneTest(frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerFrontendHidlTest, BlindScanFrontendWithEndFrequency) {
     description("Run an blind frontend scan with v1_1 extended setting and check lock scanMessage");
+    if (!scan.hasFrontendConnection) {
+        return;
+    }
     mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
 }
 
 TEST_P(TunerBroadcastHidlTest, MediaFilterWithSharedMemoryHandle) {
     description("Test the Media Filter with shared memory handle");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     mediaFilterUsingSharedMemoryTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
 }
 
@@ -225,6 +278,9 @@
 
 TEST_P(TunerFrontendHidlTest, LinkToCiCam) {
     description("Test Frontend link to CiCam");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     if (!frontendMap[live.frontendId].canConnectToCiCam) {
         return;
     }
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
index 863f649..007e3d5 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
@@ -97,7 +97,6 @@
 
     void recordSingleFilterTest(FilterConfig1_1 filterConf, FrontendConfig1_1 frontendConf,
                                 DvrConfig dvrConf);
-    AssertionResult filterDataOutputTest();
 
     sp<ITuner> mService;
     FrontendTests mFrontendTests;
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
index 390bd4c..2b5ad46 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
@@ -148,6 +148,11 @@
 };
 
 inline bool validateConnections() {
+    if (record.support && !record.hasFrontendConnection &&
+        record.dvrSourceId.compare(emptyHardwareId) == 0) {
+        ALOGW("[vts config] Record must support either a DVR source or a Frontend source.");
+        return false;
+    }
     bool feIsValid = frontendMap.find(live.frontendId) != frontendMap.end() &&
                      frontendMap.find(scan.frontendId) != frontendMap.end();
     feIsValid &= record.support ? frontendMap.find(record.frontendId) != frontendMap.end() : true;
@@ -160,9 +165,14 @@
     bool dvrIsValid = frontendMap[live.frontendId].config1_0.isSoftwareFe
                               ? dvrMap.find(live.dvrSoftwareFeId) != dvrMap.end()
                               : true;
+
     if (record.support) {
-        if (frontendMap[record.frontendId].config1_0.isSoftwareFe) {
-            dvrIsValid &= dvrMap.find(record.dvrSoftwareFeId) != dvrMap.end();
+        if (record.hasFrontendConnection) {
+            if (frontendMap[record.frontendId].config1_0.isSoftwareFe) {
+                dvrIsValid &= dvrMap.find(record.dvrSoftwareFeId) != dvrMap.end();
+            }
+        } else {
+            dvrIsValid &= dvrMap.find(record.dvrSourceId) != dvrMap.end();
         }
         dvrIsValid &= dvrMap.find(record.dvrRecordId) != dvrMap.end();
     }
diff --git a/tv/tuner/config/TunerTestingConfigReaderV1_0.h b/tv/tuner/config/TunerTestingConfigReaderV1_0.h
index cff4af1..d049b07 100644
--- a/tv/tuner/config/TunerTestingConfigReaderV1_0.h
+++ b/tv/tuner/config/TunerTestingConfigReaderV1_0.h
@@ -52,6 +52,7 @@
 using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtPlpMode;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
@@ -123,6 +124,7 @@
 };
 
 struct LiveBroadcastHardwareConnections {
+    bool hasFrontendConnection;
     string frontendId;
     string dvrSoftwareFeId;
     string audioFilterId;
@@ -134,6 +136,7 @@
 };
 
 struct ScanHardwareConnections {
+    bool hasFrontendConnection;
     string frontendId;
 };
 
@@ -149,19 +152,23 @@
 
 struct DvrRecordHardwareConnections {
     bool support;
+    bool hasFrontendConnection;
     string frontendId;
     string dvrRecordId;
     string dvrSoftwareFeId;
     string recordFilterId;
+    string dvrSourceId;
 };
 
 struct DescramblingHardwareConnections {
     bool support;
+    bool hasFrontendConnection;
     string frontendId;
     string dvrSoftwareFeId;
     string audioFilterId;
     string videoFilterId;
     string descramblerId;
+    string dvrSourceId;
     /* list string of extra filters; */
 };
 
@@ -402,7 +409,14 @@
     }
 
     static void connectLiveBroadcast(LiveBroadcastHardwareConnections& live) {
-        auto liveConfig = *getDataFlowConfiguration().getFirstClearLiveBroadcast();
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasClearLiveBroadcast()) {
+            live.hasFrontendConnection = true;
+        } else {
+            live.hasFrontendConnection = false;
+            return;
+        }
+        auto liveConfig = *dataFlow.getFirstClearLiveBroadcast();
         live.frontendId = liveConfig.getFrontendConnection();
 
         live.audioFilterId = liveConfig.getAudioFilterConnection();
@@ -428,8 +442,15 @@
     }
 
     static void connectScan(ScanHardwareConnections& scan) {
-        auto scanConfig = getDataFlowConfiguration().getFirstScan();
-        scan.frontendId = scanConfig->getFrontendConnection();
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasScan()) {
+            scan.hasFrontendConnection = true;
+        } else {
+            scan.hasFrontendConnection = false;
+            return;
+        }
+        auto scanConfig = *dataFlow.getFirstScan();
+        scan.frontendId = scanConfig.getFrontendConnection();
     }
 
     static void connectDvrPlayback(DvrPlaybackHardwareConnections& playback) {
@@ -437,6 +458,7 @@
         if (dataFlow.hasDvrPlayback()) {
             playback.support = true;
         } else {
+            playback.support = false;
             return;
         }
         auto playbackConfig = *dataFlow.getFirstDvrPlayback();
@@ -455,15 +477,23 @@
         if (dataFlow.hasDvrRecord()) {
             record.support = true;
         } else {
+            record.support = false;
             return;
         }
         auto recordConfig = *dataFlow.getFirstDvrRecord();
-        record.frontendId = recordConfig.getFrontendConnection();
         record.recordFilterId = recordConfig.getRecordFilterConnection();
         record.dvrRecordId = recordConfig.getDvrRecordConnection();
         if (recordConfig.hasDvrSoftwareFeConnection()) {
             record.dvrSoftwareFeId = recordConfig.getDvrSoftwareFeConnection();
         }
+        if (recordConfig.getHasFrontendConnection()) {
+            record.hasFrontendConnection = true;
+            record.dvrSourceId = emptyHardwareId;
+            record.frontendId = recordConfig.getFrontendConnection();
+        } else {
+            record.hasFrontendConnection = false;
+            record.dvrSourceId = recordConfig.getDvrSourceConnection();
+        }
     }
 
     static void connectDescrambling(DescramblingHardwareConnections& descrambling) {
@@ -471,16 +501,24 @@
         if (dataFlow.hasDescrambling()) {
             descrambling.support = true;
         } else {
+            descrambling.support = false;
             return;
         }
         auto descConfig = *dataFlow.getFirstDescrambling();
-        descrambling.frontendId = descConfig.getFrontendConnection();
         descrambling.descramblerId = descConfig.getDescramblerConnection();
         descrambling.audioFilterId = descConfig.getAudioFilterConnection();
         descrambling.videoFilterId = descConfig.getVideoFilterConnection();
         if (descConfig.hasDvrSoftwareFeConnection()) {
             descrambling.dvrSoftwareFeId = descConfig.getDvrSoftwareFeConnection();
         }
+        if (descConfig.getHasFrontendConnection()) {
+            descrambling.hasFrontendConnection = true;
+            descrambling.dvrSourceId = emptyHardwareId;
+            descrambling.frontendId = descConfig.getFrontendConnection();
+        } else {
+            descrambling.hasFrontendConnection = false;
+            descrambling.dvrSourceId = descConfig.getDvrSourceConnection();
+        }
     }
 
     static void connectLnbLive(LnbLiveHardwareConnections& lnbLive) {
@@ -488,6 +526,7 @@
         if (dataFlow.hasLnbLive()) {
             lnbLive.support = true;
         } else {
+            lnbLive.support = false;
             return;
         }
         auto lnbLiveConfig = *dataFlow.getFirstLnbLive();
@@ -507,6 +546,7 @@
         if (dataFlow.hasLnbRecord()) {
             lnbRecord.support = true;
         } else {
+            lnbRecord.support = false;
             return;
         }
         auto lnbRecordConfig = *dataFlow.getFirstLnbRecord();
@@ -526,6 +566,7 @@
         if (dataFlow.hasTimeFilter()) {
             timeFilter.support = true;
         } else {
+            timeFilter.support = false;
             return;
         }
         auto timeFilterConfig = *dataFlow.getFirstTimeFilter();
@@ -553,6 +594,16 @@
         }
         dvbtSettings.bandwidth = static_cast<FrontendDvbtBandwidth>(dvbt->getBandwidth());
         dvbtSettings.isHighPriority = dvbt->getIsHighPriority();
+        dvbtSettings.hierarchy = static_cast<FrontendDvbtHierarchy>(dvbt->getHierarchy());
+        dvbtSettings.hpCoderate = static_cast<FrontendDvbtCoderate>(dvbt->getHpCoderate());
+        dvbtSettings.lpCoderate = static_cast<FrontendDvbtCoderate>(dvbt->getLpCoderate());
+        dvbtSettings.guardInterval =
+                static_cast<FrontendDvbtGuardInterval>(dvbt->getGuardInterval());
+        dvbtSettings.standard = static_cast<FrontendDvbtStandard>(dvbt->getStandard());
+        dvbtSettings.isMiso = dvbt->getIsMiso();
+        dvbtSettings.plpMode = static_cast<FrontendDvbtPlpMode>(dvbt->getPlpMode());
+        dvbtSettings.plpId = dvbt->getPlpId();
+        dvbtSettings.plpGroupId = dvbt->getPlpGroupId();
         if (dvbt->hasConstellation()) {
             dvbtSettings.constellation =
                     static_cast<FrontendDvbtConstellation>(dvbt->getConstellation());
diff --git a/tv/tuner/config/api/current.txt b/tv/tuner/config/api/current.txt
index a9602e7..d026bf9 100644
--- a/tv/tuner/config/api/current.txt
+++ b/tv/tuner/config/api/current.txt
@@ -54,12 +54,16 @@
     method @Nullable public String getAudioFilterConnection();
     method @Nullable public String getDescramblerConnection();
     method @Nullable public String getDvrSoftwareFeConnection();
+    method @Nullable public String getDvrSourceConnection();
     method @Nullable public String getFrontendConnection();
+    method @Nullable public boolean getHasFrontendConnection();
     method @Nullable public String getVideoFilterConnection();
     method public void setAudioFilterConnection(@Nullable String);
     method public void setDescramblerConnection(@Nullable String);
     method public void setDvrSoftwareFeConnection(@Nullable String);
+    method public void setDvrSourceConnection(@Nullable String);
     method public void setFrontendConnection(@Nullable String);
+    method public void setHasFrontendConnection(@Nullable boolean);
     method public void setVideoFilterConnection(@Nullable String);
   }
 
@@ -79,11 +83,15 @@
     ctor public DataFlowConfiguration.DvrRecord();
     method @Nullable public String getDvrRecordConnection();
     method @Nullable public String getDvrSoftwareFeConnection();
+    method @Nullable public String getDvrSourceConnection();
     method @Nullable public String getFrontendConnection();
+    method @Nullable public boolean getHasFrontendConnection();
     method @Nullable public String getRecordFilterConnection();
     method public void setDvrRecordConnection(@Nullable String);
     method public void setDvrSoftwareFeConnection(@Nullable String);
+    method public void setDvrSourceConnection(@Nullable String);
     method public void setFrontendConnection(@Nullable String);
+    method public void setHasFrontendConnection(@Nullable boolean);
     method public void setRecordFilterConnection(@Nullable String);
   }
 
@@ -172,11 +180,29 @@
     ctor public DvbtFrontendSettings();
     method @Nullable public java.math.BigInteger getBandwidth();
     method @Nullable public java.math.BigInteger getConstellation();
+    method @Nullable public java.math.BigInteger getGuardInterval();
+    method @Nullable public java.math.BigInteger getHierarchy();
+    method @Nullable public java.math.BigInteger getHpCoderate();
     method @Nullable public java.math.BigInteger getIsHighPriority();
+    method @Nullable public java.math.BigInteger getIsMiso();
+    method @Nullable public java.math.BigInteger getLpCoderate();
+    method @Nullable public java.math.BigInteger getPlpGroupId();
+    method @Nullable public java.math.BigInteger getPlpId();
+    method @Nullable public java.math.BigInteger getPlpMode();
+    method @Nullable public java.math.BigInteger getStandard();
     method @Nullable public java.math.BigInteger getTransmissionMode();
     method public void setBandwidth(@Nullable java.math.BigInteger);
     method public void setConstellation(@Nullable java.math.BigInteger);
+    method public void setGuardInterval(@Nullable java.math.BigInteger);
+    method public void setHierarchy(@Nullable java.math.BigInteger);
+    method public void setHpCoderate(@Nullable java.math.BigInteger);
     method public void setIsHighPriority(@Nullable java.math.BigInteger);
+    method public void setIsMiso(@Nullable java.math.BigInteger);
+    method public void setLpCoderate(@Nullable java.math.BigInteger);
+    method public void setPlpGroupId(@Nullable java.math.BigInteger);
+    method public void setPlpId(@Nullable java.math.BigInteger);
+    method public void setPlpMode(@Nullable java.math.BigInteger);
+    method public void setStandard(@Nullable java.math.BigInteger);
     method public void setTransmissionMode(@Nullable java.math.BigInteger);
   }
 
diff --git a/tv/tuner/config/sample_tuner_vts_config_1_0.xml b/tv/tuner/config/sample_tuner_vts_config_1_0.xml
index 570171e..347e984 100644
--- a/tv/tuner/config/sample_tuner_vts_config_1_0.xml
+++ b/tv/tuner/config/sample_tuner_vts_config_1_0.xml
@@ -54,7 +54,10 @@
         <frontends>
             <frontend id="FE_DEFAULT" type="DVBT" isSoftwareFrontend="true"
                       connectToCicamId="0" frequency="578000" endFrequency="800000">
-                <dvbtFrontendSettings bandwidth="8" transmissionMode="1" isHighPriority="1"/>
+                <dvbtFrontendSettings bandwidth="8" transmissionMode="1" isHighPriority="1"
+                                      constellation="1" hierarchy="1" hpCoderate="1" lpCoderate="1"
+                                      guardInterval="1" standard="1" isMiso="0" plpMode="1"
+                                      plpId="0" plpGroupId="0"/>
             </frontend>
             <frontend id="FE_DVBS_0" type="DVBS" isSoftwareFrontend="true"
                       connectToCicamId="0" frequency="578000" endFrequency="800000">
@@ -196,7 +199,8 @@
                             sectionFilterConnection="FILTER_TS_SECTION_0"
                             dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
         <scan frontendConnection="FE_DEFAULT"/>
-        <descrambling frontendConnection="FE_DEFAULT"
+        <descrambling hasFrontendConnection="true"
+                      frontendConnection="FE_DEFAULT"
                       descramblerConnection="DESCRAMBLER_0"
                       audioFilterConnection="FILTER_AUDIO_DEFAULT"
                       videoFilterConnection="FILTER_VIDEO_DEFAULT"
@@ -205,7 +209,8 @@
                      audioFilterConnection="FILTER_AUDIO_DEFAULT"
                      videoFilterConnection="FILTER_VIDEO_DEFAULT"
                      sectionFilterConnection="FILTER_TS_SECTION_0"/>
-        <dvrRecord frontendConnection="FE_DEFAULT"
+        <dvrRecord hasFrontendConnection="true"
+                   frontendConnection="FE_DEFAULT"
                    recordFilterConnection="FILTER_TS_RECORD_0"
                    dvrRecordConnection="DVR_RECORD_0"
                    dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
diff --git a/tv/tuner/config/sample_tuner_vts_config_1_1.xml b/tv/tuner/config/sample_tuner_vts_config_1_1.xml
index 191e51c..8c99207 100644
--- a/tv/tuner/config/sample_tuner_vts_config_1_1.xml
+++ b/tv/tuner/config/sample_tuner_vts_config_1_1.xml
@@ -145,14 +145,15 @@
                             audioFilterConnection="FILTER_AUDIO_DEFAULT"
                             videoFilterConnection="FILTER_VIDEO_DEFAULT"
                             ipFilterConnection="FILTER_IP_IP_0"
-                            dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+                            dvrSoftwareFeConnection="DVR_PLAYBACK_1"/>
         <scan frontendConnection="FE_DEFAULT"/>
-        <dvrPlayback dvrConnection="DVR_PLAYBACK_0"
+        <dvrPlayback dvrConnection="DVR_PLAYBACK_1"
                      audioFilterConnection="FILTER_AUDIO_DEFAULT"
                      videoFilterConnection="FILTER_VIDEO_DEFAULT"/>
-        <dvrRecord frontendConnection="FE_DEFAULT"
+        <dvrRecord hasFrontendConnection="true"
+                   frontendConnection="FE_DEFAULT"
                    recordFilterConnection="FILTER_TS_RECORD_0"
                    dvrRecordConnection="DVR_RECORD_0"
-                   dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+                   dvrSoftwareFeConnection="DVR_PLAYBACK_1"/>
     </dataFlowConfiguration>
 </TunerConfiguration>
diff --git a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
index 3303657..6a04b7e 100644
--- a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
+++ b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
@@ -61,9 +61,18 @@
 
     <xs:complexType name="dvbtFrontendSettings">
         <xs:attribute name="bandwidth" type="xs:nonNegativeInteger" use="required"/>
-        <xs:attribute name="transmissionMode" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="constellation" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="guardInterval" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="hierarchy" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="hpCoderate" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="lpCoderate" type="xs:nonNegativeInteger" use="required"/>
         <xs:attribute name="isHighPriority" type="xs:nonNegativeInteger" use="required"/>
-        <xs:attribute name="constellation" type="xs:nonNegativeInteger" use="optional"/>
+        <xs:attribute name="isMiso" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="plpGroupId" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="plpId" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="plpMode" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="standard" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="transmissionMode" type="xs:nonNegativeInteger" use="required"/>
     </xs:complexType>
     <xs:complexType name="dvbsFrontendSettings">
         <xs:attribute name="inputStreamId" type="xs:nonNegativeInteger" use="required"/>
@@ -587,7 +596,9 @@
     <!-- DATA FLOW CONFIGURATION SESSION -->
     <xs:complexType name="dataFlowConfiguration">
         <xs:sequence>
-            <xs:element name="clearLiveBroadcast" minOccurs="1" maxOccurs="1">
+            <!-- clearLiveBroadcast is only optional when there is no physical frontend. In this
+              case, the dvrPlayback config is required. -->
+            <xs:element name="clearLiveBroadcast" minOccurs="0" maxOccurs="1">
                 <xs:complexType>
                     <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
                     <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
@@ -600,20 +611,27 @@
                     <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
                 </xs:complexType>
             </xs:element>
-            <xs:element name="scan" minOccurs="1" maxOccurs="1">
+            <!-- scan is only optional when there is no physical frontend. In this case, the
+              dvrPlayback config is required. -->
+            <xs:element name="scan" minOccurs="0" maxOccurs="1">
                 <xs:complexType>
                     <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
                 </xs:complexType>
             </xs:element>
             <xs:element name="descrambling" minOccurs="0" maxOccurs="1">
                 <xs:complexType>
+                    <!-- If there is a software or hardware frontend connection or not. If false,
+                      dvrSourceConnection config is required when testing dvrRecord.  -->
+                    <xs:attribute name="hasFrontendConnection" type="xs:boolean" use="required"/>
                     <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
                     <xs:attribute name="descramblerConnection" type="descramblerId" use="required"/>
                     <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
                     <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
                     <!-- TODO: b/182519645 allow the users to insert extra filters -->
-                    <!-- DVR is only required when the frontend is using the software input -->
+                    <!-- This DVR is only required when the frontend is using the software input -->
                     <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+                    <!-- This Dvr is only required when there's no frontend(sw or hw) connection -->
+                    <xs:attribute name="dvrSourceConnection" type="dvrId" use="optional"/>
                 </xs:complexType>
             </xs:element>
             <xs:element name="dvrPlayback" minOccurs="0" maxOccurs="1">
@@ -627,10 +645,15 @@
             </xs:element>
             <xs:element name="dvrRecord" minOccurs="0" maxOccurs="1">
                 <xs:complexType>
-                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <!-- If there is a software or hardware frontend connection or not. If false,
+                      dvrSourceConnection config is required when testing dvrRecord.  -->
+                    <xs:attribute name="hasFrontendConnection" type="xs:boolean" use="required"/>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="optional"/>
                     <xs:attribute name="dvrRecordConnection" type="dvrId" use="required"/>
-                    <!-- DVR is only required when the frontend is using the software input -->
+                    <!-- This Dvr is only required when the frontend is using the software input -->
                     <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+                    <!-- This Dvr is only required when there's no frontend(sw or hw) connection -->
+                    <xs:attribute name="dvrSourceConnection" type="dvrId" use="optional"/>
                     <xs:attribute name="recordFilterConnection" type="filterId" use="required"/>
                 </xs:complexType>
             </xs:element>
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
index 3be58a1..679c82c 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
@@ -35,6 +35,6 @@
 @VintfStability
 parcelable CompositeEffect {
   int delayMs;
-  android.hardware.vibrator.CompositePrimitive primitive;
+  android.hardware.vibrator.CompositePrimitive primitive = android.hardware.vibrator.CompositePrimitive.NOOP;
   float scale;
 }
diff --git a/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
index 406a899..5a990c0 100644
--- a/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
@@ -22,7 +22,7 @@
 parcelable CompositeEffect {
     /* Period of silence preceding primitive. */
     int delayMs;
-    CompositePrimitive primitive;
+    CompositePrimitive primitive = CompositePrimitive.NOOP;
     /*
      * 0.0 (inclusive) - 1.0 (inclusive),
      * where 0.0 is minimum "feelable" amplitude.
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index 4d03ebf..713ec75 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -276,7 +276,9 @@
             if (!status.isOk())
                 continue;
 
-            std::chrono::milliseconds timeout{lengthMs * 2};
+            //TODO(b/187207798): revert back to conservative timeout values once
+            //latencies have been fixed
+            std::chrono::milliseconds timeout{lengthMs * 8};
             EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
         }
     }
@@ -588,7 +590,9 @@
             EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, callback).exceptionCode())
                 << toString(primitive);
 
-            EXPECT_EQ(completionFuture.wait_for(duration * 2), std::future_status::ready)
+            //TODO(b/187207798): revert back to conservative timeout values once
+            //latencies have been fixed
+            EXPECT_EQ(completionFuture.wait_for(duration * 4), std::future_status::ready)
                 << toString(primitive);
             end = high_resolution_clock::now();
 
@@ -739,7 +743,9 @@
     sp<CompletionCallback> callback =
         new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
     uint32_t durationMs = 2100;  // Sum of 2 active and 1 braking below
-    std::chrono::milliseconds timeout{durationMs * 2};
+    //TODO(b/187207798): revert back to conservative timeout values once
+    //latencies have been fixed
+    std::chrono::milliseconds timeout{durationMs * 4};
 
     ActivePwle active = composeValidActivePwle(vibrator, capabilities);