Merge "media.bufferpool2: ensure message-ids are safe from overflow/underflow" into main
diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING
index e325001..d848af9 100644
--- a/audio/aidl/TEST_MAPPING
+++ b/audio/aidl/TEST_MAPPING
@@ -78,6 +78,12 @@
           "include-filter": "android.virtualdevice.cts.VirtualAudioTest"
         }
       ]
+    },
+    {
+      "name": "VtsHalBassBoostTargetTest"
+    },
+    {
+      "name": "VtsHalDynamicsProcessingTest"
     }
   ]
 }
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 8c6c537..fc82a65 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -245,7 +245,7 @@
         "EffectFactory.cpp",
         "EffectMain.cpp",
     ],
-    installable: false, //installed in apex com.android.hardware.audio.effect
+    installable: false, //installed in apex com.android.hardware.audio
 }
 
 cc_library_headers {
@@ -267,9 +267,3 @@
     sub_dir: "vintf",
     installable: false,
 }
-
-prebuilt_etc {
-    name: "audio_effects_config.xml",
-    src: "audio_effects_config.xml",
-    installable: false,
-}
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 807348f..697ff0d 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -21,6 +21,7 @@
 #include <Utils.h>
 #include <android-base/logging.h>
 #include <android/binder_ibinder_platform.h>
+#include <cutils/properties.h>
 #include <utils/SystemClock.h>
 #include <utils/Trace.h>
 
@@ -652,16 +653,34 @@
          isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::input>(),
                               AudioInputFlags::FAST)) ||
         (flags.getTag() == AudioIoFlags::Tag::output &&
-         isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
-                              AudioOutputFlags::FAST))) {
+         (isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
+                               AudioOutputFlags::FAST) ||
+          isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
+                               AudioOutputFlags::SPATIALIZER)))) {
         // FAST workers should be run with a SCHED_FIFO scheduler, however the host process
         // might be lacking the capability to request it, thus a failure to set is not an error.
         pid_t workerTid = mWorker->getTid();
         if (workerTid > 0) {
-            struct sched_param param;
-            param.sched_priority = 3;  // Must match SchedulingPolicyService.PRIORITY_MAX (Java).
+            constexpr int32_t kRTPriorityMin = 1;  // SchedulingPolicyService.PRIORITY_MIN (Java).
+            constexpr int32_t kRTPriorityMax = 3;  // SchedulingPolicyService.PRIORITY_MAX (Java).
+            int priorityBoost = kRTPriorityMax;
+            if (flags.getTag() == AudioIoFlags::Tag::output &&
+                isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
+                                     AudioOutputFlags::SPATIALIZER)) {
+                const int32_t sptPrio =
+                        property_get_int32("audio.spatializer.priority", kRTPriorityMin);
+                if (sptPrio >= kRTPriorityMin && sptPrio <= kRTPriorityMax) {
+                    priorityBoost = sptPrio;
+                } else {
+                    LOG(WARNING) << __func__ << ": invalid spatializer priority: " << sptPrio;
+                    return ndk::ScopedAStatus::ok();
+                }
+            }
+            struct sched_param param = {
+                    .sched_priority = priorityBoost,
+            };
             if (sched_setscheduler(workerTid, SCHED_FIFO | SCHED_RESET_ON_FORK, &param) != 0) {
-                PLOG(WARNING) << __func__ << ": failed to set FIFO scheduler for a fast thread";
+                PLOG(WARNING) << __func__ << ": failed to set FIFO scheduler and priority";
             }
         } else {
             LOG(WARNING) << __func__ << ": invalid worker tid: " << workerTid;
diff --git a/audio/aidl/default/apex/com.android.hardware.audio/Android.bp b/audio/aidl/default/apex/com.android.hardware.audio/Android.bp
index 9c91e27..ee92512 100644
--- a/audio/aidl/default/apex/com.android.hardware.audio/Android.bp
+++ b/audio/aidl/default/apex/com.android.hardware.audio/Android.bp
@@ -38,6 +38,7 @@
         "libpreprocessingaidl",
         "libpresetreverbsw",
         "libreverbaidl",
+        "libspatializersw",
         "libvirtualizersw",
         "libvisualizeraidl",
         "libvolumesw",
@@ -45,6 +46,6 @@
     prebuilts: [
         "android.hardware.audio.service-aidl.example.rc",
         "android.hardware.audio.service-aidl.xml",
-        "audio_effects_config.xml",
+        "android.hardware.bluetooth.audio.xml",
     ],
 }
diff --git a/audio/aidl/default/audio_effects_config.xml b/audio/aidl/default/audio_effects_config.xml
index 827ff80..e859a0e 100644
--- a/audio/aidl/default/audio_effects_config.xml
+++ b/audio/aidl/default/audio_effects_config.xml
@@ -72,10 +72,7 @@
 
     <effects>
         <effect name="automatic_gain_control_v2" library="pre_processing" uuid="89f38e65-d4d2-4d64-ad0e-2b3e799ea886"/>
-        <effectProxy name="bassboost" uuid="14804144-a5ee-4d24-aa88-0002a5d5c51b">
-            <libsw library="bassboostsw" uuid="fa8181f2-588b-11ed-9b6a-0242ac120002"/>
-            <libsw library="bundle" uuid="8631f300-72e2-11df-b57e-0002a5d5c51b"/>
-        </effectProxy>
+        <effect name="bassboost" library="bundle" uuid="8631f300-72e2-11df-b57e-0002a5d5c51b"/>
         <effect name="downmix" library="downmix" uuid="93f04452-e4fe-41cc-91f9-e475b6d1d69f"/>
         <effect name="dynamics_processing" library="dynamics_processing" uuid="e0e6539b-1781-7261-676f-6d7573696340"/>
         <effect name="haptic_generator" library="haptic_generator" uuid="97c4acd1-8b82-4f2f-832e-c2fe5d7a9931"/>
@@ -86,16 +83,10 @@
         <effect name="reverb_env_ins" library="reverb" uuid="c7a511a0-a3bb-11df-860e-0002a5d5c51b"/>
         <effect name="reverb_pre_aux" library="reverb" uuid="f29a1400-a3bb-11df-8ddc-0002a5d5c51b"/>
         <effect name="reverb_pre_ins" library="reverb" uuid="172cdf00-a3bc-11df-a72f-0002a5d5c51b"/>
-        <effectProxy name="virtualizer" uuid="d3467faa-acc7-4d34-acaf-0002a5d5c51b">
-            <libsw library="virtualizersw" uuid="fa819d86-588b-11ed-9b6a-0242ac120002"/>
-            <libsw library="bundle" uuid="1d4033c0-8557-11df-9f2d-0002a5d5c51b"/>
-        </effectProxy>
+        <effect name="virtualizer" library="bundle" uuid="1d4033c0-8557-11df-9f2d-0002a5d5c51b"/>
         <effect name="visualizer" library="visualizer" uuid="d069d9e0-8329-11df-9168-0002a5d5c51b"/>
         <effect name="volume" library="bundle" uuid="119341a0-8469-11df-81f9-0002a5d5c51b"/>
-        <effectProxy name="equalizer" uuid="c8e70ecd-48ca-456e-8a4f-0002a5d5c51b">
-            <libsw library="equalizersw" uuid="0bed4300-847d-11df-bb17-0002a5d5c51b"/>
-            <libsw library="bundle" uuid="ce772f20-847d-11df-bb17-0002a5d5c51b"/>
-        </effectProxy>
+        <effect name="equalizer" library="bundle" uuid="ce772f20-847d-11df-bb17-0002a5d5c51b"/>
         <effect name="extension_effect" library="extensioneffect" uuid="fa81dd00-588b-11ed-9b6a-0242ac120002" type="fa81de0e-588b-11ed-9b6a-0242ac120002"/>
         <effect name="acoustic_echo_canceler" library="pre_processing" uuid="bb392ec0-8d4d-11e0-a896-0002a5d5c51b"/>
         <effect name="noise_suppression" library="pre_processing" uuid="c06c8400-8e06-11e0-9cb6-0002a5d5c51b"/>
diff --git a/audio/aidl/default/bluetooth/StreamBluetooth.cpp b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
index 77e48df..f22b7a9 100644
--- a/audio/aidl/default/bluetooth/StreamBluetooth.cpp
+++ b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
@@ -16,16 +16,13 @@
 
 #include <algorithm>
 
-#define ATRACE_TAG ATRACE_TAG_AUDIO
 #define LOG_TAG "AHAL_StreamBluetooth"
 #include <Utils.h>
 #include <android-base/logging.h>
 #include <audio_utils/clock.h>
-#include <utils/Trace.h>
 
 #include "core-impl/StreamBluetooth.h"
 
-using aidl::android::hardware::audio::common::frameCountFromDurationUs;
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
 using aidl::android::hardware::audio::core::VendorParameter;
@@ -67,8 +64,6 @@
                                        : (mIsInput ? kBluetoothDefaultInputBufferMs
                                                    : kBluetoothDefaultOutputBufferMs) *
                                                  1000),
-      mPreferredFrameCount(
-              frameCountFromDurationUs(mPreferredDataIntervalUs, pcmConfig.sampleRateHz)),
       mBtDeviceProxy(btDeviceProxy) {}
 
 ::android::status_t StreamBluetooth::init() {
@@ -77,6 +72,7 @@
         // This is a normal situation in VTS tests.
         LOG(INFO) << __func__ << ": no BT HAL proxy, stream is non-functional";
     }
+    LOG(INFO) << __func__ << ": preferred data interval (us): " << mPreferredDataIntervalUs;
     return ::android::OK;
 }
 
@@ -108,12 +104,10 @@
         LOG(ERROR) << __func__ << ": state= " << mBtDeviceProxy->getState() << " failed to start";
         return -EIO;
     }
-    const size_t fc = std::min(frameCount, mPreferredFrameCount);
-    const size_t bytesToTransfer = fc * mFrameSizeBytes;
+    const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
     const size_t bytesTransferred = mIsInput ? mBtDeviceProxy->readData(buffer, bytesToTransfer)
                                              : mBtDeviceProxy->writeData(buffer, bytesToTransfer);
     *actualFrameCount = bytesTransferred / mFrameSizeBytes;
-    ATRACE_INT("BTdropped", bytesToTransfer - bytesTransferred);
     PresentationPosition presentation_position;
     if (!mBtDeviceProxy->getPresentationPosition(presentation_position)) {
         presentation_position.remoteDeviceAudioDelayNanos =
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index ce71d70..d6e8905 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -211,9 +211,12 @@
         const int32_t rawSizeFrames =
                 aidl::android::hardware::audio::common::frameCountFromDurationMs(latencyMs,
                                                                                  sampleRateHz);
-        if (latencyMs >= 5) return rawSizeFrames;
+        // Round up to nearest 16 frames since in the framework this is the size of a mixer burst.
+        const int32_t multipleOf16 = (rawSizeFrames + 15) & ~15;
+        if (multipleOf16 <= 512) return multipleOf16;
+        // Larger buffers should use powers of 2.
         int32_t powerOf2 = 1;
-        while (powerOf2 < rawSizeFrames) powerOf2 <<= 1;
+        while (powerOf2 < multipleOf16) powerOf2 <<= 1;
         return powerOf2;
     }
 
diff --git a/audio/aidl/default/include/core-impl/StreamBluetooth.h b/audio/aidl/default/include/core-impl/StreamBluetooth.h
index 35c3183..7f4239c 100644
--- a/audio/aidl/default/include/core-impl/StreamBluetooth.h
+++ b/audio/aidl/default/include/core-impl/StreamBluetooth.h
@@ -63,7 +63,6 @@
     const std::weak_ptr<IBluetoothA2dp> mBluetoothA2dp;
     const std::weak_ptr<IBluetoothLe> mBluetoothLe;
     const size_t mPreferredDataIntervalUs;
-    const size_t mPreferredFrameCount;
     mutable std::mutex mLock;
     // The lock is also used to serialize calls to the proxy.
     std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl> mBtDeviceProxy
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index fa4135d..2376ed9 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -136,10 +136,12 @@
     mCurrentRoute->exitStandby(mIsInput);
     RETURN_STATUS_IF_ERROR(mIsInput ? inRead(buffer, frameCount, actualFrameCount)
                                     : outWrite(buffer, frameCount, actualFrameCount));
+    mFramesSinceStart += *actualFrameCount;
+    if (!mIsInput) return ::android::OK;
+    // Only input streams need to block, for output this is implemented by MonoPipe.
     const long bufferDurationUs =
             (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
     const auto totalDurationUs = (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
-    mFramesSinceStart += *actualFrameCount;
     const long totalOffsetUs =
             mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs;
     LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs;
@@ -275,8 +277,9 @@
     char* buff = (char*)buffer;
     size_t actuallyRead = 0;
     long remainingFrames = frameCount;
-    const int64_t deadlineTimeNs = ::android::uptimeNanos() +
-                                   getDelayInUsForFrameCount(frameCount) * NANOS_PER_MICROSECOND;
+    const int64_t deadlineTimeNs =
+            ::android::uptimeNanos() +
+            getDelayInUsForFrameCount(frameCount) * NANOS_PER_MICROSECOND / 2;
     while (remainingFrames > 0) {
         ssize_t framesRead = source->read(buff, remainingFrames);
         LOG(VERBOSE) << __func__ << ": frames read " << framesRead;
diff --git a/audio/aidl/default/spatializer/Android.bp b/audio/aidl/default/spatializer/Android.bp
index 400629e..2c229fe 100644
--- a/audio/aidl/default/spatializer/Android.bp
+++ b/audio/aidl/default/spatializer/Android.bp
@@ -35,6 +35,6 @@
     ],
     relative_install_path: "soundfx",
     visibility: [
-        "//hardware/interfaces/audio/aidl/default",
+        "//hardware/interfaces/audio/aidl/default:__subpackages__",
     ],
 }
diff --git a/audio/aidl/default/spatializer/SpatializerSw.cpp b/audio/aidl/default/spatializer/SpatializerSw.cpp
index 6d3c4bd..ab4a53e 100644
--- a/audio/aidl/default/spatializer/SpatializerSw.cpp
+++ b/audio/aidl/default/spatializer/SpatializerSw.cpp
@@ -65,8 +65,6 @@
 const std::string SpatializerSw::kEffectName = "SpatializerSw";
 
 const std::vector<Range::SpatializerRange> SpatializerSw::kRanges = {
-        MAKE_RANGE(Spatializer, supportedChannelLayout, std::vector<AudioChannelLayout>{},
-                   std::vector<AudioChannelLayout>{}),
         MAKE_RANGE(Spatializer, spatializationLevel, Spatialization::Level::NONE,
                    Spatialization::Level::BED_PLUS_OBJECTS),
         MAKE_RANGE(Spatializer, spatializationMode, Spatialization::Mode::BINAURAL,
@@ -108,6 +106,8 @@
 
 ndk::ScopedAStatus SpatializerSw::getParameterSpecific(const Parameter::Id& id,
                                                        Parameter::Specific* specific) {
+    RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
     auto tag = id.getTag();
     RETURN_IF(Parameter::Id::spatializerTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
     auto spatializerId = id.get<Parameter::Id::spatializerTag>();
@@ -173,11 +173,19 @@
     if (mParamsMap.find(tag) != mParamsMap.end()) {
         return mParamsMap.at(tag);
     }
+    if (tag == Spatializer::supportedChannelLayout) {
+        return Spatializer::make<Spatializer::supportedChannelLayout>(
+                {AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
+                        AudioChannelLayout::LAYOUT_5POINT1)});
+    }
     return std::nullopt;
 }
 
 template <typename TAG>
 ndk::ScopedAStatus SpatializerSwContext::setParam(TAG tag, Spatializer spatializer) {
+    RETURN_IF(tag == Spatializer::supportedChannelLayout, EX_ILLEGAL_ARGUMENT,
+              "supportedChannelLayoutGetOnly");
+
     mParamsMap[tag] = spatializer;
     return ndk::ScopedAStatus::ok();
 }
diff --git a/audio/aidl/default/visualizer/VisualizerSw.h b/audio/aidl/default/visualizer/VisualizerSw.h
index 4b87b04..819351a 100644
--- a/audio/aidl/default/visualizer/VisualizerSw.h
+++ b/audio/aidl/default/visualizer/VisualizerSw.h
@@ -19,20 +19,22 @@
 #include <vector>
 
 #include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <system/audio_effects/effect_visualizer.h>
 #include "effect-impl/EffectImpl.h"
 
 namespace aidl::android::hardware::audio::effect {
 
 class VisualizerSwContext final : public EffectContext {
   public:
-    static const int kMinCaptureSize = 0x80;
-    static const int kMaxCaptureSize = 0x400;
-    static const int kMaxLatencyMs = 3000;
-    static const int kMaxCaptureBufSize = 0xffff;
+    // need align the min/max capture size to VISUALIZER_CAPTURE_SIZE_MIN and
+    // VISUALIZER_CAPTURE_SIZE_MAX because of limitation in audio_utils fixedfft.
+    static constexpr int32_t kMinCaptureSize = VISUALIZER_CAPTURE_SIZE_MIN;
+    static constexpr int32_t kMaxCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
+    static constexpr int32_t kMaxLatencyMs = 3000;
     VisualizerSwContext(int statusDepth, const Parameter::Common& common)
         : EffectContext(statusDepth, common) {
         LOG(DEBUG) << __func__;
-        mCaptureSampleBuffer.resize(kMaxCaptureBufSize);
+        mCaptureSampleBuffer.resize(kMaxCaptureSize);
         fill(mCaptureSampleBuffer.begin(), mCaptureSampleBuffer.end(), 0x80);
     }
 
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 5218fdd..d219fa4 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -26,6 +26,7 @@
         "android.hardware.common.fmq-V1-ndk",
         "libaudioaidlcommon",
         "libaidlcommonsupport",
+        "libpffft",
     ],
     header_libs: [
         "libaudioaidl_headers",
@@ -36,6 +37,7 @@
         "-Wextra",
         "-Werror",
         "-Wthread-safety",
+        "-Wno-error=unused-parameter",
     ],
     test_config_template: "VtsHalAudioTargetTestTemplate.xml",
     test_suites: [
diff --git a/audio/aidl/vts/EffectFactoryHelper.h b/audio/aidl/vts/EffectFactoryHelper.h
index 7100431..54b82d3 100644
--- a/audio/aidl/vts/EffectFactoryHelper.h
+++ b/audio/aidl/vts/EffectFactoryHelper.h
@@ -23,6 +23,7 @@
 
 #include <aidl/Vintf.h>
 #include <android/binder_auto_utils.h>
+#include <system/audio_effects/aidl_effects_utils.h>
 
 #include "AudioHalBinderServiceUtil.h"
 #include "TestUtils.h"
@@ -35,21 +36,6 @@
 
 class EffectFactoryHelper {
   public:
-    explicit EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
-
-    void ConnectToFactoryService() {
-        mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
-        ASSERT_NE(mEffectFactory, nullptr);
-    }
-
-    void RestartFactoryService() {
-        ASSERT_NE(mEffectFactory, nullptr);
-        mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
-        ASSERT_NE(mEffectFactory, nullptr);
-    }
-
-    std::shared_ptr<IFactory> GetFactory() const { return mEffectFactory; }
-
     static std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> getAllEffectDescriptors(
             std::string serviceName, std::optional<AudioUuid> type = std::nullopt) {
         AudioHalBinderServiceUtil util;
@@ -74,8 +60,13 @@
         return result;
     }
 
-  private:
-    std::shared_ptr<IFactory> mEffectFactory;
-    std::string mServiceName;
-    AudioHalBinderServiceUtil binderUtil;
+    static int getHalVersion(const std::shared_ptr<IFactory>& factory) {
+        int version = 0;
+        return (factory && factory->getInterfaceVersion(&version).isOk()) ? version : 0;
+    }
+
+    static bool isReopenSupported(const std::shared_ptr<IFactory>& factory) {
+        return EffectFactoryHelper::getHalVersion(factory) >=
+               aidl::android::hardware::audio::effect::kReopenSupportedVersion;
+    }
 };
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
index 0be4e50..82a07fd 100644
--- a/audio/aidl/vts/EffectHelper.h
+++ b/audio/aidl/vts/EffectHelper.h
@@ -37,6 +37,7 @@
 
 #include "EffectFactoryHelper.h"
 #include "TestUtils.h"
+#include "pffft.hpp"
 
 using namespace android;
 using aidl::android::hardware::audio::effect::CommandId;
@@ -329,4 +330,45 @@
         ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET));
         ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
     }
+
+    // Find FFT bin indices for testFrequencies and get bin center frequencies
+    void roundToFreqCenteredToFftBin(std::vector<int>& testFrequencies,
+                                     std::vector<int>& binOffsets, const float kBinWidth) {
+        for (size_t i = 0; i < testFrequencies.size(); i++) {
+            binOffsets[i] = std::round(testFrequencies[i] / kBinWidth);
+            testFrequencies[i] = std::round(binOffsets[i] * kBinWidth);
+        }
+    }
+
+    // Generate multitone input between -1 to +1 using testFrequencies
+    void generateMultiTone(const std::vector<int>& testFrequencies, std::vector<float>& input,
+                           const int samplingFrequency) {
+        for (size_t i = 0; i < input.size(); i++) {
+            input[i] = 0;
+
+            for (size_t j = 0; j < testFrequencies.size(); j++) {
+                input[i] += sin(2 * M_PI * testFrequencies[j] * i / samplingFrequency);
+            }
+            input[i] /= testFrequencies.size();
+        }
+    }
+
+    // Use FFT transform to convert the buffer to frequency domain
+    // Compute its magnitude at binOffsets
+    std::vector<float> calculateMagnitude(const std::vector<float>& buffer,
+                                          const std::vector<int>& binOffsets, const int nPointFFT) {
+        std::vector<float> fftInput(nPointFFT);
+        PFFFT_Setup* inputHandle = pffft_new_setup(nPointFFT, PFFFT_REAL);
+        pffft_transform_ordered(inputHandle, buffer.data(), fftInput.data(), nullptr,
+                                PFFFT_FORWARD);
+        pffft_destroy_setup(inputHandle);
+        std::vector<float> bufferMag(binOffsets.size());
+        for (size_t i = 0; i < binOffsets.size(); i++) {
+            size_t k = binOffsets[i];
+            bufferMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
+                                (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
+        }
+
+        return bufferMag;
+    }
 };
diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
index 4e86ec3..21df163 100644
--- a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
@@ -56,10 +56,7 @@
 /// Effect factory testing.
 class EffectFactoryTest : public testing::TestWithParam<std::string> {
   public:
-    void SetUp() override {
-        mFactoryHelper = std::make_unique<EffectFactoryHelper>(GetParam());
-        connectAndGetFactory();
-    }
+    void SetUp() override { connectAndGetFactory(); }
 
     void TearDown() override {
         for (auto& effect : mEffects) {
@@ -68,13 +65,14 @@
         }
     }
 
-    std::unique_ptr<EffectFactoryHelper> mFactoryHelper;
+    std::string kServiceName = GetParam();
     std::shared_ptr<IFactory> mEffectFactory;
     std::vector<std::shared_ptr<IEffect>> mEffects;
     const Descriptor::Identity kNullId = {.uuid = getEffectUuidNull()};
     const Descriptor::Identity kZeroId = {.uuid = getEffectUuidZero()};
     const Descriptor kNullDesc = {.common.id = kNullId};
     const Descriptor kZeroDesc = {.common.id = kZeroId};
+    AudioHalBinderServiceUtil mBinderUtil;
 
     template <typename Functor>
     void ForEachId(const std::vector<Descriptor::Identity> ids, Functor functor) {
@@ -117,8 +115,7 @@
         }
     }
     void connectAndGetFactory() {
-        ASSERT_NO_FATAL_FAILURE(mFactoryHelper->ConnectToFactoryService());
-        mEffectFactory = mFactoryHelper->GetFactory();
+        mEffectFactory = IFactory::fromBinder(mBinderUtil.connectToService(kServiceName));
         ASSERT_NE(mEffectFactory, nullptr);
     }
 };
@@ -128,7 +125,9 @@
 }
 
 TEST_P(EffectFactoryTest, CanBeRestarted) {
-    ASSERT_NO_FATAL_FAILURE(mFactoryHelper->RestartFactoryService());
+    ASSERT_NE(mEffectFactory, nullptr);
+    mEffectFactory = IFactory::fromBinder(mBinderUtil.restartService());
+    ASSERT_NE(mEffectFactory, nullptr);
 }
 
 /**
@@ -250,7 +249,8 @@
     EXPECT_NE(descs.size(), 0UL);
     creatAndDestroyDescs(descs);
 
-    mFactoryHelper->RestartFactoryService();
+    mEffectFactory = IFactory::fromBinder(mBinderUtil.restartService());
+    ASSERT_NE(mEffectFactory, nullptr);
 
     connectAndGetFactory();
     creatAndDestroyDescs(descs);
@@ -263,7 +263,8 @@
     EXPECT_NE(descs.size(), 0UL);
     std::vector<std::shared_ptr<IEffect>> effects = createWithDescs(descs);
 
-    ASSERT_NO_FATAL_FAILURE(mFactoryHelper->RestartFactoryService());
+    mEffectFactory = IFactory::fromBinder(mBinderUtil.restartService());
+    ASSERT_NE(mEffectFactory, nullptr);
 
     connectAndGetFactory();
     destroyEffects(effects, EX_ILLEGAL_ARGUMENT);
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index 5479825..4693f10 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -42,6 +42,7 @@
 using aidl::android::hardware::audio::effect::Flags;
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::hardware::audio::effect::kReopenSupportedVersion;
 using aidl::android::hardware::audio::effect::Parameter;
 using aidl::android::hardware::audio::effect::State;
 using aidl::android::media::audio::common::AudioDeviceDescription;
@@ -613,6 +614,10 @@
  * verify reopen sequence.
  */
 TEST_P(AudioEffectDataPathTest, SetCommonParameterAndReopen) {
+    if (!EffectFactoryHelper::isReopenSupported(mFactory)) {
+        GTEST_SKIP() << "Skipping test as effect does not support reopen";
+    }
+
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
 
     Parameter::Common common = EffectHelper::createParamCommon(
@@ -732,6 +737,10 @@
 // Send data to effects and expect it to be consumed after effect reopen (IO AudioConfig change).
 // Effects exposing bypass flags or operating in offload mode will be skipped.
 TEST_P(AudioEffectDataPathTest, ConsumeDataAfterReopen) {
+    if (!EffectFactoryHelper::isReopenSupported(mFactory)) {
+        GTEST_SKIP() << "Skipping test as effect does not support reopen";
+    }
+
     ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
 
     Parameter::Common common = EffectHelper::createParamCommon(
diff --git a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
index aa2c05f..059d6ab 100644
--- a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
@@ -21,6 +21,7 @@
 
 using namespace android;
 
+using aidl::android::hardware::audio::common::getChannelCount;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::getEffectTypeUuidVolume;
 using aidl::android::hardware::audio::effect::IEffect;
@@ -29,6 +30,80 @@
 using aidl::android::hardware::audio::effect::Volume;
 using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
+class VolumeControlHelper : public EffectHelper {
+  public:
+    void SetUpVolumeControl() {
+        ASSERT_NE(nullptr, mFactory);
+        ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+        initFrameCount();
+        Parameter::Specific specific = getDefaultParamSpecific();
+        Parameter::Common common = EffectHelper::createParamCommon(
+                0 /* session */, 1 /* ioHandle */, kSamplingFrequency /* iSampleRate */,
+                kSamplingFrequency /* oSampleRate */, mInputFrameCount /* iFrameCount */,
+                mInputFrameCount /* oFrameCount */);
+        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE));
+        ASSERT_NE(nullptr, mEffect);
+    }
+
+    void TearDownVolumeControl() {
+        ASSERT_NO_FATAL_FAILURE(close(mEffect));
+        ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+        mOpenEffectReturn = IEffect::OpenEffectReturn{};
+    }
+
+    Parameter::Specific getDefaultParamSpecific() {
+        Volume vol = Volume::make<Volume::levelDb>(kMinLevel);
+        Parameter::Specific specific = Parameter::Specific::make<Parameter::Specific::volume>(vol);
+        return specific;
+    }
+
+    Parameter createVolumeParam(int param, Volume::Tag volTag) {
+        return Parameter::make<Parameter::specific>(
+                Parameter::Specific::make<Parameter::Specific::volume>(
+                        (volTag == Volume::mute) ? Volume::make<Volume::mute>(param)
+                                                 : Volume::make<Volume::levelDb>(param)));
+    }
+
+    void initFrameCount() {
+        int channelCount = getChannelCount(
+                AudioChannelLayout::make<AudioChannelLayout::layoutMask>(kDefaultChannelLayout));
+        mInputFrameCount = kBufferSize / channelCount;
+        mOutputFrameCount = kBufferSize / channelCount;
+    }
+
+    bool isLevelValid(int level) {
+        auto vol = Volume::make<Volume::levelDb>(level);
+        return isParameterValid<Volume, Range::volume>(vol, mDescriptor);
+    }
+
+    void setAndVerifyParameters(Volume::Tag volTag, int param, binder_exception_t expected) {
+        auto expectedParam = createVolumeParam(param, volTag);
+        EXPECT_STATUS(expected, mEffect->setParameter(expectedParam)) << expectedParam.toString();
+
+        if (expected == EX_NONE) {
+            Volume::Id volId = Volume::Id::make<Volume::Id::commonTag>(volTag);
+
+            auto id = Parameter::Id::make<Parameter::Id::volumeTag>(volId);
+            // get parameter
+            Parameter getParam;
+            // if set success, then get should match
+            EXPECT_STATUS(expected, mEffect->getParameter(id, &getParam));
+            EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString()
+                                               << "\ngetParam:" << getParam.toString();
+        }
+    }
+
+    static constexpr int kSamplingFrequency = 44100;
+    static constexpr int kDurationMilliSec = 2000;
+    static constexpr int kBufferSize = kSamplingFrequency * kDurationMilliSec / 1000;
+    static constexpr int kMinLevel = -96;
+    static constexpr int kDefaultChannelLayout = AudioChannelLayout::LAYOUT_STEREO;
+    long mInputFrameCount, mOutputFrameCount;
+    std::shared_ptr<IFactory> mFactory;
+    std::shared_ptr<IEffect> mEffect;
+    IEffect::OpenEffectReturn mOpenEffectReturn;
+    Descriptor mDescriptor;
+};
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
  * VtsAudioEffectTargetTest.
@@ -37,7 +112,8 @@
 using VolumeParamTestParam =
         std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int, bool>;
 
-class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>, public EffectHelper {
+class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>,
+                        public VolumeControlHelper {
   public:
     VolumeParamTest()
         : mParamLevel(std::get<PARAM_LEVEL>(GetParam())),
@@ -45,94 +121,167 @@
         std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
     }
 
-    void SetUp() override {
-        ASSERT_NE(nullptr, mFactory);
-        ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+    void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); }
+    void TearDown() override { TearDownVolumeControl(); }
 
-        Parameter::Specific specific = getDefaultParamSpecific();
-        Parameter::Common common = EffectHelper::createParamCommon(
-                0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
-                kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
-        IEffect::OpenEffectReturn ret;
-        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE));
-        ASSERT_NE(nullptr, mEffect);
-    }
-    void TearDown() override {
-        ASSERT_NO_FATAL_FAILURE(close(mEffect));
-        ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
-    }
-
-    Parameter::Specific getDefaultParamSpecific() {
-        Volume vol = Volume::make<Volume::levelDb>(-9600);
-        Parameter::Specific specific = Parameter::Specific::make<Parameter::Specific::volume>(vol);
-        return specific;
-    }
-
-    static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
-    std::shared_ptr<IFactory> mFactory;
-    std::shared_ptr<IEffect> mEffect;
-    Descriptor mDescriptor;
     int mParamLevel = 0;
     bool mParamMute = false;
-
-    void SetAndGetParameters() {
-        for (auto& it : mTags) {
-            auto& tag = it.first;
-            auto& vol = it.second;
-
-            // validate parameter
-            Descriptor desc;
-            ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
-            const bool valid = isParameterValid<Volume, Range::volume>(it.second, desc);
-            const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
-
-            // set parameter
-            Parameter expectParam;
-            Parameter::Specific specific;
-            specific.set<Parameter::Specific::volume>(vol);
-            expectParam.set<Parameter::specific>(specific);
-            EXPECT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString();
-
-            // only get if parameter is in range and set success
-            if (expected == EX_NONE) {
-                Parameter getParam;
-                Parameter::Id id;
-                Volume::Id volId;
-                volId.set<Volume::Id::commonTag>(tag);
-                id.set<Parameter::Id::volumeTag>(volId);
-                EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
-
-                EXPECT_EQ(expectParam, getParam) << "\nexpect:" << expectParam.toString()
-                                                 << "\ngetParam:" << getParam.toString();
-            }
-        }
-    }
-
-    void addLevelParam(int level) {
-        Volume vol;
-        vol.set<Volume::levelDb>(level);
-        mTags.push_back({Volume::levelDb, vol});
-    }
-
-    void addMuteParam(bool mute) {
-        Volume vol;
-        vol.set<Volume::mute>(mute);
-        mTags.push_back({Volume::mute, vol});
-    }
-
-  private:
-    std::vector<std::pair<Volume::Tag, Volume>> mTags;
-    void CleanUp() { mTags.clear(); }
 };
 
-TEST_P(VolumeParamTest, SetAndGetLevel) {
-    EXPECT_NO_FATAL_FAILURE(addLevelParam(mParamLevel));
-    SetAndGetParameters();
+TEST_P(VolumeParamTest, SetAndGetParams) {
+    ASSERT_NO_FATAL_FAILURE(
+            setAndVerifyParameters(Volume::levelDb, mParamLevel,
+                                   isLevelValid(mParamLevel) ? EX_NONE : EX_ILLEGAL_ARGUMENT));
+    ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, mParamMute, EX_NONE));
 }
 
-TEST_P(VolumeParamTest, SetAndGetMute) {
-    EXPECT_NO_FATAL_FAILURE(addMuteParam(mParamMute));
-    SetAndGetParameters();
+using VolumeDataTestParam = std::pair<std::shared_ptr<IFactory>, Descriptor>;
+
+class VolumeDataTest : public ::testing::TestWithParam<VolumeDataTestParam>,
+                       public VolumeControlHelper {
+  public:
+    VolumeDataTest() {
+        std::tie(mFactory, mDescriptor) = GetParam();
+        mInput.resize(kBufferSize);
+        mInputMag.resize(mTestFrequencies.size());
+        mBinOffsets.resize(mTestFrequencies.size());
+        roundToFreqCenteredToFftBin(mTestFrequencies, mBinOffsets, kBinWidth);
+        generateMultiTone(mTestFrequencies, mInput, kSamplingFrequency);
+        mInputMag = calculateMagnitude(mInput, mBinOffsets, kNPointFFT);
+    }
+
+    std::vector<int> calculatePercentageDiff(const std::vector<float>& outputMag) {
+        std::vector<int> percentages(mTestFrequencies.size());
+
+        for (size_t i = 0; i < mInputMag.size(); i++) {
+            float diff = mInputMag[i] - outputMag[i];
+            percentages[i] = std::round(diff / mInputMag[i] * 100);
+        }
+        return percentages;
+    }
+
+    // Convert Decibel value to Percentage
+    int percentageDb(float level) { return std::round((1 - (pow(10, level / 20))) * 100); }
+
+    void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); }
+    void TearDown() override { TearDownVolumeControl(); }
+
+    static constexpr int kMaxAudioSample = 1;
+    static constexpr int kTransitionDuration = 300;
+    static constexpr int kNPointFFT = 32768;
+    static constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT;
+    static constexpr size_t offset = kSamplingFrequency * kTransitionDuration / 1000;
+    static constexpr float kBaseLevel = 0;
+    std::vector<int> mTestFrequencies = {100, 1000};
+    std::vector<float> mInput;
+    std::vector<float> mInputMag;
+    std::vector<int> mBinOffsets;
+};
+
+TEST_P(VolumeDataTest, ApplyLevelMuteUnmute) {
+    std::vector<float> output(kBufferSize);
+    std::vector<int> diffs(mTestFrequencies.size());
+    std::vector<float> outputMag(mTestFrequencies.size());
+
+    if (!isLevelValid(kBaseLevel)) {
+        GTEST_SKIP() << "Volume Level not supported, skipping the test\n";
+    }
+
+    // Apply Volume Level
+
+    ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE));
+    ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+    outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+    diffs = calculatePercentageDiff(outputMag);
+
+    for (size_t i = 0; i < diffs.size(); i++) {
+        ASSERT_EQ(diffs[i], percentageDb(kBaseLevel));
+    }
+
+    // Apply Mute
+
+    ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, true /*mute*/, EX_NONE));
+    ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+    std::vector<float> subOutputMute(output.begin() + offset, output.end());
+    outputMag = calculateMagnitude(subOutputMute, mBinOffsets, kNPointFFT);
+    diffs = calculatePercentageDiff(outputMag);
+
+    for (size_t i = 0; i < diffs.size(); i++) {
+        ASSERT_EQ(diffs[i], percentageDb(kMinLevel /*Mute*/));
+    }
+
+    // Verifying Fade out
+    outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+    diffs = calculatePercentageDiff(outputMag);
+
+    for (size_t i = 0; i < diffs.size(); i++) {
+        ASSERT_LT(diffs[i], percentageDb(kMinLevel /*Mute*/));
+    }
+
+    // Apply Unmute
+
+    ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, false /*unmute*/, EX_NONE));
+    ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+    std::vector<float> subOutputUnmute(output.begin() + offset, output.end());
+
+    outputMag = calculateMagnitude(subOutputUnmute, mBinOffsets, kNPointFFT);
+    diffs = calculatePercentageDiff(outputMag);
+
+    for (size_t i = 0; i < diffs.size(); i++) {
+        ASSERT_EQ(diffs[i], percentageDb(kBaseLevel));
+    }
+
+    // Verifying Fade in
+    outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+    diffs = calculatePercentageDiff(outputMag);
+
+    for (size_t i = 0; i < diffs.size(); i++) {
+        ASSERT_GT(diffs[i], percentageDb(kBaseLevel));
+    }
+}
+
+TEST_P(VolumeDataTest, DecreasingLevels) {
+    std::vector<int> decreasingLevels = {-24, -48, -96};
+    std::vector<float> baseOutput(kBufferSize);
+    std::vector<int> baseDiffs(mTestFrequencies.size());
+    std::vector<float> outputMag(mTestFrequencies.size());
+
+    if (!isLevelValid(kBaseLevel)) {
+        GTEST_SKIP() << "Volume Level not supported, skipping the test\n";
+    }
+
+    ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE));
+    ASSERT_NO_FATAL_FAILURE(
+            processAndWriteToOutput(mInput, baseOutput, mEffect, &mOpenEffectReturn));
+
+    outputMag = calculateMagnitude(baseOutput, mBinOffsets, kNPointFFT);
+    baseDiffs = calculatePercentageDiff(outputMag);
+
+    for (int level : decreasingLevels) {
+        std::vector<float> output(kBufferSize);
+        std::vector<int> diffs(mTestFrequencies.size());
+
+        // Skipping the further steps for unnsupported level values
+        if (!isLevelValid(level)) {
+            continue;
+        }
+        ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, level, EX_NONE));
+        ASSERT_NO_FATAL_FAILURE(
+                processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+        outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+        diffs = calculatePercentageDiff(outputMag);
+
+        // Decrease in volume level results in greater magnitude difference
+        for (size_t i = 0; i < diffs.size(); i++) {
+            ASSERT_GT(diffs[i], baseDiffs[i]);
+        }
+
+        baseDiffs = diffs;
+    }
 }
 
 std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
@@ -157,6 +306,20 @@
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VolumeParamTest);
 
+INSTANTIATE_TEST_SUITE_P(VolumeTest, VolumeDataTest,
+                         testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                                 IFactory::descriptor, getEffectTypeUuidVolume())),
+                         [](const testing::TestParamInfo<VolumeDataTest::ParamType>& info) {
+                             auto descriptor = info.param;
+                             std::string name = getPrefix(descriptor.second);
+                             std::replace_if(
+                                     name.begin(), name.end(),
+                                     [](const char c) { return !std::isalnum(c); }, '_');
+                             return name;
+                         });
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VolumeDataTest);
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
diff --git a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp
index c7761c5..1eb6a6d 100644
--- a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp
+++ b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp
@@ -120,6 +120,12 @@
 ndk::ScopedAStatus A2dpOffloadAudioProvider::parseA2dpConfiguration(
     const CodecId& codec_id, const std::vector<uint8_t>& configuration,
     CodecParameters* codec_parameters, A2dpStatus* _aidl_return) {
+  if (!kEnableA2dpCodecExtensibility) {
+    // parseA2dpConfiguration must not be implemented if A2dp codec
+    // extensibility is not supported.
+    return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION);
+  }
+
   auto codec = codec_factory_.GetCodec(codec_id);
   if (!codec) {
     LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
@@ -136,6 +142,12 @@
     const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
     const A2dpConfigurationHint& hint,
     std::optional<audio::A2dpConfiguration>* _aidl_return) {
+  if (!kEnableA2dpCodecExtensibility) {
+    // getA2dpConfiguration must not be implemented if A2dp codec
+    // extensibility is not supported.
+    return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION);
+  }
+
   *_aidl_return = std::nullopt;
   A2dpConfiguration avdtp_configuration;
 
diff --git a/bluetooth/audio/aidl/default/Android.bp b/bluetooth/audio/aidl/default/Android.bp
index 69db1b3..af6bf86 100644
--- a/bluetooth/audio/aidl/default/Android.bp
+++ b/bluetooth/audio/aidl/default/Android.bp
@@ -40,3 +40,10 @@
         "libbluetooth_audio_session_aidl",
     ],
 }
+
+prebuilt_etc {
+    name: "android.hardware.bluetooth.audio.xml",
+    src: "bluetooth_audio.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h
index 2c21440..866eaeb 100644
--- a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h
+++ b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h
@@ -35,6 +35,23 @@
 namespace bluetooth {
 namespace audio {
 
+/// Enable flag for the reference implementation for A2dp Codec
+/// Extensibility.
+///
+/// A2dp Codec extensibility cannot be enabled until the following
+/// requirements are fulfilled.
+///
+///  1. The Bluetooth controller must support the HCI Requirements
+///     v1.04 or later, and must support the vendor HCI command
+///     A2DP Offload Start (v2), A2DP Offload Stop (v2) as indicated
+///     by the field a2dp_offload_v2 of the vendor capabilities.
+///
+///  2. The implementation of the provider must be completed with
+///     DSP configuration for streaming.
+enum : bool {
+  kEnableA2dpCodecExtensibility = false,
+};
+
 class BluetoothAudioProvider : public BnBluetoothAudioProvider {
  public:
   BluetoothAudioProvider();
diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
index c7c6e6d..584640b 100644
--- a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
+++ b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
@@ -159,6 +159,12 @@
 
   if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
       session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+    if (!kEnableA2dpCodecExtensibility) {
+      // Implementing getProviderInfo equates supporting
+      // A2dp codec extensibility.
+      return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION);
+    }
+
     auto& provider_info = _aidl_return->emplace();
 
     provider_info.name = a2dp_offload_codec_factory_.name;
diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
index 7b98634..789e8a1 100644
--- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
+++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
@@ -2583,6 +2583,10 @@
 
 TEST_P(BluetoothAudioProviderLeAudioOutputHardwareAidl,
        GetDataPathConfiguration) {
+  if (GetProviderFactoryInterfaceVersion() <
+      BluetoothAudioHalVersion::VERSION_AIDL_V4) {
+    GTEST_SKIP();
+  }
   IBluetoothAudioProvider::StreamConfig sink_requirement;
   IBluetoothAudioProvider::StreamConfig source_requirement;
   std::vector<IBluetoothAudioProvider::LeAudioDataPathConfiguration>
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.cpp b/gnss/1.1/vts/functional/gnss_hal_test.cpp
index 6663a19..5ec9806 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test.cpp
@@ -168,8 +168,7 @@
     manager->listManifestByInterface(
             "android.hardware.gnss@1.1::IGnss",
             [&hasGnssHalVersion_1_1](const hidl_vec<hidl_string>& registered) {
-                ASSERT_EQ(1, registered.size());
-                hasGnssHalVersion_1_1 = true;
+                hasGnssHalVersion_1_1 = registered.size() != 0;
             });
 
     bool hasGnssHalVersion_2_0 = false;
diff --git a/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp b/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
index 4778020..0430ea7 100644
--- a/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
+++ b/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
@@ -144,7 +144,8 @@
             auto status = mAllocator->getIMapperLibrarySuffix(&mapperSuffix);
             ASSERT_TRUE(status.isOk());
             std::string lib_name = "mapper." + mapperSuffix + ".so";
-            void* so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW);
+            void* so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
+                                                                  RTLD_LOCAL | RTLD_NOW);
             ASSERT_NE(nullptr, so) << "Failed to load " << lib_name;
             auto loadIMapper = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper");
             ASSERT_NE(nullptr, loadIMapper) << "AIMapper_locaIMapper missing from " << lib_name;
diff --git a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
index b329de2..1e0c427 100644
--- a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
+++ b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
@@ -166,7 +166,8 @@
         auto status = mAllocator->getIMapperLibrarySuffix(&mapperSuffix);
         ASSERT_TRUE(status.isOk()) << "Failed to get IMapper library suffix";
         std::string lib_name = "mapper." + mapperSuffix + ".so";
-        void* so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW);
+        void* so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
+                                                              RTLD_LOCAL | RTLD_NOW);
         ASSERT_NE(nullptr, so) << "Failed to load " << lib_name;
         mIMapperLoader = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper");
         ASSERT_NE(nullptr, mIMapperLoader) << "AIMapper_locaIMapper missing from " << lib_name;
diff --git a/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
index 1bff076..a076438 100644
--- a/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
+++ b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
@@ -51,7 +51,7 @@
     };
 
     using KeymasterVec = std::vector<sp<IKeymasterDevice>>;
-    using ByteString = std::basic_string<uint8_t>;
+    using ByteString = std::vector<uint8_t>;
     // using NonceVec = std::vector<HidlBuf>;
 
     GetParamsResult getHmacSharingParameters(IKeymasterDevice& keymaster) {
@@ -98,7 +98,7 @@
     std::vector<ByteString> copyNonces(const hidl_vec<HmacSharingParameters>& paramsVec) {
         std::vector<ByteString> nonces;
         for (auto& param : paramsVec) {
-            nonces.emplace_back(param.nonce.data(), param.nonce.size());
+            nonces.emplace_back(param.nonce.data(), param.nonce.data() + param.nonce.size());
         }
         return nonces;
     }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index aeb0163..4ebafee 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -794,33 +794,40 @@
             in @nullable HardwareAuthToken authToken);
 
     /**
-     * Called by client to notify the IKeyMintDevice that the device is now locked, and keys with
-     * the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable.  When this function is called,
-     * the IKeyMintDevice should note the current timestamp, and attempts to use
-     * UNLOCKED_DEVICE_REQUIRED keys must be rejected with Error::DEVICE_LOCKED until an
-     * authentication token with a later timestamp is presented.  If the `passwordOnly' argument is
-     * set to true the sufficiently-recent authentication token must indicate that the user
-     * authenticated with a password, not a biometric.
+     * This method is deprecated and has never been used.  Implementations should return
+     * ErrorCode::UNIMPLEMENTED.
      *
-     * 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 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.
+     * This method was originally intended to be used to notify KeyMint that the device is now
+     * locked, and keys with the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable until a
+     * later valid HardwareAuthToken is presented.  However, Android has never called this method
+     * and it cannot start doing so, because KeyMint's enforcement of UNLOCKED_DEVICE_REQUIRED did
+     * not provide the correct semantics and therefore could never be enabled.  Specifically, the
+     * following issues existed with the design of KeyMint's enforcement of
+     * UNLOCKED_DEVICE_REQUIRED:
      *
-     * @param passwordOnly specifies whether the device must be unlocked with a password, rather
-     * than a biometric, before UNLOCKED_DEVICE_REQUIRED keys can be used.
+     * o It assumed a global device lock state only.  Android actually has a separate lock state for
+     *   each user.  See the javadoc for KeyguardManager#isDeviceLocked().
+     * o It assumed that unlocking the device involves a successful user authentication that
+     *   generates a HardwareAuthToken.  This is not necessarily the case, since Android supports
+     *   weaker unlock methods including class 1 and 2 biometrics and trust agents.  These unlock
+     *   methods do not generate a HardwareAuthToken or interact with KeyMint in any way.  Also,
+     *   UNLOCKED_DEVICE_REQUIRED must work even for users who do not have a secure lock screen.
+     * o It would have made UNLOCKED_DEVICE_REQUIRED incompatible with requiring user
+     *   authentication in some cases.  These two key protections can each require a different
+     *   HardwareAuthToken, but KeyMint only supports one HardwareAuthToken per operation.
+     * o It would have provided no security benefit over Keystore's enforcement of
+     *   UNLOCKED_DEVICE_REQUIRED.  This is because since Android 12, Keystore enforces
+     *   UNLOCKED_DEVICE_REQUIRED not just logically, but it also cryptographically by
+     *   superencrypting all such keys and wiping or re-encrypting the superencryption key when the
+     *   device is locked (whenever possible).  KeyMint is still used to support biometric unlocks,
+     *   but this mechanism does not use KeyMint's direct enforcement of UNLOCKED_DEVICE_REQUIRED.
      *
-     * @param timestampToken is used by StrongBox implementations of IKeyMintDevice.  It
-     * provides the StrongBox IKeyMintDevice with a fresh, MACed timestamp which it can use as the
-     * device-lock time, for future comparison against auth tokens when operations using
-     * UNLOCKED_DEVICE_REQUIRED keys are attempted.  Unless the auth token timestamp is newer than
-     * the timestamp in the timestampToken, the device is still considered to be locked.
-     * Crucially, if a StrongBox IKeyMintDevice receives a deviceLocked() call with a timestampToken
-     * timestamp that is less than the timestamp in the last deviceLocked() call, it must ignore the
-     * new timestamp.  TEE IKeyMintDevice implementations will receive an empty timestampToken (zero
-     * values and empty vectors) and should use their own clock as the device-lock time.
+     * Therefore, this method is not useful, and there is no reason for it be called.
+     * Implementations should return ErrorCode::UNIMPLEMENTED and should not include
+     * UNLOCKED_DEVICE_REQUIRED in the list of hardware-enforced key parameters.
+     *
+     * @param passwordOnly N/A due to the deprecation
+     * @param timestampToken N/A due to the deprecation
      */
     void deviceLocked(in boolean passwordOnly, in @nullable TimeStampToken timestampToken);
 
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index be29f59..996e4e3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -482,11 +482,12 @@
 
     /**
      * Tag::UNLOCKED_DEVICE_REQUIRED specifies that the key may only be used when the device is
-     * unlocked, as reported to KeyMint via authToken operation parameter and the
-     * IKeyMintDevice::deviceLocked() method
+     * unlocked.
      *
-     * Must be hardware-enforced (but is also keystore-enforced on a per-user basis: see the
-     * deviceLocked() documentation).
+     * This tag was originally intended to be hardware-enforced.  However, the support for hardware
+     * enforcement of this tag is now considered deprecated because it cannot work correctly, and
+     * even if implemented it does nothing because it was never enabled by Keystore.  Refer to the
+     * documentation for the deprecated method IKeyMintDevice::deviceLocked().
      */
     UNLOCKED_DEVICE_REQUIRED = TagType.BOOL | 509,
 
diff --git a/security/keymint/aidl/default/Android.bp b/security/keymint/aidl/default/Android.bp
index 953630b..d815ff7 100644
--- a/security/keymint/aidl/default/Android.bp
+++ b/security/keymint/aidl/default/Android.bp
@@ -7,39 +7,29 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-cc_binary {
+rust_binary {
     name: "android.hardware.security.keymint-service",
     relative_install_path: "hw",
+    vendor: true,
     init_rc: ["android.hardware.security.keymint-service.rc"],
     vintf_fragments: [
         "android.hardware.security.keymint-service.xml",
         "android.hardware.security.sharedsecret-service.xml",
         "android.hardware.security.secureclock-service.xml",
     ],
-    vendor: true,
-    cflags: [
-        "-Wall",
-        "-Wextra",
-    ],
     defaults: [
-        "keymint_use_latest_hal_aidl_ndk_shared",
-    ],
-    shared_libs: [
-        "android.hardware.security.rkp-V3-ndk",
-        "android.hardware.security.sharedsecret-V1-ndk",
-        "android.hardware.security.secureclock-V1-ndk",
-        "libbase",
-        "libbinder_ndk",
-        "libcppbor_external",
-        "libcrypto",
-        "libkeymaster_portable",
-        "libkeymint",
-        "liblog",
-        "libpuresoftkeymasterdevice",
-        "libutils",
+        "keymint_use_latest_hal_aidl_rust",
     ],
     srcs: [
-        "service.cpp",
+        "main.rs",
+    ],
+    rustlibs: [
+        "libandroid_logger",
+        "libbinder_rs",
+        "liblog_rust",
+        "libkmr_hal",
+        "libkmr_hal_nonsecure",
+        "libkmr_ta_nonsecure",
     ],
     required: [
         "android.hardware.hardware_keystore.xml",
@@ -52,3 +42,39 @@
     vendor: true,
     src: "android.hardware.hardware_keystore.xml",
 }
+
+rust_library {
+    name: "libkmr_hal_nonsecure",
+    crate_name: "kmr_hal_nonsecure",
+    vendor_available: true,
+    lints: "android",
+    rustlibs: [
+        "libbinder_rs",
+        "libhex",
+        "liblibc",
+        "liblog_rust",
+        "libkmr_hal",
+        "libkmr_wire",
+    ],
+    srcs: ["hal/lib.rs"],
+
+}
+
+rust_library {
+    name: "libkmr_ta_nonsecure",
+    crate_name: "kmr_ta_nonsecure",
+    vendor_available: true,
+    host_supported: true,
+    lints: "android",
+    rustlibs: [
+        "libhex",
+        "liblibc",
+        "liblog_rust",
+        "libkmr_common",
+        "libkmr_crypto_boring",
+        "libkmr_ta",
+        "libkmr_wire",
+    ],
+    srcs: ["ta/lib.rs"],
+
+}
diff --git a/security/keymint/aidl/default/hal/lib.rs b/security/keymint/aidl/default/hal/lib.rs
new file mode 100644
index 0000000..621f077
--- /dev/null
+++ b/security/keymint/aidl/default/hal/lib.rs
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+//! KeyMint helper functions that are only suitable for non-secure environments
+//! such as Cuttlefish.
+
+use kmr_hal::env::get_property;
+use log::error;
+
+/// Populate attestation ID information based on properties (where available).
+/// Retrieving the serial number requires SELinux permission.
+pub fn attestation_id_info() -> kmr_wire::AttestationIdInfo {
+    let prop = |name| {
+        get_property(name)
+            .unwrap_or_else(|_| format!("{} unavailable", name))
+            .as_bytes()
+            .to_vec()
+    };
+    kmr_wire::AttestationIdInfo {
+        brand: prop("ro.product.brand"),
+        device: prop("ro.product.device"),
+        product: prop("ro.product.name"),
+        serial: prop("ro.serialno"),
+        manufacturer: prop("ro.product.manufacturer"),
+        model: prop("ro.product.model"),
+        // Currently modem_simulator always returns one fixed value. See `handleGetIMEI` in
+        // device/google/cuttlefish/host/commands/modem_simulator/misc_service.cpp for more details.
+        // TODO(b/263188546): Use device-specific IMEI values when available.
+        imei: b"867400022047199".to_vec(),
+        imei2: b"867400022047199".to_vec(),
+        meid: vec![],
+    }
+}
+
+/// Get boot information based on system properties.
+pub fn get_boot_info() -> kmr_wire::SetBootInfoRequest {
+    // No access to a verified boot key.
+    let verified_boot_key = vec![0; 32];
+    let vbmeta_digest = get_property("ro.boot.vbmeta.digest").unwrap_or_else(|_| "00".repeat(32));
+    let verified_boot_hash = hex::decode(&vbmeta_digest).unwrap_or_else(|_e| {
+        error!("failed to parse hex data in '{}'", vbmeta_digest);
+        vec![0; 32]
+    });
+    let device_boot_locked = match get_property("ro.boot.vbmeta.device_state")
+        .unwrap_or_else(|_| "no-prop".to_string())
+        .as_str()
+    {
+        "locked" => true,
+        "unlocked" => false,
+        v => {
+            error!("Unknown device_state '{}', treating as unlocked", v);
+            false
+        }
+    };
+    let verified_boot_state = match get_property("ro.boot.verifiedbootstate")
+        .unwrap_or_else(|_| "no-prop".to_string())
+        .as_str()
+    {
+        "green" => 0,  // Verified
+        "yellow" => 1, // SelfSigned
+        "orange" => 2, // Unverified,
+        "red" => 3,    // Failed,
+        v => {
+            error!("Unknown boot state '{}', treating as Unverified", v);
+            2
+        }
+    };
+
+    // Attempt to get the boot patchlevel from a system property.  This requires an SELinux
+    // permission, so fall back to re-using the OS patchlevel if this can't be done.
+    let boot_patchlevel_prop = get_property("ro.vendor.boot_security_patch").unwrap_or_else(|e| {
+        error!("Failed to retrieve boot patchlevel: {:?}", e);
+        get_property(kmr_hal::env::OS_PATCHLEVEL_PROPERTY)
+            .unwrap_or_else(|_| "1970-09-19".to_string())
+    });
+    let boot_patchlevel =
+        kmr_hal::env::extract_patchlevel(&boot_patchlevel_prop).unwrap_or(19700919);
+
+    kmr_wire::SetBootInfoRequest {
+        verified_boot_key,
+        device_boot_locked,
+        verified_boot_state,
+        verified_boot_hash,
+        boot_patchlevel,
+    }
+}
diff --git a/security/keymint/aidl/default/main.rs b/security/keymint/aidl/default/main.rs
new file mode 100644
index 0000000..ea432d1
--- /dev/null
+++ b/security/keymint/aidl/default/main.rs
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+//! Default implementation of the KeyMint HAL and related HALs.
+//!
+//! This implementation of the HAL is only intended to allow testing and policy compliance.  A real
+//! implementation **must be implemented in a secure environment**.
+
+use kmr_hal::SerializedChannel;
+use kmr_hal_nonsecure::{attestation_id_info, get_boot_info};
+use log::{debug, error, info};
+use std::ops::DerefMut;
+use std::sync::{mpsc, Arc, Mutex};
+
+/// Name of KeyMint binder device instance.
+static SERVICE_INSTANCE: &str = "default";
+
+static KM_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
+static RPC_SERVICE_NAME: &str = "android.hardware.security.keymint.IRemotelyProvisionedComponent";
+static CLOCK_SERVICE_NAME: &str = "android.hardware.security.secureclock.ISecureClock";
+static SECRET_SERVICE_NAME: &str = "android.hardware.security.sharedsecret.ISharedSecret";
+
+/// Local error type for failures in the HAL service.
+#[derive(Debug, Clone)]
+struct HalServiceError(String);
+
+impl From<String> for HalServiceError {
+    fn from(s: String) -> Self {
+        Self(s)
+    }
+}
+
+fn main() {
+    if let Err(e) = inner_main() {
+        panic!("HAL service failed: {:?}", e);
+    }
+}
+
+fn inner_main() -> Result<(), HalServiceError> {
+    // Initialize Android logging.
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag("keymint-hal-nonsecure")
+            .with_max_level(log::LevelFilter::Info)
+            .with_log_buffer(android_logger::LogId::System),
+    );
+    // Redirect panic messages to logcat.
+    std::panic::set_hook(Box::new(|panic_info| {
+        error!("{}", panic_info);
+    }));
+
+    info!("Insecure KeyMint HAL service is starting.");
+
+    info!("Starting thread pool now.");
+    binder::ProcessState::start_thread_pool();
+
+    // Create a TA in-process, which acts as a local channel for communication.
+    let channel = Arc::new(Mutex::new(LocalTa::new()));
+
+    let km_service = kmr_hal::keymint::Device::new_as_binder(channel.clone());
+    let service_name = format!("{}/{}", KM_SERVICE_NAME, SERVICE_INSTANCE);
+    binder::add_service(&service_name, km_service.as_binder()).map_err(|e| {
+        HalServiceError(format!(
+            "Failed to register service {} because of {:?}.",
+            service_name, e
+        ))
+    })?;
+
+    let rpc_service = kmr_hal::rpc::Device::new_as_binder(channel.clone());
+    let service_name = format!("{}/{}", RPC_SERVICE_NAME, SERVICE_INSTANCE);
+    binder::add_service(&service_name, rpc_service.as_binder()).map_err(|e| {
+        HalServiceError(format!(
+            "Failed to register service {} because of {:?}.",
+            service_name, e
+        ))
+    })?;
+
+    let clock_service = kmr_hal::secureclock::Device::new_as_binder(channel.clone());
+    let service_name = format!("{}/{}", CLOCK_SERVICE_NAME, SERVICE_INSTANCE);
+    binder::add_service(&service_name, clock_service.as_binder()).map_err(|e| {
+        HalServiceError(format!(
+            "Failed to register service {} because of {:?}.",
+            service_name, e
+        ))
+    })?;
+
+    let secret_service = kmr_hal::sharedsecret::Device::new_as_binder(channel.clone());
+    let service_name = format!("{}/{}", SECRET_SERVICE_NAME, SERVICE_INSTANCE);
+    binder::add_service(&service_name, secret_service.as_binder()).map_err(|e| {
+        HalServiceError(format!(
+            "Failed to register service {} because of {:?}.",
+            service_name, e
+        ))
+    })?;
+
+    info!("Successfully registered KeyMint HAL services.");
+
+    // Let the TA know information about the boot environment. In a real device this
+    // is communicated directly from the bootloader to the TA, but here we retrieve
+    // the information from system properties and send from the HAL service.
+    let boot_req = get_boot_info();
+    debug!("boot/HAL->TA: boot info is {:?}", boot_req);
+    kmr_hal::send_boot_info(channel.lock().unwrap().deref_mut(), boot_req)
+        .map_err(|e| HalServiceError(format!("Failed to send boot info: {:?}", e)))?;
+
+    // Let the TA know information about the userspace environment.
+    if let Err(e) = kmr_hal::send_hal_info(channel.lock().unwrap().deref_mut()) {
+        error!("Failed to send HAL info: {:?}", e);
+    }
+
+    // Let the TA know about attestation IDs. (In a real device these would be pre-provisioned into
+    // the TA.)
+    let attest_ids = attestation_id_info();
+    if let Err(e) = kmr_hal::send_attest_ids(channel.lock().unwrap().deref_mut(), attest_ids) {
+        error!("Failed to send attestation ID info: {:?}", e);
+    }
+
+    info!("Successfully registered KeyMint HAL services.");
+    binder::ProcessState::join_thread_pool();
+    info!("KeyMint HAL service is terminating."); // should not reach here
+    Ok(())
+}
+
+/// Implementation of the KeyMint TA that runs locally in-process (and which is therefore
+/// insecure).
+#[derive(Debug)]
+pub struct LocalTa {
+    in_tx: mpsc::Sender<Vec<u8>>,
+    out_rx: mpsc::Receiver<Vec<u8>>,
+}
+
+impl LocalTa {
+    /// Create a new instance.
+    pub fn new() -> Self {
+        // Create a pair of channels to communicate with the TA thread.
+        let (in_tx, in_rx) = mpsc::channel();
+        let (out_tx, out_rx) = mpsc::channel();
+
+        // The TA code expects to run single threaded, so spawn a thread to run it in.
+        std::thread::spawn(move || {
+            let mut ta = kmr_ta_nonsecure::build_ta();
+            loop {
+                let req_data: Vec<u8> = in_rx.recv().expect("failed to receive next req");
+                let rsp_data = ta.process(&req_data);
+                out_tx.send(rsp_data).expect("failed to send out rsp");
+            }
+        });
+        Self { in_tx, out_rx }
+    }
+}
+
+impl SerializedChannel for LocalTa {
+    const MAX_SIZE: usize = usize::MAX;
+
+    fn execute(&mut self, req_data: &[u8]) -> binder::Result<Vec<u8>> {
+        self.in_tx
+            .send(req_data.to_vec())
+            .expect("failed to send in request");
+        Ok(self.out_rx.recv().expect("failed to receive response"))
+    }
+}
diff --git a/security/keymint/aidl/default/service.cpp b/security/keymint/aidl/default/service.cpp
deleted file mode 100644
index 10cbf07..0000000
--- a/security/keymint/aidl/default/service.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2020, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "android.hardware.security.keymint-service"
-
-#include <android-base/logging.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-
-#include <AndroidKeyMintDevice.h>
-#include <AndroidRemotelyProvisionedComponentDevice.h>
-#include <AndroidSecureClock.h>
-#include <AndroidSharedSecret.h>
-#include <keymaster/soft_keymaster_logger.h>
-
-using aidl::android::hardware::security::keymint::AndroidKeyMintDevice;
-using aidl::android::hardware::security::keymint::AndroidRemotelyProvisionedComponentDevice;
-using aidl::android::hardware::security::keymint::SecurityLevel;
-using aidl::android::hardware::security::secureclock::AndroidSecureClock;
-using aidl::android::hardware::security::sharedsecret::AndroidSharedSecret;
-
-template <typename T, class... Args>
-std::shared_ptr<T> addService(Args&&... args) {
-    std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
-    auto instanceName = std::string(T::descriptor) + "/default";
-    LOG(INFO) << "adding keymint service instance: " << instanceName;
-    binder_status_t status =
-            AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
-    CHECK_EQ(status, STATUS_OK);
-    return ser;
-}
-
-int main() {
-    // The global logger object required by keymaster's logging macros in keymaster/logger.h.
-    keymaster::SoftKeymasterLogger km_logger;
-    // Zero threads seems like a useless pool, but below we'll join this thread to it, increasing
-    // the pool size to 1.
-    ABinderProcess_setThreadPoolMaxThreadCount(0);
-    // Add Keymint Service
-    std::shared_ptr<AndroidKeyMintDevice> keyMint =
-            addService<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
-    // Add Secure Clock Service
-    addService<AndroidSecureClock>(keyMint);
-    // Add Shared Secret Service
-    addService<AndroidSharedSecret>(keyMint);
-    // Add Remotely Provisioned Component Service
-    addService<AndroidRemotelyProvisionedComponentDevice>(keyMint);
-    ABinderProcess_joinThreadPool();
-    return EXIT_FAILURE;  // should not reach
-}
diff --git a/security/keymint/aidl/default/ta/attest.rs b/security/keymint/aidl/default/ta/attest.rs
new file mode 100644
index 0000000..1ce2066
--- /dev/null
+++ b/security/keymint/aidl/default/ta/attest.rs
@@ -0,0 +1,425 @@
+//
+// Copyright (C) 2022 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.
+
+//! Attestation keys and certificates.
+//!
+//! Hard-coded keys and certs copied from system/keymaster/context/soft_attestation_cert.cpp
+
+use kmr_common::{
+    crypto::ec, crypto::rsa, crypto::CurveType, crypto::KeyMaterial, wire::keymint,
+    wire::keymint::EcCurve, Error,
+};
+use kmr_ta::device::{RetrieveCertSigningInfo, SigningAlgorithm, SigningKeyType};
+
+/// RSA attestation private key in PKCS#1 format.
+///
+/// Decoded contents (using [der2ascii](https://github.com/google/der-ascii)):
+///
+/// ```
+/// SEQUENCE {
+///   INTEGER { 0 }
+///   INTEGER { `00c08323dc56881bb8302069f5b08561c6eebe7f05e2f5a842048abe8b47be76feaef25cf29b2afa3200141601429989a15fcfc6815eb363583c2fd2f20be4983283dd814b16d7e185417ae54abc296a3a6db5c004083b68c556c1f02339916419864d50b74d40aeca484c77356c895a0c275abfac499d5d7d2362f29c5e02e871` }
+///   INTEGER { 65537 }
+///   INTEGER { `00be860b0b99a802a6fb1a59438a7bb715065b09a36dc6e9cacc6bf3c02c34d7d79e94c6606428d88c7b7f6577c1cdea64074abe8e7286df1f0811dc9728260868de95d32efc96b6d084ff271a5f60defcc703e7a38e6e29ba9a3c5fc2c28076b6a896af1d34d78828ce9bddb1f34f9c9404430781298e201316725bbdbc993a41` }
+///   INTEGER { `00e1c6d927646c0916ec36826d594983740c21f1b074c4a1a59867c669795c85d3dc464c5b929e94bfb34e0dcc5014b10f13341ab7fdd5f60414d2a326cad41cc5` }
+///   INTEGER { `00da485997785cd5630fb0fd8c5254f98e538e18983aae9e6b7e6a5a7b5d343755b9218ebd40320d28387d789f76fa218bcc2d8b68a5f6418fbbeca5179ab3afbd` }
+///   INTEGER { `50fefc32649559616ed6534e154509329d93a3d810dbe5bdb982292cf78bd8badb8020ae8d57f4b71d05386ffe9e9db271ca3477a34999db76f8e5ece9c0d49d` }
+///   INTEGER { `15b74cf27cceff8bb36bf04d9d8346b09a2f70d2f4439b0f26ac7e03f7e9d1f77d4b915fd29b2823f03acb5d5200e0857ff2a803e93eee96d6235ce95442bc21` }
+///   INTEGER { `0090a745da8970b2cd649660324228c5f82856ffd665ba9a85c8d60f1b8bee717ecd2c72eae01dad86ba7654d4cf45adb5f1f2b31d9f8122cfa5f1a5570f9b2d25` }
+/// }
+/// ```
+const RSA_ATTEST_KEY: &str = concat!(
+    "3082025d02010002818100c08323dc56881bb8302069f5b08561c6eebe7f05e2",
+    "f5a842048abe8b47be76feaef25cf29b2afa3200141601429989a15fcfc6815e",
+    "b363583c2fd2f20be4983283dd814b16d7e185417ae54abc296a3a6db5c00408",
+    "3b68c556c1f02339916419864d50b74d40aeca484c77356c895a0c275abfac49",
+    "9d5d7d2362f29c5e02e871020301000102818100be860b0b99a802a6fb1a5943",
+    "8a7bb715065b09a36dc6e9cacc6bf3c02c34d7d79e94c6606428d88c7b7f6577",
+    "c1cdea64074abe8e7286df1f0811dc9728260868de95d32efc96b6d084ff271a",
+    "5f60defcc703e7a38e6e29ba9a3c5fc2c28076b6a896af1d34d78828ce9bddb1",
+    "f34f9c9404430781298e201316725bbdbc993a41024100e1c6d927646c0916ec",
+    "36826d594983740c21f1b074c4a1a59867c669795c85d3dc464c5b929e94bfb3",
+    "4e0dcc5014b10f13341ab7fdd5f60414d2a326cad41cc5024100da485997785c",
+    "d5630fb0fd8c5254f98e538e18983aae9e6b7e6a5a7b5d343755b9218ebd4032",
+    "0d28387d789f76fa218bcc2d8b68a5f6418fbbeca5179ab3afbd024050fefc32",
+    "649559616ed6534e154509329d93a3d810dbe5bdb982292cf78bd8badb8020ae",
+    "8d57f4b71d05386ffe9e9db271ca3477a34999db76f8e5ece9c0d49d024015b7",
+    "4cf27cceff8bb36bf04d9d8346b09a2f70d2f4439b0f26ac7e03f7e9d1f77d4b",
+    "915fd29b2823f03acb5d5200e0857ff2a803e93eee96d6235ce95442bc210241",
+    "0090a745da8970b2cd649660324228c5f82856ffd665ba9a85c8d60f1b8bee71",
+    "7ecd2c72eae01dad86ba7654d4cf45adb5f1f2b31d9f8122cfa5f1a5570f9b2d",
+    "25",
+);
+
+/// Attestation certificate corresponding to [`RSA_ATTEST_KEY`], signed by the key in
+/// [`RSA_ATTEST_ROOT_CERT`].
+///
+/// Decoded contents:
+///
+/// ```
+/// Certificate:
+///     Data:
+///         Version: 3 (0x2)
+///         Serial Number: 4096 (0x1000)
+///     Signature Algorithm: SHA256-RSA
+///         Issuer: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California
+///         Validity:
+///             Not Before: 2016-01-04 12:40:53 +0000 UTC
+///             Not After : 2035-12-30 12:40:53 +0000 UTC
+///         Subject: C=US, O=Google, Inc., OU=Android, ST=California, CN=Android Software Attestation Key
+///         Subject Public Key Info:
+///             Public Key Algorithm: rsaEncryption
+///                 Public Key: (1024 bit)
+///                 Modulus:
+///                     c0:83:23:dc:56:88:1b:b8:30:20:69:f5:b0:85:61:
+///                     c6:ee:be:7f:05:e2:f5:a8:42:04:8a:be:8b:47:be:
+///                     76:fe:ae:f2:5c:f2:9b:2a:fa:32:00:14:16:01:42:
+///                     99:89:a1:5f:cf:c6:81:5e:b3:63:58:3c:2f:d2:f2:
+///                     0b:e4:98:32:83:dd:81:4b:16:d7:e1:85:41:7a:e5:
+///                     4a:bc:29:6a:3a:6d:b5:c0:04:08:3b:68:c5:56:c1:
+///                     f0:23:39:91:64:19:86:4d:50:b7:4d:40:ae:ca:48:
+///                     4c:77:35:6c:89:5a:0c:27:5a:bf:ac:49:9d:5d:7d:
+///                     23:62:f2:9c:5e:02:e8:71:
+///                 Exponent: 65537 (0x10001)
+///         X509v3 extensions:
+///             X509v3 Authority Key Identifier:
+///                 keyid:29faf1accc4dd24c96402775b6b0e932e507fe2e
+///             X509v3 Subject Key Identifier:
+///                 keyid:d40c101bf8cd63b9f73952b50e135ca6d7999386
+///             X509v3 Key Usage: critical
+///                 Digital Signature, Certificate Signing
+///             X509v3 Basic Constraints: critical
+///                 CA:true, pathlen:0
+///     Signature Algorithm: SHA256-RSA
+///          9e:2d:48:5f:8c:67:33:dc:1a:85:ad:99:d7:50:23:ea:14:ec:
+///          43:b0:e1:9d:ea:c2:23:46:1e:72:b5:19:dc:60:22:e4:a5:68:
+///          31:6c:0b:55:c4:e6:9c:a2:2d:9f:3a:4f:93:6b:31:8b:16:78:
+///          16:0d:88:cb:d9:8b:cc:80:9d:84:f0:c2:27:e3:6b:38:f1:fd:
+///          d1:e7:17:72:31:59:35:7d:96:f3:c5:7f:ab:9d:8f:96:61:26:
+///          4f:b2:be:81:bb:0d:49:04:22:8a:ce:9f:f7:f5:42:2e:25:44:
+///          fa:21:07:12:5a:83:b5:55:ad:18:82:f8:40:14:9b:9c:20:63:
+///          04:7f:
+/// ```
+const RSA_ATTEST_CERT: &str = concat!(
+    "308202b63082021fa00302010202021000300d06092a864886f70d01010b0500",
+    "3063310b30090603550406130255533113301106035504080c0a43616c69666f",
+    "726e69613116301406035504070c0d4d6f756e7461696e205669657731153013",
+    "060355040a0c0c476f6f676c652c20496e632e3110300e060355040b0c07416e",
+    "64726f6964301e170d3136303130343132343035335a170d3335313233303132",
+    "343035335a3076310b30090603550406130255533113301106035504080c0a43",
+    "616c69666f726e696131153013060355040a0c0c476f6f676c652c20496e632e",
+    "3110300e060355040b0c07416e64726f69643129302706035504030c20416e64",
+    "726f696420536f667477617265204174746573746174696f6e204b657930819f",
+    "300d06092a864886f70d010101050003818d0030818902818100c08323dc5688",
+    "1bb8302069f5b08561c6eebe7f05e2f5a842048abe8b47be76feaef25cf29b2a",
+    "fa3200141601429989a15fcfc6815eb363583c2fd2f20be4983283dd814b16d7",
+    "e185417ae54abc296a3a6db5c004083b68c556c1f02339916419864d50b74d40",
+    "aeca484c77356c895a0c275abfac499d5d7d2362f29c5e02e8710203010001a3",
+    "663064301d0603551d0e04160414d40c101bf8cd63b9f73952b50e135ca6d799",
+    "9386301f0603551d2304183016801429faf1accc4dd24c96402775b6b0e932e5",
+    "07fe2e30120603551d130101ff040830060101ff020100300e0603551d0f0101",
+    "ff040403020284300d06092a864886f70d01010b0500038181009e2d485f8c67",
+    "33dc1a85ad99d75023ea14ec43b0e19deac223461e72b519dc6022e4a568316c",
+    "0b55c4e69ca22d9f3a4f936b318b1678160d88cbd98bcc809d84f0c227e36b38",
+    "f1fdd1e717723159357d96f3c57fab9d8f9661264fb2be81bb0d4904228ace9f",
+    "f7f5422e2544fa2107125a83b555ad1882f840149b9c2063047f",
+);
+
+/// Attestation self-signed root certificate holding the key that signed [`RSA_ATTEST_CERT`].
+///
+/// Decoded contents:
+///
+/// ```
+/// Certificate:
+///     Data:
+///         Version: 3 (0x2)
+///         Serial Number: 18416584322103887884 (0xff94d9dd9f07c80c)
+///     Signature Algorithm: SHA256-RSA
+///         Issuer: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California
+///         Validity:
+///             Not Before: 2016-01-04 12:31:08 +0000 UTC
+///             Not After : 2035-12-30 12:31:08 +0000 UTC
+///         Subject: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California
+///         Subject Public Key Info:
+///             Public Key Algorithm: rsaEncryption
+///                 Public Key: (1024 bit)
+///                 Modulus:
+///                     a2:6b:ad:eb:6e:2e:44:61:ef:d5:0e:82:e6:b7:94:
+///                     d1:75:23:1f:77:9b:63:91:63:ff:f7:aa:ff:0b:72:
+///                     47:4e:c0:2c:43:ec:33:7c:d7:ac:ed:40:3e:8c:28:
+///                     a0:66:d5:f7:87:0b:33:97:de:0e:b8:4e:13:40:ab:
+///                     af:a5:27:bf:95:69:a0:31:db:06:52:65:f8:44:59:
+///                     57:61:f0:bb:f2:17:4b:b7:41:80:64:c0:28:0e:8f:
+///                     52:77:8e:db:d2:47:b6:45:e9:19:c8:e9:8b:c3:db:
+///                     c2:91:3f:d7:d7:50:c4:1d:35:66:f9:57:e4:97:96:
+///                     0b:09:ac:ce:92:35:85:9b:
+///                 Exponent: 65537 (0x10001)
+///         X509v3 extensions:
+///             X509v3 Authority Key Identifier:
+///                 keyid:29faf1accc4dd24c96402775b6b0e932e507fe2e
+///             X509v3 Subject Key Identifier:
+///                 keyid:29faf1accc4dd24c96402775b6b0e932e507fe2e
+///             X509v3 Key Usage: critical
+///                 Digital Signature, Certificate Signing
+///             X509v3 Basic Constraints: critical
+///                 CA:true
+///     Signature Algorithm: SHA256-RSA
+///          4f:72:f3:36:59:8d:0e:c1:b9:74:5b:31:59:f6:f0:8d:25:49:
+///          30:9e:a3:1c:1c:29:d2:45:2d:20:b9:4d:5f:64:b4:e8:80:c7:
+///          78:7a:9c:39:de:a8:b3:f5:bf:2f:70:5f:47:10:5c:c5:e6:eb:
+///          4d:06:99:61:d2:ae:9a:07:ff:f7:7c:b8:ab:eb:9c:0f:24:07:
+///          5e:b1:7f:ba:79:71:fd:4d:5b:9e:df:14:a9:fe:df:ed:7c:c0:
+///          88:5d:f8:dd:9b:64:32:56:d5:35:9a:e2:13:f9:8f:ce:c1:7c:
+///          dc:ef:a4:aa:b2:55:c3:83:a9:2e:fb:5c:f6:62:f5:27:52:17:
+///          be:63:
+/// ```
+const RSA_ATTEST_ROOT_CERT: &str = concat!(
+    "308202a730820210a003020102020900ff94d9dd9f07c80c300d06092a864886",
+    "f70d01010b05003063310b30090603550406130255533113301106035504080c",
+    "0a43616c69666f726e69613116301406035504070c0d4d6f756e7461696e2056",
+    "69657731153013060355040a0c0c476f6f676c652c20496e632e3110300e0603",
+    "55040b0c07416e64726f6964301e170d3136303130343132333130385a170d33",
+    "35313233303132333130385a3063310b30090603550406130255533113301106",
+    "035504080c0a43616c69666f726e69613116301406035504070c0d4d6f756e74",
+    "61696e205669657731153013060355040a0c0c476f6f676c652c20496e632e31",
+    "10300e060355040b0c07416e64726f696430819f300d06092a864886f70d0101",
+    "01050003818d0030818902818100a26badeb6e2e4461efd50e82e6b794d17523",
+    "1f779b639163fff7aaff0b72474ec02c43ec337cd7aced403e8c28a066d5f787",
+    "0b3397de0eb84e1340abafa527bf9569a031db065265f844595761f0bbf2174b",
+    "b7418064c0280e8f52778edbd247b645e919c8e98bc3dbc2913fd7d750c41d35",
+    "66f957e497960b09acce9235859b0203010001a3633061301d0603551d0e0416",
+    "041429faf1accc4dd24c96402775b6b0e932e507fe2e301f0603551d23041830",
+    "16801429faf1accc4dd24c96402775b6b0e932e507fe2e300f0603551d130101",
+    "ff040530030101ff300e0603551d0f0101ff040403020284300d06092a864886",
+    "f70d01010b0500038181004f72f336598d0ec1b9745b3159f6f08d2549309ea3",
+    "1c1c29d2452d20b94d5f64b4e880c7787a9c39dea8b3f5bf2f705f47105cc5e6",
+    "eb4d069961d2ae9a07fff77cb8abeb9c0f24075eb17fba7971fd4d5b9edf14a9",
+    "fedfed7cc0885df8dd9b643256d5359ae213f98fcec17cdcefa4aab255c383a9",
+    "2efb5cf662f5275217be63",
+);
+
+/// EC attestation private key in `ECPrivateKey` format.
+///
+/// Decoded contents (using [der2ascii](https://github.com/google/der-ascii)):
+///
+/// ```
+/// SEQUENCE {
+///   INTEGER { 1 }
+///   OCTET_STRING { `21e086432a15198459cf363a50fc14c9daadf935f527c2dfd71e4d6dbc42e544` }
+///   [0] {
+///     # secp256r1
+///     OBJECT_IDENTIFIER { 1.2.840.10045.3.1.7 }
+///   }
+///   [1] {
+///     BIT_STRING { `00` `04eb9e79f8426359accb2a914c8986cc70ad90669382a9732613feaccbf821274c2174974a2afea5b94d7f66d4e065106635bc53b7a0a3a671583edb3e11ae1014` }
+///   }
+/// }
+/// ```
+const EC_ATTEST_KEY: &str = concat!(
+    "3077020101042021e086432a15198459cf363a50fc14c9daadf935f527c2dfd7",
+    "1e4d6dbc42e544a00a06082a8648ce3d030107a14403420004eb9e79f8426359",
+    "accb2a914c8986cc70ad90669382a9732613feaccbf821274c2174974a2afea5",
+    "b94d7f66d4e065106635bc53b7a0a3a671583edb3e11ae1014",
+);
+
+/// Attestation certificate corresponding to [`EC_ATTEST_KEY`], signed by the key in
+/// [`EC_ATTEST_ROOT_CERT`].
+///
+/// Decoded contents:
+///
+/// ```
+/// Certificate:
+///     Data:
+///         Version: 3 (0x2)
+///         Serial Number: 4097 (0x1001)
+///     Signature Algorithm: ECDSA-SHA256
+///         Issuer: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California, CN=Android Keystore Software Attestation Root
+///         Validity:
+///             Not Before: 2016-01-11 00:46:09 +0000 UTC
+///             Not After : 2026-01-08 00:46:09 +0000 UTC
+///         Subject: C=US, O=Google, Inc., OU=Android, ST=California, CN=Android Keystore Software Attestation Intermediate
+///         Subject Public Key Info:
+///             Public Key Algorithm: id-ecPublicKey
+///                 Public Key: (256 bit)
+///                 pub:
+///                     04:eb:9e:79:f8:42:63:59:ac:cb:2a:91:4c:89:86:
+///                     cc:70:ad:90:66:93:82:a9:73:26:13:fe:ac:cb:f8:
+///                     21:27:4c:21:74:97:4a:2a:fe:a5:b9:4d:7f:66:d4:
+///                     e0:65:10:66:35:bc:53:b7:a0:a3:a6:71:58:3e:db:
+///                     3e:11:ae:10:14:
+///                 ASN1 OID: prime256v1
+///         X509v3 extensions:
+///             X509v3 Authority Key Identifier:
+///                 keyid:c8ade9774c45c3a3cf0d1610e479433a215a30cf
+///             X509v3 Subject Key Identifier:
+///                 keyid:3ffcacd61ab13a9e8120b8d5251cc565bb1e91a9
+///             X509v3 Key Usage: critical
+///                 Digital Signature, Certificate Signing
+///             X509v3 Basic Constraints: critical
+///                 CA:true, pathlen:0
+///     Signature Algorithm: ECDSA-SHA256
+///          30:45:02:20:4b:8a:9b:7b:ee:82:bc:c0:33:87:ae:2f:c0:89:
+///          98:b4:dd:c3:8d:ab:27:2a:45:9f:69:0c:c7:c3:92:d4:0f:8e:
+///          02:21:00:ee:da:01:5d:b6:f4:32:e9:d4:84:3b:62:4c:94:04:
+///          ef:3a:7c:cc:bd:5e:fb:22:bb:e7:fe:b9:77:3f:59:3f:fb:
+/// ```
+const EC_ATTEST_CERT: &str = concat!(
+    "308202783082021ea00302010202021001300a06082a8648ce3d040302308198",
+    "310b30090603550406130255533113301106035504080c0a43616c69666f726e",
+    "69613116301406035504070c0d4d6f756e7461696e2056696577311530130603",
+    "55040a0c0c476f6f676c652c20496e632e3110300e060355040b0c07416e6472",
+    "6f69643133303106035504030c2a416e64726f6964204b657973746f72652053",
+    "6f667477617265204174746573746174696f6e20526f6f74301e170d31363031",
+    "31313030343630395a170d3236303130383030343630395a308188310b300906",
+    "03550406130255533113301106035504080c0a43616c69666f726e6961311530",
+    "13060355040a0c0c476f6f676c652c20496e632e3110300e060355040b0c0741",
+    "6e64726f6964313b303906035504030c32416e64726f6964204b657973746f72",
+    "6520536f667477617265204174746573746174696f6e20496e7465726d656469",
+    "6174653059301306072a8648ce3d020106082a8648ce3d03010703420004eb9e",
+    "79f8426359accb2a914c8986cc70ad90669382a9732613feaccbf821274c2174",
+    "974a2afea5b94d7f66d4e065106635bc53b7a0a3a671583edb3e11ae1014a366",
+    "3064301d0603551d0e041604143ffcacd61ab13a9e8120b8d5251cc565bb1e91",
+    "a9301f0603551d23041830168014c8ade9774c45c3a3cf0d1610e479433a215a",
+    "30cf30120603551d130101ff040830060101ff020100300e0603551d0f0101ff",
+    "040403020284300a06082a8648ce3d040302034800304502204b8a9b7bee82bc",
+    "c03387ae2fc08998b4ddc38dab272a459f690cc7c392d40f8e022100eeda015d",
+    "b6f432e9d4843b624c9404ef3a7cccbd5efb22bbe7feb9773f593ffb",
+);
+
+/// Attestation self-signed root certificate holding the key that signed [`EC_ATTEST_CERT`].
+///
+/// Decoded contents:
+///
+/// ```
+/// Certificate:
+///     Data:
+///         Version: 3 (0x2)
+///         Serial Number: 11674912229752527703 (0xa2059ed10e435b57)
+///     Signature Algorithm: ECDSA-SHA256
+///         Issuer: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California, CN=Android Keystore Software Attestation Root
+///         Validity:
+///             Not Before: 2016-01-11 00:43:50 +0000 UTC
+///             Not After : 2036-01-06 00:43:50 +0000 UTC
+///         Subject: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California, CN=Android Keystore Software Attestation Root
+///         Subject Public Key Info:
+///             Public Key Algorithm: id-ecPublicKey
+///                 Public Key: (256 bit)
+///                 pub:
+///                     04:ee:5d:5e:c7:e1:c0:db:6d:03:a6:7e:e6:b6:1b:
+///                     ec:4d:6a:5d:6a:68:2e:0f:ff:7f:49:0e:7d:77:1f:
+///                     44:22:6d:bd:b1:af:fa:16:cb:c7:ad:c5:77:d2:56:
+///                     9c:aa:b7:b0:2d:54:01:5d:3e:43:2b:2a:8e:d7:4e:
+///                     ec:48:75:41:a4:
+///                 ASN1 OID: prime256v1
+///         X509v3 extensions:
+///             X509v3 Authority Key Identifier:
+///                 keyid:c8ade9774c45c3a3cf0d1610e479433a215a30cf
+///             X509v3 Subject Key Identifier:
+///                 keyid:c8ade9774c45c3a3cf0d1610e479433a215a30cf
+///             X509v3 Key Usage: critical
+///                 Digital Signature, Certificate Signing
+///             X509v3 Basic Constraints: critical
+///                 CA:true
+///     Signature Algorithm: ECDSA-SHA256
+///          30:44:02:20:35:21:a3:ef:8b:34:46:1e:9c:d5:60:f3:1d:58:
+///          89:20:6a:dc:a3:65:41:f6:0d:9e:ce:8a:19:8c:66:48:60:7b:
+///          02:20:4d:0b:f3:51:d9:30:7c:7d:5b:da:35:34:1d:a8:47:1b:
+///          63:a5:85:65:3c:ad:4f:24:a7:e7:4d:af:41:7d:f1:bf:
+/// ```
+const EC_ATTEST_ROOT_CERT: &str = concat!(
+    "3082028b30820232a003020102020900a2059ed10e435b57300a06082a8648ce",
+    "3d040302308198310b30090603550406130255533113301106035504080c0a43",
+    "616c69666f726e69613116301406035504070c0d4d6f756e7461696e20566965",
+    "7731153013060355040a0c0c476f6f676c652c20496e632e3110300e06035504",
+    "0b0c07416e64726f69643133303106035504030c2a416e64726f6964204b6579",
+    "73746f726520536f667477617265204174746573746174696f6e20526f6f7430",
+    "1e170d3136303131313030343335305a170d3336303130363030343335305a30",
+    "8198310b30090603550406130255533113301106035504080c0a43616c69666f",
+    "726e69613116301406035504070c0d4d6f756e7461696e205669657731153013",
+    "060355040a0c0c476f6f676c652c20496e632e3110300e060355040b0c07416e",
+    "64726f69643133303106035504030c2a416e64726f6964204b657973746f7265",
+    "20536f667477617265204174746573746174696f6e20526f6f74305930130607",
+    "2a8648ce3d020106082a8648ce3d03010703420004ee5d5ec7e1c0db6d03a67e",
+    "e6b61bec4d6a5d6a682e0fff7f490e7d771f44226dbdb1affa16cbc7adc577d2",
+    "569caab7b02d54015d3e432b2a8ed74eec487541a4a3633061301d0603551d0e",
+    "04160414c8ade9774c45c3a3cf0d1610e479433a215a30cf301f0603551d2304",
+    "1830168014c8ade9774c45c3a3cf0d1610e479433a215a30cf300f0603551d13",
+    "0101ff040530030101ff300e0603551d0f0101ff040403020284300a06082a86",
+    "48ce3d040302034700304402203521a3ef8b34461e9cd560f31d5889206adca3",
+    "6541f60d9ece8a198c6648607b02204d0bf351d9307c7d5bda35341da8471b63",
+    "a585653cad4f24a7e74daf417df1bf",
+);
+
+/// Per-algorithm attestation certificate signing information.
+pub struct CertSignAlgoInfo {
+    key: KeyMaterial,
+    chain: Vec<keymint::Certificate>,
+}
+
+/// Certificate signing information for all asymmetric key types.
+pub struct CertSignInfo {
+    rsa_info: CertSignAlgoInfo,
+    ec_info: CertSignAlgoInfo,
+}
+
+impl CertSignInfo {
+    /// Create a new cert signing impl.
+    pub fn new() -> Self {
+        CertSignInfo {
+            rsa_info: CertSignAlgoInfo {
+                key: KeyMaterial::Rsa(rsa::Key(hex::decode(RSA_ATTEST_KEY).unwrap()).into()),
+                chain: vec![
+                    keymint::Certificate {
+                        encoded_certificate: hex::decode(RSA_ATTEST_CERT).unwrap(),
+                    },
+                    keymint::Certificate {
+                        encoded_certificate: hex::decode(RSA_ATTEST_ROOT_CERT).unwrap(),
+                    },
+                ],
+            },
+            ec_info: CertSignAlgoInfo {
+                key: KeyMaterial::Ec(
+                    EcCurve::P256,
+                    CurveType::Nist,
+                    ec::Key::P256(ec::NistKey(hex::decode(EC_ATTEST_KEY).unwrap())).into(),
+                ),
+                chain: vec![
+                    keymint::Certificate {
+                        encoded_certificate: hex::decode(EC_ATTEST_CERT).unwrap(),
+                    },
+                    keymint::Certificate {
+                        encoded_certificate: hex::decode(EC_ATTEST_ROOT_CERT).unwrap(),
+                    },
+                ],
+            },
+        }
+    }
+}
+
+impl RetrieveCertSigningInfo for CertSignInfo {
+    fn signing_key(&self, key_type: SigningKeyType) -> Result<KeyMaterial, Error> {
+        Ok(match key_type.algo_hint {
+            SigningAlgorithm::Rsa => self.rsa_info.key.clone(),
+            SigningAlgorithm::Ec => self.ec_info.key.clone(),
+        })
+    }
+
+    fn cert_chain(&self, key_type: SigningKeyType) -> Result<Vec<keymint::Certificate>, Error> {
+        Ok(match key_type.algo_hint {
+            SigningAlgorithm::Rsa => self.rsa_info.chain.clone(),
+            SigningAlgorithm::Ec => self.ec_info.chain.clone(),
+        })
+    }
+}
diff --git a/security/keymint/aidl/default/ta/clock.rs b/security/keymint/aidl/default/ta/clock.rs
new file mode 100644
index 0000000..ad8509a
--- /dev/null
+++ b/security/keymint/aidl/default/ta/clock.rs
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2022 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.
+
+//! Monotonic clock implementation.
+
+use kmr_common::crypto;
+use std::time::Instant;
+
+/// Monotonic clock.
+pub struct StdClock {
+    start: Instant,
+}
+
+impl StdClock {
+    /// Create new clock instance, holding time since construction.
+    pub fn new() -> Self {
+        Self {
+            start: Instant::now(),
+        }
+    }
+}
+
+impl crypto::MonotonicClock for StdClock {
+    fn now(&self) -> crypto::MillisecondsSinceEpoch {
+        let duration = self.start.elapsed();
+        crypto::MillisecondsSinceEpoch(duration.as_millis().try_into().unwrap())
+    }
+}
diff --git a/security/keymint/aidl/default/ta/lib.rs b/security/keymint/aidl/default/ta/lib.rs
new file mode 100644
index 0000000..fe8ad95
--- /dev/null
+++ b/security/keymint/aidl/default/ta/lib.rs
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+//! Local in-process implementation of the KeyMint TA. This is insecure and should
+//! only be used for testing purposes.
+
+// This crate is `std` using, but some of the code uses macros from a `no_std` world.
+extern crate alloc;
+
+use kmr_common::crypto;
+use kmr_crypto_boring::{
+    aes::BoringAes, aes_cmac::BoringAesCmac, des::BoringDes, ec::BoringEc, eq::BoringEq,
+    hmac::BoringHmac, rng::BoringRng, rsa::BoringRsa, sha256::BoringSha256,
+};
+use kmr_ta::device::{
+    BootloaderDone, CsrSigningAlgorithm, Implementation, TrustedPresenceUnsupported,
+};
+use kmr_ta::{HardwareInfo, KeyMintTa, RpcInfo, RpcInfoV3};
+use kmr_wire::keymint::SecurityLevel;
+use kmr_wire::rpc::MINIMUM_SUPPORTED_KEYS_IN_CSR;
+use log::info;
+
+pub mod attest;
+pub mod clock;
+pub mod rpc;
+pub mod soft;
+
+/// Build a set of crypto trait implementations based around BoringSSL and the standard library
+/// clock.
+pub fn boringssl_crypto_impls() -> crypto::Implementation {
+    let rng = BoringRng;
+    let clock = clock::StdClock::new();
+    let rsa = BoringRsa::default();
+    let ec = BoringEc::default();
+    crypto::Implementation {
+        rng: Box::new(rng),
+        clock: Some(Box::new(clock)),
+        compare: Box::new(BoringEq),
+        aes: Box::new(BoringAes),
+        des: Box::new(BoringDes),
+        hmac: Box::new(BoringHmac),
+        rsa: Box::new(rsa),
+        ec: Box::new(ec),
+        ckdf: Box::new(BoringAesCmac),
+        hkdf: Box::new(BoringHmac),
+        sha256: Box::new(BoringSha256),
+    }
+}
+
+/// Build a [`kmr_ta::KeyMintTa`] instance for nonsecure use.
+pub fn build_ta() -> kmr_ta::KeyMintTa {
+    info!("Building NON-SECURE KeyMint Rust TA");
+    let hw_info = HardwareInfo {
+        version_number: 1,
+        security_level: SecurityLevel::TrustedEnvironment,
+        impl_name: "Rust reference implementation",
+        author_name: "Google",
+        unique_id: "NON-SECURE KeyMint TA",
+    };
+    let rpc_sign_algo = CsrSigningAlgorithm::EdDSA;
+    let rpc_info_v3 = RpcInfoV3 {
+        author_name: "Google",
+        unique_id: "NON-SECURE KeyMint TA",
+        fused: false,
+        supported_num_of_keys_in_csr: MINIMUM_SUPPORTED_KEYS_IN_CSR,
+    };
+
+    let sign_info = attest::CertSignInfo::new();
+    let keys: Box<dyn kmr_ta::device::RetrieveKeyMaterial> = Box::new(soft::Keys);
+    let rpc: Box<dyn kmr_ta::device::RetrieveRpcArtifacts> = Box::new(soft::RpcArtifacts::new(
+        soft::Derive::default(),
+        rpc_sign_algo,
+    ));
+    let dev = Implementation {
+        keys,
+        sign_info: Box::new(sign_info),
+        // HAL populates attestation IDs from properties.
+        attest_ids: None,
+        sdd_mgr: None,
+        // `BOOTLOADER_ONLY` keys not supported.
+        bootloader: Box::new(BootloaderDone),
+        // `STORAGE_KEY` keys not supported.
+        sk_wrapper: None,
+        // `TRUSTED_USER_PRESENCE_REQUIRED` keys not supported
+        tup: Box::new(TrustedPresenceUnsupported),
+        // No support for converting previous implementation's keyblobs.
+        legacy_key: None,
+        rpc,
+    };
+    KeyMintTa::new(
+        hw_info,
+        RpcInfo::V3(rpc_info_v3),
+        boringssl_crypto_impls(),
+        dev,
+    )
+}
diff --git a/security/keymint/aidl/default/ta/rpc.rs b/security/keymint/aidl/default/ta/rpc.rs
new file mode 100644
index 0000000..39da50e
--- /dev/null
+++ b/security/keymint/aidl/default/ta/rpc.rs
@@ -0,0 +1,234 @@
+//
+// Copyright (C) 2022 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.
+
+//! Emulated implementation of device traits for `IRemotelyProvisionedComponent`.
+
+use core::cell::RefCell;
+use kmr_common::crypto::{ec, ec::CoseKeyPurpose, Ec, KeyMaterial};
+use kmr_common::{crypto, explicit, rpc_err, vec_try, Error};
+use kmr_crypto_boring::{ec::BoringEc, hmac::BoringHmac, rng::BoringRng};
+use kmr_ta::device::{
+    CsrSigningAlgorithm, DiceInfo, PubDiceArtifacts, RetrieveRpcArtifacts, RpcV2Req,
+};
+use kmr_wire::coset::{iana, CoseSign1Builder, HeaderBuilder};
+use kmr_wire::keymint::{Digest, EcCurve};
+use kmr_wire::{cbor::value::Value, coset::AsCborValue, rpc, CborError};
+
+/// Trait to encapsulate deterministic derivation of secret data.
+pub trait DeriveBytes {
+    /// Derive `output_len` bytes of data from `context`, deterministically.
+    fn derive_bytes(&self, context: &[u8], output_len: usize) -> Result<Vec<u8>, Error>;
+}
+
+/// Common emulated implementation of RPC artifact retrieval.
+pub struct Artifacts<T: DeriveBytes> {
+    derive: T,
+    sign_algo: CsrSigningAlgorithm,
+    // Invariant once populated: `self.dice_info.signing_algorithm` == `self.sign_algo`
+    dice_info: RefCell<Option<DiceInfo>>,
+    // Invariant once populated: `self.bcc_signing_key` is a variant that matches `self.sign_algo`
+    bcc_signing_key: RefCell<Option<ec::Key>>,
+}
+
+impl<T: DeriveBytes> RetrieveRpcArtifacts for Artifacts<T> {
+    fn derive_bytes_from_hbk(
+        &self,
+        _hkdf: &dyn crypto::Hkdf,
+        context: &[u8],
+        output_len: usize,
+    ) -> Result<Vec<u8>, Error> {
+        self.derive.derive_bytes(context, output_len)
+    }
+
+    fn get_dice_info(&self, _test_mode: rpc::TestMode) -> Result<DiceInfo, Error> {
+        if self.dice_info.borrow().is_none() {
+            let (dice_info, priv_key) = self.generate_dice_artifacts(rpc::TestMode(false))?;
+            *self.dice_info.borrow_mut() = Some(dice_info);
+            *self.bcc_signing_key.borrow_mut() = Some(priv_key);
+        }
+
+        Ok(self
+            .dice_info
+            .borrow()
+            .as_ref()
+            .ok_or_else(|| rpc_err!(Failed, "DICE artifacts are not initialized."))?
+            .clone())
+    }
+
+    fn sign_data(
+        &self,
+        ec: &dyn crypto::Ec,
+        data: &[u8],
+        _rpc_v2: Option<RpcV2Req>,
+    ) -> Result<Vec<u8>, Error> {
+        // DICE artifacts should have been initialized via `get_dice_info()` by the time this
+        // method is called.
+        let private_key = self
+            .bcc_signing_key
+            .borrow()
+            .as_ref()
+            .ok_or_else(|| rpc_err!(Failed, "DICE artifacts are not initialized."))?
+            .clone();
+
+        let mut op = ec.begin_sign(private_key.into(), self.signing_digest())?;
+        op.update(data)?;
+        let sig = op.finish()?;
+        crypto::ec::to_cose_signature(self.signing_curve(), sig)
+    }
+}
+
+impl<T: DeriveBytes> Artifacts<T> {
+    /// Constructor.
+    pub fn new(derive: T, sign_algo: CsrSigningAlgorithm) -> Self {
+        Self {
+            derive,
+            sign_algo,
+            dice_info: RefCell::new(None),
+            bcc_signing_key: RefCell::new(None),
+        }
+    }
+
+    /// Indicate the curve used in signing.
+    fn signing_curve(&self) -> EcCurve {
+        match self.sign_algo {
+            CsrSigningAlgorithm::ES256 => EcCurve::P256,
+            CsrSigningAlgorithm::ES384 => EcCurve::P384,
+            CsrSigningAlgorithm::EdDSA => EcCurve::Curve25519,
+        }
+    }
+
+    /// Indicate the digest used in signing.
+    fn signing_digest(&self) -> Digest {
+        match self.sign_algo {
+            CsrSigningAlgorithm::ES256 => Digest::Sha256,
+            CsrSigningAlgorithm::ES384 => Digest::Sha384,
+            CsrSigningAlgorithm::EdDSA => Digest::None,
+        }
+    }
+
+    /// Indicate the COSE algorithm value associated with signing.
+    fn signing_cose_algo(&self) -> iana::Algorithm {
+        match self.sign_algo {
+            CsrSigningAlgorithm::ES256 => iana::Algorithm::ES256,
+            CsrSigningAlgorithm::ES384 => iana::Algorithm::ES384,
+            CsrSigningAlgorithm::EdDSA => iana::Algorithm::EdDSA,
+        }
+    }
+
+    fn generate_dice_artifacts(
+        &self,
+        _test_mode: rpc::TestMode,
+    ) -> Result<(DiceInfo, ec::Key), Error> {
+        let ec = BoringEc::default();
+
+        let key_material = match self.sign_algo {
+            CsrSigningAlgorithm::EdDSA => {
+                let secret = self.derive_bytes_from_hbk(&BoringHmac, b"Device Key Seed", 32)?;
+                ec::import_raw_ed25519_key(&secret)
+            }
+            // TODO: generate the *same* key after reboot, by use of the TPM.
+            CsrSigningAlgorithm::ES256 => {
+                ec.generate_nist_key(&mut BoringRng, ec::NistCurve::P256, &[])
+            }
+            CsrSigningAlgorithm::ES384 => {
+                ec.generate_nist_key(&mut BoringRng, ec::NistCurve::P384, &[])
+            }
+        }?;
+        let (pub_cose_key, private_key) = match key_material {
+            KeyMaterial::Ec(curve, curve_type, key) => (
+                key.public_cose_key(
+                    &ec,
+                    curve,
+                    curve_type,
+                    CoseKeyPurpose::Sign,
+                    None, /* no key ID */
+                    rpc::TestMode(false),
+                )?,
+                key,
+            ),
+            _ => {
+                return Err(rpc_err!(
+                    Failed,
+                    "expected the Ec variant of KeyMaterial for the cdi leaf key."
+                ))
+            }
+        };
+
+        let cose_key_cbor = pub_cose_key.to_cbor_value().map_err(CborError::from)?;
+        let cose_key_cbor_data = kmr_ta::rkp::serialize_cbor(&cose_key_cbor)?;
+
+        // Construct `DiceChainEntryPayload`
+        let dice_chain_entry_payload = Value::Map(vec_try![
+            // Issuer
+            (
+                Value::Integer(1.into()),
+                Value::Text(String::from("Issuer"))
+            ),
+            // Subject
+            (
+                Value::Integer(2.into()),
+                Value::Text(String::from("Subject"))
+            ),
+            // Subject public key
+            (
+                Value::Integer((-4670552).into()),
+                Value::Bytes(cose_key_cbor_data)
+            ),
+            // Key Usage field contains a CBOR byte string of the bits which correspond
+            // to `keyCertSign` as per RFC 5280 Section 4.2.1.3 (in little-endian byte order)
+            (
+                Value::Integer((-4670553).into()),
+                Value::Bytes(vec_try![0x20]?)
+            ),
+        ]?);
+        let dice_chain_entry_payload_data = kmr_ta::rkp::serialize_cbor(&dice_chain_entry_payload)?;
+
+        // Construct `DiceChainEntry`
+        let protected = HeaderBuilder::new()
+            .algorithm(self.signing_cose_algo())
+            .build();
+        let dice_chain_entry = CoseSign1Builder::new()
+            .protected(protected)
+            .payload(dice_chain_entry_payload_data)
+            .try_create_signature(&[], |input| {
+                let mut op = ec.begin_sign(private_key.clone(), self.signing_digest())?;
+                op.update(input)?;
+                let sig = op.finish()?;
+                crypto::ec::to_cose_signature(self.signing_curve(), sig)
+            })?
+            .build();
+        let dice_chain_entry_cbor = dice_chain_entry.to_cbor_value().map_err(CborError::from)?;
+
+        // Construct `DiceCertChain`
+        let dice_cert_chain = Value::Array(vec_try![cose_key_cbor, dice_chain_entry_cbor]?);
+        let dice_cert_chain_data = kmr_ta::rkp::serialize_cbor(&dice_cert_chain)?;
+
+        // Construct `UdsCerts` as an empty CBOR map
+        let uds_certs_data = kmr_ta::rkp::serialize_cbor(&Value::Map(Vec::new()))?;
+
+        let pub_dice_artifacts = PubDiceArtifacts {
+            dice_cert_chain: dice_cert_chain_data,
+            uds_certs: uds_certs_data,
+        };
+
+        let dice_info = DiceInfo {
+            pub_dice_artifacts,
+            signing_algorithm: self.sign_algo,
+            rpc_v2_test_cdi_priv: None,
+        };
+
+        Ok((dice_info, explicit!(private_key)?))
+    }
+}
diff --git a/security/keymint/aidl/default/ta/soft.rs b/security/keymint/aidl/default/ta/soft.rs
new file mode 100644
index 0000000..5bbe060
--- /dev/null
+++ b/security/keymint/aidl/default/ta/soft.rs
@@ -0,0 +1,67 @@
+//
+// Copyright (C) 2022 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.
+
+//! Software-only trait implementations using fake keys.
+
+use kmr_common::{
+    crypto,
+    crypto::{Hkdf, Rng},
+    Error,
+};
+use kmr_crypto_boring::{hmac::BoringHmac, rng::BoringRng};
+use kmr_ta::device::RetrieveKeyMaterial;
+
+/// Root key retrieval using hard-coded fake keys.
+pub struct Keys;
+
+impl RetrieveKeyMaterial for Keys {
+    fn root_kek(&self, _context: &[u8]) -> Result<crypto::OpaqueOr<crypto::hmac::Key>, Error> {
+        // Matches `MASTER_KEY` in system/keymaster/key_blob_utils/software_keyblobs.cpp
+        Ok(crypto::hmac::Key::new([0; 16].to_vec()).into())
+    }
+    fn kak(&self) -> Result<crypto::OpaqueOr<crypto::aes::Key>, Error> {
+        // Matches `kFakeKeyAgreementKey` in
+        // system/keymaster/km_openssl/soft_keymaster_enforcement.cpp.
+        Ok(crypto::aes::Key::Aes256([0; 32]).into())
+    }
+    fn unique_id_hbk(&self, _ckdf: &dyn crypto::Ckdf) -> Result<crypto::hmac::Key, Error> {
+        // Matches value used in system/keymaster/contexts/pure_soft_keymaster_context.cpp.
+        crypto::hmac::Key::new_from(b"MustBeRandomBits")
+    }
+}
+
+/// Implementation of key derivation using a random fake key.
+pub struct Derive {
+    hbk: Vec<u8>,
+}
+
+impl Default for Derive {
+    fn default() -> Self {
+        // Use random data as an emulation of a hardware-backed key.
+        let mut hbk = vec![0; 32];
+        let mut rng = BoringRng;
+        rng.fill_bytes(&mut hbk);
+        Self { hbk }
+    }
+}
+
+impl crate::rpc::DeriveBytes for Derive {
+    fn derive_bytes(&self, context: &[u8], output_len: usize) -> Result<Vec<u8>, Error> {
+        BoringHmac.hkdf(&[], &self.hbk, context, output_len)
+    }
+}
+
+/// RPC artifact retrieval using software fake key.
+pub type RpcArtifacts = crate::rpc::Artifacts<Derive>;
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 087f763..c121d31 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -2024,7 +2024,7 @@
         }
     }
 
-    if (KeyMintAidlTestBase::dump_Attestations) std::cout << cert_data.str();
+    if (KeyMintAidlTestBase::dump_Attestations) std::cout << "cert chain:\n" << cert_data.str();
     return AssertionSuccess();
 }
 
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index e098aca..9575183 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -1064,32 +1064,54 @@
 TEST_P(NewKeyGenerationTest, RsaWithSpecifiedValidity) {
     vector<uint8_t> key_blob;
     vector<KeyCharacteristics> key_characteristics;
-    ASSERT_EQ(ErrorCode::OK,
-              GenerateKey(AuthorizationSetBuilder()
-                                  .RsaSigningKey(2048, 65537)
-                                  .Digest(Digest::NONE)
-                                  .Padding(PaddingMode::NONE)
-                                  .Authorization(TAG_CERTIFICATE_NOT_BEFORE,
-                                                 1183806000000 /* 2007-07-07T11:00:00Z */)
-                                  .Authorization(TAG_CERTIFICATE_NOT_AFTER,
-                                                 1916049600000 /* 2030-09-19T12:00:00Z */),
-                          &key_blob, &key_characteristics));
-    ASSERT_GT(cert_chain_.size(), 0);
+    vector<uint64_t> test_vector_not_before_millis = {
+            458046000000,    /* 1984-07-07T11:00:00Z */
+            1183806000000,   /* 2007-07-07T11:00:00Z */
+            1924991999000,   /* 2030-12-31T23:59:59Z */
+            3723753599000,   /* 2087-12-31T23:59:59Z */
+            26223868799000,  /* 2800-12-31T23:59:59Z */
+            45157996799000,  /* 3400-12-31T23:59:59Z */
+            60719587199000,  /* 3894-02-15T23:59:59Z */
+            95302051199000,  /* 4989-12-31T23:59:59Z */
+            86182012799000,  /* 4700-12-31T23:59:59Z */
+            111427574399000, /* 5500-12-31T23:59:59Z */
+            136988668799000, /* 6310-12-31T23:59:59Z */
+            139828895999000, /* 6400-12-31T23:59:59Z */
+            169839503999000, /* 7351-12-31T23:59:59Z */
+            171385804799000, /* 7400-12-31T23:59:59Z */
+            190320019199000, /* 8000-12-31T23:59:59Z */
+            193475692799000, /* 8100-12-31T23:59:59Z */
+            242515209599000, /* 9654-12-31T23:59:59Z */
+            250219065599000, /* 9899-02-15T23:59:59Z */
+    };
+    for (auto notBefore : test_vector_not_before_millis) {
+        uint64_t notAfter = notBefore + 378691200000 /* 12 years milliseconds*/;
+        SCOPED_TRACE(testing::Message() << "notBefore: " << notBefore << " notAfter: " << notAfter);
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(2048, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .Authorization(TAG_CERTIFICATE_NOT_BEFORE, notBefore)
+                                      .Authorization(TAG_CERTIFICATE_NOT_AFTER, notAfter),
+                              &key_blob, &key_characteristics));
+        ASSERT_GT(cert_chain_.size(), 0);
 
-    X509_Ptr cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
-    ASSERT_TRUE(!!cert.get());
+        X509_Ptr cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+        ASSERT_TRUE(!!cert.get());
 
-    const ASN1_TIME* not_before = X509_get0_notBefore(cert.get());
-    ASSERT_NE(not_before, nullptr);
-    time_t not_before_time;
-    ASSERT_EQ(ASN1_TIME_to_time_t(not_before, &not_before_time), 1);
-    EXPECT_EQ(not_before_time, 1183806000);
+        const ASN1_TIME* not_before = X509_get0_notBefore(cert.get());
+        ASSERT_NE(not_before, nullptr);
+        int64_t not_before_time;
+        ASSERT_EQ(ASN1_TIME_to_posix(not_before, &not_before_time), 1);
+        EXPECT_EQ(not_before_time, (notBefore / 1000));
 
-    const ASN1_TIME* not_after = X509_get0_notAfter(cert.get());
-    ASSERT_NE(not_after, nullptr);
-    time_t not_after_time;
-    ASSERT_EQ(ASN1_TIME_to_time_t(not_after, &not_after_time), 1);
-    EXPECT_EQ(not_after_time, 1916049600);
+        const ASN1_TIME* not_after = X509_get0_notAfter(cert.get());
+        ASSERT_NE(not_after, nullptr);
+        int64_t not_after_time;
+        ASSERT_EQ(ASN1_TIME_to_posix(not_after, &not_after_time), 1);
+        EXPECT_EQ(not_after_time, (notAfter / 1000));
+    }
 }
 
 /*
@@ -8739,40 +8761,6 @@
 
 INSTANTIATE_KEYMINT_AIDL_TEST(EarlyBootKeyTest);
 
-using UnlockedDeviceRequiredTest = KeyMintAidlTestBase;
-
-// This may be a problematic test.  It can't be run repeatedly without unlocking the device in
-// between runs... and on most test devices there are no enrolled credentials so it can't be
-// unlocked at all, meaning the only way to get the test to pass again on a properly-functioning
-// device is to reboot it.  For that reason, this is disabled by default.  It can be used as part of
-// a manual test process, which includes unlocking between runs, which is why it's included here.
-// Well, that and the fact that it's the only test we can do without also making calls into the
-// Gatekeeper HAL.  We haven't written any cross-HAL tests, and don't know what all of the
-// implications might be, so that may or may not be a solution.
-TEST_P(UnlockedDeviceRequiredTest, DISABLED_KeysBecomeUnusable) {
-    auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
-            CreateTestKeys(TAG_UNLOCKED_DEVICE_REQUIRED, ErrorCode::OK);
-    KeyBlobDeleter aes_deleter(keymint_, aesKeyData.blob);
-    KeyBlobDeleter hmac_deleter(keymint_, hmacKeyData.blob);
-    KeyBlobDeleter rsa_deleter(keymint_, rsaKeyData.blob);
-    KeyBlobDeleter ecdsa_deleter(keymint_, ecdsaKeyData.blob);
-
-    EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob));
-    EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob));
-    EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob));
-    EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
-
-    ErrorCode rc = GetReturnErrorCode(
-            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));
-    EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseRsaKey(rsaKeyData.blob));
-    EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseEcdsaKey(ecdsaKeyData.blob));
-}
-
-INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest);
-
 using VsrRequirementTest = KeyMintAidlTestBase;
 
 // @VsrTest = VSR-3.10-008
diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp
index 630f7bb..89469f1 100644
--- a/security/keymint/support/remote_prov_utils_test.cpp
+++ b/security/keymint/support/remote_prov_utils_test.cpp
@@ -14,20 +14,23 @@
  * limitations under the License.
  */
 
-#include "cppbor.h"
-#include "keymaster/cppcose/cppcose.h"
 #include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
 #include <android-base/properties.h>
+#include <cppbor.h>
 #include <cppbor_parse.h>
-#include <cstdint>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <keymaster/android_keymaster_utils.h>
+#include <keymaster/cppcose/cppcose.h>
 #include <keymaster/logger.h>
 #include <keymaster/remote_provisioning_utils.h>
 #include <openssl/curve25519.h>
 #include <remote_prov/remote_prov_utils.h>
 
+#include <algorithm>
+#include <cstdint>
+#include <span>
+
 namespace aidl::android::hardware::security::keymint::remote_prov {
 namespace {
 
@@ -36,7 +39,11 @@
 using ::keymaster::kStatusInvalidEek;
 using ::keymaster::StatusOr;
 using ::testing::ElementsAreArray;
-using byte_view = std::basic_string_view<uint8_t>;
+using byte_view = std::span<const uint8_t>;
+
+inline bool equal_byte_views(const byte_view& view1, const byte_view& view2) {
+    return std::equal(view1.begin(), view1.end(), view2.begin(), view2.end());
+}
 
 struct KeyInfoEcdsa {
     CoseKeyCurve curve;
@@ -44,7 +51,8 @@
     byte_view pubKeyY;
 
     bool operator==(const KeyInfoEcdsa& other) const {
-        return curve == other.curve && pubKeyX == other.pubKeyX && pubKeyY == other.pubKeyY;
+        return curve == other.curve && equal_byte_views(pubKeyX, other.pubKeyX) &&
+               equal_byte_views(pubKeyY, other.pubKeyY);
     }
 };
 
diff --git a/security/secretkeeper/aidl/vts/Android.bp b/security/secretkeeper/aidl/vts/Android.bp
index e6521ae..b9ce5ce 100644
--- a/security/secretkeeper/aidl/vts/Android.bp
+++ b/security/secretkeeper/aidl/vts/Android.bp
@@ -47,6 +47,7 @@
         "android.hardware.security.secretkeeper-V1-rust",
         "libauthgraph_boringssl",
         "libauthgraph_core",
+        "libauthgraph_wire",
         "libauthgraph_vts_test",
         "libbinder_rs",
         "libciborium",
diff --git a/security/secretkeeper/aidl/vts/secretkeeper_cli.rs b/security/secretkeeper/aidl/vts/secretkeeper_cli.rs
index d02bfe6..377ed37 100644
--- a/security/secretkeeper/aidl/vts/secretkeeper_cli.rs
+++ b/security/secretkeeper/aidl/vts/secretkeeper_cli.rs
@@ -120,7 +120,7 @@
     fn new(instance: &str, dice_artifacts: OwnedDiceArtifactsWithExplicitKey) -> Self {
         let sk: binder::Strong<dyn ISecretkeeper> =
             binder::get_interface(&format!("{SECRETKEEPER_SERVICE}/{instance}")).unwrap();
-        let session = SkSession::new(sk.clone(), &dice_artifacts).unwrap();
+        let session = SkSession::new(sk.clone(), &dice_artifacts, None).unwrap();
         Self { sk, session, dice_artifacts }
     }
 
diff --git a/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
index 72d3e57..7c00aa9 100644
--- a/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
+++ b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
@@ -19,11 +19,11 @@
 use authgraph_vts_test as ag_vts;
 use authgraph_boringssl as boring;
 use authgraph_core::key;
-use coset::{CborSerializable, CoseEncrypt0};
+use coset::{CborOrdering, CborSerializable, CoseEncrypt0, CoseKey};
 use dice_policy_builder::{CertIndex, ConstraintSpec, ConstraintType, MissingAction, WILDCARD_FULL_ARRAY, policy_for_dice_chain};
 use rdroidtest::{ignore_if, rdroidtest};
 use secretkeeper_client::dice::OwnedDiceArtifactsWithExplicitKey;
-use secretkeeper_client::SkSession;
+use secretkeeper_client::{SkSession, Error as SkClientError};
 use secretkeeper_core::cipher;
 use secretkeeper_comm::data_types::error::SecretkeeperError;
 use secretkeeper_comm::data_types::request::Request;
@@ -38,9 +38,14 @@
     SUBCOMPONENT_DESCRIPTORS, SUBCOMPONENT_SECURITY_VERSION,
     dice_sample::make_explicit_owned_dice
 };
+use std::fs;
+use std::path::Path;
 
 const SECRETKEEPER_SERVICE: &str = "android.hardware.security.secretkeeper.ISecretkeeper";
 const CURRENT_VERSION: u64 = 1;
+const SECRETKEEPER_KEY_HOST_DT: &str =
+    "/proc/device-tree/avf/reference/avf/secretkeeper_public_key";
+
 // Random bytes (of ID_SIZE/SECRET_SIZE) generated for tests.
 const ID_EXAMPLE: Id = Id([
     0xF1, 0xB2, 0xED, 0x3B, 0xD1, 0xBD, 0xF0, 0x7D, 0xE1, 0xF0, 0x01, 0xFC, 0x61, 0x71, 0xD3, 0x42,
@@ -65,6 +70,22 @@
     0x06, 0xAC, 0x36, 0x8B, 0x3C, 0x95, 0x50, 0x16, 0x67, 0x71, 0x65, 0x26, 0xEB, 0xD0, 0xC3, 0x98,
 ]);
 
+// Android expects the public key of Secretkeeper instance to be present in the Linux device tree.
+// This allows clients to (cryptographically) verify that they are indeed talking to the real
+// secretkeeper.
+// Note that this is the identity of the `default` instance (and not `nonsecure`)!
+fn get_secretkeeper_identity() -> Option<CoseKey> {
+    let path = Path::new(SECRETKEEPER_KEY_HOST_DT);
+    if path.exists() {
+        let key = fs::read(path).unwrap();
+        let mut key = CoseKey::from_slice(&key).unwrap();
+        key.canonicalize(CborOrdering::Lexicographic);
+        Some(key)
+    } else {
+        None
+    }
+}
+
 fn get_instances() -> Vec<(String, String)> {
     // Determine which instances are available.
     binder::get_declared_instances(SECRETKEEPER_SERVICE)
@@ -80,6 +101,7 @@
 }
 
 /// Secretkeeper client information.
+#[derive(Debug)]
 struct SkClient {
     sk: binder::Strong<dyn ISecretkeeper>,
     session: SkSession,
@@ -95,19 +117,40 @@
 
 impl SkClient {
     /// Create an `SkClient` using the default `OwnedDiceArtifactsWithExplicitKey` for identity.
-    fn new(instance: &str) -> Self {
+    fn new(instance: &str) -> Result<Self, SkClientError> {
         let default_dice = make_explicit_owned_dice(/*Security version in a node */ 5);
-        Self::with_identity(instance, default_dice)
+        Self::create(instance, default_dice, None)
     }
 
-    /// Create an `SkClient` using the given `OwnedDiceArtifactsWithExplicitKey` for identity.
-    fn with_identity(instance: &str, dice_artifacts: OwnedDiceArtifactsWithExplicitKey) -> Self {
+    /// Create an `SkClient` using given `OwnedDiceArtifactsWithExplicitKey` as client identity.
+    fn with_identity(
+        instance: &str,
+        dice_artifacts: OwnedDiceArtifactsWithExplicitKey,
+    ) -> Result<Self, SkClientError> {
+        Self::create(instance, dice_artifacts, None)
+    }
+
+    /// Create an `SkClient` with default client identity, requiring Secretkeeper's identity to be
+    /// matched against given `expected_sk_key`.
+    fn with_expected_sk_identity(
+        instance: &str,
+        expected_sk_key: coset::CoseKey,
+    ) -> Result<Self, SkClientError> {
+        let default_dice = make_explicit_owned_dice(/*Security version in a node */ 5);
+        Self::create(instance, default_dice, Some(expected_sk_key))
+    }
+
+    fn create(
+        instance: &str,
+        dice_artifacts: OwnedDiceArtifactsWithExplicitKey,
+        expected_sk_key: Option<coset::CoseKey>,
+    ) -> Result<Self, SkClientError> {
         let sk = get_connection(instance);
-        Self {
+        Ok(Self {
             sk: sk.clone(),
-            session: SkSession::new(sk, &dice_artifacts).unwrap(),
+            session: SkSession::new(sk, &dice_artifacts, expected_sk_key)?,
             dice_artifacts,
-        }
+        })
     }
 
     /// This method is wrapper that use `SkSession::secret_management_request` which handles
@@ -347,7 +390,7 @@
 
 #[rdroidtest(get_instances())]
 fn secret_management_get_version(instance: String) {
-    let mut sk_client = SkClient::new(&instance);
+    let mut sk_client = SkClient::new(&instance).unwrap();
 
     let request = GetVersionRequest {};
     let request_packet = request.serialize_to_packet();
@@ -364,7 +407,7 @@
 
 #[rdroidtest(get_instances())]
 fn secret_management_malformed_request(instance: String) {
-    let mut sk_client = SkClient::new(&instance);
+    let mut sk_client = SkClient::new(&instance).unwrap();
 
     let request = GetVersionRequest {};
     let request_packet = request.serialize_to_packet();
@@ -383,7 +426,7 @@
 
 #[rdroidtest(get_instances())]
 fn secret_management_store_get_secret_found(instance: String) {
-    let mut sk_client = SkClient::new(&instance);
+    let mut sk_client = SkClient::new(&instance).unwrap();
 
     sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
 
@@ -393,7 +436,7 @@
 
 #[rdroidtest(get_instances())]
 fn secret_management_store_get_secret_not_found(instance: String) {
-    let mut sk_client = SkClient::new(&instance);
+    let mut sk_client = SkClient::new(&instance).unwrap();
 
     // Store a secret (corresponding to an id).
     sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
@@ -404,7 +447,7 @@
 
 #[rdroidtest(get_instances())]
 fn secretkeeper_store_delete_ids(instance: String) {
-    let mut sk_client = SkClient::new(&instance);
+    let mut sk_client = SkClient::new(&instance).unwrap();
 
     sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
     sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -420,7 +463,7 @@
 
 #[rdroidtest(get_instances())]
 fn secretkeeper_store_delete_multiple_ids(instance: String) {
-    let mut sk_client = SkClient::new(&instance);
+    let mut sk_client = SkClient::new(&instance).unwrap();
 
     sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
     sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -431,7 +474,7 @@
 }
 #[rdroidtest(get_instances())]
 fn secretkeeper_store_delete_duplicate_ids(instance: String) {
-    let mut sk_client = SkClient::new(&instance);
+    let mut sk_client = SkClient::new(&instance).unwrap();
 
     sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
     sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -444,7 +487,7 @@
 
 #[rdroidtest(get_instances())]
 fn secretkeeper_store_delete_nonexistent(instance: String) {
-    let mut sk_client = SkClient::new(&instance);
+    let mut sk_client = SkClient::new(&instance).unwrap();
 
     sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
     sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -459,7 +502,7 @@
 #[rdroidtest(get_instances())]
 #[ignore_if(|p| p != "nonsecure")]
 fn secretkeeper_store_delete_all(instance: String) {
-    let mut sk_client = SkClient::new(&instance);
+    let mut sk_client = SkClient::new(&instance).unwrap();
 
     sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
     sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -486,7 +529,7 @@
 fn secret_management_replay_protection_seq_num(instance: String) {
     let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 5);
     let sealing_policy = sealing_policy(dice_chain.explicit_key_dice_chain().unwrap());
-    let sk_client = SkClient::with_identity(&instance, dice_chain);
+    let sk_client = SkClient::with_identity(&instance, dice_chain).unwrap();
     // Construct encoded request packets for the test
     let (req_1, req_2, req_3) = construct_secret_management_requests(sealing_policy);
 
@@ -522,7 +565,7 @@
 fn secret_management_replay_protection_seq_num_per_session(instance: String) {
     let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 5);
     let sealing_policy = sealing_policy(dice_chain.explicit_key_dice_chain().unwrap());
-    let sk_client = SkClient::with_identity(&instance, dice_chain);
+    let sk_client = SkClient::with_identity(&instance, dice_chain).unwrap();
 
     // Construct encoded request packets for the test
     let (req_1, _, _) = construct_secret_management_requests(sealing_policy);
@@ -538,7 +581,7 @@
     assert_eq!(res.response_type().unwrap(), ResponseType::Success);
 
     // Start another session
-    let sk_client_diff = SkClient::new(&instance);
+    let sk_client_diff = SkClient::new(&instance).unwrap();
     // Check first request/response is with seq_0 is successful
     let res = ResponsePacket::from_slice(
         &sk_client_diff.secret_management_request_custom_aad(&req_1, &seq_0, &seq_0).unwrap(),
@@ -553,7 +596,7 @@
 fn secret_management_replay_protection_out_of_seq_req_not_accepted(instance: String) {
     let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 5);
     let sealing_policy = sealing_policy(dice_chain.explicit_key_dice_chain().unwrap());
-    let sk_client = SkClient::with_identity(&instance, dice_chain);
+    let sk_client = SkClient::with_identity(&instance, dice_chain).unwrap();
 
     // Construct encoded request packets for the test
     let (req_1, req_2, _) = construct_secret_management_requests(sealing_policy);
@@ -578,18 +621,19 @@
 #[rdroidtest(get_instances())]
 fn secret_management_policy_gate(instance: String) {
     let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 100);
-    let mut sk_client_original = SkClient::with_identity(&instance, dice_chain);
+    let mut sk_client_original = SkClient::with_identity(&instance, dice_chain).unwrap();
     sk_client_original.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
     assert_eq!(sk_client_original.get(&ID_EXAMPLE).unwrap(), SECRET_EXAMPLE);
 
     // Start a session with higher security_version & get the stored secret.
     let dice_chain_upgraded = make_explicit_owned_dice(/*Security version in a node */ 101);
-    let mut sk_client_upgraded = SkClient::with_identity(&instance, dice_chain_upgraded);
+    let mut sk_client_upgraded = SkClient::with_identity(&instance, dice_chain_upgraded).unwrap();
     assert_eq!(sk_client_upgraded.get(&ID_EXAMPLE).unwrap(), SECRET_EXAMPLE);
 
     // Start a session with lower security_version (This should be denied access to the secret).
     let dice_chain_downgraded = make_explicit_owned_dice(/*Security version in a node */ 99);
-    let mut sk_client_downgraded = SkClient::with_identity(&instance, dice_chain_downgraded);
+    let mut sk_client_downgraded =
+        SkClient::with_identity(&instance, dice_chain_downgraded).unwrap();
     assert!(matches!(
         sk_client_downgraded.get(&ID_EXAMPLE).unwrap_err(),
         Error::SecretkeeperError(SecretkeeperError::DicePolicyError)
@@ -610,6 +654,30 @@
     ));
 }
 
+// This test checks that the identity of Secretkeeper (in context of AuthGraph key exchange) is
+// same as the one advertized in Linux device tree. This is only expected from `default` instance.
+#[rdroidtest(get_instances())]
+#[ignore_if(|p| p != "default")]
+fn secretkeeper_check_identity(instance: String) {
+    let sk_key = get_secretkeeper_identity()
+        .expect("Failed to extract identity of default instance from device tree");
+    // Create a session with this expected identity. This succeeds only if the identity used by
+    // Secretkeeper is sk_key.
+    let _ = SkClient::with_expected_sk_identity(&instance, sk_key).unwrap();
+    // Create a session using any other expected sk identity, this should fail. Note that the
+    // failure arises from validation which happens at the local participant.
+    let mut any_other_key = CoseKey::default();
+    any_other_key.canonicalize(CborOrdering::Lexicographic);
+    let err = SkClient::with_expected_sk_identity(&instance, any_other_key).unwrap_err();
+    assert!(matches!(
+        err,
+        SkClientError::AuthgraphError(authgraph_core::error::Error(
+            authgraph_wire::ErrorCode::InvalidPeerKeKey,
+            _
+        ))
+    ));
+}
+
 // Helper method that constructs 3 SecretManagement requests. Callers would usually not care about
 // what each of the request concretely is.
 fn construct_secret_management_requests(sealing_policy: Vec<u8>) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
diff --git a/staging/security/see/storage/aidl/Android.bp b/staging/security/see/storage/aidl/Android.bp
new file mode 100644
index 0000000..f669be8
--- /dev/null
+++ b/staging/security/see/storage/aidl/Android.bp
@@ -0,0 +1,26 @@
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.security.see.storage",
+    unstable: true,
+    host_supported: true,
+    srcs: [
+        "android/hardware/security/see/storage/*.aidl",
+    ],
+    backend: {
+        java: {
+            enabled: false,
+        },
+        cpp: {
+            enabled: true,
+        },
+        ndk: {
+            enabled: true,
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/CreationMode.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/CreationMode.aidl
new file mode 100644
index 0000000..1c65038
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/CreationMode.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+enum CreationMode {
+    /** Returns an error if the file does not already exist. */
+    NO_CREATE,
+
+    /** Creates the file or returns an error if it already exists. */
+    CREATE_EXCLUSIVE,
+
+    /** Creates the file if it does not already exist. */
+    CREATE,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/DeleteOptions.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/DeleteOptions.aidl
new file mode 100644
index 0000000..1a94eb2
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/DeleteOptions.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.ReadIntegrity;
+
+parcelable DeleteOptions {
+    /**
+     * Set to acknowledge possible files tampering.
+     *
+     * If unacknowledged tampering is detected, the operation will fail with an ERR_FS_*
+     * service-specific code.
+     */
+    ReadIntegrity readIntegrity = ReadIntegrity.NO_TAMPER;
+
+    /**
+     * Allow writes to succeed while the filesystem is in the middle of an A/B update.
+     *
+     * If the A/B update fails, the operation will be rolled back. This rollback will not
+     * cause subsequent operations fail with any ERR_FS_* code nor will need to be
+     * acknowledged by setting the `readIntegrity`.
+     */
+    boolean allowWritesDuringAbUpdate = false;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileAvailability.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileAvailability.aidl
new file mode 100644
index 0000000..d339170
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileAvailability.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+/** Determines how early during the boot process file is able to be accessed. */
+enum FileAvailability {
+    /** Available before userdata is mounted, but after android has booted. */
+    BEFORE_USERDATA,
+
+    /** Available after userdata is mounted. */
+    AFTER_USERDATA,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileIntegrity.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileIntegrity.aidl
new file mode 100644
index 0000000..1879b16
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileIntegrity.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+enum FileIntegrity {
+    /** REE may prevent operations, but cannot alter data once written. */
+    TAMPER_PROOF_AT_REST,
+
+    /**
+     * REE may alter written data, but changes will be detected and reported as
+     * an error on read.
+     */
+    TAMPER_DETECT,
+
+    /**
+     * REE may alter written data. Changes other than full filesystem resets will be detected and
+     * reported.
+     */
+    TAMPER_DETECT_IGNORE_RESET,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileMode.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileMode.aidl
new file mode 100644
index 0000000..18a2eae
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileMode.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+enum FileMode {
+    /** The file may only be read from. */
+    READ_ONLY,
+
+    /** The file may only be written to. */
+    WRITE_ONLY,
+
+    /** The file may be both read from and written to. */
+    READ_WRITE,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileProperties.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileProperties.aidl
new file mode 100644
index 0000000..733b5b0
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileProperties.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.FileAvailability;
+import android.hardware.security.see.storage.FileIntegrity;
+
+parcelable FileProperties {
+    FileIntegrity integrity = FileIntegrity.TAMPER_PROOF_AT_REST;
+    FileAvailability availability = FileAvailability.BEFORE_USERDATA;
+
+    /** Whether the file is reset when user data is wiped. */
+    boolean persistent;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/IDir.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IDir.aidl
new file mode 100644
index 0000000..a0a9f3d
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IDir.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+/** The interface for an open directory */
+interface IDir {
+    /**
+     * Gets the next batch of filenames in this directory.
+     *
+     * Calling multiple times will return different results as the IDir iterates through all the
+     * files it contains. When all filenames have been returned, all successive calls will return an
+     * empty list.
+     *
+     * @maxCount:
+     *     the maximum number of filenames to return. A @maxCount of 0 signifies no limit on the
+     * number of filenames returned.
+     *
+     * Returns:
+     *     An ordered list of filenames. If @maxCount > 0, the length of the returned list will be
+     * less than or equal to @maxCount.
+     *
+     * May return service-specific errors:
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+     *       dir was opened with does not acknowledge
+     */
+    @utf8InCpp String[] readNextFilenames(int maxCount);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/IFile.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IFile.aidl
new file mode 100644
index 0000000..ff26aa4
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IFile.aidl
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.CreationMode;
+
+/** The interface for an open file */
+interface IFile {
+    /**
+     * Read bytes from this file.
+     *
+     * @size:
+     *     the size (in bytes) of the segment to read. If @size is larger than the service's maximum
+     *       read size, the call will return an error (EX_ILLEGAL_ARGUMENT).
+     * @offset:
+     *     the offset (in bytes) at which to start reading
+     *
+     * Return:
+     *     the sequence of bytes at [offset, offset + size) in the file
+     *
+     * May return service-specific errors:
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+     *       file was opened with does not acknowledge
+     */
+    byte[] read(long size, long offset);
+
+    /**
+     * Write the bytes in `buffer` to this file.
+     *
+     * @offset:
+     *     the offset (in bytes) at which to start writing
+     *
+     * Return:
+     *     the number of bytes written successfully
+     *
+     * May return service-specific errors:
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+     *       file was opened with does not acknowledge
+     */
+    long write(long offset, in byte[] buffer);
+
+    /**
+     * Reads this file's size.
+     *
+     * May return service-specific errors:
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+     *       file was opened with does not acknowledge
+     */
+    long getSize();
+
+    /**
+     * Sets this file's size.
+     *
+     * Truncates the file if `new_size` is less than the current size. If `new_size` is greater than
+     * the current size, the file will be extended with zeroed data.
+     *
+     * @newSize:
+     *     the file's new size
+     *
+     * May return service-specific errors:
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+     *       file was opened with does not acknowledge
+     */
+    void setSize(long newSize);
+
+    /**
+     * Renames this file.
+     *
+     * @destPath:
+     *     the file's new path, relative to filesystem root
+     * @destCreateMode:
+     *     controls creation behavior of the dest file
+     *
+     * May return service-specific errors:
+     *   - ERR_NOT_FOUND if no file exists at @destPath and @destCreateMode is `NO_CREATE`
+     *   - ERR_ALREADY_EXISTS if a file already exists at @destPath and @destCreateMode is
+     *       `CREATE_EXCLUSIVE`
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+     *       file was opened with does not acknowledge
+     */
+    void rename(in @utf8InCpp String destPath, in CreationMode destCreateMode);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/ISecureStorage.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ISecureStorage.aidl
new file mode 100644
index 0000000..be3c045
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ISecureStorage.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.FileProperties;
+import android.hardware.security.see.storage.IStorageSession;
+
+/**
+ * Interface for the Secure Storage HAL
+ *
+ * Creates sessions which can be used to access storage.
+ */
+interface ISecureStorage {
+    const int ERR_UNSUPPORTED_PROPERTIES = 1;
+    const int ERR_NOT_FOUND = 2;
+    const int ERR_ALREADY_EXISTS = 3;
+    const int ERR_BAD_TRANSACTION = 4;
+
+    const int ERR_FS_RESET = 5;
+    const int ERR_FS_ROLLED_BACK = 6;
+    const int ERR_FS_TAMPERED = 7;
+
+    /**
+     * Starts a storage session for a filesystem.
+     *
+     * @properties:
+     *     the minimum filesystem properties requested for the session.
+     *
+     * May return service-specific errors:
+     *   - ERR_UNSUPPORTED_PROPERTIES if no filesystems exist which meet the minimum requested
+     * requirements
+     */
+    IStorageSession startSession(in FileProperties properties);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/IStorageSession.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IStorageSession.aidl
new file mode 100644
index 0000000..cd126b8
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IStorageSession.aidl
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.DeleteOptions;
+import android.hardware.security.see.storage.IDir;
+import android.hardware.security.see.storage.IFile;
+import android.hardware.security.see.storage.OpenOptions;
+import android.hardware.security.see.storage.ReadIntegrity;
+import android.hardware.security.see.storage.RenameOptions;
+
+/**
+ * Interface for a Secure Storage session
+ *
+ * When the connection is opened, it will start a transaction and any changes made through this
+ * session or the interfaces this session returns will be added to this transaction's pending
+ * changes. Calling `CommitChanges`/`AbandonChanges` will commit/abandon these pending changes, and
+ * start a new, empty transaction. The interfaces this session returns _remain_ valid across
+ * transactions; it is not necessary, for example, to reopen a file after a commit.
+ *
+ * Any changes still pending when the session is dropped will be abandoned.
+ */
+interface IStorageSession {
+    /**
+     * Commits any pending changes made through this session to storage.
+     *
+     * The session will no longer have pending changes after this call returns. Files may then still
+     * be modified through this session to create another commit.
+     *
+     * May return service-specific errors:
+     *   - ERR_BAD_TRANSACTION
+     */
+    void commitChanges();
+
+    /**
+     * Abandons any pending changes made through this session.
+     *
+     * The session can then be reused to make new changes.
+     */
+    void abandonChanges();
+
+    /**
+     * Opens a secure file for writing and/or reading.
+     *
+     * Changes made to the file are part of the current transaction. Dropping this session
+     * invalidates the returned `IFile` interface
+     *
+     * @filePath:
+     *     path to the file, relative to filesystem root
+     * @options:
+     *     options controlling opening behavior
+     *
+     * May return service-specific errors:
+     *   - ERR_NOT_FOUND
+     *   - ERR_ALREADY_EXISTS
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that @options.readIntegrity
+     *       does not acknowledge
+     */
+    IFile openFile(in @utf8InCpp String filePath, in OpenOptions options);
+
+    /**
+     * Delete a file.
+     *
+     * @filePath:
+     *     path to the file, relative to filesystem root
+     * @options:
+     *     options controlling deletion behavior
+     *
+     * May return service-specific errors:
+     *   - ERR_NOT_FOUND
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that @options.readIntegrity
+     *       does not acknowledge
+     */
+    void deleteFile(in @utf8InCpp String filePath, in DeleteOptions options);
+
+    /**
+     * Renames an existing file.
+     *
+     * The file must not already be opened. (If it is, use `IFile::rename`.)
+     *
+     * @currentPath:
+     *     path to the file, relative to filesystem root
+     * @destPath:
+     *     the file's new path, relative to filesystem root
+     * @options:
+     *     options controlling rename behavior
+     *
+     * May return service-specific errors:
+     *   - ERR_NOT_FOUND if no file exists at @currentPath, or if @options.destCreateMode is
+     *       `NO_CREATE` and no file exists at @destPath
+     *   - ERR_ALREADY_EXISTS if @options.destCreateMode is `CREATE_EXCLUSIVE` and a file exists at
+     *       @destPath
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that @options.readIntegrity
+     *       does not acknowledge
+     */
+    void renameFile(in @utf8InCpp String currentPath, in @utf8InCpp String destPath,
+            in RenameOptions options);
+
+    /**
+     * Opens a directory from a filesystem with the given properties.
+     *
+     * Dropping this session invalidates the returned `IDir` interface.
+     *
+     * @path:
+     *     path to the directory, relative to filesystem root
+     * @readIntegrity:
+     *     allow opening (and subsequent read/write operations) despite possible tampering for the
+     * directory
+     *
+     * May return service-specific errors:
+     *   - ERR_NOT_FOUND
+     *   - ERR_FS_* if the filesystem has been tampered with in a way that @readIntegrity does not
+     *       acknowledge
+     */
+    IDir openDir(in @utf8InCpp String path, in ReadIntegrity readIntegrity);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/OpenOptions.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/OpenOptions.aidl
new file mode 100644
index 0000000..997ca62
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/OpenOptions.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.CreationMode;
+import android.hardware.security.see.storage.FileMode;
+import android.hardware.security.see.storage.ReadIntegrity;
+
+parcelable OpenOptions {
+    /** Controls creation behavior of the to-be-opened file. See `CreationMode` docs for details. */
+    CreationMode createMode = CreationMode.NO_CREATE;
+
+    /** Controls access behavior of the to-be-opened file. See `FileMode` docs for details. */
+    FileMode accessMode = FileMode.READ_WRITE;
+
+    /**
+     * Set to acknowledge possible files tampering.
+     *
+     * If unacknowledged tampering is detected, the operation will fail with an ERR_FS_*
+     * service-specific code.
+     */
+    ReadIntegrity readIntegrity = ReadIntegrity.NO_TAMPER;
+
+    /**
+     * If this file already exists, discard existing content and open
+     * it as a new file. No semantic change if the file does not exist.
+     */
+    boolean truncateOnOpen;
+
+    /**
+     * Allow writes to succeed while the filesystem is in the middle of an A/B update.
+     *
+     * If the A/B update fails, the operation will be rolled back. This rollback will not
+     * cause subsequent operations fail with any ERR_FS_* code nor will need to be
+     * acknowledged by setting the `readIntegrity`.
+     */
+    boolean allowWritesDuringAbUpdate = false;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/ReadIntegrity.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ReadIntegrity.aidl
new file mode 100644
index 0000000..cc0e4f9
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ReadIntegrity.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+enum ReadIntegrity {
+    /**
+     * Return an error on reads if any REE alteration of the written data
+     * has been detected.
+     */
+    NO_TAMPER,
+
+    /**
+     * Return an error on reads if any REE alteration other than a reset
+     * has been detected.
+     */
+    IGNORE_RESET,
+
+    /**
+     * Return an error if any REE alteration other than a rollback to a
+     * valid checkpoint has been detected. (What makes a checkpoint valid is
+     * implementation defined; an implementation might take a checkpoint on its
+     * first post-factory boot. A reset is a rollback to the initial state.)
+     */
+    IGNORE_ROLLBACK,
+
+    // There's no `IGNORE_ALL` because if REE has done any alteration other
+    // than a rollback, the file contents will be known-bad data.
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/RenameOptions.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/RenameOptions.aidl
new file mode 100644
index 0000000..f55ea7f
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/RenameOptions.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.CreationMode;
+import android.hardware.security.see.storage.ReadIntegrity;
+
+parcelable RenameOptions {
+    /** Controls creation behavior of the dest file. See `CreationMode` docs for details. */
+    CreationMode destCreateMode = CreationMode.CREATE_EXCLUSIVE;
+
+    /**
+     * Set to acknowledge possible files tampering.
+     *
+     * If unacknowledged tampering is detected, the operation will fail with an ERR_FS_*
+     * service-specific code.
+     */
+    ReadIntegrity readIntegrity = ReadIntegrity.NO_TAMPER;
+
+    /**
+     * Allow writes to succeed while the filesystem is in the middle of an A/B update.
+     *
+     * If the A/B update fails, the operation will be rolled back. This rollback will not
+     * cause subsequent operations fail with any ERR_FS_* code nor will need to be
+     * acknowledged by setting the `readIntegrity`.
+     */
+    boolean allowWritesDuringAbUpdate = false;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/Tamper.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/Tamper.aidl
new file mode 100644
index 0000000..0a39fdd
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/Tamper.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.security.see.storage;
+
+/** Specifies types of REE tampering the filesystem may detect */
+enum Tamper {
+    /** REE has reset this file or the containing file system. */
+    RESET,
+
+    /** REE has rolled back this file or the containing file system to a previous state. */
+    ROLLBACK,
+
+    /** REE has made some other modification to the file. */
+    OTHER,
+}
diff --git a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
index fa7149f..e456e49 100644
--- a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
@@ -74,7 +74,17 @@
         return testing::deviceSupportsFeature("com.google.android.tv.mdns_offload");
     }
 
-    // Detected panel TV device by using ro.oem.key1 property.
+    bool doesDeviceSupportFullNetworkingUnder2w() {
+        return testing::deviceSupportsFeature("com.google.android.tv.full_networking_under_2w");
+    }
+
+    // Detect TV devices.
+    bool isTvDevice() {
+        return testing::deviceSupportsFeature("android.software.leanback") ||
+               testing::deviceSupportsFeature("android.hardware.type.television");
+    }
+
+    // Detect Panel TV devices by using ro.oem.key1 property.
     // https://docs.partner.android.com/tv/build/platform/props-vars/ro-oem-key1
     bool isPanelTvDevice() {
         const std::string oem_key1 = getPropertyString("ro.oem.key1");
@@ -135,10 +145,23 @@
  */
 // @VsrTest = 5.3.12
 TEST_P(WifiStaIfaceAidlTest, CheckApfIsSupported) {
-    // Flat panel TV devices that support MDNS offload do not have to implement APF if the WiFi
-    // chipset does not have sufficient RAM to do so.
-    if (isPanelTvDevice() && isMdnsOffloadPresentInNIC()) {
-        GTEST_SKIP() << "Panel TV supports mDNS offload. It is not required to support APF";
+    const std::string oem_key1 = getPropertyString("ro.oem.key1");
+    if (isTvDevice()) {
+        // Flat panel TV devices that support MDNS offload do not have to implement APF if the WiFi
+        // chipset does not have sufficient RAM to do so.
+        if (isPanelTvDevice() && isMdnsOffloadPresentInNIC()) {
+            GTEST_SKIP() << "Panel TV supports mDNS offload. It is not required to support APF";
+        }
+        // For TV devices declaring the
+        // com.google.android.tv.full_networking_under_2w feature, this indicates
+        // the device can meet the <= 2W standby power requirement while
+        // continuously processing network packets on the CPU, even in standby mode.
+        // In these cases, APF support is strongly recommended rather than being
+        // mandatory.
+        if (doesDeviceSupportFullNetworkingUnder2w()) {
+            GTEST_SKIP() << "TV Device meets the <= 2W standby power demand requirement. It is not "
+                            "required to support APF.";
+        }
     }
     int vendor_api_level = property_get_int32("ro.vendor.api_level", 0);
     // Before VSR 14, APF support is optional.