Merge "audio: Add a missing include to ModuleConfig.cpp" into main
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index 0ab990a..89d186c 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -297,6 +297,7 @@
         "android/hardware/audio/effect/PresetReverb.aidl",
         "android/hardware/audio/effect/Processing.aidl",
         "android/hardware/audio/effect/Range.aidl",
+        "android/hardware/audio/effect/Spatializer.aidl",
         "android/hardware/audio/effect/State.aidl",
         "android/hardware/audio/effect/VendorExtension.aidl",
         "android/hardware/audio/effect/Virtualizer.aidl",
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
index 0422bd9..7313b57 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
@@ -60,6 +60,7 @@
     android.hardware.audio.effect.Visualizer.Id visualizerTag;
     android.hardware.audio.effect.Volume.Id volumeTag;
     android.hardware.audio.effect.Parameter.Tag commonTag;
+    android.hardware.audio.effect.Spatializer.Id spatializerTag;
   }
   @VintfStability
   parcelable Common {
@@ -91,5 +92,6 @@
     android.hardware.audio.effect.Virtualizer virtualizer;
     android.hardware.audio.effect.Visualizer visualizer;
     android.hardware.audio.effect.Volume volume;
+    android.hardware.audio.effect.Spatializer spatializer;
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl
index 93edc5e..40ee6b5 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl
@@ -50,6 +50,7 @@
   android.hardware.audio.effect.Range.VirtualizerRange[] virtualizer;
   android.hardware.audio.effect.Range.VisualizerRange[] visualizer;
   android.hardware.audio.effect.Range.VolumeRange[] volume;
+  android.hardware.audio.effect.Range.SpatializerRange[] spatializer;
   @VintfStability
   parcelable AcousticEchoCancelerRange {
     android.hardware.audio.effect.AcousticEchoCanceler min;
@@ -111,6 +112,11 @@
     android.hardware.audio.effect.PresetReverb max;
   }
   @VintfStability
+  parcelable SpatializerRange {
+    android.hardware.audio.effect.Spatializer min;
+    android.hardware.audio.effect.Spatializer max;
+  }
+  @VintfStability
   parcelable VendorExtensionRange {
     android.hardware.audio.effect.VendorExtension min;
     android.hardware.audio.effect.VendorExtension max;
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Spatializer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Spatializer.aidl
new file mode 100644
index 0000000..9f97de0
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Spatializer.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union Spatializer {
+  android.hardware.audio.effect.VendorExtension vendor;
+  android.media.audio.common.Spatialization.Level spatializationLevel;
+  android.media.audio.common.HeadTracking.Mode headTrackingMode;
+  android.media.audio.common.AudioChannelLayout[] supportedChannelLayout;
+  android.media.audio.common.Spatialization.Mode spatializationMode;
+  float[6] headToStage;
+  const int HEAD_TO_STAGE_VEC_SIZE = 6;
+  @VintfStability
+  union Id {
+    android.hardware.audio.effect.VendorExtension vendorExtensionTag;
+    android.hardware.audio.effect.Spatializer.Tag commonTag;
+  }
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
index 0954055..6ec7226 100644
--- a/audio/aidl/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
@@ -28,6 +28,7 @@
 import android.hardware.audio.effect.LoudnessEnhancer;
 import android.hardware.audio.effect.NoiseSuppression;
 import android.hardware.audio.effect.PresetReverb;
+import android.hardware.audio.effect.Spatializer;
 import android.hardware.audio.effect.VendorExtension;
 import android.hardware.audio.effect.Virtualizer;
 import android.hardware.audio.effect.Visualizer;
@@ -103,6 +104,11 @@
          * directly.
          */
         Parameter.Tag commonTag;
+
+        /**
+         * Parameter tag defined for Spatializer parameters.
+         */
+        Spatializer.Id spatializerTag;
     }
 
     /**
@@ -189,6 +195,7 @@
         Virtualizer virtualizer;
         Visualizer visualizer;
         Volume volume;
+        Spatializer spatializer;
     }
     Specific specific;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Range.aidl b/audio/aidl/android/hardware/audio/effect/Range.aidl
index 567320a..e5acb68 100644
--- a/audio/aidl/android/hardware/audio/effect/Range.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Range.aidl
@@ -28,6 +28,7 @@
 import android.hardware.audio.effect.LoudnessEnhancer;
 import android.hardware.audio.effect.NoiseSuppression;
 import android.hardware.audio.effect.PresetReverb;
+import android.hardware.audio.effect.Spatializer;
 import android.hardware.audio.effect.VendorExtension;
 import android.hardware.audio.effect.Virtualizer;
 import android.hardware.audio.effect.Visualizer;
@@ -169,6 +170,12 @@
     }
 
     @VintfStability
+    parcelable SpatializerRange {
+        Spatializer min;
+        Spatializer max;
+    }
+
+    @VintfStability
     parcelable VendorExtensionRange {
         VendorExtension min;
         VendorExtension max;
@@ -217,4 +224,5 @@
     VirtualizerRange[] virtualizer;
     VisualizerRange[] visualizer;
     VolumeRange[] volume;
+    SpatializerRange[] spatializer;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Spatializer.aidl b/audio/aidl/android/hardware/audio/effect/Spatializer.aidl
new file mode 100644
index 0000000..4edb2e8
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Spatializer.aidl
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+import android.media.audio.common.AudioChannelLayout;
+import android.media.audio.common.HeadTracking;
+import android.media.audio.common.Spatialization;
+
+/**
+ * Union representing parameters for audio spatialization effects.
+ *
+ * Sound spatialization simulates sounds around the listener as if they were emanating from virtual
+ * positions based on the original recording.
+ * For more details, refer to the documentation:
+ * https://developer.android.com/reference/android/media/Spatializer.
+ *
+ * android.hardware.audio.effect.Spatializer specifies parameters for the implementation of audio
+ * spatialization effects.
+ *
+ * A Spatializer implementation must report its supported parameter ranges using Capability.Range.
+ * spatializer.
+ */
+@VintfStability
+union Spatializer {
+    /**
+     * Parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        VendorExtension vendorExtensionTag;
+        Spatializer.Tag commonTag;
+    }
+
+    /**
+     * Vendor extension implementation for additional parameters.
+     */
+    VendorExtension vendor;
+
+    /**
+     * Level of spatialization.
+     */
+    Spatialization.Level spatializationLevel;
+
+    /**
+     * Head tracking mode for spatialization.
+     */
+    HeadTracking.Mode headTrackingMode;
+
+    /**
+     * List of supported input channel layouts.
+     */
+    AudioChannelLayout[] supportedChannelLayout;
+
+    /**
+     * Spatialization mode, Binaural or Transaural for example.
+     */
+    Spatialization.Mode spatializationMode;
+
+    /**
+     * Vector representing of the head-to-stage pose with six floats: first three are a translation
+     * vector, and the last three are a rotation vector.
+     */
+    const int HEAD_TO_STAGE_VEC_SIZE = 6;
+    float[HEAD_TO_STAGE_VEC_SIZE] headToStage;
+}
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
index 59ca92a..ef312d5 100644
--- a/audio/aidl/common/include/Utils.h
+++ b/audio/aidl/common/include/Utils.h
@@ -175,7 +175,7 @@
 }
 
 constexpr int32_t frameCountFromDurationUs(long durationUs, int32_t sampleRateHz) {
-    return (durationUs * sampleRateHz) / 1000000LL;
+    return (static_cast<long long>(durationUs) * sampleRateHz) / 1000000LL;
 }
 
 constexpr int32_t frameCountFromDurationMs(int32_t durationMs, int32_t sampleRateHz) {
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index d09552b..254eb46 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -302,8 +302,9 @@
 //   2. The canonical r_submix configuration only lists 'STEREO' and '48000',
 //      however the framework attempts to open streams for other sample rates
 //      as well. The legacy r_submix implementation allowed that, but libaudiohal@aidl
-//      will not find a mix port to use. Because of that, list all channel
-//      masks and sample rates that the legacy implementation allowed.
+//      will not find a mix port to use. Because of that, list all sample rates that
+//      the legacy implementation allowed (note that mono was not allowed, the framework
+//      is expected to upmix mono tracks into stereo if needed).
 //   3. The legacy implementation had a hard limit on the number of routes (10),
 //      and this is checked indirectly by AudioPlaybackCaptureTest#testPlaybackCaptureDoS
 //      CTS test. Instead of hardcoding the number of routes, we can use
@@ -331,9 +332,8 @@
 std::unique_ptr<Configuration> getRSubmixConfiguration() {
     static const Configuration configuration = []() {
         Configuration c;
-        const std::vector<AudioProfile> standardPcmAudioProfiles{
-                createProfile(PcmType::INT_16_BIT,
-                              {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
+        const std::vector<AudioProfile> remoteSubmixPcmAudioProfiles{
+                createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO},
                               {8000, 11025, 16000, 32000, 44100, 48000})};
 
         // Device ports
@@ -343,25 +343,25 @@
                            createDeviceExt(AudioDeviceType::OUT_SUBMIX, 0,
                                            AudioDeviceDescription::CONNECTION_VIRTUAL));
         c.ports.push_back(rsubmixOutDevice);
-        c.connectedProfiles[rsubmixOutDevice.id] = standardPcmAudioProfiles;
+        c.connectedProfiles[rsubmixOutDevice.id] = remoteSubmixPcmAudioProfiles;
 
         AudioPort rsubmixInDevice =
                 createPort(c.nextPortId++, "Remote Submix In", 0, true,
                            createDeviceExt(AudioDeviceType::IN_SUBMIX, 0,
                                            AudioDeviceDescription::CONNECTION_VIRTUAL));
         c.ports.push_back(rsubmixInDevice);
-        c.connectedProfiles[rsubmixInDevice.id] = standardPcmAudioProfiles;
+        c.connectedProfiles[rsubmixInDevice.id] = remoteSubmixPcmAudioProfiles;
 
         // Mix ports
 
         AudioPort rsubmixOutMix =
                 createPort(c.nextPortId++, "r_submix output", 0, false, createPortMixExt(20, 10));
-        rsubmixOutMix.profiles = standardPcmAudioProfiles;
+        rsubmixOutMix.profiles = remoteSubmixPcmAudioProfiles;
         c.ports.push_back(rsubmixOutMix);
 
         AudioPort rsubmixInMix =
                 createPort(c.nextPortId++, "r_submix input", 0, true, createPortMixExt(20, 10));
-        rsubmixInMix.profiles = standardPcmAudioProfiles;
+        rsubmixInMix.profiles = remoteSubmixPcmAudioProfiles;
         c.ports.push_back(rsubmixInMix);
 
         c.routes.push_back(createRoute({rsubmixOutMix}, rsubmixOutDevice));
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index 38281b9..fc61dcb 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -131,6 +131,8 @@
             LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink";
 
             sink->shutdown(true);
+            // The client already considers this stream as closed, release the output end.
+            route->closeStream(mIsInput);
         } else {
             LOG(DEBUG) << __func__ << ": stream already closed.";
             ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
diff --git a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp
index 96110db..e882160 100644
--- a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp
+++ b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp
@@ -18,9 +18,9 @@
 
 namespace android::hardware::automotive::can::V1_0::implementation::fuzzer {
 
-constexpr CanController::InterfaceType kInterfaceType[] = {CanController::InterfaceType::VIRTUAL,
-                                                           CanController::InterfaceType::SOCKETCAN,
-                                                           CanController::InterfaceType::SLCAN};
+constexpr CanController::InterfaceType kInterfaceType[] = {
+        CanController::InterfaceType::VIRTUAL, CanController::InterfaceType::SOCKETCAN,
+        CanController::InterfaceType::SLCAN, CanController::InterfaceType::INDEXED};
 constexpr FilterFlag kFilterFlag[] = {FilterFlag::DONT_CARE, FilterFlag::SET, FilterFlag::NOT_SET};
 constexpr size_t kInterfaceTypeLength = std::size(kInterfaceType);
 constexpr size_t kFilterFlagLength = std::size(kFilterFlag);
@@ -28,8 +28,8 @@
 constexpr size_t kMaxPayloadBytes = 64;
 constexpr size_t kMaxFilters = 20;
 constexpr size_t kMaxSerialNumber = 1000;
-constexpr size_t kMaxBuses = 10;
-constexpr size_t kMaxRepeat = 5;
+constexpr size_t kMaxBuses = 100;
+constexpr size_t kMaxRepeat = 100;
 
 Bus CanFuzzer::makeBus() {
     ICanController::BusConfig config = {};
@@ -56,9 +56,13 @@
 }
 
 void CanFuzzer::invokeUpInterface() {
-    const CanController::InterfaceType iftype =
-            kInterfaceType[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
-                    0, kInterfaceTypeLength - 1)];
+    CanController::InterfaceType controller;
+    if (mFuzzedDataProvider->ConsumeBool()) {
+        controller = (CanController::InterfaceType)mFuzzedDataProvider->ConsumeIntegral<uint8_t>();
+    } else {
+        controller = kInterfaceType[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+                0, kInterfaceTypeLength - 1)];
+    }
     std::string configName;
 
     if (const bool shouldInvokeValidBus = mFuzzedDataProvider->ConsumeBool();
@@ -73,7 +77,7 @@
 
     ICanController::BusConfig config = {.name = configName};
 
-    if (iftype == CanController::InterfaceType::SOCKETCAN) {
+    if (controller == CanController::InterfaceType::SOCKETCAN) {
         CanController::BusConfig::InterfaceId::Socketcan socketcan = {};
         if (const bool shouldPassSerialSocket = mFuzzedDataProvider->ConsumeBool();
             shouldPassSerialSocket) {
@@ -83,7 +87,7 @@
             socketcan.ifname(ifname);
         }
         config.interfaceId.socketcan(socketcan);
-    } else if (iftype == CanController::InterfaceType::SLCAN) {
+    } else if (controller == CanController::InterfaceType::SLCAN) {
         CanController::BusConfig::InterfaceId::Slcan slcan = {};
         if (const bool shouldPassSerialSlcan = mFuzzedDataProvider->ConsumeBool();
             shouldPassSerialSlcan) {
@@ -93,8 +97,12 @@
             slcan.ttyname(ifname);
         }
         config.interfaceId.slcan(slcan);
-    } else if (iftype == CanController::InterfaceType::VIRTUAL) {
+    } else if (controller == CanController::InterfaceType::VIRTUAL) {
         config.interfaceId.virtualif({ifname});
+    } else if (controller == CanController::InterfaceType::INDEXED) {
+        CanController::BusConfig::InterfaceId::Indexed indexed;
+        indexed.index = mFuzzedDataProvider->ConsumeIntegral<uint8_t>();
+        config.interfaceId.indexed(indexed);
     }
 
     const size_t numInvocations =
@@ -108,8 +116,13 @@
     hidl_string configName;
     if (const bool shouldInvokeValidBus = mFuzzedDataProvider->ConsumeBool();
         (shouldInvokeValidBus) && (mBusNames.size() > 0)) {
-        const size_t busNameIndex =
-                mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(0, mBusNames.size() - 1);
+        size_t busNameIndex;
+        if (mBusNames.size() == 1) {
+            busNameIndex = 0;
+        } else {
+            busNameIndex =
+                    mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(0, mBusNames.size() - 1);
+        }
         configName = mBusNames[busNameIndex];
     } else {
         configName = mFuzzedDataProvider->ConsumeRandomLengthString(kMaxCharacters);
@@ -122,12 +135,6 @@
     }
 }
 
-void CanFuzzer::invokeController() {
-    getSupportedInterfaceTypes();
-    invokeUpInterface();
-    invokeDownInterface();
-}
-
 void CanFuzzer::invokeBus() {
     const size_t numBuses = mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(1, kMaxBuses);
     for (size_t i = 0; i < numBuses; ++i) {
@@ -152,12 +159,22 @@
             for (uint32_t k = 0; k < numFilters; ++k) {
                 filterVector[k].id = mFuzzedDataProvider->ConsumeIntegral<uint32_t>();
                 filterVector[k].mask = mFuzzedDataProvider->ConsumeIntegral<uint32_t>();
-                filterVector[k].rtr =
-                        kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
-                                0, kFilterFlagLength - 1)];
-                filterVector[k].extendedFormat =
-                        kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
-                                0, kFilterFlagLength - 1)];
+                if (mFuzzedDataProvider->ConsumeBool()) {
+                    filterVector[k].rtr =
+                            (FilterFlag)mFuzzedDataProvider->ConsumeIntegral<uint8_t>();
+                } else {
+                    filterVector[k].rtr =
+                            kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+                                    0, kFilterFlagLength - 1)];
+                }
+                if (mFuzzedDataProvider->ConsumeBool()) {
+                    filterVector[k].extendedFormat =
+                            (FilterFlag)mFuzzedDataProvider->ConsumeIntegral<uint8_t>();
+                } else {
+                    filterVector[k].extendedFormat =
+                            kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+                                    0, kFilterFlagLength - 1)];
+                }
                 filterVector[k].exclude = mFuzzedDataProvider->ConsumeBool();
             }
             auto listener = listeningBus.listen(filterVector);
@@ -175,8 +192,16 @@
 
 void CanFuzzer::process(const uint8_t* data, size_t size) {
     mFuzzedDataProvider = new FuzzedDataProvider(data, size);
-    invokeController();
-    invokeBus();
+    while (mFuzzedDataProvider->remaining_bytes()) {
+        auto CanFuzzerFunction =
+                mFuzzedDataProvider->PickValueInArray<const std::function<void()>>({
+                        [&]() { getSupportedInterfaceTypes(); },
+                        [&]() { invokeUpInterface(); },
+                        [&]() { invokeDownInterface(); },
+                        [&]() { invokeBus(); },
+                });
+        CanFuzzerFunction();
+    }
 }
 
 bool CanFuzzer::init() {
diff --git a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h
index 930cddd..3211bd0 100644
--- a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h
+++ b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h
@@ -116,7 +116,6 @@
     hidl_vec<hidl_string> getBusNames();
     void getSupportedInterfaceTypes();
     void invokeBus();
-    void invokeController();
     void invokeUpInterface();
     void invokeDownInterface();
     FuzzedDataProvider* mFuzzedDataProvider = nullptr;
diff --git a/bluetooth/aidl/default/BluetoothHci.cpp b/bluetooth/aidl/default/BluetoothHci.cpp
index 9862e9e..a247cb0 100644
--- a/bluetooth/aidl/default/BluetoothHci.cpp
+++ b/bluetooth/aidl/default/BluetoothHci.cpp
@@ -320,6 +320,7 @@
   {
     std::lock_guard<std::mutex> guard(mStateMutex);
     mState = HalState::READY;
+    mH4 = nullptr;
   }
   return ndk::ScopedAStatus::ok();
 }
@@ -346,13 +347,16 @@
 
 ndk::ScopedAStatus BluetoothHci::send(PacketType type,
     const std::vector<uint8_t>& v) {
-  if (mH4 == nullptr) {
-    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-  }
   if (v.empty()) {
     ALOGE("Packet is empty, no data was found to be sent");
     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
   }
+
+  std::lock_guard<std::mutex> guard(mStateMutex);
+  if (mH4 == nullptr) {
+    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+  }
+
   mH4->Send(type, v);
   return ndk::ScopedAStatus::ok();
 }
diff --git a/camera/device/default/ExternalCameraDeviceSession.cpp b/camera/device/default/ExternalCameraDeviceSession.cpp
index c962974..88e4b75 100644
--- a/camera/device/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/default/ExternalCameraDeviceSession.cpp
@@ -786,9 +786,8 @@
                 outputBuffer.bufferId = buffer.bufferId;
                 outputBuffer.status = BufferStatus::ERROR;
                 if (buffer.acquireFence >= 0) {
-                    native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
-                    handle->data[0] = buffer.acquireFence;
-                    outputBuffer.releaseFence = android::makeToAidl(handle);
+                    outputBuffer.releaseFence.fds.resize(1);
+                    outputBuffer.releaseFence.fds.at(0).set(buffer.acquireFence);
                 }
             } else {
                 offlineBuffers.push_back(buffer);
@@ -1769,9 +1768,8 @@
         result.outputBuffers[i].bufferId = req->buffers[i].bufferId;
         result.outputBuffers[i].status = BufferStatus::ERROR;
         if (req->buffers[i].acquireFence >= 0) {
-            native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
-            handle->data[0] = req->buffers[i].acquireFence;
-            result.outputBuffers[i].releaseFence = ::android::makeToAidl(handle);
+            result.outputBuffers[i].releaseFence.fds.resize(1);
+            result.outputBuffers[i].releaseFence.fds.at(0).set(req->buffers[i].acquireFence);
         }
     }
 
@@ -1815,18 +1813,16 @@
         if (req->buffers[i].fenceTimeout) {
             result.outputBuffers[i].status = BufferStatus::ERROR;
             if (req->buffers[i].acquireFence >= 0) {
-                native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
-                handle->data[0] = req->buffers[i].acquireFence;
-                result.outputBuffers[i].releaseFence = ::android::makeToAidl(handle);
+                result.outputBuffers[i].releaseFence.fds.resize(1);
+                result.outputBuffers[i].releaseFence.fds.at(0).set(req->buffers[i].acquireFence);
             }
             notifyError(req->frameNumber, req->buffers[i].streamId, ErrorCode::ERROR_BUFFER);
         } else {
             result.outputBuffers[i].status = BufferStatus::OK;
             // TODO: refactor
             if (req->buffers[i].acquireFence >= 0) {
-                native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
-                handle->data[0] = req->buffers[i].acquireFence;
-                result.outputBuffers[i].releaseFence = ::android::makeToAidl(handle);
+                result.outputBuffers[i].releaseFence.fds.resize(1);
+                result.outputBuffers[i].releaseFence.fds.at(0).set(req->buffers[i].acquireFence);
             }
         }
     }
diff --git a/cas/aidl/default/service.cpp b/cas/aidl/default/service.cpp
index bed2f01..076c7bb 100644
--- a/cas/aidl/default/service.cpp
+++ b/cas/aidl/default/service.cpp
@@ -31,6 +31,7 @@
 
 int main() {
     ABinderProcess_setThreadPoolMaxThreadCount(8);
+    ABinderProcess_startThreadPool();
 
     // Setup hwbinder service
     std::shared_ptr<MediaCasService> service = ::ndk::SharedRefBase::make<MediaCasService>();
diff --git a/compatibility_matrices/Android.mk b/compatibility_matrices/Android.mk
index d356cf3..c2ffb84 100644
--- a/compatibility_matrices/Android.mk
+++ b/compatibility_matrices/Android.mk
@@ -106,9 +106,15 @@
     framework_compatibility_matrix.6.xml \
     framework_compatibility_matrix.7.xml \
     framework_compatibility_matrix.8.xml \
-    framework_compatibility_matrix.9.xml \
     framework_compatibility_matrix.device.xml \
 
+# Only allow the use of the unreleased compatibility matrix when we can use unfrozen
+# interfaces (in the `next` release configuration).
+ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
+my_system_matrix_deps += \
+    framework_compatibility_matrix.9.xml
+endif
+
 my_framework_matrix_deps += \
     $(my_system_matrix_deps)
 
diff --git a/compatibility_matrices/compatibility_matrix.8.xml b/compatibility_matrices/compatibility_matrix.8.xml
index 99dcdbb..9057788 100644
--- a/compatibility_matrices/compatibility_matrix.8.xml
+++ b/compatibility_matrices/compatibility_matrix.8.xml
@@ -52,7 +52,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="true">
+    <hal format="aidl" optional="true" updatable-via-apex="true">
          <name>android.hardware.authsecret</name>
          <version>1</version>
          <interface>
@@ -123,7 +123,7 @@
             <instance>virtual</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="true">
+    <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.biometrics.fingerprint</name>
         <version>3</version>
         <interface>
@@ -314,7 +314,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="true">
+    <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.security.keymint</name>
         <version>1-3</version>
         <interface>
@@ -323,7 +323,7 @@
             <instance>strongbox</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="true">
+    <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.security.keymint</name>
         <version>1-3</version>
         <interface>
@@ -532,7 +532,7 @@
             <regex-instance>SIM[1-9][0-9]*</regex-instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="true">
+    <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.security.secureclock</name>
         <version>1</version>
         <interface>
@@ -540,7 +540,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="true">
+    <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.security.sharedsecret</name>
         <version>1</version>
         <interface>
@@ -692,7 +692,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="aidl" optional="true">
+    <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.uwb</name>
         <version>1</version>
         <interface>
diff --git a/keymaster/4.0/support/fuzzer/Android.bp b/keymaster/4.0/support/fuzzer/Android.bp
index 8bc681a..f61d10f 100644
--- a/keymaster/4.0/support/fuzzer/Android.bp
+++ b/keymaster/4.0/support/fuzzer/Android.bp
@@ -39,9 +39,17 @@
     ],
     fuzz_config: {
         cc: [
-            "android-media-fuzzing-reports@google.com",
+            "android-hardware-security@google.com",
         ],
-        componentid: 533764,
+        componentid: 1084733,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libkeymaster4support",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
 
diff --git a/security/authgraph/aidl/vts/functional/lib.rs b/security/authgraph/aidl/vts/functional/lib.rs
index 7b9b2b9..da3fa1c 100644
--- a/security/authgraph/aidl/vts/functional/lib.rs
+++ b/security/authgraph/aidl/vts/functional/lib.rs
@@ -24,22 +24,19 @@
     PlainPubKey::PlainPubKey, PubKey::PubKey, SessionIdSignature::SessionIdSignature,
 };
 use authgraph_boringssl as boring;
-use authgraph_core::keyexchange as ke;
-use authgraph_core::{arc, key, traits};
-use authgraph_nonsecure::StdClock;
+use authgraph_core::{error::Error as AgError, keyexchange as ke};
 use coset::CborSerializable;
 
 pub mod sink;
 pub mod source;
 
-/// Return a collection of AuthGraph trait implementations suitable for testing.
-pub fn test_impls() -> traits::TraitImpl {
-    // Note that the local implementation is using a clock with a potentially different epoch than
-    // the implementation under test.
-    boring::trait_impls(
+/// Return an AuthGraphParticipant suitable for testing.
+pub fn test_ag_participant() -> Result<ke::AuthGraphParticipant, AgError> {
+    Ok(ke::AuthGraphParticipant::new(
+        boring::crypto_trait_impls(),
         Box::<boring::test_device::AgDevice>::default(),
-        Some(Box::new(StdClock::default())),
-    )
+        ke::MAX_OPENED_SESSIONS,
+    )?)
 }
 
 fn build_plain_pub_key(pub_key: &Option<Vec<u8>>) -> PubKey {
@@ -56,14 +53,6 @@
     }
 }
 
-fn verification_key_from_identity(impls: &traits::TraitImpl, identity: &[u8]) -> key::EcVerifyKey {
-    let identity = key::Identity::from_slice(identity).expect("invalid identity CBOR");
-    impls
-        .device
-        .process_peer_cert_chain(&identity.cert_chain, &*impls.ecdsa)
-        .expect("failed to extract signing key")
-}
-
 fn vec_to_identity(data: &[u8]) -> Identity {
     Identity {
         identity: data.to_vec(),
@@ -75,26 +64,3 @@
         signature: data.to_vec(),
     }
 }
-
-/// Decrypt a pair of AES-256 keys encrypted with the AuthGraph PBK.
-pub fn decipher_aes_keys(imp: &traits::TraitImpl, arc: &[Vec<u8>; 2]) -> [key::AesKey; 2] {
-    [
-        decipher_aes_key(imp, &arc[0]),
-        decipher_aes_key(imp, &arc[1]),
-    ]
-}
-
-/// Decrypt an AES-256 key encrypted with the AuthGraph PBK.
-pub fn decipher_aes_key(imp: &traits::TraitImpl, arc: &[u8]) -> key::AesKey {
-    let pbk = imp.device.get_per_boot_key().expect("no PBK available");
-    let arc::ArcContent {
-        payload,
-        protected_headers: _,
-        unprotected_headers: _,
-    } = arc::decipher_arc(&pbk, arc, &*imp.aes_gcm).expect("failed to decrypt arc");
-    assert_eq!(payload.0.len(), 32);
-    let mut key = key::AesKey([0; 32]);
-    key.0.copy_from_slice(&payload.0);
-    assert_ne!(key.0, [0; 32], "agreed AES-256 key should be non-zero");
-    key
-}
diff --git a/security/authgraph/aidl/vts/functional/role_test.rs b/security/authgraph/aidl/vts/functional/role_test.rs
index e95361a..71a2fae 100644
--- a/security/authgraph/aidl/vts/functional/role_test.rs
+++ b/security/authgraph/aidl/vts/functional/role_test.rs
@@ -48,31 +48,31 @@
 
 #[test]
 fn test_nonsecure_source_mainline() {
-    let mut impls = vts::test_impls();
-    vts::source::test_mainline(&mut impls, require_nonsecure!());
+    let mut sink = vts::test_ag_participant().expect("failed to create a local sink");
+    vts::source::test_mainline(&mut sink, require_nonsecure!());
 }
 #[test]
 fn test_nonsecure_source_corrupt_sig() {
-    let mut impls = vts::test_impls();
-    vts::source::test_corrupt_sig(&mut impls, require_nonsecure!());
+    let mut sink = vts::test_ag_participant().expect("failed to create a local sink");
+    vts::source::test_corrupt_sig(&mut sink, require_nonsecure!());
 }
 #[test]
 fn test_nonsecure_source_corrupt_keys() {
-    let mut impls = vts::test_impls();
-    vts::source::test_corrupt_key(&mut impls, require_nonsecure!());
+    let mut sink = vts::test_ag_participant().expect("failed to create a local sink");
+    vts::source::test_corrupt_key(&mut sink, require_nonsecure!());
 }
 #[test]
 fn test_nonsecure_sink_mainline() {
-    let mut impls = vts::test_impls();
-    vts::sink::test_mainline(&mut impls, require_nonsecure!());
+    let mut source = vts::test_ag_participant().expect("failed to create a local source");
+    vts::sink::test_mainline(&mut source, require_nonsecure!());
 }
 #[test]
 fn test_nonsecure_sink_corrupt_sig() {
-    let mut impls = vts::test_impls();
-    vts::sink::test_corrupt_sig(&mut impls, require_nonsecure!());
+    let mut source = vts::test_ag_participant().expect("failed to create a local source");
+    vts::sink::test_corrupt_sig(&mut source, require_nonsecure!());
 }
 #[test]
 fn test_nonsecure_sink_corrupt_keys() {
-    let mut impls = vts::test_impls();
-    vts::sink::test_corrupt_keys(&mut impls, require_nonsecure!());
+    let mut source = vts::test_ag_participant().expect("failed to create a local source");
+    vts::sink::test_corrupt_keys(&mut source, require_nonsecure!());
 }
diff --git a/security/authgraph/aidl/vts/functional/sink.rs b/security/authgraph/aidl/vts/functional/sink.rs
index 5c81593..bb357b8 100644
--- a/security/authgraph/aidl/vts/functional/sink.rs
+++ b/security/authgraph/aidl/vts/functional/sink.rs
@@ -16,23 +16,28 @@
 
 //! VTS tests for sinks
 use super::*;
-use authgraph_core::traits;
+use authgraph_core::{key, keyexchange as ke};
 
 /// Run AuthGraph tests against the provided sink, using a local test source implementation.
-pub fn test(impls: &mut traits::TraitImpl, sink: binder::Strong<dyn IAuthGraphKeyExchange>) {
-    test_mainline(impls, sink.clone());
-    test_corrupt_sig(impls, sink.clone());
-    test_corrupt_keys(impls, sink);
+pub fn test(
+    local_source: &mut ke::AuthGraphParticipant,
+    sink: binder::Strong<dyn IAuthGraphKeyExchange>,
+) {
+    test_mainline(local_source, sink.clone());
+    test_corrupt_sig(local_source, sink.clone());
+    test_corrupt_keys(local_source, sink);
 }
 
 /// Perform mainline AuthGraph key exchange with the provided sink and local implementation.
 /// Return the agreed AES keys in plaintext.
 pub fn test_mainline(
-    impls: &mut traits::TraitImpl,
+    local_source: &mut ke::AuthGraphParticipant,
     sink: binder::Strong<dyn IAuthGraphKeyExchange>,
 ) -> [key::AesKey; 2] {
     // Step 1: create an ephemeral ECDH key at the (local) source.
-    let source_init_info = ke::create(impls).expect("failed to create() with local impl");
+    let source_init_info = local_source
+        .create()
+        .expect("failed to create() with local impl");
 
     // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
     let init_result = sink
@@ -50,40 +55,43 @@
     assert!(!sink_info.sessionId.is_empty());
 
     // The AuthGraph core library will verify the session ID signature, but do it here too.
-    let sink_verification_key =
-        verification_key_from_identity(&impls, &sink_init_info.identity.identity);
-    ke::verify_signature_on_session_id(
-        &sink_verification_key,
-        &sink_info.sessionId,
-        &sink_info.signature.signature,
-        &*impls.ecdsa,
-    )
-    .expect("failed verification of signed session ID");
+    let sink_verification_key = local_source
+        .peer_verification_key_from_identity(&sink_init_info.identity.identity)
+        .expect("failed to get peer verification from identity");
+    local_source
+        .verify_signature_on_session_id(
+            &sink_verification_key,
+            &sink_info.sessionId,
+            &sink_info.signature.signature,
+        )
+        .expect("failed verification of signed session ID");
 
     // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
     // can calculate the same pair of symmetric keys.
-    let source_info = ke::finish(
-        impls,
-        &sink_pub_key.plainPubKey,
-        &sink_init_info.identity.identity,
-        &sink_info.signature.signature,
-        &sink_init_info.nonce,
-        sink_init_info.version,
-        source_init_info.ke_key,
-    )
-    .expect("failed to finish() with local impl");
+    let source_info = local_source
+        .finish(
+            &sink_pub_key.plainPubKey,
+            &sink_init_info.identity.identity,
+            &sink_info.signature.signature,
+            &sink_init_info.nonce,
+            sink_init_info.version,
+            source_init_info.ke_key,
+        )
+        .expect("failed to finish() with local impl");
     assert!(!source_info.session_id.is_empty());
 
     // The AuthGraph core library will verify the session ID signature, but do it here too.
-    let source_verification_key =
-        verification_key_from_identity(&impls, &source_init_info.identity);
-    ke::verify_signature_on_session_id(
-        &source_verification_key,
-        &source_info.session_id,
-        &source_info.session_id_signature,
-        &*impls.ecdsa,
-    )
-    .expect("failed verification of signed session ID");
+    let source_verification_key = key::Identity::from_slice(&source_init_info.identity)
+        .expect("invalid identity CBOR")
+        .cert_chain
+        .root_key;
+    local_source
+        .verify_signature_on_session_id(
+            &source_verification_key,
+            &source_info.session_id,
+            &source_info.session_id_signature,
+        )
+        .expect("failed verification of signed session ID");
 
     // Both ends should agree on the session ID.
     assert_eq!(source_info.session_id, sink_info.sessionId);
@@ -96,19 +104,28 @@
             &sink_info.sharedKeys,
         )
         .expect("failed to authenticationComplete() with remote sink");
-
     // Decrypt and return the session keys.
-    decipher_aes_keys(&impls, &source_info.shared_keys)
+    let decrypted_shared_keys = local_source
+        .decipher_shared_keys_from_arcs(&source_info.shared_keys)
+        .expect("failed to decrypt shared key arcs")
+        .try_into();
+    let decrypted_shared_keys_array = match decrypted_shared_keys {
+        Ok(array) => array,
+        Err(_) => panic!("wrong number of decrypted shared key arcs"),
+    };
+    decrypted_shared_keys_array
 }
 
 /// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid
 /// session ID signature.
 pub fn test_corrupt_sig(
-    impls: &mut traits::TraitImpl,
+    local_source: &mut ke::AuthGraphParticipant,
     sink: binder::Strong<dyn IAuthGraphKeyExchange>,
 ) {
     // Step 1: create an ephemeral ECDH key at the (local) source.
-    let source_init_info = ke::create(impls).expect("failed to create() with local impl");
+    let source_init_info = local_source
+        .create()
+        .expect("failed to create() with local impl");
 
     // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
     let init_result = sink
@@ -127,16 +144,16 @@
 
     // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
     // can calculate the same pair of symmetric keys.
-    let source_info = ke::finish(
-        impls,
-        &sink_pub_key.plainPubKey,
-        &sink_init_info.identity.identity,
-        &sink_info.signature.signature,
-        &sink_init_info.nonce,
-        sink_init_info.version,
-        source_init_info.ke_key,
-    )
-    .expect("failed to finish() with local impl");
+    let source_info = local_source
+        .finish(
+            &sink_pub_key.plainPubKey,
+            &sink_init_info.identity.identity,
+            &sink_info.signature.signature,
+            &sink_init_info.nonce,
+            sink_init_info.version,
+            source_init_info.ke_key,
+        )
+        .expect("failed to finish() with local impl");
     assert!(!source_info.session_id.is_empty());
 
     // Build a corrupted version of the (local) source's session ID signature.
@@ -158,11 +175,13 @@
 /// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid
 /// Arc for the sink's key.
 pub fn test_corrupt_keys(
-    impls: &mut traits::TraitImpl,
+    local_source: &mut ke::AuthGraphParticipant,
     sink: binder::Strong<dyn IAuthGraphKeyExchange>,
 ) {
     // Step 1: create an ephemeral ECDH key at the (local) source.
-    let source_init_info = ke::create(impls).expect("failed to create() with local impl");
+    let source_init_info = local_source
+        .create()
+        .expect("failed to create() with local impl");
 
     // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
     let init_result = sink
@@ -181,16 +200,16 @@
 
     // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
     // can calculate the same pair of symmetric keys.
-    let source_info = ke::finish(
-        impls,
-        &sink_pub_key.plainPubKey,
-        &sink_init_info.identity.identity,
-        &sink_info.signature.signature,
-        &sink_init_info.nonce,
-        sink_init_info.version,
-        source_init_info.ke_key,
-    )
-    .expect("failed to finish() with local impl");
+    let source_info = local_source
+        .finish(
+            &sink_pub_key.plainPubKey,
+            &sink_init_info.identity.identity,
+            &sink_info.signature.signature,
+            &sink_init_info.nonce,
+            sink_init_info.version,
+            source_init_info.ke_key,
+        )
+        .expect("failed to finish() with local impl");
     assert!(!source_info.session_id.is_empty());
 
     // Deliberately corrupt the sink's shared key Arcs before returning them
diff --git a/security/authgraph/aidl/vts/functional/source.rs b/security/authgraph/aidl/vts/functional/source.rs
index 9aaaaee..4178a99 100644
--- a/security/authgraph/aidl/vts/functional/source.rs
+++ b/security/authgraph/aidl/vts/functional/source.rs
@@ -16,19 +16,22 @@
 
 //! VTS tests for sources
 use super::*;
-use authgraph_core::traits;
+use authgraph_core::{key, keyexchange as ke};
 
 /// Run AuthGraph tests against the provided source, using a local test sink implementation.
-pub fn test(impls: &mut traits::TraitImpl, source: binder::Strong<dyn IAuthGraphKeyExchange>) {
-    test_mainline(impls, source.clone());
-    test_corrupt_sig(impls, source.clone());
-    test_corrupt_key(impls, source);
+pub fn test(
+    local_sink: &mut ke::AuthGraphParticipant,
+    source: binder::Strong<dyn IAuthGraphKeyExchange>,
+) {
+    test_mainline(local_sink, source.clone());
+    test_corrupt_sig(local_sink, source.clone());
+    test_corrupt_key(local_sink, source);
 }
 
 /// Perform mainline AuthGraph key exchange with the provided source.
 /// Return the agreed AES keys in plaintext.
 pub fn test_mainline(
-    impls: &mut traits::TraitImpl,
+    local_sink: &mut ke::AuthGraphParticipant,
     source: binder::Strong<dyn IAuthGraphKeyExchange>,
 ) -> [key::AesKey; 2] {
     // Step 1: create an ephemeral ECDH key at the (remote) source.
@@ -40,14 +43,14 @@
     let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey);
 
     // Step 2: pass the source's ECDH public key and other session info to the (local) sink.
-    let init_result = ke::init(
-        impls,
-        &source_pub_key.plainPubKey,
-        &source_init_info.identity.identity,
-        &source_init_info.nonce,
-        source_init_info.version,
-    )
-    .expect("failed to init() with local impl");
+    let init_result = local_sink
+        .init(
+            &source_pub_key.plainPubKey,
+            &source_init_info.identity.identity,
+            &source_init_info.nonce,
+            source_init_info.version,
+        )
+        .expect("failed to init() with local impl");
     let sink_init_info = init_result.session_init_info;
     let sink_pub_key = sink_init_info
         .ke_key
@@ -58,14 +61,17 @@
     assert!(!sink_info.session_id.is_empty());
 
     // The AuthGraph core library will verify the session ID signature, but do it here too.
-    let sink_verification_key = verification_key_from_identity(&impls, &sink_init_info.identity);
-    ke::verify_signature_on_session_id(
-        &sink_verification_key,
-        &sink_info.session_id,
-        &sink_info.session_id_signature,
-        &*impls.ecdsa,
-    )
-    .expect("failed verification of signed session ID");
+    let sink_verification_key = key::Identity::from_slice(&sink_init_info.identity)
+        .expect("invalid identity CBOR")
+        .cert_chain
+        .root_key;
+    local_sink
+        .verify_signature_on_session_id(
+            &sink_verification_key,
+            &sink_info.session_id,
+            &sink_info.session_id_signature,
+        )
+        .expect("failed verification of signed session ID");
 
     // Step 3: pass the sink's ECDH public key and other session info to the (remote) source, so it
     // can calculate the same pair of symmetric keys.
@@ -86,36 +92,41 @@
     assert!(!source_info.sessionId.is_empty());
 
     // The AuthGraph core library will verify the session ID signature, but do it here too.
-    let source_verification_key =
-        verification_key_from_identity(&impls, &source_init_info.identity.identity);
-    ke::verify_signature_on_session_id(
-        &source_verification_key,
-        &source_info.sessionId,
-        &source_info.signature.signature,
-        &*impls.ecdsa,
-    )
-    .expect("failed verification of signed session ID");
+    let source_verification_key = local_sink
+        .peer_verification_key_from_identity(&source_init_info.identity.identity)
+        .expect("failed to get peer verification from identity");
+    local_sink
+        .verify_signature_on_session_id(
+            &source_verification_key,
+            &source_info.sessionId,
+            &source_info.signature.signature,
+        )
+        .expect("failed verification of signed session ID");
 
     // Both ends should agree on the session ID.
     assert_eq!(source_info.sessionId, sink_info.session_id);
 
     // Step 4: pass the (remote) source's session ID signature back to the sink, so it can check it
     // and update the symmetric keys so they're marked as authentication complete.
-    let sink_arcs = ke::authentication_complete(
-        impls,
-        &source_info.signature.signature,
-        sink_info.shared_keys,
-    )
-    .expect("failed to authenticationComplete() with local sink");
-
+    let sink_arcs = local_sink
+        .authentication_complete(&source_info.signature.signature, sink_info.shared_keys)
+        .expect("failed to authenticationComplete() with local sink");
     // Decrypt and return the session keys.
-    decipher_aes_keys(&impls, &sink_arcs)
+    let decrypted_shared_keys = local_sink
+        .decipher_shared_keys_from_arcs(&sink_arcs)
+        .expect("failed to decrypt shared key arcs")
+        .try_into();
+    let decrypted_shared_keys_array = match decrypted_shared_keys {
+        Ok(array) => array,
+        Err(_) => panic!("wrong number of decrypted shared key arcs"),
+    };
+    decrypted_shared_keys_array
 }
 
 /// Perform mainline AuthGraph key exchange with the provided source, but provide an invalid session
 /// ID signature.
 pub fn test_corrupt_sig(
-    impls: &mut traits::TraitImpl,
+    local_sink: &mut ke::AuthGraphParticipant,
     source: binder::Strong<dyn IAuthGraphKeyExchange>,
 ) {
     // Step 1: create an ephemeral ECDH key at the (remote) source.
@@ -127,14 +138,14 @@
     let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey);
 
     // Step 2: pass the source's ECDH public key and other session info to the (local) sink.
-    let init_result = ke::init(
-        impls,
-        &source_pub_key.plainPubKey,
-        &source_init_info.identity.identity,
-        &source_init_info.nonce,
-        source_init_info.version,
-    )
-    .expect("failed to init() with local impl");
+    let init_result = local_sink
+        .init(
+            &source_pub_key.plainPubKey,
+            &source_init_info.identity.identity,
+            &source_init_info.nonce,
+            source_init_info.version,
+        )
+        .expect("failed to init() with local impl");
     let sink_init_info = init_result.session_init_info;
     let sink_pub_key = sink_init_info
         .ke_key
@@ -172,7 +183,7 @@
 /// Perform mainline AuthGraph key exchange with the provided source, but give it back
 /// a corrupted key.
 pub fn test_corrupt_key(
-    impls: &mut traits::TraitImpl,
+    local_sink: &mut ke::AuthGraphParticipant,
     source: binder::Strong<dyn IAuthGraphKeyExchange>,
 ) {
     // Step 1: create an ephemeral ECDH key at the (remote) source.
@@ -184,14 +195,14 @@
     let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey);
 
     // Step 2: pass the source's ECDH public key and other session info to the (local) sink.
-    let init_result = ke::init(
-        impls,
-        &source_pub_key.plainPubKey,
-        &source_init_info.identity.identity,
-        &source_init_info.nonce,
-        source_init_info.version,
-    )
-    .expect("failed to init() with local impl");
+    let init_result = local_sink
+        .init(
+            &source_pub_key.plainPubKey,
+            &source_init_info.identity.identity,
+            &source_init_info.nonce,
+            source_init_info.version,
+        )
+        .expect("failed to init() with local impl");
     let sink_init_info = init_result.session_init_info;
     let sink_pub_key = sink_init_info
         .ke_key
@@ -202,14 +213,17 @@
     assert!(!sink_info.session_id.is_empty());
 
     // The AuthGraph core library will verify the session ID signature, but do it here too.
-    let sink_verification_key = verification_key_from_identity(&impls, &sink_init_info.identity);
-    ke::verify_signature_on_session_id(
-        &sink_verification_key,
-        &sink_info.session_id,
-        &sink_info.session_id_signature,
-        &*impls.ecdsa,
-    )
-    .expect("failed verification of signed session ID");
+    let sink_verification_key = key::Identity::from_slice(&sink_init_info.identity)
+        .expect("invalid identity CBOR")
+        .cert_chain
+        .root_key;
+    local_sink
+        .verify_signature_on_session_id(
+            &sink_verification_key,
+            &sink_info.session_id,
+            &sink_info.session_id_signature,
+        )
+        .expect("failed verification of signed session ID");
 
     // Deliberately corrupt the source's encrypted key.
     let mut corrupt_key = source_init_info.key.clone();
diff --git a/security/authgraph/default/src/fuzzer.rs b/security/authgraph/default/src/fuzzer.rs
index 6a9cfdd..d401777 100644
--- a/security/authgraph/default/src/fuzzer.rs
+++ b/security/authgraph/default/src/fuzzer.rs
@@ -25,7 +25,7 @@
 use std::sync::{Arc, Mutex};
 
 fuzz_target!(|data: &[u8]| {
-    let local_ta = LocalTa::new();
+    let local_ta = LocalTa::new().expect("Failed to create an AuthGraph local TA.");
     let service = AuthGraphService::new_as_binder(Arc::new(Mutex::new(local_ta)));
     fuzz_service(&mut service.as_binder(), data);
 });
diff --git a/security/authgraph/default/src/lib.rs b/security/authgraph/default/src/lib.rs
index 4cd0cb7..43d037c 100644
--- a/security/authgraph/default/src/lib.rs
+++ b/security/authgraph/default/src/lib.rs
@@ -18,7 +18,9 @@
 
 use authgraph_boringssl as boring;
 use authgraph_core::{
+    error,
     key::MillisecondsSinceEpoch,
+    keyexchange,
     ta::{AuthGraphTa, Role},
     traits,
 };
@@ -57,16 +59,17 @@
 
 impl LocalTa {
     /// Create a new instance.
-    pub fn new() -> Self {
-        Self {
+    pub fn new() -> Result<Self, error::Error> {
+        Ok(Self {
             ta: Arc::new(Mutex::new(AuthGraphTa::new(
-                boring::trait_impls(
+                keyexchange::AuthGraphParticipant::new(
+                    boring::crypto_trait_impls(),
                     Box::<boring::test_device::AgDevice>::default(),
-                    Some(Box::new(StdClock::default())),
-                ),
+                    keyexchange::MAX_OPENED_SESSIONS,
+                )?,
                 Role::Both,
             ))),
-        }
+        })
     }
 }
 
diff --git a/security/authgraph/default/src/main.rs b/security/authgraph/default/src/main.rs
index 873eb4e..81f2dd6 100644
--- a/security/authgraph/default/src/main.rs
+++ b/security/authgraph/default/src/main.rs
@@ -65,7 +65,8 @@
     binder::ProcessState::start_thread_pool();
 
     // Register the service
-    let local_ta = LocalTa::new();
+    let local_ta =
+        LocalTa::new().map_err(|e| format!("Failed to create the TA because: {e:?}"))?;
     let service = service::AuthGraphService::new_as_binder(Arc::new(Mutex::new(local_ta)));
     let service_name = format!("{}/{}", SERVICE_NAME, SERVICE_INSTANCE);
     binder::add_service(&service_name, service.as_binder()).map_err(|e| {
diff --git a/tests/multithread/1.0/default/Multithread.h b/tests/multithread/1.0/default/Multithread.h
index 0d4a007..7df75cc 100644
--- a/tests/multithread/1.0/default/Multithread.h
+++ b/tests/multithread/1.0/default/Multithread.h
@@ -33,7 +33,7 @@
     std::condition_variable mCv;
     std::mutex mCvMutex;
 
-    static constexpr auto kTimeoutDuration = 100ms;
+    static constexpr auto kTimeoutDuration = 1000ms;
 };
 
 extern "C" IMultithread* HIDL_FETCH_IMultithread(const char* name);