diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 8ebd078c50bdad034be2d5a3ef2134415e6cb7ce..5113ccf4b6ef951c1d4e3862f9cd7903a73b4570 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -648,6 +648,77 @@ prebuilt_etc {
 
 // bta unit tests for LE Audio
 // ========================================================
+cc_test {
+    name: "bluetooth_le_audio_codec_manager_test",
+    test_suites: ["device-tests"],
+    defaults: [
+        "bluetooth_gtest_x86_asan_workaround",
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        android: {
+            sanitize: {
+                misc_undefined: ["bounds"],
+            },
+            whole_static_libs: [
+                "libPlatformProperties",
+            ],
+        },
+    },
+    include_dirs: [
+        "packages/modules/Bluetooth/system",
+        "packages/modules/Bluetooth/system/bta/include",
+        "packages/modules/Bluetooth/system/bta/test/common",
+        "packages/modules/Bluetooth/system/btif/include",
+        "packages/modules/Bluetooth/system/gd",
+        "packages/modules/Bluetooth/system/stack/include",
+    ],
+    srcs: [
+        ":TestCommonMockFunctions",
+        ":TestMockBtaLeAudioHalVerifier",
+        ":TestStubOsi",
+        "le_audio/codec_manager.cc",
+        "le_audio/codec_manager_test.cc",
+        "le_audio/le_audio_set_configuration_provider_json.cc",
+        "le_audio/le_audio_types.cc",
+        "test/common/btm_api_mock.cc",
+        "test/common/mock_controller.cc",
+    ],
+    data: [
+        ":audio_set_configurations_bfbs",
+        ":audio_set_configurations_json",
+        ":audio_set_scenarios_bfbs",
+        ":audio_set_scenarios_json",
+    ],
+    generated_headers: [
+        "LeAudioSetConfigSchemas_h",
+    ],
+    shared_libs: [
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hardware.bluetooth.audio@2.1",
+        "libcrypto",
+        "libhidlbase",
+        "liblog", // __android_log_print
+    ],
+    static_libs: [
+        "libbluetooth_gd",
+        "libbt-common",
+        "libchrome",
+        "libevent",
+        "libflatbuffers-cpp",
+        "libgmock",
+        "libosi",
+    ],
+    sanitize: {
+        cfi: false,
+    },
+}
+
 cc_test {
     name: "bluetooth_le_audio_test",
     test_suites: ["device-tests"],
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index c41248fbe47397393ffb03d36b079dd1d2b082d8..19f7e409b88ad6b7bfcf7b1c8cd5264192e37460 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -3398,12 +3398,16 @@ class LeAudioClientImpl : public LeAudioClient {
     ConfirmLocalAudioSourceStreamingRequest();
 
     if (!LeAudioHalVerifier::SupportsStreamActiveApi()) {
-      /* We update the target audio allocation before streamStarted that the
-       * offloder would know how to configure offloader encoder. We should check
-       * if we need to update the current
-       * allocation here as the target allocation and the current allocation is
-       * different */
-      updateOffloaderIfNeeded(group);
+      /* We update the target audio allocation before streamStarted so that the
+       * CodecManager would know how to configure the encoder. */
+      BidirectionalPair<uint16_t> delays_pair = {
+          .sink = group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink),
+          .source =
+              group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource)};
+      CodecManager::GetInstance()->UpdateActiveAudioConfig(
+          group->stream_conf.stream_params, delays_pair,
+          std::bind(&LeAudioSourceAudioHalClient::UpdateAudioConfigToHal,
+                    le_audio_source_hal_client_.get(), std::placeholders::_1));
     }
 
     return true;
@@ -3464,12 +3468,16 @@ class LeAudioClientImpl : public LeAudioClient {
     ConfirmLocalAudioSinkStreamingRequest();
 
     if (!LeAudioHalVerifier::SupportsStreamActiveApi()) {
-      /* We update the target audio allocation before streamStarted that the
-       * offloder would know how to configure offloader encoder. We should check
-       * if we need to update the current
-       * allocation here as the target allocation and the current allocation is
-       * different */
-      updateOffloaderIfNeeded(group);
+      /* We update the target audio allocation before streamStarted so that the
+       * CodecManager would know how to configure the encoder. */
+      BidirectionalPair<uint16_t> delays_pair = {
+          .sink = group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink),
+          .source =
+              group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource)};
+      CodecManager::GetInstance()->UpdateActiveAudioConfig(
+          group->stream_conf.stream_params, delays_pair,
+          std::bind(&LeAudioSourceAudioHalClient::UpdateAudioConfigToHal,
+                    le_audio_source_hal_client_.get(), std::placeholders::_1));
     }
   }
 
@@ -5018,41 +5026,6 @@ class LeAudioClientImpl : public LeAudioClient {
     }
   }
 
-  void updateOffloaderIfNeeded(LeAudioDeviceGroup* group) {
-    if (CodecManager::GetInstance()->GetCodecLocation() !=
-        le_audio::types::CodecLocation::ADSP) {
-      return;
-    }
-
-    LOG_INFO("Group %p, group_id %d", group, group->group_id_);
-
-    const auto* stream_conf = &group->stream_conf;
-
-    if (stream_conf->offloader_config.sink.has_changed ||
-        stream_conf->offloader_config.sink.is_initial) {
-      LOG_INFO("Update sink offloader streams");
-      uint16_t remote_delay_ms =
-          group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink);
-      CodecManager::GetInstance()->UpdateActiveSourceAudioConfig(
-          *stream_conf, remote_delay_ms,
-          std::bind(&LeAudioSourceAudioHalClient::UpdateAudioConfigToHal,
-                    le_audio_source_hal_client_.get(), std::placeholders::_1));
-      group->StreamOffloaderUpdated(le_audio::types::kLeAudioDirectionSink);
-    }
-
-    if (stream_conf->offloader_config.source.has_changed ||
-        stream_conf->offloader_config.source.is_initial) {
-      LOG_INFO("Update source offloader streams");
-      uint16_t remote_delay_ms =
-          group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource);
-      CodecManager::GetInstance()->UpdateActiveSinkAudioConfig(
-          *stream_conf, remote_delay_ms,
-          std::bind(&LeAudioSinkAudioHalClient::UpdateAudioConfigToHal,
-                    le_audio_sink_hal_client_.get(), std::placeholders::_1));
-      group->StreamOffloaderUpdated(le_audio::types::kLeAudioDirectionSource);
-    }
-  }
-
   void NotifyUpperLayerGroupTurnedIdleDuringCall(int group_id) {
     if (!osi_property_get_bool(kNotifyUpperLayerAboutGroupBeingInIdleDuringCall,
                                false)) {
@@ -5118,7 +5091,16 @@ class LeAudioClientImpl : public LeAudioClient {
         }
 
         if (group) {
-          updateOffloaderIfNeeded(group);
+          BidirectionalPair<uint16_t> delays_pair = {
+              .sink =
+                  group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink),
+              .source = group->GetRemoteDelay(
+                  le_audio::types::kLeAudioDirectionSource)};
+          CodecManager::GetInstance()->UpdateActiveAudioConfig(
+              group->stream_conf.stream_params, delays_pair,
+              std::bind(&LeAudioSourceAudioHalClient::UpdateAudioConfigToHal,
+                        le_audio_source_hal_client_.get(),
+                        std::placeholders::_1));
           if (reconnection_mode_ ==
               BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS) {
             group->AddToAllowListNotConnectedGroupMembers(gatt_if_);
@@ -5228,6 +5210,17 @@ class LeAudioClientImpl : public LeAudioClient {
     }
   }
 
+  void OnUpdatedCisConfiguration(int group_id, uint8_t direction) {
+    LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
+    if (!group) {
+      LOG_ERROR("Invalid group_id: %d", group_id);
+      return;
+    }
+    CodecManager::GetInstance()->UpdateCisConfiguration(
+        group->cises_, group->stream_conf.stream_params.get(direction),
+        direction);
+  }
+
  private:
   tGATT_IF gatt_if_;
   bluetooth::le_audio::LeAudioClientCallbacks* callbacks_;
@@ -5445,6 +5438,10 @@ class CallbacksImpl : public LeAudioGroupStateMachine::Callbacks {
   void OnStateTransitionTimeout(int group_id) override {
     if (instance) instance->OnLeAudioDeviceSetStateTimeout(group_id);
   }
+
+  void OnUpdatedCisConfiguration(int group_id, uint8_t direction) {
+    if (instance) instance->OnUpdatedCisConfiguration(group_id, direction);
+  }
 };
 
 CallbacksImpl stateMachineCallbacksImpl;
diff --git a/system/bta/le_audio/codec_manager.cc b/system/bta/le_audio/codec_manager.cc
index dfe11ab401cd882d5d3602a82485000f32b6c1e4..0e13b12a4d4eb26caee4e6874079baa10252bdbf 100644
--- a/system/bta/le_audio/codec_manager.cc
+++ b/system/bta/le_audio/codec_manager.cc
@@ -39,9 +39,24 @@ using le_audio::set_configurations::AudioSetConfiguration;
 using le_audio::set_configurations::AudioSetConfigurations;
 using le_audio::set_configurations::SetConfiguration;
 
+typedef struct offloader_stream_maps {
+  std::vector<le_audio::stream_map_info> streams_map_target;
+  std::vector<le_audio::stream_map_info> streams_map_current;
+  bool has_changed;
+  bool is_initial;
+} offloader_stream_maps_t;
 }  // namespace
 
 namespace le_audio {
+template <>
+offloader_stream_maps_t& types::BidirectionalPair<offloader_stream_maps_t>::get(
+    uint8_t direction) {
+  ASSERT_LOG(direction < types::kLeAudioDirectionBoth,
+             "Unsupported complex direction. Reference to a single complex"
+             " direction value is not supported.");
+  return (direction == types::kLeAudioDirectionSink) ? sink : source;
+}
+
 // The mapping for sampling rate, frame duration, and the QoS config
 static std::unordered_map<
     int, std::unordered_map<int, le_audio::broadcaster::BroadcastQosConfig>>
@@ -112,62 +127,43 @@ struct codec_manager_impl {
   }
   CodecLocation GetCodecLocation(void) const { return codec_location_; }
 
-  void UpdateActiveSourceAudioConfig(
-      const le_audio::stream_configuration& stream_conf, uint16_t delay_ms,
-      std::function<void(const ::le_audio::offload_config& config)>
-          update_receiver) {
-    if (stream_conf.stream_params.sink.stream_locations.empty()) return;
-
-    if (stream_conf.offloader_config.sink.is_initial ||
-        LeAudioHalVerifier::SupportsStreamActiveApi()) {
-      sink_config.stream_map =
-          stream_conf.offloader_config.sink.streams_target_allocation;
-    } else {
-      sink_config.stream_map =
-          stream_conf.offloader_config.sink.streams_current_allocation;
+  void UpdateActiveAudioConfig(
+      const types::BidirectionalPair<stream_parameters>& stream_params,
+      types::BidirectionalPair<uint16_t> delays_ms,
+      std::function<void(const offload_config& config)> update_receiver) {
+    if (GetCodecLocation() != le_audio::types::CodecLocation::ADSP) {
+      return;
     }
-    // TODO: set the default value 16 for now, would change it if we support
-    // mode bits_per_sample
-    sink_config.bits_per_sample = 16;
-    sink_config.sampling_rate =
-        stream_conf.stream_params.sink.sample_frequency_hz;
-    sink_config.frame_duration =
-        stream_conf.stream_params.sink.frame_duration_us;
-    sink_config.octets_per_frame =
-        stream_conf.stream_params.sink.octets_per_codec_frame;
-    sink_config.blocks_per_sdu =
-        stream_conf.stream_params.sink.codec_frames_blocks_per_sdu;
-    sink_config.peer_delay_ms = delay_ms;
-    update_receiver(sink_config);
-  }
 
-  void UpdateActiveSinkAudioConfig(
-      const le_audio::stream_configuration& stream_conf, uint16_t delay_ms,
-      std::function<void(const ::le_audio::offload_config& config)>
-          update_receiver) {
-    if (stream_conf.stream_params.source.stream_locations.empty()) return;
-
-    if (stream_conf.offloader_config.source.is_initial ||
-        LeAudioHalVerifier::SupportsStreamActiveApi()) {
-      source_config.stream_map =
-          stream_conf.offloader_config.source.streams_target_allocation;
-    } else {
-      source_config.stream_map =
-          stream_conf.offloader_config.source.streams_current_allocation;
+    for (auto direction : {le_audio::types::kLeAudioDirectionSink,
+                           le_audio::types::kLeAudioDirectionSource}) {
+      auto& stream_map = offloader_stream_maps.get(direction);
+      if (!stream_map.has_changed && !stream_map.is_initial) {
+        continue;
+      }
+      if (stream_params.get(direction).stream_locations.empty()) {
+        continue;
+      }
+
+      le_audio::offload_config unicast_cfg = {
+          .stream_map = (stream_map.is_initial ||
+                         LeAudioHalVerifier::SupportsStreamActiveApi())
+                            ? stream_map.streams_map_target
+                            : stream_map.streams_map_current,
+          // TODO: set the default value 16 for now, would change it if we
+          // support mode bits_per_sample
+          .bits_per_sample = 16,
+          .sampling_rate = stream_params.get(direction).sample_frequency_hz,
+          .frame_duration = stream_params.get(direction).frame_duration_us,
+          .octets_per_frame =
+              stream_params.get(direction).octets_per_codec_frame,
+          .blocks_per_sdu =
+              stream_params.get(direction).codec_frames_blocks_per_sdu,
+          .peer_delay_ms = delays_ms.get(direction),
+      };
+      update_receiver(unicast_cfg);
+      stream_map.is_initial = false;
     }
-    // TODO: set the default value 16 for now, would change it if we support
-    // mode bits_per_sample
-    source_config.bits_per_sample = 16;
-    source_config.sampling_rate =
-        stream_conf.stream_params.source.sample_frequency_hz;
-    source_config.frame_duration =
-        stream_conf.stream_params.source.frame_duration_us;
-    source_config.octets_per_frame =
-        stream_conf.stream_params.source.octets_per_codec_frame;
-    source_config.blocks_per_sdu =
-        stream_conf.stream_params.source.codec_frames_blocks_per_sdu;
-    source_config.peer_delay_ms = delay_ms;
-    update_receiver(source_config);
   }
 
   const AudioSetConfigurations* GetOffloadCodecConfig(
@@ -268,6 +264,119 @@ struct codec_manager_impl {
     update_receiver(broadcast_config);
   }
 
+  void ClearCisConfiguration(uint8_t direction) {
+    if (GetCodecLocation() != le_audio::types::CodecLocation::ADSP) {
+      return;
+    }
+
+    auto& stream_map = offloader_stream_maps.get(direction);
+    stream_map.streams_map_target.clear();
+    stream_map.streams_map_current.clear();
+  }
+
+  static uint32_t AdjustAllocationForOffloader(uint32_t allocation) {
+    if ((allocation & codec_spec_conf::kLeAudioLocationAnyLeft) &&
+        (allocation & codec_spec_conf::kLeAudioLocationAnyRight)) {
+      return codec_spec_conf::kLeAudioLocationStereo;
+    }
+    if (allocation & codec_spec_conf::kLeAudioLocationAnyLeft) {
+      return codec_spec_conf::kLeAudioLocationFrontLeft;
+    }
+    if (allocation & codec_spec_conf::kLeAudioLocationAnyRight) {
+      return codec_spec_conf::kLeAudioLocationFrontRight;
+    }
+    return 0;
+  }
+
+  void UpdateCisConfiguration(const std::vector<struct types::cis>& cises,
+                              const stream_parameters& stream_params,
+                              uint8_t direction) {
+    if (GetCodecLocation() != le_audio::types::CodecLocation::ADSP) {
+      return;
+    }
+
+    auto available_allocations =
+        AdjustAllocationForOffloader(stream_params.audio_channel_allocation);
+    if (available_allocations == 0) {
+      LOG_ERROR("There is no CIS connected");
+      return;
+    }
+
+    auto& stream_map = offloader_stream_maps.get(direction);
+    if (stream_map.streams_map_target.empty()) {
+      stream_map.is_initial = true;
+    } else if (stream_map.is_initial ||
+               LeAudioHalVerifier::SupportsStreamActiveApi()) {
+      /* As multiple CISes phone call case, the target_allocation already have
+       * the previous data, but the is_initial flag not be cleared. We need to
+       * clear here to avoid make duplicated target allocation stream map. */
+      stream_map.streams_map_target.clear();
+    }
+
+    stream_map.streams_map_current.clear();
+    stream_map.has_changed = true;
+    bool all_cises_connected =
+        (available_allocations == codec_spec_conf::kLeAudioLocationStereo);
+
+    /* If all the cises are connected as stream started, reset changed_flag that
+     * the bt stack wouldn't send another audio configuration for the connection
+     * status. */
+    if (stream_map.is_initial && all_cises_connected) {
+      stream_map.has_changed = false;
+    }
+
+    const std::string tag = types::BidirectionalPair<std::string>(
+                                {.sink = "Sink", .source = "Source"})
+                                .get(direction);
+
+    constexpr types::BidirectionalPair<types::CisType> cis_types = {
+        .sink = types::CisType::CIS_TYPE_UNIDIRECTIONAL_SINK,
+        .source = types::CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE};
+    auto cis_type = cis_types.get(direction);
+
+    for (auto const& cis_entry : cises) {
+      if ((cis_entry.type == types::CisType::CIS_TYPE_BIDIRECTIONAL ||
+           cis_entry.type == cis_type) &&
+          cis_entry.conn_handle != 0) {
+        uint32_t target_allocation = 0;
+        uint32_t current_allocation = 0;
+        bool is_active = false;
+        for (const auto& s : stream_params.stream_locations) {
+          if (s.first == cis_entry.conn_handle) {
+            is_active = true;
+            target_allocation = AdjustAllocationForOffloader(s.second);
+            current_allocation = target_allocation;
+            if (!all_cises_connected) {
+              /* Tell offloader to mix on this CIS.*/
+              current_allocation = codec_spec_conf::kLeAudioLocationStereo;
+            }
+            break;
+          }
+        }
+
+        if (target_allocation == 0) {
+          /* Take missing allocation for that one .*/
+          target_allocation =
+              codec_spec_conf::kLeAudioLocationStereo & ~available_allocations;
+        }
+
+        LOG_INFO(
+            "%s: Cis handle 0x%04x, target allocation  0x%08x, current "
+            "allocation 0x%08x, active: %d",
+            tag.c_str(), cis_entry.conn_handle, target_allocation,
+            current_allocation, is_active);
+
+        if (stream_map.is_initial ||
+            LeAudioHalVerifier::SupportsStreamActiveApi()) {
+          stream_map.streams_map_target.emplace_back(stream_map_info(
+              cis_entry.conn_handle, target_allocation, is_active));
+        }
+        stream_map.streams_map_current.emplace_back(stream_map_info(
+            cis_entry.conn_handle, current_allocation, is_active));
+      }
+    }
+  }
+
  private:
   void SetCodecLocation(CodecLocation location) {
     if (offload_enable_ == false) return;
@@ -365,7 +474,8 @@ struct codec_manager_impl {
     std::unordered_set<uint8_t> offload_preference_set;
 
     if (AudioSetConfigurationProvider::Get() == nullptr) {
-      LOG(ERROR) << __func__ << " Audio set configuration provider is not available.";
+      LOG(ERROR) << __func__
+                 << " Audio set configuration provider is not available.";
       return;
     }
 
@@ -405,8 +515,7 @@ struct codec_manager_impl {
 
   CodecLocation codec_location_ = CodecLocation::HOST;
   bool offload_enable_ = false;
-  le_audio::offload_config sink_config;
-  le_audio::offload_config source_config;
+  types::BidirectionalPair<offloader_stream_maps_t> offloader_stream_maps;
   std::vector<le_audio::broadcast_offload_config> supported_broadcast_config;
   std::unordered_map<types::LeAudioContextType, AudioSetConfigurations>
       context_type_offload_config_map_;
@@ -456,22 +565,13 @@ types::CodecLocation CodecManager::GetCodecLocation(void) const {
   return pimpl_->codec_manager_impl_->GetCodecLocation();
 }
 
-void CodecManager::UpdateActiveSourceAudioConfig(
-    const stream_configuration& stream_conf, uint16_t delay_ms,
-    std::function<void(const ::le_audio::offload_config& config)>
-        update_receiver) {
+void CodecManager::UpdateActiveAudioConfig(
+    const types::BidirectionalPair<stream_parameters>& stream_params,
+    types::BidirectionalPair<uint16_t> delays_ms,
+    std::function<void(const offload_config& config)> update_receiver) {
   if (pimpl_->IsRunning())
-    pimpl_->codec_manager_impl_->UpdateActiveSourceAudioConfig(
-        stream_conf, delay_ms, update_receiver);
-}
-
-void CodecManager::UpdateActiveSinkAudioConfig(
-    const stream_configuration& stream_conf, uint16_t delay_ms,
-    std::function<void(const ::le_audio::offload_config& config)>
-        update_receiver) {
-  if (pimpl_->IsRunning())
-    pimpl_->codec_manager_impl_->UpdateActiveSinkAudioConfig(
-        stream_conf, delay_ms, update_receiver);
+    pimpl_->codec_manager_impl_->UpdateActiveAudioConfig(
+        stream_params, delays_ms, update_receiver);
 }
 
 const AudioSetConfigurations* CodecManager::GetOffloadCodecConfig(
@@ -502,4 +602,19 @@ void CodecManager::UpdateBroadcastConnHandle(
   }
 }
 
+void CodecManager::UpdateCisConfiguration(
+    const std::vector<struct types::cis>& cises,
+    const stream_parameters& stream_params, uint8_t direction) {
+  if (pimpl_->IsRunning()) {
+    return pimpl_->codec_manager_impl_->UpdateCisConfiguration(
+        cises, stream_params, direction);
+  }
+}
+
+void CodecManager::ClearCisConfiguration(uint8_t direction) {
+  if (pimpl_->IsRunning()) {
+    return pimpl_->codec_manager_impl_->ClearCisConfiguration(direction);
+  }
+}
+
 }  // namespace le_audio
diff --git a/system/bta/le_audio/codec_manager.h b/system/bta/le_audio/codec_manager.h
index f79fb618da07ef97ef78e7926e432b5b5dd18db0..1159735725a847a745dd4cff13b5cc470992f32e 100644
--- a/system/bta/le_audio/codec_manager.h
+++ b/system/bta/le_audio/codec_manager.h
@@ -21,6 +21,17 @@
 
 namespace le_audio {
 
+struct stream_map_info {
+  stream_map_info(uint16_t stream_handle, uint32_t audio_channel_allocation,
+                  bool is_stream_active)
+      : stream_handle(stream_handle),
+        audio_channel_allocation(audio_channel_allocation),
+        is_stream_active(is_stream_active) {}
+  uint16_t stream_handle;
+  uint32_t audio_channel_allocation;
+  bool is_stream_active;
+};
+
 struct offload_config {
   std::vector<stream_map_info> stream_map;
   uint8_t bits_per_sample;
@@ -45,7 +56,6 @@ struct broadcast_offload_config {
 
 class CodecManager {
  public:
-  CodecManager();
   virtual ~CodecManager() = default;
   static CodecManager* GetInstance(void) {
     static CodecManager* instance = new CodecManager();
@@ -55,14 +65,14 @@ class CodecManager {
                  offloading_preference);
   void Stop(void);
   virtual types::CodecLocation GetCodecLocation(void) const;
-  virtual void UpdateActiveSourceAudioConfig(
-      const stream_configuration& stream_conf, uint16_t delay_ms,
-      std::function<void(const ::le_audio::offload_config& config)>
-          update_receiver);
-  virtual void UpdateActiveSinkAudioConfig(
-      const stream_configuration& stream_conf, uint16_t delay_ms,
-      std::function<void(const ::le_audio::offload_config& config)>
-          update_receiver);
+  virtual void UpdateCisConfiguration(
+      const std::vector<struct types::cis>& cises,
+      const stream_parameters& stream_params, uint8_t direction);
+  virtual void ClearCisConfiguration(uint8_t direction);
+  virtual void UpdateActiveAudioConfig(
+      const types::BidirectionalPair<stream_parameters>& stream_params,
+      types::BidirectionalPair<uint16_t> delays_ms,
+      std::function<void(const offload_config& config)> update_receiver);
   virtual const ::le_audio::set_configurations::AudioSetConfigurations*
   GetOffloadCodecConfig(::le_audio::types::LeAudioContextType ctx_type);
   virtual const ::le_audio::broadcast_offload_config*
@@ -73,6 +83,7 @@ class CodecManager {
           update_receiver);
 
  private:
+  CodecManager();
   struct impl;
   std::unique_ptr<impl> pimpl_;
 };
diff --git a/system/bta/le_audio/codec_manager_test.cc b/system/bta/le_audio/codec_manager_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..6d04f579170531bb55daa868963e29618c99fdf0
--- /dev/null
+++ b/system/bta/le_audio/codec_manager_test.cc
@@ -0,0 +1,316 @@
+/*
+ * Copyright 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.
+ */
+
+#include "codec_manager.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "btm_api_mock.h"
+#include "gd/common/init_flags.h"
+#include "mock_controller.h"
+#include "osi/include/properties.h"
+#include "stack/acl/acl.h"
+
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::Test;
+
+using bluetooth::hci::iso_manager::kIsoDataPathHci;
+using bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault;
+using le_audio::set_configurations::AudioSetConfiguration;
+using le_audio::types::CodecLocation;
+using le_audio::types::kLeAudioDirectionSink;
+using le_audio::types::kLeAudioDirectionSource;
+
+void osi_property_set_bool(const char* key, bool value);
+
+std::vector<AudioSetConfiguration> offload_capabilities(0);
+
+const char* test_flags[] = {
+    "INIT_default_log_level_str=LOG_VERBOSE",
+};
+
+namespace bluetooth {
+namespace audio {
+namespace le_audio {
+std::vector<AudioSetConfiguration> get_offload_capabilities() {
+  return offload_capabilities;
+}
+}  // namespace le_audio
+}  // namespace audio
+}  // namespace bluetooth
+
+namespace le_audio {
+namespace {
+
+static constexpr char kPropLeAudioOffloadSupported[] =
+    "ro.bluetooth.leaudio_offload.supported";
+static constexpr char kPropLeAudioOffloadDisabled[] =
+    "persist.bluetooth.leaudio_offload.disabled";
+
+class CodecManagerTestBase : public Test {
+ public:
+  virtual void SetUp() override {
+    bluetooth::common::InitFlags::Load(test_flags);
+
+    offload_capabilities.clear();
+
+    ON_CALL(controller_interface, SupportsBleIsochronousBroadcaster)
+        .WillByDefault(Return(true));
+    ON_CALL(controller_interface, SupportsConfigureDataPath)
+        .WillByDefault(Return(true));
+
+    controller::SetMockControllerInterface(&controller_interface);
+    bluetooth::manager::SetMockBtmInterface(&btm_interface);
+
+    codec_manager = CodecManager::GetInstance();
+  }
+
+  virtual void TearDown() override {
+    codec_manager->Stop();
+
+    bluetooth::manager::SetMockBtmInterface(nullptr);
+    controller::SetMockControllerInterface(nullptr);
+  }
+
+  NiceMock<bluetooth::manager::MockBtmInterface> btm_interface;
+  NiceMock<controller::MockControllerInterface> controller_interface;
+  CodecManager* codec_manager;
+};
+
+/*----------------- ADSP codec manager tests ------------------*/
+class CodecManagerTestAdsp : public CodecManagerTestBase {
+ public:
+  virtual void SetUp() override {
+    // Enable the HW offloader
+    osi_property_set_bool(kPropLeAudioOffloadSupported, true);
+    osi_property_set_bool(kPropLeAudioOffloadDisabled, false);
+
+    CodecManagerTestBase::SetUp();
+  }
+};
+
+TEST_F(CodecManagerTestAdsp, test_init) {
+  ASSERT_EQ(codec_manager, CodecManager::GetInstance());
+}
+
+TEST_F(CodecManagerTestAdsp, test_start) {
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER,
+                                kIsoDataPathPlatformDefault, _))
+      .Times(1);
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST,
+                                kIsoDataPathPlatformDefault, _))
+      .Times(1);
+
+  const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>
+      offloading_preference(0);
+  codec_manager->Start(offloading_preference);
+  Mock::VerifyAndClearExpectations(&btm_interface);
+
+  ASSERT_EQ(codec_manager->GetCodecLocation(), CodecLocation::ADSP);
+
+  // Verify data path is reset on Stop()
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER,
+                                kIsoDataPathHci, _))
+      .Times(1);
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST,
+                                kIsoDataPathHci, _))
+      .Times(1);
+}
+
+TEST_F(CodecManagerTestAdsp, testStreamConfigurationAdspDownMix) {
+  const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>
+      offloading_preference(0);
+  codec_manager->Start(offloading_preference);
+
+  // Current CIS configuration for two earbuds
+  std::vector<struct types::cis> cises{
+      {
+          .id = 0x00,
+          .type = types::CisType::CIS_TYPE_BIDIRECTIONAL,
+          .conn_handle = 96,
+      },
+      {
+          .id = 0x01,
+          .type = types::CisType::CIS_TYPE_BIDIRECTIONAL,
+          .conn_handle = 97,
+      },
+  };
+
+  // Stream parameters
+  types::BidirectionalPair<stream_parameters> stream_params{
+      .sink =
+          {
+              .audio_channel_allocation =
+                  codec_spec_conf::kLeAudioLocationFrontLeft,
+              .sample_frequency_hz = 16000,
+              .frame_duration_us = 10000,
+              .octets_per_codec_frame = 40,
+              .codec_frames_blocks_per_sdu = 1,
+              .num_of_channels = 1,
+              .num_of_devices = 1,
+              .stream_locations =
+                  {
+                      std::pair<uint16_t, uint32_t>{
+                          97 /*conn_handle*/,
+                          codec_spec_conf::kLeAudioLocationFrontLeft},
+                  },
+          },
+      .source =
+          {
+              .audio_channel_allocation =
+                  codec_spec_conf::kLeAudioLocationFrontLeft,
+              .sample_frequency_hz = 16000,
+              .frame_duration_us = 10000,
+              .octets_per_codec_frame = 40,
+              .codec_frames_blocks_per_sdu = 1,
+              .num_of_channels = 1,
+              .num_of_devices = 1,
+              {
+                  std::pair<uint16_t, uint32_t>{
+                      97 /*conn_handle*/,
+                      codec_spec_conf::kLeAudioLocationBackLeft},
+              },
+          },
+  };
+
+  codec_manager->UpdateCisConfiguration(cises, stream_params.sink,
+                                        kLeAudioDirectionSink);
+  codec_manager->UpdateCisConfiguration(cises, stream_params.source,
+                                        kLeAudioDirectionSource);
+
+  // Verify the offloader config content
+  std::vector<offload_config> out_offload_configs;
+  codec_manager->UpdateActiveAudioConfig(
+      stream_params, {.sink = 44, .source = 44},
+      [&out_offload_configs](const offload_config& config) {
+        out_offload_configs.push_back(config);
+      });
+
+  // Expect the same configuration for sink and source
+  ASSERT_EQ(2lu, out_offload_configs.size());
+  for (const auto& config : out_offload_configs) {
+    uint32_t allocation = 0;
+    ASSERT_EQ(2lu, config.stream_map.size());
+    for (const auto& info : config.stream_map) {
+      if (info.stream_handle == 96) {
+        ASSERT_EQ(codec_spec_conf::kLeAudioLocationFrontRight,
+                  info.audio_channel_allocation);
+        // The disconnected should be inactive
+        ASSERT_FALSE(info.is_stream_active);
+
+      } else if (info.stream_handle == 97) {
+        ASSERT_EQ(codec_spec_conf::kLeAudioLocationFrontLeft,
+                  info.audio_channel_allocation);
+        // The connected should be active
+        ASSERT_TRUE(info.is_stream_active);
+
+      } else {
+        ASSERT_EQ(97, info.stream_handle);
+      }
+      allocation |= info.audio_channel_allocation;
+    }
+
+    ASSERT_EQ(16, config.bits_per_sample);
+    ASSERT_EQ(16000u, config.sampling_rate);
+    ASSERT_EQ(10000u, config.frame_duration);
+    ASSERT_EQ(40u, config.octets_per_frame);
+    ASSERT_EQ(1, config.blocks_per_sdu);
+    ASSERT_EQ(44, config.peer_delay_ms);
+    ASSERT_EQ(codec_spec_conf::kLeAudioLocationStereo, allocation);
+  }
+
+  // Clear the CIS configuration map (no active CISes).
+  codec_manager->ClearCisConfiguration(kLeAudioDirectionSink);
+  codec_manager->ClearCisConfiguration(kLeAudioDirectionSource);
+  out_offload_configs.clear();
+  codec_manager->UpdateActiveAudioConfig(
+      stream_params, {.sink = 44, .source = 44},
+      [&out_offload_configs](const offload_config& config) {
+        out_offload_configs.push_back(config);
+      });
+
+  // Expect sink & source configurations with empty CIS channel allocation map.
+  ASSERT_EQ(2lu, out_offload_configs.size());
+  for (const auto& config : out_offload_configs) {
+    ASSERT_EQ(0lu, config.stream_map.size());
+    ASSERT_EQ(16, config.bits_per_sample);
+    ASSERT_EQ(16000u, config.sampling_rate);
+    ASSERT_EQ(10000u, config.frame_duration);
+    ASSERT_EQ(40u, config.octets_per_frame);
+    ASSERT_EQ(1, config.blocks_per_sdu);
+    ASSERT_EQ(44, config.peer_delay_ms);
+  }
+}
+
+// TODO: Add the unit tests for:
+// GetOffloadCodecConfig
+// GetBroadcastOffloadConfig
+// UpdateBroadcastConnHandle
+
+/*----------------- HOST codec manager tests ------------------*/
+class CodecManagerTestHost : public CodecManagerTestBase {
+ public:
+  virtual void SetUp() override {
+    // Enable the HW offloader
+    osi_property_set_bool(kPropLeAudioOffloadSupported, false);
+    osi_property_set_bool(kPropLeAudioOffloadDisabled, false);
+
+    CodecManagerTestBase::SetUp();
+  }
+};
+
+TEST_F(CodecManagerTestHost, test_init) {
+  ASSERT_EQ(codec_manager, CodecManager::GetInstance());
+}
+
+TEST_F(CodecManagerTestHost, test_start) {
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER,
+                                kIsoDataPathPlatformDefault, _))
+      .Times(0);
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST,
+                                kIsoDataPathPlatformDefault, _))
+      .Times(0);
+
+  const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>
+      offloading_preference(0);
+  codec_manager->Start(offloading_preference);
+  Mock::VerifyAndClearExpectations(&btm_interface);
+
+  ASSERT_EQ(codec_manager->GetCodecLocation(), CodecLocation::HOST);
+
+  // Verify data path is NOT reset on Stop() for the Host encoding session
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER,
+                                kIsoDataPathHci, _))
+      .Times(0);
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST,
+                                kIsoDataPathHci, _))
+      .Times(0);
+}
+
+}  // namespace
+}  // namespace le_audio
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index c13d2855f249f4936895a21e0e452b65f333786d..0feb62bfc3438aac7aac4e500e1f3e279077d954 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -59,18 +59,6 @@ using le_audio::types::LeAudioContextType;
 using le_audio::types::LeAudioLc3Config;
 
 namespace le_audio {
-namespace types {
-template struct BidirectionalPair<offloader_stream_config>;
-template <>
-offloader_stream_config& types::BidirectionalPair<offloader_stream_config>::get(
-    uint8_t direction) {
-  ASSERT_LOG(direction < types::kLeAudioDirectionBoth,
-             "Unsupported complex direction. Reference to a single complex"
-             " direction value is not supported.");
-  return (direction == types::kLeAudioDirectionSink) ? sink : source;
-}
-}  // namespace types
-
 std::ostream& operator<<(std::ostream& os, const DeviceConnectState& state) {
   const char* char_value_ = "UNKNOWN";
 
@@ -182,9 +170,7 @@ void LeAudioDeviceGroup::ClearSinksFromConfiguration(void) {
   stream_params.octets_per_codec_frame = 0;
   stream_params.frame_duration_us = 0;
 
-  auto& offload_config = stream_conf.offloader_config.get(direction);
-  offload_config.streams_target_allocation.clear();
-  offload_config.streams_current_allocation.clear();
+  CodecManager::GetInstance()->ClearCisConfiguration(direction);
 }
 
 void LeAudioDeviceGroup::ClearSourcesFromConfiguration(void) {
@@ -201,9 +187,7 @@ void LeAudioDeviceGroup::ClearSourcesFromConfiguration(void) {
   stream_params.octets_per_codec_frame = 0;
   stream_params.frame_duration_us = 0;
 
-  auto& offload_config = stream_conf.offloader_config.get(direction);
-  offload_config.streams_target_allocation.clear();
-  offload_config.streams_current_allocation.clear();
+  CodecManager::GetInstance()->ClearCisConfiguration(direction);
 }
 
 void LeAudioDeviceGroup::CigClearCis(void) {
@@ -1927,14 +1911,6 @@ bool LeAudioDeviceGroup::IsCisPartOfCurrentStream(uint16_t cis_conn_hdl) const {
   return (iter != source_stream_locations.end());
 }
 
-void LeAudioDeviceGroup::StreamOffloaderUpdated(uint8_t direction) {
-  if (direction == le_audio::types::kLeAudioDirectionSource) {
-    stream_conf.offloader_config.source.is_initial = false;
-  } else {
-    stream_conf.offloader_config.sink.is_initial = false;
-  }
-}
-
 void LeAudioDeviceGroup::RemoveCisFromStreamIfNeeded(
     LeAudioDevice* leAudioDevice, uint16_t cis_conn_hdl) {
   LOG_INFO(" CIS Connection Handle: %d", cis_conn_hdl);
@@ -1985,126 +1961,23 @@ void LeAudioDeviceGroup::RemoveCisFromStreamIfNeeded(
     ClearSourcesFromConfiguration();
   }
 
-  /* Update offloader streams if needed */
+  /* Update CodecManager CIS configuration */
   if (old_sink_channels > stream_conf.stream_params.sink.num_of_channels) {
-    CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSink);
+    CodecManager::GetInstance()->UpdateCisConfiguration(
+        cises_,
+        stream_conf.stream_params.get(le_audio::types::kLeAudioDirectionSink),
+        le_audio::types::kLeAudioDirectionSink);
   }
   if (old_source_channels > stream_conf.stream_params.source.num_of_channels) {
-    CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSource);
+    CodecManager::GetInstance()->UpdateCisConfiguration(
+        cises_,
+        stream_conf.stream_params.get(le_audio::types::kLeAudioDirectionSource),
+        le_audio::types::kLeAudioDirectionSource);
   }
 
   CigUnassignCis(leAudioDevice);
 }
 
-void LeAudioDeviceGroup::CreateStreamVectorForOffloader(uint8_t direction) {
-  if (CodecManager::GetInstance()->GetCodecLocation() !=
-      le_audio::types::CodecLocation::ADSP) {
-    return;
-  }
-
-  CisType cis_type;
-  std::vector<std::pair<uint16_t, uint32_t>>* streams;
-  std::vector<stream_map_info>* offloader_streams_target_allocation;
-  std::vector<stream_map_info>* offloader_streams_current_allocation;
-  std::string tag;
-  uint32_t available_allocations = 0;
-  bool* changed_flag;
-  bool* is_initial;
-  if (direction == le_audio::types::kLeAudioDirectionSource) {
-    changed_flag = &stream_conf.offloader_config.source.has_changed;
-    is_initial = &stream_conf.offloader_config.source.is_initial;
-    cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE;
-    streams = &stream_conf.stream_params.source.stream_locations;
-    offloader_streams_target_allocation =
-        &stream_conf.offloader_config.source.streams_target_allocation;
-    offloader_streams_current_allocation =
-        &stream_conf.offloader_config.source.streams_current_allocation;
-    tag = "Source";
-    available_allocations = AdjustAllocationForOffloader(
-        stream_conf.stream_params.source.audio_channel_allocation);
-  } else {
-    changed_flag = &stream_conf.offloader_config.sink.has_changed;
-    is_initial = &stream_conf.offloader_config.sink.is_initial;
-    cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SINK;
-    streams = &stream_conf.stream_params.sink.stream_locations;
-    offloader_streams_target_allocation =
-        &stream_conf.offloader_config.sink.streams_target_allocation;
-    offloader_streams_current_allocation =
-        &stream_conf.offloader_config.sink.streams_current_allocation;
-    tag = "Sink";
-    available_allocations = AdjustAllocationForOffloader(
-        stream_conf.stream_params.sink.audio_channel_allocation);
-  }
-
-  if (available_allocations == 0) {
-    LOG_ERROR("There is no CIS connected");
-    return;
-  }
-
-  if (offloader_streams_target_allocation->size() == 0) {
-    *is_initial = true;
-  } else if (*is_initial || LeAudioHalVerifier::SupportsStreamActiveApi()) {
-    // As multiple CISes phone call case, the target_allocation already have the
-    // previous data, but the is_initial flag not be cleared. We need to clear
-    // here to avoid make duplicated target allocation stream map.
-    offloader_streams_target_allocation->clear();
-  }
-
-  offloader_streams_current_allocation->clear();
-  *changed_flag = true;
-  bool not_all_cises_connected = false;
-  if (available_allocations != codec_spec_conf::kLeAudioLocationStereo) {
-    not_all_cises_connected = true;
-  }
-
-  /* If the all cises are connected as stream started, reset changed_flag that
-   * the bt stack wouldn't send another audio configuration for the connection
-   * status */
-  if (*is_initial && !not_all_cises_connected) {
-    *changed_flag = false;
-  }
-  for (auto const& cis_entry : cises_) {
-    if ((cis_entry.type == CisType::CIS_TYPE_BIDIRECTIONAL ||
-         cis_entry.type == cis_type) &&
-        cis_entry.conn_handle != 0) {
-      uint32_t target_allocation = 0;
-      uint32_t current_allocation = 0;
-      bool is_active = false;
-      for (const auto& s : *streams) {
-        if (s.first == cis_entry.conn_handle) {
-          is_active = true;
-          target_allocation = AdjustAllocationForOffloader(s.second);
-          current_allocation = target_allocation;
-          if (not_all_cises_connected) {
-            /* Tell offloader to mix on this CIS.*/
-            current_allocation = codec_spec_conf::kLeAudioLocationStereo;
-          }
-          break;
-        }
-      }
-
-      if (target_allocation == 0) {
-        /* Take missing allocation for that one .*/
-        target_allocation =
-            codec_spec_conf::kLeAudioLocationStereo & ~available_allocations;
-      }
-
-      LOG_INFO(
-          "%s: Cis handle 0x%04x, target allocation  0x%08x, current "
-          "allocation 0x%08x, active: %d",
-          tag.c_str(), cis_entry.conn_handle, target_allocation,
-          current_allocation, is_active);
-
-      if (*is_initial || LeAudioHalVerifier::SupportsStreamActiveApi()) {
-        offloader_streams_target_allocation->emplace_back(stream_map_info(
-            cis_entry.conn_handle, target_allocation, is_active));
-      }
-      offloader_streams_current_allocation->emplace_back(stream_map_info(
-          cis_entry.conn_handle, current_allocation, is_active));
-    }
-  }
-}
-
 bool LeAudioDeviceGroup::IsPendingConfiguration(void) const {
   return stream_conf.pending_configuration;
 }
diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h
index 358d470daa1bb8463ac576f8cb9ecbe76d13a0f8..e1029e7d8b30aec453faf83b76a3ce3c8ed16535 100644
--- a/system/bta/le_audio/devices.h
+++ b/system/bta/le_audio/devices.h
@@ -416,8 +416,6 @@ class LeAudioDeviceGroup {
   bool IsMetadataChanged(
       const types::BidirectionalPair<types::AudioContexts>& context_types,
       const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists) const;
-  void CreateStreamVectorForOffloader(uint8_t direction);
-  void StreamOffloaderUpdated(uint8_t direction);
   bool IsConfiguredForContext(types::LeAudioContextType context_type) const;
   void RemoveCisFromStreamIfNeeded(LeAudioDevice* leAudioDevice,
                                    uint16_t cis_conn_hdl);
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index 283531a6f4e1a6e1194c117b5a98830f39ea13d5..f068ab9f55046f242852bb2aa0ce142c8e59b990 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -769,21 +769,6 @@ uint8_t GetMaxCodecFramesPerSduFromPac(const acs_ac_record* pac) {
   return 1;
 }
 
-uint32_t AdjustAllocationForOffloader(uint32_t allocation) {
-  if ((allocation & codec_spec_conf::kLeAudioLocationAnyLeft) &&
-      (allocation & codec_spec_conf::kLeAudioLocationAnyRight)) {
-    return codec_spec_conf::kLeAudioLocationStereo;
-  }
-  if (allocation & codec_spec_conf::kLeAudioLocationAnyLeft) {
-    return codec_spec_conf::kLeAudioLocationFrontLeft;
-  }
-
-  if (allocation & codec_spec_conf::kLeAudioLocationAnyRight) {
-    return codec_spec_conf::kLeAudioLocationFrontRight;
-  }
-  return 0;
-}
-
 namespace types {
 std::ostream& operator<<(std::ostream& os,
                          const AudioStreamDataPathState& state) {
@@ -945,9 +930,13 @@ AudioLocations get_bidirectional(BidirectionalPair<AudioLocations> bidir) {
 
 template struct BidirectionalPair<AudioContexts>;
 template struct BidirectionalPair<AudioLocations>;
-template struct BidirectionalPair<std::vector<uint8_t>>;
+template struct BidirectionalPair<CisType>;
 template struct BidirectionalPair<ase*>;
+template struct BidirectionalPair<std::string>;
+template struct BidirectionalPair<std::vector<uint8_t>>;
+template struct BidirectionalPair<stream_configuration>;
 template struct BidirectionalPair<stream_parameters>;
+template struct BidirectionalPair<uint16_t>;
 
 }  // namespace types
 }  // namespace le_audio
diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h
index c6b2c2aa5a7cd819bdc4a328bb2d0b897895693e..d59d12c33c91a4386148b597349e4c1e10eabbad 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -837,26 +837,6 @@ uint8_t get_num_of_devices_in_configuration(
     const AudioSetConfiguration* audio_set_configuration);
 }  // namespace set_configurations
 
-struct stream_map_info {
-  stream_map_info(uint16_t stream_handle, uint32_t audio_channel_allocation,
-                  bool is_stream_active)
-      : stream_handle(stream_handle),
-        audio_channel_allocation(audio_channel_allocation),
-        is_stream_active(is_stream_active) {}
-  uint16_t stream_handle;
-  uint32_t audio_channel_allocation;
-  bool is_stream_active;
-};
-
-struct offloader_stream_config {
-  /* cis_handle, target allocation, stream active state */
-  std::vector<stream_map_info> streams_target_allocation;
-  /* cis_handle, current allocation, stream active state */
-  std::vector<stream_map_info> streams_current_allocation;
-  bool has_changed;
-  bool is_initial;
-};
-
 struct stream_parameters {
   /* For now we have always same frequency for all the channels */
   uint32_t sample_frequency_hz;
@@ -882,9 +862,6 @@ struct stream_configuration {
   /* Sink & Source configuration */
   types::BidirectionalPair<stream_parameters> stream_params;
 
-  /* TODO: Make this struct free from the offloader specific data */
-  types::BidirectionalPair<offloader_stream_config> offloader_config;
-
   bool is_active;
 };
 
@@ -893,5 +870,4 @@ void AppendMetadataLtvEntryForCcidList(std::vector<uint8_t>& metadata,
 void AppendMetadataLtvEntryForStreamingContext(
     std::vector<uint8_t>& metadata, types::AudioContexts context_type);
 uint8_t GetMaxCodecFramesPerSduFromPac(const types::acs_ac_record* pac_record);
-uint32_t AdjustAllocationForOffloader(uint32_t allocation);
 }  // namespace le_audio
\ No newline at end of file
diff --git a/system/bta/le_audio/mock_codec_manager.cc b/system/bta/le_audio/mock_codec_manager.cc
index 2f8aaad427ba6454ef4ef799b0fa01d3bff67c0e..2e2818281ef671a09c3f71140e37cceba77b4640 100644
--- a/system/bta/le_audio/mock_codec_manager.cc
+++ b/system/bta/le_audio/mock_codec_manager.cc
@@ -37,22 +37,14 @@ types::CodecLocation CodecManager::GetCodecLocation() const {
   return pimpl_->GetCodecLocation();
 }
 
-void CodecManager::UpdateActiveSourceAudioConfig(
-    const stream_configuration& stream_conf, uint16_t delay_ms,
+void CodecManager::UpdateActiveAudioConfig(
+    const types::BidirectionalPair<stream_parameters>& stream_params,
+    types::BidirectionalPair<uint16_t> delays_ms,
     std::function<void(const ::le_audio::offload_config& config)>
         update_receiver) {
   if (pimpl_)
-    return pimpl_->UpdateActiveSourceAudioConfig(stream_conf, delay_ms,
-                                                 update_receiver);
-}
-
-void CodecManager::UpdateActiveSinkAudioConfig(
-    const stream_configuration& stream_conf, uint16_t delay_ms,
-    std::function<void(const ::le_audio::offload_config& config)>
-        update_receiver) {
-  if (pimpl_)
-    return pimpl_->UpdateActiveSinkAudioConfig(stream_conf, delay_ms,
-                                               update_receiver);
+    return pimpl_->UpdateActiveAudioConfig(stream_params, delays_ms,
+                                           update_receiver);
 }
 
 const set_configurations::AudioSetConfigurations*
@@ -99,6 +91,17 @@ void CodecManager::Stop() {
   mock_codec_manager_pimpl_ = nullptr;
 }
 
+void CodecManager::UpdateCisConfiguration(
+    const std::vector<struct types::cis>& cises,
+    const stream_parameters& stream_params, uint8_t direction) {
+  if (pimpl_)
+    return pimpl_->UpdateCisConfiguration(cises, stream_params, direction);
+}
+
+void CodecManager::ClearCisConfiguration(uint8_t direction) {
+  if (pimpl_) return pimpl_->ClearCisConfiguration(direction);
+}
+
 // CodecManager::~CodecManager() = default;
 
 }  // namespace le_audio
diff --git a/system/bta/le_audio/mock_codec_manager.h b/system/bta/le_audio/mock_codec_manager.h
index d73132d6d72a6cc8cdfec2749827c0961ff2d2ef..09f2b0636a59db340e2efea028ad1eeb435b1f1d 100644
--- a/system/bta/le_audio/mock_codec_manager.h
+++ b/system/bta/le_audio/mock_codec_manager.h
@@ -31,14 +31,10 @@ class MockCodecManager {
   virtual ~MockCodecManager() = default;
 
   MOCK_METHOD((le_audio::types::CodecLocation), GetCodecLocation, (), (const));
-  MOCK_METHOD((void), UpdateActiveSourceAudioConfig,
-              (const le_audio::stream_configuration& stream_conf,
-               uint16_t delay,
-               std::function<void(const ::le_audio::offload_config& config)>
-                   update_receiver));
-  MOCK_METHOD((void), UpdateActiveSinkAudioConfig,
-              (const le_audio::stream_configuration& stream_conf,
-               uint16_t delay,
+  MOCK_METHOD((void), UpdateActiveAudioConfig,
+              (const le_audio::types::BidirectionalPair<
+                   le_audio::stream_parameters>& stream_params,
+               le_audio::types::BidirectionalPair<uint16_t> delays_ms,
                std::function<void(const ::le_audio::offload_config& config)>
                    update_receiver));
   MOCK_METHOD((le_audio::set_configurations::AudioSetConfigurations*),
@@ -51,6 +47,12 @@ class MockCodecManager {
       (const std::vector<uint16_t>& conn_handle,
        std::function<void(const ::le_audio::broadcast_offload_config& config)>
            update_receiver));
+  MOCK_METHOD((void), UpdateCisConfiguration,
+              (const std::vector<struct le_audio::types::cis>& cises,
+               const le_audio::stream_parameters& stream_params,
+               uint8_t direction),
+              (const));
+  MOCK_METHOD((void), ClearCisConfiguration, (uint8_t direction));
 
   MOCK_METHOD((void), Start, ());
   MOCK_METHOD((void), Stop, ());
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 75e670ed7b009a074a08148f6e6b5bd97dd83adb..74e8b02698449c040d8b4171ec983049a3b7a5ee 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -759,7 +759,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
     if (group->IsAnyDeviceConnected()) {
       /*
        * ACL of one of the device has been dropped. If number of CISes has
-       * changed notify upper layer so the offloader can be updated with CIS
+       * changed notify upper layer so the CodecManager can be updated with CIS
        * information.
        */
       if (!group->HaveAllCisesDisconnected()) {
@@ -769,7 +769,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
             (group->GetTargetState() ==
              AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
           /* We keep streaming but want others to let know user that it might
-           * be need to update offloader with new CIS configuration
+           * be need to update CodecManager with new CIS configuration
            */
           state_machine_callbacks_->StatusReportCb(
               group->group_id_, GroupStreamStatus::STREAMING);
@@ -1235,8 +1235,9 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
         cis_conn_hdl, ase_audio_channel_allocation, params.num_of_devices,
         params.num_of_channels);
 
-    /* Update offloader streams */
-    group->CreateStreamVectorForOffloader(ase->direction);
+    /* Update CodecManager stream configuration */
+    state_machine_callbacks_->OnUpdatedCisConfiguration(group->group_id_,
+                                                        ase->direction);
   }
 
   bool CigCreate(LeAudioDeviceGroup* group) {
diff --git a/system/bta/le_audio/state_machine.h b/system/bta/le_audio/state_machine.h
index 596df9d27dd891f8848de9765cfea0723e55878c..fa690455bbe6e32578b051e1fdbcef9f0d9c8ef3 100644
--- a/system/bta/le_audio/state_machine.h
+++ b/system/bta/le_audio/state_machine.h
@@ -37,6 +37,7 @@ class LeAudioGroupStateMachine {
     virtual void StatusReportCb(
         int group_id, bluetooth::le_audio::GroupStreamStatus status) = 0;
     virtual void OnStateTransitionTimeout(int group_id) = 0;
+    virtual void OnUpdatedCisConfiguration(int group_id, uint8_t direction) = 0;
   };
 
   virtual ~LeAudioGroupStateMachine() = default;
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index 3d6a742ed8c4fb7c9b21c86e5e490f3eb2aa880c..32a46782e8a959740eaaa6b1de04dd88fbd19e16 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -165,6 +165,8 @@ class MockLeAudioGroupStateMachineCallbacks
               (int group_id, bluetooth::le_audio::GroupStreamStatus status),
               (override));
   MOCK_METHOD((void), OnStateTransitionTimeout, (int group_id), (override));
+  MOCK_METHOD((void), OnUpdatedCisConfiguration,
+              (int group_id, uint8_t direction), (override));
 };
 
 class StateMachineTestBase : public Test {
@@ -3438,8 +3440,17 @@ TEST_F(StateMachineTestAdsp, testStreamConfigurationAdspDownMix) {
       leaudio_group_id, context_type, num_devices,
       types::AudioContexts(kContextTypeConversational));
 
-  /* Can be called for every context when fetching the configuration from the
-   * AudioSetConfigurationProvider.
+  EXPECT_CALL(mock_callbacks_,
+              OnUpdatedCisConfiguration(group->group_id_,
+                                        le_audio::types::kLeAudioDirectionSink))
+      .Times(1);
+  EXPECT_CALL(mock_callbacks_,
+              OnUpdatedCisConfiguration(
+                  group->group_id_, le_audio::types::kLeAudioDirectionSource))
+      .Times(1);
+
+  /* Can be called for every context when fetching the configuration from
+   * the AudioSetConfigurationProvider.
    */
   EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()).Times(AtLeast(1));
 
@@ -3459,46 +3470,11 @@ TEST_F(StateMachineTestAdsp, testStreamConfigurationAdspDownMix) {
       {.sink = types::AudioContexts(context_type),
        .source = types::AudioContexts(context_type)}));
 
-  ASSERT_EQ(static_cast<int>(group->stream_conf.offloader_config.sink
-                                 .streams_target_allocation.size()),
-            2);
-  ASSERT_EQ(static_cast<int>(group->stream_conf.offloader_config.source
-                                 .streams_target_allocation.size()),
-            2);
-
   // Check if group has transitioned to a proper state
   ASSERT_EQ(group->GetState(),
             types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
 
-  uint32_t allocation = 0;
-  for (const auto& s :
-       group->stream_conf.offloader_config.sink.streams_target_allocation) {
-    allocation |= s.audio_channel_allocation;
-    ASSERT_FALSE(allocation == 0);
-  }
-  ASSERT_TRUE(allocation == codec_spec_conf::kLeAudioLocationStereo);
-
-  allocation = 0;
-  for (const auto& s :
-       group->stream_conf.offloader_config.source.streams_target_allocation) {
-    allocation |= s.audio_channel_allocation;
-    ASSERT_FALSE(allocation == 0);
-  }
-  ASSERT_TRUE(allocation == codec_spec_conf::kLeAudioLocationStereo);
-
-  for (const auto& s :
-       group->stream_conf.offloader_config.sink.streams_target_allocation) {
-    ASSERT_TRUE((s.audio_channel_allocation != 0) &&
-                (s.audio_channel_allocation !=
-                 codec_spec_conf::kLeAudioLocationStereo));
-  }
-
-  for (const auto& s :
-       group->stream_conf.offloader_config.source.streams_target_allocation) {
-    ASSERT_TRUE((s.audio_channel_allocation != 0) &&
-                (s.audio_channel_allocation !=
-                 codec_spec_conf::kLeAudioLocationStereo));
-  }
+  // Note: The actual channel mixing is verified by the CodecManager unit tests.
 }
 
 static void InjectCisDisconnected(LeAudioDeviceGroup* group,
diff --git a/system/bta/test/common/mock_controller.cc b/system/bta/test/common/mock_controller.cc
index 7cf28651c80bfdb998781c6228ec7ebef305a93f..1aeb5527b21cce0b6d4e290f66442f427701f666 100644
--- a/system/bta/test/common/mock_controller.cc
+++ b/system/bta/test/common/mock_controller.cc
@@ -57,6 +57,11 @@ bool supports_ble_connected_isochronous_stream_peripheral(void) {
       ->SupportsBleConnectedIsochronousStreamPeripheral();
 }
 
+bool supports_configure_data_path(void) {
+  LOG_ASSERT(controller_interface) << "Mock controller not set!";
+  return controller_interface->SupportsConfigureDataPath();
+}
+
 const controller_t* controller_get_interface() {
   static controller_t* controller_instance = new controller_t();
 
@@ -69,6 +74,8 @@ const controller_t* controller_get_interface() {
       &supports_ble_connected_isochronous_stream_central;
   controller_instance->supports_ble_connected_isochronous_stream_peripheral =
       &supports_ble_connected_isochronous_stream_peripheral;
+  controller_instance->supports_configure_data_path =
+      &supports_configure_data_path;
 
   return controller_instance;
 }
diff --git a/system/bta/test/common/mock_controller.h b/system/bta/test/common/mock_controller.h
index 6d27a18e72c4832837107438a49c999584301d3f..39f4d15234f26f0aad782353b7a8d57bdc0776d3 100644
--- a/system/bta/test/common/mock_controller.h
+++ b/system/bta/test/common/mock_controller.h
@@ -30,6 +30,7 @@ class ControllerInterface {
   virtual bool SupportsBleConnectedIsochronousStreamPeripheral(void) = 0;
   virtual bool SupportsBleIsochronousBroadcaster(void) = 0;
   virtual bool SupportsBle2mPhy(void) = 0;
+  virtual bool SupportsConfigureDataPath(void) = 0;
 
   virtual ~ControllerInterface() = default;
 };
@@ -44,6 +45,7 @@ class MockControllerInterface : public ControllerInterface {
               (override));
   MOCK_METHOD((bool), SupportsBleIsochronousBroadcaster, (), (override));
   MOCK_METHOD((bool), SupportsBle2mPhy, (), (override));
+  MOCK_METHOD((bool), SupportsConfigureDataPath, (), (override));
 };
 
 void SetMockControllerInterface(