Add a mechanism for configuring the A2DP Source codecs

* Codec config internal abstraction:
 - Add new classes A2dpCodecConfig and A2dpCodecs that (will)
   encapsulate all codec-related APIs
 - Add unit tests for the above two classes
 - Add method A2dpCodecConfig.buildCodecConfig(), and use it when
   creating the codec configuration instead of A2DP_InitSource2SinkCodec().
   The new method can build the codec config by taking into account
   optional user codec-related configuration preferences.
 - Use the A2DP codec config API from the hardware/libhardware bt_av.h API
 - Replace enum tA2DP_CODEC_SEP_INDEX with btav_a2dp_codec_index_t
   from the bt_av.h API
 - Move codec-specific functions from stack/include/a2dp_api.h
   and stack/a2dp/a2dp_api.cc to stack/include/a2dp_codec_api.h
   and stack/a2dp/a2dp_codec_config.cc
 - Create a new base class A2dpCodecConfig() to hold some of the
   codec-related state, and implement the corresponding A2dpCodecConfigSbc
   and A2dpCodecConfigSbcSink derived classes.
 - Move A2DP spec-related constants from stack/include/a2dp_api.h
   to stack/include/a2dp_constants.h
 - Move A2DP-related error codes from stack/include/a2dp_api.h
   to stack/include/a2dp_error_codes.h
 - Move A2DP SBC spec-related constants from stack/include/a2dp_sbc.h to
   stack/include/a2dp_sbc_constants.h

* Implement the backend mechanism for handling user (re)configuration of
  A2DP Source codecs as requested via the JNI API calls.
  Also, any codec changes are reported back via JNI API callbacks.
  The current audio parameter selection (sample rate, bits per
  sample, channel mode - mono/stereo) is as follows:
  - If the user selected parameters are acceptable (based on
    local codec capability and the remote Sink capability),
    those parameters are used.
  - Else if the Audio HAL's requested parameters are acceptable,
    those are used.
  - Else if the default settings are acceptable, those are used.
  - Else use the best match among the local and the remote device's
    capabilities.

* Update the mechanism for handling OTA configuration requests from the
  remote Sink device.
  - The OTA prefered codec configuration is ignored if the current
  codec configuration contains explicit user configuration, or if the
  codec configuration for the same codec contains explicit user
  configuration.

* Refactor the Audio HAL <-> Bluetooth audio parameter negotiation
  mechanism:
  The new mechanism gives some flexibility to the Media Framework to
  choose the appropriate audio format (sample rate, bits per sample,
  and channel mode - mono/stereo), and at the same time allows
  the Bluetooth stack to double-check / overwrite the choice.
 - out_get_parameters() on the Audio HAL side asks the Bluetooth stack
   for all currently supported formats (for the current codec),
   and returns them to the Media Framework: sample rate, bits per sample,
   and channel mode (mono/stereo).
 - The first time adev_open_output_stream() is called on the Audio HAL,
   it asks the Bluetooth stack about the audio format currently selected
   by the Bluetooth stack (based on codec negotiation with the Sink device,
   and User Configuration).
 - The second time adev_open_output_stream() is called on the Audio HAL,
   its "config" will eventually contain the audio format selected
   internally by the Media Framework. That audio format is sent to the
   Bluetooth stack.
   If that format is acceptable to the Bluetooth stack, the Bluetooth
   stack will reconfigure itself internally, and will respond back with
   those values. Otherwise, it will respond back with the values that
   should be used instead.

* Misc other fixes and refactoring:
 - Fix the BTA handling of A2DP codec reconfiguration
 - Fix a bug in the implementation of A2DP_BitsSet(), and add the
   approriate unit test. Also, fix the code that was using this function
   incorrectly.
 - The SBC encoder is compiled as a separate library
 - Replace leftover usage of "false" with "FALSE" for macros, and
   vice-versa for variable values.

Test: A2DP streaming to headsets, TestPlans/71390
Bug: 30958229
Change-Id: I440b6126e2250e33b0075f9789dd93154c007c2b
diff --git a/audio_a2dp_hw/audio_a2dp_hw.cc b/audio_a2dp_hw/audio_a2dp_hw.cc
index a1e4a20..ecc27a6 100644
--- a/audio_a2dp_hw/audio_a2dp_hw.cc
+++ b/audio_a2dp_hw/audio_a2dp_hw.cc
@@ -40,6 +40,7 @@
 #include <mutex>
 
 #include <hardware/audio.h>
+#include <hardware/bt_av.h>
 #include <hardware/hardware.h>
 #include <system/audio.h>
 
@@ -326,7 +327,7 @@
  ****************************************************************************/
 
 static int a2dp_ctrl_receive(struct a2dp_stream_common* common, void* buffer,
-                             int length) {
+                             size_t length) {
   ssize_t ret;
   int i;
 
@@ -336,18 +337,18 @@
       break;
     }
     if (ret == 0) {
-      ERROR("ack failed: peer closed");
+      ERROR("receive control data failed: peer closed");
       break;
     }
     if (errno != EWOULDBLOCK && errno != EAGAIN) {
-      ERROR("ack failed: error(%s)", strerror(errno));
+      ERROR("receive control data failed: error(%s)", strerror(errno));
       break;
     }
     if (i == (CTRL_CHAN_RETRY_COUNT - 1)) {
-      ERROR("ack failed: max retry count");
+      ERROR("receive control data failed: max retry count");
       break;
     }
-    INFO("ack failed (%s), retrying", strerror(errno));
+    INFO("receive control data failed (%s), retrying", strerror(errno));
   }
   if (ret <= 0) {
     skt_disconnect(common->ctrl_fd);
@@ -356,6 +357,48 @@
   return ret;
 }
 
+// Sends control info for stream |common|. The data to send is stored in
+// |buffer| and has size |length|.
+// On success, returns the number of octets sent, otherwise -1.
+static int a2dp_ctrl_send(struct a2dp_stream_common* common, const void* buffer,
+                          size_t length) {
+  ssize_t sent;
+  size_t remaining = length;
+  int i;
+
+  if (length == 0) return 0;  // Nothing to do
+
+  for (i = 0;; i++) {
+    OSI_NO_INTR(sent = send(common->ctrl_fd, buffer, remaining, MSG_NOSIGNAL));
+    if (sent == static_cast<ssize_t>(remaining)) {
+      remaining = 0;
+      break;
+    }
+    if (sent > 0) {
+      buffer = (static_cast<const char*>(buffer) + sent);
+      remaining -= sent;
+      continue;
+    }
+    if (sent < 0) {
+      if (errno != EWOULDBLOCK && errno != EAGAIN) {
+        ERROR("send control data failed: error(%s)", strerror(errno));
+        break;
+      }
+      INFO("send control data failed (%s), retrying", strerror(errno));
+    }
+    if (i >= (CTRL_CHAN_RETRY_COUNT - 1)) {
+      ERROR("send control data failed: max retry count");
+      break;
+    }
+  }
+  if (remaining > 0) {
+    skt_disconnect(common->ctrl_fd);
+    common->ctrl_fd = AUDIO_SKT_DISCONNECTED;
+    return -1;
+  }
+  return length;
+}
+
 static int a2dp_command(struct a2dp_stream_common* common, tA2DP_CTRL_CMD cmd) {
   char ack;
 
@@ -452,73 +495,201 @@
   return 0;
 }
 
-static int a2dp_read_output_audio_config(struct a2dp_stream_common* common) {
-  tA2DP_SAMPLE_RATE sample_rate;
-  tA2DP_CHANNEL_COUNT channel_count;
-  tA2DP_BITS_PER_SAMPLE bits_per_sample;
+static int a2dp_read_output_audio_config(
+    struct a2dp_stream_common* common, btav_a2dp_codec_config_t* codec_config,
+    btav_a2dp_codec_config_t* codec_capability, bool update_stream_config) {
+  struct a2dp_config stream_config;
 
   if (a2dp_command(common, A2DP_CTRL_GET_OUTPUT_AUDIO_CONFIG) < 0) {
     ERROR("get a2dp output audio config failed");
     return -1;
   }
 
-  if (a2dp_ctrl_receive(common, &sample_rate, sizeof(tA2DP_SAMPLE_RATE)) < 0)
-    return -1;
-  if (a2dp_ctrl_receive(common, &channel_count, sizeof(tA2DP_CHANNEL_COUNT)) <
-      0) {
+  // Receive the current codec config
+  if (a2dp_ctrl_receive(common, &codec_config->sample_rate,
+                        sizeof(btav_a2dp_codec_sample_rate_t)) < 0) {
     return -1;
   }
-  if (a2dp_ctrl_receive(common, &bits_per_sample,
-                        sizeof(tA2DP_BITS_PER_SAMPLE)) < 0) {
+  if (a2dp_ctrl_receive(common, &codec_config->bits_per_sample,
+                        sizeof(btav_a2dp_codec_bits_per_sample_t)) < 0) {
+    return -1;
+  }
+  if (a2dp_ctrl_receive(common, &codec_config->channel_mode,
+                        sizeof(btav_a2dp_codec_channel_mode_t)) < 0) {
     return -1;
   }
 
-  // Check the sample rate
-  switch (sample_rate) {
-    case 44100:
-    case 48000:
-    case 88200:
-    case 96000:
-      common->cfg.rate = sample_rate;
+  // Receive the current codec capability
+  if (a2dp_ctrl_receive(common, &codec_capability->sample_rate,
+                        sizeof(btav_a2dp_codec_sample_rate_t)) < 0) {
+    return -1;
+  }
+  if (a2dp_ctrl_receive(common, &codec_capability->bits_per_sample,
+                        sizeof(btav_a2dp_codec_bits_per_sample_t)) < 0) {
+    return -1;
+  }
+  if (a2dp_ctrl_receive(common, &codec_capability->channel_mode,
+                        sizeof(btav_a2dp_codec_channel_mode_t)) < 0) {
+    return -1;
+  }
+
+  // Check the codec config sample rate
+  switch (codec_config->sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+      stream_config.rate = 44100;
       break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      stream_config.rate = 48000;
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+      stream_config.rate = 88200;
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+      stream_config.rate = 96000;
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+      stream_config.rate = 176400;
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+      stream_config.rate = 192000;
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
     default:
-      ERROR("Invalid sample rate: %" PRIu32, sample_rate);
+      ERROR("Invalid sample rate: 0x%x", codec_config->sample_rate);
       return -1;
   }
 
-  // Check the channel count
-  switch (channel_count) {
-    case 1:
-      common->cfg.channel_mask = AUDIO_CHANNEL_OUT_MONO;
+  // Check the codec config bits per sample
+  switch (codec_config->bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      stream_config.format = AUDIO_FORMAT_PCM_16_BIT;
       break;
-    case 2:
-      common->cfg.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+      stream_config.format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
       break;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+      stream_config.format = AUDIO_FORMAT_PCM_32_BIT;
+      break;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
     default:
-      ERROR("Invalid channel count: %" PRIu8, channel_count);
+      ERROR("Invalid bits per sample: 0x%x", codec_config->bits_per_sample);
       return -1;
   }
 
-  // Check the bits per sample
-  switch (bits_per_sample) {
-    case 16:
-      common->cfg.format = AUDIO_FORMAT_PCM_16_BIT;
+  // Check the codec config channel mode
+  switch (codec_config->channel_mode) {
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+      stream_config.channel_mask = AUDIO_CHANNEL_OUT_MONO;
       break;
-    case 24:
-      common->cfg.format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+      stream_config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
       break;
-    case 32:
-      common->cfg.format = AUDIO_FORMAT_PCM_32_BIT;
-      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
     default:
-      ERROR("Invalid bits per sample: %" PRIu8, bits_per_sample);
+      ERROR("Invalid channel mode: 0x%x", codec_config->channel_mode);
       return -1;
   }
 
+  // Update the output stream configuration
+  if (update_stream_config) {
+    common->cfg.rate = stream_config.rate;
+    common->cfg.channel_mask = stream_config.channel_mask;
+    common->cfg.format = stream_config.format;
+  }
+
   INFO(
-      "got output audio config: sample_rate=%u channel_count=%u "
-      "bits_per_sample=%u",
-      sample_rate, channel_count, bits_per_sample);
+      "got output codec capability: sample_rate=0x%x bits_per_sample=0x%x "
+      "channel_mode=0x%x",
+      codec_capability->sample_rate, codec_capability->bits_per_sample,
+      codec_capability->channel_mode);
+
+  return 0;
+}
+
+static int a2dp_write_output_audio_config(struct a2dp_stream_common* common) {
+  btav_a2dp_codec_config_t codec_config;
+
+  if (a2dp_command(common, A2DP_CTRL_SET_OUTPUT_AUDIO_CONFIG) < 0) {
+    ERROR("set a2dp output audio config failed");
+    return -1;
+  }
+
+  codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+  codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+  codec_config.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+
+  switch (common->cfg.rate) {
+    case 44100:
+      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+      break;
+    case 48000:
+      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+      break;
+    case 88200:
+      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_88200;
+      break;
+    case 96000:
+      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_96000;
+      break;
+    case 176400:
+      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_176400;
+      break;
+    case 192000:
+      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_192000;
+      break;
+    default:
+      ERROR("Invalid sample rate: %" PRIu32, common->cfg.rate);
+      return -1;
+  }
+
+  switch (common->cfg.format) {
+    case AUDIO_FORMAT_PCM_16_BIT:
+      codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+      break;
+    case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+      codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+      break;
+    case AUDIO_FORMAT_PCM_32_BIT:
+      codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
+      break;
+    case AUDIO_FORMAT_PCM_8_24_BIT:
+    // FALLTHROUGH
+    // All 24-bit audio is expected in AUDIO_FORMAT_PCM_24_BIT_PACKED format
+    default:
+      ERROR("Invalid audio format: 0x%x", common->cfg.format);
+      return -1;
+  }
+
+  switch (common->cfg.channel_mask) {
+    case AUDIO_CHANNEL_OUT_MONO:
+      codec_config.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+      break;
+    case AUDIO_CHANNEL_OUT_STEREO:
+      codec_config.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+      break;
+    default:
+      ERROR("Invalid channel mask: 0x%x", common->cfg.channel_mask);
+      return -1;
+  }
+
+  // Send the current codec config that has been selected by us
+  if (a2dp_ctrl_send(common, &codec_config.sample_rate,
+                     sizeof(btav_a2dp_codec_sample_rate_t)) < 0)
+    return -1;
+  if (a2dp_ctrl_send(common, &codec_config.bits_per_sample,
+                     sizeof(btav_a2dp_codec_bits_per_sample_t)) < 0) {
+    return -1;
+  }
+  if (a2dp_ctrl_send(common, &codec_config.channel_mode,
+                     sizeof(btav_a2dp_codec_channel_mode_t)) < 0) {
+    return -1;
+  }
+
+  INFO(
+      "sent output codec config: sample_rate=0x%x bits_per_sample=0x%x "
+      "channel_mode=0x%x",
+      codec_config.sample_rate, codec_config.bits_per_sample,
+      codec_config.channel_mode);
 
   return 0;
 }
@@ -826,6 +997,9 @@
                                 const char* keys) {
   FNLOG();
 
+  btav_a2dp_codec_config_t codec_config;
+  btav_a2dp_codec_config_t codec_capability;
+
   struct a2dp_stream_out* out = (struct a2dp_stream_out*)stream;
 
   std::unordered_map<std::string, std::string> params =
@@ -836,7 +1010,9 @@
 
   std::lock_guard<std::recursive_mutex> lock(*out->common.mutex);
 
-  if (a2dp_read_output_audio_config(&out->common) < 0) {
+  if (a2dp_read_output_audio_config(&out->common, &codec_config,
+                                    &codec_capability,
+                                    false /* update_stream_config */) < 0) {
     ERROR("a2dp_read_output_audio_config failed");
     goto done;
   }
@@ -844,66 +1020,83 @@
   // Add the format
   if (params.find(AUDIO_PARAMETER_STREAM_SUP_FORMATS) != params.end()) {
     std::string param;
-    switch (out->common.cfg.format) {
-      case AUDIO_FORMAT_PCM_16_BIT:
-        param = "AUDIO_FORMAT_PCM_16_BIT";
-        break;
-      case AUDIO_FORMAT_PCM_24_BIT_PACKED:
-        param = "AUDIO_FORMAT_PCM_24_BIT_PACKED";
-        break;
-      case AUDIO_FORMAT_PCM_8_24_BIT:
-        param = "AUDIO_FORMAT_PCM_8_24_BIT";
-        break;
-      case AUDIO_FORMAT_PCM_32_BIT:
-        param = "AUDIO_FORMAT_PCM_32_BIT";
-        break;
-      default:
-        ERROR("Invalid audio format: 0x%x", out->common.cfg.format);
-        break;
+    if (codec_capability.bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) {
+      if (!param.empty()) param += "|";
+      param += "AUDIO_FORMAT_PCM_16_BIT";
     }
-    if (!param.empty()) {
+    if (codec_capability.bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) {
+      if (!param.empty()) param += "|";
+      param += "AUDIO_FORMAT_PCM_24_BIT_PACKED";
+    }
+    if (codec_capability.bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) {
+      if (!param.empty()) param += "|";
+      param += "AUDIO_FORMAT_PCM_32_BIT";
+    }
+    if (param.empty()) {
+      ERROR("Invalid codec capability bits_per_sample=0x%x",
+            codec_capability.bits_per_sample);
+      goto done;
+    } else {
       return_params[AUDIO_PARAMETER_STREAM_SUP_FORMATS] = param;
     }
   }
 
-  // Add the channel mask
-  if (params.find(AUDIO_PARAMETER_STREAM_SUP_CHANNELS) != params.end()) {
-    std::string param;
-    switch (out->common.cfg.channel_mask) {
-      case AUDIO_CHANNEL_OUT_MONO:
-        param = "AUDIO_CHANNEL_OUT_MONO";
-        break;
-      case AUDIO_CHANNEL_OUT_STEREO:
-        param = "AUDIO_CHANNEL_OUT_STEREO";
-        break;
-      default:
-        ERROR("Invalid channel mask: 0x%x", out->common.cfg.channel_mask);
-        break;
-    }
-    if (!param.empty()) {
-      return_params[AUDIO_PARAMETER_STREAM_SUP_CHANNELS] = param;
-    }
-  }
-
   // Add the sample rate
   if (params.find(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) != params.end()) {
     std::string param;
-    switch (out->common.cfg.rate) {
-      case 44100:
-      case 48000:
-      case 88200:
-      case 96000:
-        param = std::to_string(out->common.cfg.rate);
-        break;
-      default:
-        ERROR("Invalid sample rate: %d", out->common.cfg.rate);
-        break;
+    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_44100) {
+      if (!param.empty()) param += "|";
+      param += "44100";
     }
-    if (!param.empty()) {
+    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_48000) {
+      if (!param.empty()) param += "|";
+      param += "48000";
+    }
+    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_88200) {
+      if (!param.empty()) param += "|";
+      param += "88200";
+    }
+    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_96000) {
+      if (!param.empty()) param += "|";
+      param += "96000";
+    }
+    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_176400) {
+      if (!param.empty()) param += "|";
+      param += "176400";
+    }
+    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_192000) {
+      if (!param.empty()) param += "|";
+      param += "192000";
+    }
+    if (param.empty()) {
+      ERROR("Invalid codec capability sample_rate=0x%x",
+            codec_capability.sample_rate);
+      goto done;
+    } else {
       return_params[AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES] = param;
     }
   }
 
+  // Add the channel mask
+  if (params.find(AUDIO_PARAMETER_STREAM_SUP_CHANNELS) != params.end()) {
+    std::string param;
+    if (codec_capability.channel_mode & BTAV_A2DP_CODEC_CHANNEL_MODE_MONO) {
+      if (!param.empty()) param += "|";
+      param += "AUDIO_CHANNEL_OUT_MONO";
+    }
+    if (codec_capability.channel_mode & BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO) {
+      if (!param.empty()) param += "|";
+      param += "AUDIO_CHANNEL_OUT_STEREO";
+    }
+    if (param.empty()) {
+      ERROR("Invalid codec capability channel_mode=0x%x",
+            codec_capability.channel_mode);
+      goto done;
+    } else {
+      return_params[AUDIO_PARAMETER_STREAM_SUP_CHANNELS] = param;
+    }
+  }
+
 done:
   std::string result;
   for (const auto& ptr : return_params) {
@@ -1187,19 +1380,49 @@
   /* initialize a2dp specifics */
   a2dp_stream_common_init(&out->common);
 
-  if (a2dp_read_output_audio_config(&out->common) < 0) {
+  // Make sure we always have the feeding parameters configured
+  btav_a2dp_codec_config_t codec_config;
+  btav_a2dp_codec_config_t codec_capability;
+  if (a2dp_read_output_audio_config(&out->common, &codec_config,
+                                    &codec_capability,
+                                    true /* update_stream_config */) < 0) {
     ERROR("a2dp_read_output_audio_config failed");
     ret = -1;
     goto err_open;
   }
 
   /* set output config values */
-  if (config) {
+  if (config != nullptr) {
+    // Try to use the config parameters and send it to the remote side
+    // TODO: Shall we use out_set_format() and similar?
+    if (config->format != 0) out->common.cfg.format = config->format;
+    if (config->sample_rate != 0) out->common.cfg.rate = config->sample_rate;
+    if (config->channel_mask != 0)
+      out->common.cfg.channel_mask = config->channel_mask;
+    if ((out->common.cfg.format != 0) || (out->common.cfg.rate != 0) ||
+        (out->common.cfg.channel_mask != 0)) {
+      if (a2dp_write_output_audio_config(&out->common) < 0) {
+        ERROR("a2dp_write_output_audio_config failed");
+        ret = -1;
+        goto err_open;
+      }
+      // Read again and make sure we use the same parameters as the remote side
+      if (a2dp_read_output_audio_config(&out->common, &codec_config,
+                                        &codec_capability,
+                                        true /* update_stream_config */) < 0) {
+        ERROR("a2dp_read_output_audio_config failed");
+        ret = -1;
+        goto err_open;
+      }
+    }
     config->format = out_get_format((const struct audio_stream*)&out->stream);
     config->sample_rate =
         out_get_sample_rate((const struct audio_stream*)&out->stream);
     config->channel_mask =
         out_get_channels((const struct audio_stream*)&out->stream);
+
+    INFO("Output stream config: format=0x%x sample_rate=%d channel_mask=0x%x",
+         config->format, config->sample_rate, config->channel_mask);
   }
   *stream_out = &out->stream;
   a2dp_dev->output = out;
diff --git a/audio_a2dp_hw/audio_a2dp_hw.h b/audio_a2dp_hw/audio_a2dp_hw.h
index 8fc6f7c..8a33b66 100644
--- a/audio_a2dp_hw/audio_a2dp_hw.h
+++ b/audio_a2dp_hw/audio_a2dp_hw.h
@@ -83,6 +83,7 @@
   A2DP_CTRL_CMD_SUSPEND,
   A2DP_CTRL_GET_INPUT_AUDIO_CONFIG,
   A2DP_CTRL_GET_OUTPUT_AUDIO_CONFIG,
+  A2DP_CTRL_SET_OUTPUT_AUDIO_CONFIG,
   A2DP_CTRL_CMD_OFFLOAD_START,
 } tA2DP_CTRL_CMD;
 
diff --git a/audio_a2dp_hw/audio_a2dp_hw_utils.cc b/audio_a2dp_hw/audio_a2dp_hw_utils.cc
index 5aedd27..199b0f9 100644
--- a/audio_a2dp_hw/audio_a2dp_hw_utils.cc
+++ b/audio_a2dp_hw/audio_a2dp_hw_utils.cc
@@ -31,6 +31,7 @@
     CASE_RETURN_STR(A2DP_CTRL_CMD_SUSPEND)
     CASE_RETURN_STR(A2DP_CTRL_GET_INPUT_AUDIO_CONFIG)
     CASE_RETURN_STR(A2DP_CTRL_GET_OUTPUT_AUDIO_CONFIG)
+    CASE_RETURN_STR(A2DP_CTRL_SET_OUTPUT_AUDIO_CONFIG)
     CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_START)
     default:
       break;
diff --git a/bta/av/bta_av_aact.cc b/bta/av/bta_av_aact.cc
index c9e8df3..4af40a1 100644
--- a/bta/av/bta_av_aact.cc
+++ b/bta/av/bta_av_aact.cc
@@ -248,7 +248,7 @@
  * Returns          void
  **********************************************/
 static uint8_t bta_av_get_scb_handle(tBTA_AV_SCB* p_scb, uint8_t local_sep) {
-  for (int i = 0; i < A2DP_CODEC_SEP_INDEX_MAX; i++) {
+  for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
     if ((p_scb->seps[i].tsep == local_sep) &&
         A2DP_CodecTypeEquals(p_scb->seps[i].codec_info,
                              p_scb->cfg.codec_info)) {
@@ -270,7 +270,7 @@
  **********************************************/
 static uint8_t bta_av_get_scb_sep_type(tBTA_AV_SCB* p_scb,
                                        uint8_t tavdt_handle) {
-  for (int i = 0; i < A2DP_CODEC_SEP_INDEX_MAX; i++) {
+  for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
     if (p_scb->seps[i].av_handle == tavdt_handle) return (p_scb->seps[i].tsep);
   }
   APPL_TRACE_DEBUG("%s: handle %d not found", __func__, tavdt_handle)
@@ -731,7 +731,7 @@
 static void bta_av_adjust_seps_idx(tBTA_AV_SCB* p_scb, uint8_t avdt_handle) {
   APPL_TRACE_DEBUG("%s: codec: %s", __func__,
                    A2DP_CodecName(p_scb->cfg.codec_info));
-  for (int i = 0; i < A2DP_CODEC_SEP_INDEX_MAX; i++) {
+  for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
     APPL_TRACE_DEBUG("%s: av_handle: %d codec: %s", __func__,
                      p_scb->seps[i].av_handle,
                      A2DP_CodecName(p_scb->seps[i].codec_info));
@@ -1041,7 +1041,7 @@
   p_scb->skip_sdp = false;
   if (p_scb->deregistring) {
     /* remove stream */
-    for (int i = 0; i < A2DP_CODEC_SEP_INDEX_MAX; i++) {
+    for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
       if (p_scb->seps[i].av_handle) AVDT_RemoveStream(p_scb->seps[i].av_handle);
       p_scb->seps[i].av_handle = 0;
     }
@@ -1923,7 +1923,7 @@
   BT_HDR* p_buf;
   uint8_t policy = HCI_ENABLE_SNIFF_MODE;
 
-  APPL_TRACE_ERROR("%s: audio_open_cnt=%d, p_data %x", __func__,
+  APPL_TRACE_ERROR("%s: audio_open_cnt=%d, p_data %p", __func__,
                    bta_av_cb.audio_open_cnt, p_data);
 
   bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
@@ -1985,8 +1985,11 @@
     suspend_rsp.initiator = true;
     APPL_TRACE_EVENT("%s: status %d", __func__, suspend_rsp.status);
 
-    /* send STOP_EVT event only if not in reconfiguring state */
-    if (p_scb->state != BTA_AV_RCFG_SST) {
+    // Send STOP_EVT event only if not in reconfiguring state.
+    // However, we should send STOP_EVT if we are reconfiguring when taking
+    // the Close->Configure->Open->Start path.
+    if (p_scb->state != BTA_AV_RCFG_SST ||
+        (p_data && p_data->api_stop.reconfig_stop)) {
       (*bta_av_cb.p_cback)(BTA_AV_STOP_EVT, (tBTA_AV*)&suspend_rsp);
     }
   }
@@ -2026,32 +2029,49 @@
   p_scb->rcfg_idx = p_rcfg->sep_info_idx;
   p_scb->p_cap->psc_mask = p_scb->cur_psc_mask;
 
-  /* if the requested index differs from the current one, we can only close/open
-   */
-  if ((p_scb->rcfg_idx == p_scb->sep_info_idx) && (p_rcfg->suspend) &&
-      (p_scb->recfg_sup) && (p_scb->suspend_sup)) {
+  // If the requested SEP index is same as the current one, then we
+  // can Suspend->Reconfigure->Start.
+  // Otherwise, we have to Close->Configure->Open->Start or
+  // Close->Configure->Open for streams that are / are not started.
+  if ((p_scb->rcfg_idx == p_scb->sep_info_idx) && p_rcfg->suspend &&
+      p_scb->recfg_sup && p_scb->suspend_sup) {
     if (p_scb->started) {
+      // Suspend->Reconfigure->Start
       stop.flush = false;
       stop.suspend = true;
+      stop.reconfig_stop = false;
       bta_av_str_stopped(p_scb, (tBTA_AV_DATA*)&stop);
     } else {
+      // Reconfigure
       APPL_TRACE_DEBUG("%s: reconfig", __func__);
       AVDT_ReconfigReq(p_scb->avdt_handle, p_scb->p_cap);
       p_scb->p_cap->psc_mask = p_scb->cur_psc_mask;
     }
   } else {
-    /* close the stream */
-    APPL_TRACE_DEBUG("%s: close/open num_protect: %d", __func__,
+    // Close the stream first, and then Configure it
+    APPL_TRACE_DEBUG("%s: Close/Open started: %d state: %d num_protect: %d",
+                     __func__, p_scb->started, p_scb->state,
                      p_cfg->num_protect);
     if (p_scb->started) {
-      bta_av_str_stopped(p_scb, NULL);
+      // Close->Configure->Open->Start
+      if ((p_scb->rcfg_idx != p_scb->sep_info_idx) && p_scb->recfg_sup) {
+        // Make sure we trigger STOP_EVT when taking the longer road to
+        // reconfiguration, otherwise we don't call Start.
+        stop.flush = false;
+        stop.suspend = false;
+        stop.reconfig_stop = true;
+        bta_av_str_stopped(p_scb, (tBTA_AV_DATA*)&stop);
+      } else {
+        bta_av_str_stopped(p_scb, NULL);
+      }
       p_scb->started = false;
-
-      /* drop the buffers queued in L2CAP */
-      L2CA_FlushChannel(p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
-
-      AVDT_CloseReq(p_scb->avdt_handle);
+    } else {
+      // Close->Configure->Open
+      bta_av_str_stopped(p_scb, NULL);
     }
+    // Drop the buffers queued in L2CAP
+    L2CA_FlushChannel(p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
+    AVDT_CloseReq(p_scb->avdt_handle);
   }
 }
 
@@ -2144,7 +2164,6 @@
  ******************************************************************************/
 void bta_av_start_ok(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   tBTA_AV_START start;
-  tBTA_AV_API_STOP stop;
   bool initiator = false;
   bool suspend = false;
   uint16_t flush_to;
@@ -2290,6 +2309,7 @@
     (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV*)&start);
 
     if (suspend) {
+      tBTA_AV_API_STOP stop;
       p_scb->role |= BTA_AV_ROLE_SUSPEND;
       p_scb->cong = true; /* do not allow the media data to go through */
       /* do not duplicate the media packets to this channel */
@@ -2297,6 +2317,7 @@
       p_scb->co_started = false;
       stop.flush = false;
       stop.suspend = true;
+      stop.reconfig_stop = false;
       bta_av_ssm_execute(p_scb, BTA_AV_AP_STOP_EVT, (tBTA_AV_DATA*)&stop);
     }
   }
diff --git a/bta/av/bta_av_api.cc b/bta/av/bta_av_api.cc
index 8c005dc..581a920 100644
--- a/bta/av/bta_av_api.cc
+++ b/bta/av/bta_av_api.cc
@@ -277,6 +277,7 @@
   p_buf->hdr.event = BTA_AV_API_STOP_EVT;
   p_buf->flush = true;
   p_buf->suspend = suspend;
+  p_buf->reconfig_stop = false;
 
   bta_sys_sendmsg(p_buf);
 }
diff --git a/bta/av/bta_av_int.h b/bta/av/bta_av_int.h
index 30cb104..3f1fccc 100644
--- a/bta/av/bta_av_int.h
+++ b/bta/av/bta_av_int.h
@@ -157,7 +157,7 @@
  ****************************************************************************/
 
 /* function types for call-out functions */
-typedef bool (*tBTA_AV_CO_INIT)(tA2DP_CODEC_SEP_INDEX codec_sep_index,
+typedef bool (*tBTA_AV_CO_INIT)(btav_a2dp_codec_index_t codec_index,
                                 tAVDT_CFG* p_cfg);
 typedef void (*tBTA_AV_CO_DISC_RES)(tBTA_AV_HNDL hndl, uint8_t num_seps,
                                     uint8_t num_snk, uint8_t num_src,
@@ -234,6 +234,7 @@
   BT_HDR hdr;
   bool suspend;
   bool flush;
+  bool reconfig_stop;  // True if the stream is stopped for reconfiguration
 } tBTA_AV_API_STOP;
 
 /* data type for BTA_AV_API_DISCONNECT_EVT */
@@ -443,7 +444,7 @@
   const tBTA_AV_ACT* p_act_tbl; /* the action table for stream state machine */
   const tBTA_AV_CO_FUNCTS* p_cos; /* the associated callout functions */
   bool sdp_discovery_started; /* variable to determine whether SDP is started */
-  tBTA_AV_SEP seps[A2DP_CODEC_SEP_INDEX_MAX];
+  tBTA_AV_SEP seps[BTAV_A2DP_CODEC_INDEX_MAX];
   tAVDT_CFG* p_cap;  /* buffer used for get capabilities */
   list_t* a2dp_list; /* used for audio channels only */
   tBTA_AV_Q_INFO q_info;
diff --git a/bta/av/bta_av_main.cc b/bta/av/bta_av_main.cc
index 02bfa4d..c8adbe9 100644
--- a/bta/av/bta_av_main.cc
+++ b/bta/av/bta_av_main.cc
@@ -510,10 +510,10 @@
       cs.media_type = AVDT_MEDIA_TYPE_AUDIO;
       cs.mtu = p_bta_av_cfg->audio_mtu;
       cs.flush_to = L2CAP_DEFAULT_FLUSH_TO;
-      tA2DP_CODEC_SEP_INDEX codec_sep_index_min =
-          A2DP_CODEC_SEP_INDEX_SOURCE_MIN;
-      tA2DP_CODEC_SEP_INDEX codec_sep_index_max =
-          A2DP_CODEC_SEP_INDEX_SOURCE_MAX;
+      btav_a2dp_codec_index_t codec_index_min =
+          BTAV_A2DP_CODEC_INDEX_SOURCE_MIN;
+      btav_a2dp_codec_index_t codec_index_max =
+          BTAV_A2DP_CODEC_INDEX_SOURCE_MAX;
 
 #if (AVDT_REPORTING == TRUE)
       if (bta_av_cb.features & BTA_AV_FEAT_REPORT) {
@@ -526,44 +526,44 @@
 
       if (profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) {
         cs.tsep = AVDT_TSEP_SRC;
-        codec_sep_index_min = A2DP_CODEC_SEP_INDEX_SOURCE_MIN;
-        codec_sep_index_max = A2DP_CODEC_SEP_INDEX_SOURCE_MAX;
+        codec_index_min = BTAV_A2DP_CODEC_INDEX_SOURCE_MIN;
+        codec_index_max = BTAV_A2DP_CODEC_INDEX_SOURCE_MAX;
       } else if (profile_initialized == UUID_SERVCLASS_AUDIO_SINK) {
         cs.tsep = AVDT_TSEP_SNK;
         cs.p_sink_data_cback = bta_av_sink_data_cback;
-        codec_sep_index_min = A2DP_CODEC_SEP_INDEX_SINK_MIN;
-        codec_sep_index_max = A2DP_CODEC_SEP_INDEX_SINK_MAX;
+        codec_index_min = BTAV_A2DP_CODEC_INDEX_SINK_MIN;
+        codec_index_max = BTAV_A2DP_CODEC_INDEX_SINK_MAX;
       }
 
       /* Initialize handles to zero */
-      for (int xx = 0; xx < A2DP_CODEC_SEP_INDEX_MAX; xx++) {
+      for (int xx = 0; xx < BTAV_A2DP_CODEC_INDEX_MAX; xx++) {
         p_scb->seps[xx].av_handle = 0;
       }
 
       /* keep the configuration in the stream control block */
       memcpy(&p_scb->cfg, &cs.cfg, sizeof(tAVDT_CFG));
-      for (int i = codec_sep_index_min; i < codec_sep_index_max; i++) {
-        tA2DP_CODEC_SEP_INDEX codec_sep_index =
-            static_cast<tA2DP_CODEC_SEP_INDEX>(i);
-        if (!(*bta_av_a2dp_cos.init)(codec_sep_index, &cs.cfg)) {
+      for (int i = codec_index_min; i < codec_index_max; i++) {
+        btav_a2dp_codec_index_t codec_index =
+            static_cast<btav_a2dp_codec_index_t>(i);
+        if (!(*bta_av_a2dp_cos.init)(codec_index, &cs.cfg)) {
           continue;
         }
-        if (AVDT_CreateStream(&p_scb->seps[codec_sep_index].av_handle, &cs) !=
+        if (AVDT_CreateStream(&p_scb->seps[codec_index].av_handle, &cs) !=
             AVDT_SUCCESS) {
           continue;
         }
         /* Save a copy of the codec */
-        memcpy(p_scb->seps[codec_sep_index].codec_info, cs.cfg.codec_info,
+        memcpy(p_scb->seps[codec_index].codec_info, cs.cfg.codec_info,
                AVDT_CODEC_SIZE);
-        p_scb->seps[codec_sep_index].tsep = cs.tsep;
+        p_scb->seps[codec_index].tsep = cs.tsep;
         if (cs.tsep == AVDT_TSEP_SNK) {
-          p_scb->seps[codec_sep_index].p_app_sink_data_cback =
+          p_scb->seps[codec_index].p_app_sink_data_cback =
               p_data->api_reg.p_app_sink_data_cback;
         } else {
           /* In case of A2DP SOURCE we don't need a callback to
            * handle media packets.
            */
-          p_scb->seps[codec_sep_index].p_app_sink_data_cback = NULL;
+          p_scb->seps[codec_index].p_app_sink_data_cback = NULL;
         }
       }
 
@@ -900,6 +900,7 @@
         p_scb->sco_suspend = true;
         stop.flush = false;
         stop.suspend = true;
+        stop.reconfig_stop = false;
         bta_av_ssm_execute(p_scb, BTA_AV_AP_STOP_EVT, (tBTA_AV_DATA*)&stop);
       }
     }
diff --git a/bta/include/bta_av_api.h b/bta/include/bta_av_api.h
index f8104db..7bd57f3 100644
--- a/bta/include/bta_av_api.h
+++ b/bta/include/bta_av_api.h
@@ -26,7 +26,7 @@
 #ifndef BTA_AV_API_H
 #define BTA_AV_API_H
 
-#include "a2dp_api.h"
+#include "a2dp_codec_api.h"
 #include "avdt_api.h"
 #include "avrc_api.h"
 #include "bta_api.h"
diff --git a/bta/include/bta_av_co.h b/bta/include/bta_av_co.h
index d1d751b..34c539a 100644
--- a/bta/include/bta_av_co.h
+++ b/bta/include/bta_av_co.h
@@ -44,7 +44,7 @@
  * Returns          Stream codec and content protection capabilities info.
  *
  ******************************************************************************/
-bool bta_av_co_audio_init(tA2DP_CODEC_SEP_INDEX codec_sep_index,
+bool bta_av_co_audio_init(btav_a2dp_codec_index_t codec_index,
                           tAVDT_CFG* p_cfg);
 
 /*******************************************************************************
diff --git a/btif/Android.mk b/btif/Android.mk
index 3cca466..843934a 100644
--- a/btif/Android.mk
+++ b/btif/Android.mk
@@ -143,7 +143,7 @@
 LOCAL_SRC_FILES := $(btifTestSrc)
 LOCAL_SHARED_LIBRARIES := liblog libhardware libhardware_legacy libcutils \
 	libchrome
-LOCAL_STATIC_LIBRARIES := libbtcore libbtif libosi
+LOCAL_STATIC_LIBRARIES := libbtcore libbtif libbt-stack libosi
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := net_test_btif
 
diff --git a/btif/co/bta_av_co.cc b/btif/co/bta_av_co.cc
index 90abbd9..de8c9cb 100644
--- a/btif/co/bta_av_co.cc
+++ b/btif/co/bta_av_co.cc
@@ -27,11 +27,13 @@
 #include <base/logging.h>
 #include <string.h>
 #include "a2dp_api.h"
+#include "a2dp_sbc.h"
 #include "bt_target.h"
 #include "bta_av_api.h"
 #include "bta_av_ci.h"
 #include "bta_sys.h"
 
+#include "btif_av.h"
 #include "btif_av_co.h"
 #include "btif_util.h"
 #include "osi/include/mutex.h"
@@ -65,8 +67,8 @@
 typedef struct {
   BD_ADDR addr; /* address of audio/video peer */
   tBTA_AV_CO_SINK
-      sinks[A2DP_CODEC_SEP_INDEX_MAX];            /* array of supported sinks */
-  tBTA_AV_CO_SINK srcs[A2DP_CODEC_SEP_INDEX_MAX]; /* array of supported srcs */
+      sinks[BTAV_A2DP_CODEC_INDEX_MAX]; /* array of supported sinks */
+  tBTA_AV_CO_SINK srcs[BTAV_A2DP_CODEC_INDEX_MAX]; /* array of supported srcs */
   uint8_t num_sinks;     /* total number of sinks at peer */
   uint8_t num_srcs;      /* total number of srcs at peer */
   uint8_t num_seps;      /* total number of seids at peer */
@@ -83,6 +85,7 @@
   bool opened;                           /* opened */
   uint16_t mtu;                          /* maximum transmit unit size */
   uint16_t uuid_to_connect;              /* uuid of peer device */
+  tBTA_AV_HNDL handle;                   /* handle to use */
 } tBTA_AV_CO_PEER;
 
 typedef struct {
@@ -90,16 +93,36 @@
   uint8_t flag;
 } tBTA_AV_CO_CP;
 
-typedef struct {
+class BtaAvCoCb {
+ public:
+  BtaAvCoCb() : codecs(nullptr) { reset(); }
+
   /* Connected peer information */
   tBTA_AV_CO_PEER peers[BTA_AV_NUM_STRS];
   /* Current codec configuration - access to this variable must be protected */
   uint8_t codec_config[AVDT_CODEC_SIZE];
+  A2dpCodecs* codecs; /* Locally supported codecs */
   tBTA_AV_CO_CP cp;
-} tBTA_AV_CO_CB;
+
+  void reset() {
+    delete codecs;
+    codecs = nullptr;
+    // TODO: Ugly leftover reset from the original C code. Should go away once
+    // the rest of the code in this file migrates to C++.
+    memset(peers, 0, sizeof(peers));
+    memset(codec_config, 0, sizeof(codec_config));
+    memset(&cp, 0, sizeof(cp));
+
+    // Initialize the handles
+    for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers); i++) {
+      tBTA_AV_CO_PEER* p_peer = &peers[i];
+      p_peer->handle = BTA_AV_CO_AUDIO_INDX_TO_HNDL(i);
+    }
+  }
+};
 
 /* Control block instance */
-static tBTA_AV_CO_CB bta_av_co_cb;
+static BtaAvCoCb bta_av_co_cb;
 
 static bool bta_av_co_cp_is_scmst(const uint8_t* p_protect_info);
 static bool bta_av_co_audio_protect_has_scmst(uint8_t num_protect,
@@ -108,11 +131,16 @@
     const tBTA_AV_CO_PEER* p_peer);
 static tBTA_AV_CO_SINK* bta_av_co_audio_set_codec(tBTA_AV_CO_PEER* p_peer);
 static tBTA_AV_CO_SINK* bta_av_co_audio_codec_selected(
-    tA2DP_CODEC_SEP_INDEX source_codec_sep_index, tBTA_AV_CO_PEER* p_peer);
+    A2dpCodecConfig& codec_config, tBTA_AV_CO_PEER* p_peer);
 static void bta_av_co_save_new_codec_config(tBTA_AV_CO_PEER* p_peer,
                                             const uint8_t* new_codec_config,
                                             uint8_t num_protect,
                                             const uint8_t* p_protect_info);
+static bool bta_av_co_set_codec_ota_config(tBTA_AV_CO_PEER* p_peer,
+                                           const uint8_t* p_ota_codec_config,
+                                           uint8_t num_protect,
+                                           const uint8_t* p_protect_info,
+                                           bool* p_restart_output);
 
 /*******************************************************************************
  **
@@ -191,9 +219,9 @@
  ** Returns          Stream codec and content protection capabilities info.
  **
  ******************************************************************************/
-bool bta_av_co_audio_init(tA2DP_CODEC_SEP_INDEX codec_sep_index,
+bool bta_av_co_audio_init(btav_a2dp_codec_index_t codec_index,
                           tAVDT_CFG* p_cfg) {
-  return A2DP_InitCodecConfig(codec_sep_index, p_cfg);
+  return A2DP_InitCodecConfig(codec_index, p_cfg);
 }
 
 /*******************************************************************************
@@ -259,7 +287,6 @@
     uint8_t seid, uint8_t* p_num_protect, uint8_t* p_protect_info) {
   tA2DP_STATUS result = A2DP_FAIL;
   tBTA_AV_CO_PEER* p_peer;
-  uint8_t pref_config[AVDT_CODEC_SIZE];
 
   APPL_TRACE_DEBUG("%s: handle:0x%x codec:%s seid:%d", __func__, hndl,
                    A2DP_CodecName(p_codec_info), seid);
@@ -312,36 +339,35 @@
     const tBTA_AV_CO_SINK* p_src =
         bta_av_co_find_peer_src_supports_codec(p_peer);
     if (p_src != NULL) {
+      uint8_t pref_config[AVDT_CODEC_SIZE];
       APPL_TRACE_DEBUG("%s: codec supported", __func__);
 
       /* Build the codec configuration for this sink */
-      {
-        /* Save the new configuration */
-        p_peer->p_src = p_src;
-        /* get preferred config from src_caps */
-        if (A2DP_BuildSrc2SinkConfig(p_src->codec_caps, pref_config) !=
-            A2DP_SUCCESS) {
-          mutex_global_unlock();
-          return A2DP_FAIL;
-        }
-        memcpy(p_peer->codec_config, pref_config, AVDT_CODEC_SIZE);
+      /* Save the new configuration */
+      p_peer->p_src = p_src;
+      /* get preferred config from src_caps */
+      if (A2DP_BuildSrc2SinkConfig(p_src->codec_caps, pref_config) !=
+          A2DP_SUCCESS) {
+        mutex_global_unlock();
+        return A2DP_FAIL;
+      }
+      memcpy(p_peer->codec_config, pref_config, AVDT_CODEC_SIZE);
 
-        APPL_TRACE_DEBUG("%s: p_codec_info[%x:%x:%x:%x:%x:%x]", __func__,
-                         p_peer->codec_config[1], p_peer->codec_config[2],
-                         p_peer->codec_config[3], p_peer->codec_config[4],
-                         p_peer->codec_config[5], p_peer->codec_config[6]);
-        /* By default, no content protection */
-        *p_num_protect = 0;
+      APPL_TRACE_DEBUG("%s: p_codec_info[%x:%x:%x:%x:%x:%x]", __func__,
+                       p_peer->codec_config[1], p_peer->codec_config[2],
+                       p_peer->codec_config[3], p_peer->codec_config[4],
+                       p_peer->codec_config[5], p_peer->codec_config[6]);
+      /* By default, no content protection */
+      *p_num_protect = 0;
 
 #if (BTA_AV_CO_CP_SCMS_T == TRUE)
-        p_peer->cp_active = false;
-        bta_av_co_cb.cp.active = false;
+      p_peer->cp_active = false;
+      bta_av_co_cb.cp.active = false;
 #endif
 
-        *p_sep_info_idx = p_src->sep_info_idx;
-        memcpy(p_codec_info, p_peer->codec_config, AVDT_CODEC_SIZE);
-        result = A2DP_SUCCESS;
-      }
+      *p_sep_info_idx = p_src->sep_info_idx;
+      memcpy(p_codec_info, p_peer->codec_config, AVDT_CODEC_SIZE);
+      result = A2DP_SUCCESS;
     }
     /* Protect access to bta_av_co_cb.codec_config */
     mutex_global_unlock();
@@ -516,7 +542,6 @@
   }
 
   if (status == A2DP_SUCCESS) {
-    uint8_t new_codec_config[AVDT_CODEC_SIZE];
     bool codec_config_supported = false;
 
     if (t_local_sep == AVDT_TSEP_SNK) {
@@ -525,19 +550,22 @@
       if (codec_config_supported) {
         // If Peer is SRC, and our config subset matches with what is
         // requested by peer, then just accept what peer wants.
-        memcpy(new_codec_config, p_codec_info, sizeof(new_codec_config));
+        bta_av_co_save_new_codec_config(p_peer, p_codec_info, num_protect,
+                                        p_protect_info);
       }
     }
     if (t_local_sep == AVDT_TSEP_SRC) {
       APPL_TRACE_DEBUG("%s: peer is A2DP SINK", __func__);
-      if (A2DP_InitSource2SinkCodec(p_codec_info, new_codec_config) !=
-          A2DP_SUCCESS) {
-        APPL_TRACE_DEBUG("%s: cannot init source codec %s", __func__,
+      bool restart_output = false;
+      if ((bta_av_co_cb.codecs == nullptr) ||
+          !bta_av_co_set_codec_ota_config(p_peer, p_codec_info, num_protect,
+                                          p_protect_info, &restart_output)) {
+        APPL_TRACE_DEBUG("%s: cannot set source codec %s", __func__,
                          A2DP_CodecName(p_codec_info));
       } else {
         codec_config_supported = true;
         // Check if reconfiguration is needed
-        if (!A2DP_CodecEquals(new_codec_config, bta_av_co_cb.codec_config) ||
+        if (restart_output ||
             ((num_protect == 1) && (!bta_av_co_cb.cp.active))) {
           reconfig_needed = true;
         }
@@ -548,9 +576,6 @@
     if (!codec_config_supported) {
       category = AVDT_ASC_CODEC;
       status = A2DP_WRONG_CODEC;
-    } else {
-      bta_av_co_save_new_codec_config(p_peer, new_codec_config, num_protect,
-                                      p_protect_info);
     }
   }
 
@@ -826,34 +851,24 @@
 // Return true on success, otherwise false.
 //
 static tBTA_AV_CO_SINK* bta_av_co_audio_set_codec(tBTA_AV_CO_PEER* p_peer) {
-  // Initial strawman codec selection mechanism: largest codec SEP index first.
-  // TODO: Replace this mechanism with a better one.
-  for (int i = A2DP_CODEC_SEP_INDEX_SOURCE_MAX - 1;
-       i >= A2DP_CODEC_SEP_INDEX_SOURCE_MIN; i--) {
-    tA2DP_CODEC_SEP_INDEX source_codec_sep_index =
-        static_cast<tA2DP_CODEC_SEP_INDEX>(i);
-    APPL_TRACE_DEBUG("%s: trying codec %s with sep_index %d", __func__,
-                     A2DP_CodecSepIndexStr(source_codec_sep_index), i);
+  for (const auto& iter : bta_av_co_cb.codecs->orderedSourceCodecs()) {
+    APPL_TRACE_DEBUG("%s: trying codec %s", __func__, iter->name().c_str());
     // Try to select an open device for the codec
-    tBTA_AV_CO_SINK* p_sink =
-        bta_av_co_audio_codec_selected(source_codec_sep_index, p_peer);
+    tBTA_AV_CO_SINK* p_sink = bta_av_co_audio_codec_selected(*iter, p_peer);
     if (p_sink != NULL) {
-      APPL_TRACE_DEBUG("%s: selected codec %s with sep_index %d", __func__,
-                       A2DP_CodecSepIndexStr(source_codec_sep_index), i);
+      APPL_TRACE_DEBUG("%s: selected codec %s", __func__, iter->name().c_str());
       return p_sink;
     }
-    APPL_TRACE_DEBUG("%s: cannot select source codec %s", __func__,
-                     A2DP_CodecSepIndexStr(source_codec_sep_index));
+    APPL_TRACE_DEBUG("%s: cannot use codec %s", __func__, iter->name().c_str());
   }
 
   return NULL;
 }
 
-// Select an open device for the preferred codec specified by
-// |source_codec_sep_index|.
+// Select an open device for the preferred codec specified by |codec_config|.
 // Return the corresponding peer that supports the codec, otherwise NULL.
 static tBTA_AV_CO_SINK* bta_av_co_audio_codec_selected(
-    tA2DP_CODEC_SEP_INDEX source_codec_sep_index, tBTA_AV_CO_PEER* p_peer) {
+    A2dpCodecConfig& codec_config, tBTA_AV_CO_PEER* p_peer) {
   uint8_t new_codec_config[AVDT_CODEC_SIZE];
 
   APPL_TRACE_DEBUG("%s", __func__);
@@ -861,16 +876,16 @@
   // Find the peer sink for the codec
   tBTA_AV_CO_SINK* p_sink = NULL;
   for (size_t index = 0; index < p_peer->num_sup_sinks; index++) {
-    tA2DP_CODEC_SEP_INDEX peer_sep_index =
-        A2DP_SourceCodecSepIndex(p_peer->sinks[index].codec_caps);
-    if (peer_sep_index != source_codec_sep_index) {
+    btav_a2dp_codec_index_t peer_codec_index =
+        A2DP_SourceCodecIndex(p_peer->sinks[index].codec_caps);
+    if (peer_codec_index != codec_config.codecIndex()) {
       continue;
     }
     if (!bta_av_co_audio_sink_supports_cp(&p_peer->sinks[index])) {
       APPL_TRACE_DEBUG(
           "%s: peer sink for codec %s does not support "
           "Copy Protection",
-          __func__, A2DP_CodecSepIndexStr(source_codec_sep_index));
+          __func__, codec_config.name().c_str());
       continue;
     }
     p_sink = &p_peer->sinks[index];
@@ -878,13 +893,13 @@
   }
   if (p_sink == NULL) {
     APPL_TRACE_DEBUG("%s: peer sink for codec %s not found", __func__,
-                     A2DP_CodecSepIndexStr(source_codec_sep_index));
+                     codec_config.name().c_str());
     return NULL;
   }
-  if (A2DP_InitSource2SinkCodec(p_sink->codec_caps, new_codec_config) !=
-      A2DP_SUCCESS) {
-    APPL_TRACE_DEBUG("%s: cannot init source codec %s", __func__,
-                     A2DP_CodecSepIndexStr(source_codec_sep_index));
+  if (!bta_av_co_cb.codecs->setCodecConfig(p_sink->codec_caps, true,
+                                           new_codec_config)) {
+    APPL_TRACE_DEBUG("%s: cannot set source codec %s", __func__,
+                     codec_config.name().c_str());
     return NULL;
   }
   p_peer->p_sink = p_sink;
@@ -917,31 +932,11 @@
   mutex_global_unlock();
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_codec_reset
- **
- ** Description      Reset the current codec configuration
- **
- ** Returns          void
- **
- ******************************************************************************/
-static void bta_av_co_audio_codec_reset(void) {
-  APPL_TRACE_DEBUG("%s", __func__);
-
-  mutex_global_lock();
-
-  /* Reset the current configuration to the default codec */
-  A2DP_InitDefaultCodec(bta_av_co_cb.codec_config);
-
-  mutex_global_unlock();
-}
-
-void bta_av_co_audio_encoder_init(tA2DP_ENCODER_INIT_PARAMS* p_init_params) {
+void bta_av_co_get_peer_params(tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params) {
   uint16_t min_mtu = 0xFFFF;
 
   APPL_TRACE_DEBUG("%s", __func__);
-  CHECK(p_init_params != nullptr);
+  CHECK(p_peer_params != nullptr);
 
   /* Protect access to bta_av_co_cb.codec_config */
   mutex_global_lock();
@@ -952,42 +947,9 @@
     if (!p_peer->opened) continue;
     if (p_peer->mtu < min_mtu) min_mtu = p_peer->mtu;
   }
-
-  memcpy(p_init_params->codec_config, bta_av_co_cb.codec_config,
-         sizeof(p_init_params->codec_config));
-  p_init_params->peer_mtu = min_mtu;
-
-  /* Protect access to bta_av_co_cb.codec_config */
-  mutex_global_unlock();
-}
-
-void bta_av_co_get_encoder_feeding_parameters(
-    tA2DP_FEEDING_PARAMS* p_feeding_params) {
-  /* Protect access to bta_av_co_cb.codec_config */
-  mutex_global_lock();
-
-  const uint8_t* p_codec_info = bta_av_co_cb.codec_config;
-  int sample_rate = A2DP_GetTrackSampleRate(p_codec_info);
-  if (sample_rate > 0) {
-    p_feeding_params->sample_rate = sample_rate;
-  } else {
-    APPL_TRACE_ERROR("%s: invalid sample rate for codec %s: %d", __func__,
-                     A2DP_CodecName(p_codec_info), sample_rate);
-  }
-  int channel_count = A2DP_GetTrackChannelCount(p_codec_info);
-  if (channel_count > 0) {
-    p_feeding_params->channel_count = channel_count;
-  } else {
-    APPL_TRACE_ERROR("%s: invalid channel count for codec %s: %d", __func__,
-                     A2DP_CodecName(p_codec_info), channel_count);
-  }
-  int bits_per_sample = A2DP_GetTrackBitsPerSample(p_codec_info);
-  if (bits_per_sample > 0) {
-    p_feeding_params->bits_per_sample = bits_per_sample;
-  } else {
-    APPL_TRACE_ERROR("%s: invalid bits per sample for codec %s: %d", __func__,
-                     A2DP_CodecName(p_codec_info), bits_per_sample);
-  }
+  p_peer_params->peer_mtu = min_mtu;
+  p_peer_params->is_peer_edr = btif_av_is_peer_edr();
+  p_peer_params->peer_supports_3mbps = btif_av_peer_supports_3mbps();
 
   /* Protect access to bta_av_co_cb.codec_config */
   mutex_global_unlock();
@@ -1006,20 +968,233 @@
   return encoder_interface;
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_init
- **
- ** Description      Initialization
- **
- ** Returns          Nothing
- **
- ******************************************************************************/
+bool bta_av_co_set_codec_user_config(
+    const btav_a2dp_codec_config_t& codec_user_config) {
+  uint8_t result_codec_config[AVDT_CODEC_SIZE];
+  bool restart_input = false;
+  bool restart_output = false;
+  bool config_updated = false;
+
+  // Find the peer that is currently open
+  tBTA_AV_CO_PEER* p_peer = nullptr;
+  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(bta_av_co_cb.peers); i++) {
+    tBTA_AV_CO_PEER* p_peer_tmp = &bta_av_co_cb.peers[i];
+    if (p_peer_tmp->opened) {
+      p_peer = p_peer_tmp;
+      break;
+    }
+  }
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR("%s: no open peer to configure", __func__);
+    return false;
+  }
+
+  // Find the peer SEP codec to use
+  const tBTA_AV_CO_SINK* p_sink = nullptr;
+  if (codec_user_config.codec_type < BTAV_A2DP_CODEC_INDEX_MAX) {
+    for (size_t index = 0; index < p_peer->num_sup_sinks; index++) {
+      btav_a2dp_codec_index_t peer_codec_index =
+          A2DP_SourceCodecIndex(p_peer->sinks[index].codec_caps);
+      if (peer_codec_index != codec_user_config.codec_type) continue;
+      if (!bta_av_co_audio_sink_supports_cp(&p_peer->sinks[index])) continue;
+      p_sink = &p_peer->sinks[index];
+      break;
+    }
+  } else {
+    // Use the current sink codec
+    p_sink = p_peer->p_sink;
+  }
+  if (p_sink == nullptr) {
+    APPL_TRACE_ERROR("%s: cannot find peer SEP to configure", __func__);
+    return false;
+  }
+
+  tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
+  bta_av_co_get_peer_params(&peer_params);
+  if (!bta_av_co_cb.codecs->setCodecUserConfig(
+          codec_user_config, &peer_params, p_sink->codec_caps,
+          result_codec_config, &restart_input, &restart_output,
+          &config_updated)) {
+    return false;
+  }
+
+  if (restart_output) {
+    uint8_t num_protect = 0;
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+    if (p_peer->cp_active) num_protect = AVDT_CP_INFO_LEN;
+#endif
+
+    p_peer->p_sink = p_sink;
+    bta_av_co_save_new_codec_config(p_peer, result_codec_config,
+                                    p_sink->num_protect, p_sink->protect_info);
+    APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(x%x)", __func__, p_peer->handle);
+    BTA_AvReconfig(p_peer->handle, true, p_sink->sep_info_idx,
+                   p_peer->codec_config, num_protect, bta_av_co_cp_scmst);
+  }
+
+  if (restart_input || config_updated) {
+    // NOTE: Currently, the input is restarted by sending an upcall
+    // and informing the Media Framework about the change.
+    btif_dispatch_sm_event(BTIF_AV_SOURCE_CONFIG_UPDATED_EVT, NULL, 0);
+  }
+
+  return true;
+}
+
+// Sets the Over-The-Air preferred codec configuration.
+// The OTA prefered codec configuration is ignored if the current
+// codec configuration contains explicit user configuration, or if the
+// codec configuration for the same codec contains explicit user
+// configuration.
+// |p_peer| is the peer device that sent the OTA codec configuration.
+// |p_ota_codec_config| contains the received OTA A2DP codec configuration
+// from the remote peer. Note: this is not the peer codec capability,
+// but the codec configuration that the peer would like to use.
+// |num_protect| is the number of content protection methods to use.
+// |p_protect_info| contains the content protection information to use.
+// If there is a change in the encoder configuration tht requires restarting
+// of the A2DP connection, flag |p_restart_output| is set to true.
+// Returns true on success, otherwise false.
+static bool bta_av_co_set_codec_ota_config(tBTA_AV_CO_PEER* p_peer,
+                                           const uint8_t* p_ota_codec_config,
+                                           uint8_t num_protect,
+                                           const uint8_t* p_protect_info,
+                                           bool* p_restart_output) {
+  uint8_t result_codec_config[AVDT_CODEC_SIZE];
+  bool restart_input = false;
+  bool restart_output = false;
+  bool config_updated = false;
+
+  *p_restart_output = false;
+
+  // Find the peer SEP codec to use
+  btav_a2dp_codec_index_t ota_codec_index =
+      A2DP_SourceCodecIndex(p_ota_codec_config);
+  if (ota_codec_index == BTAV_A2DP_CODEC_INDEX_MAX) {
+    APPL_TRACE_WARNING("%s: invalid peer codec config", __func__);
+    return false;
+  }
+  const tBTA_AV_CO_SINK* p_sink = nullptr;
+  for (size_t index = 0; index < p_peer->num_sup_sinks; index++) {
+    btav_a2dp_codec_index_t peer_codec_index =
+        A2DP_SourceCodecIndex(p_peer->sinks[index].codec_caps);
+    if (peer_codec_index != ota_codec_index) continue;
+    if (!bta_av_co_audio_sink_supports_cp(&p_peer->sinks[index])) continue;
+    p_sink = &p_peer->sinks[index];
+    break;
+  }
+  if ((p_peer->num_sup_sinks > 0) && (p_sink == nullptr)) {
+    // There are no peer SEPs if we didn't do the discovery procedure yet.
+    // We have all the information we need from the peer, so we can
+    // proceed with the OTA codec configuration.
+    APPL_TRACE_ERROR("%s: cannot find peer SEP to configure", __func__);
+    return false;
+  }
+
+  tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
+  bta_av_co_get_peer_params(&peer_params);
+  if (!bta_av_co_cb.codecs->setCodecOtaConfig(
+          p_ota_codec_config, &peer_params, result_codec_config, &restart_input,
+          &restart_output, &config_updated)) {
+    APPL_TRACE_ERROR("%s: cannot set OTA config", __func__);
+    return false;
+  }
+
+  if (restart_output) {
+    *p_restart_output = true;
+    p_peer->p_sink = p_sink;
+    bta_av_co_save_new_codec_config(p_peer, result_codec_config, num_protect,
+                                    p_protect_info);
+  }
+
+  if (restart_input || config_updated) {
+    // NOTE: Currently, the input is restarted by sending an upcall
+    // and informing the Media Framework about the change.
+    btif_dispatch_sm_event(BTIF_AV_SOURCE_CONFIG_UPDATED_EVT, NULL, 0);
+  }
+
+  return true;
+}
+
+bool bta_av_co_set_codec_audio_config(
+    const btav_a2dp_codec_config_t& codec_audio_config) {
+  uint8_t result_codec_config[AVDT_CODEC_SIZE];
+  bool restart_output = false;
+  bool config_updated = false;
+
+  // Find the peer that is currently open
+  tBTA_AV_CO_PEER* p_peer = nullptr;
+  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(bta_av_co_cb.peers); i++) {
+    tBTA_AV_CO_PEER* p_peer_tmp = &bta_av_co_cb.peers[i];
+    if (p_peer_tmp->opened) {
+      p_peer = p_peer_tmp;
+      break;
+    }
+  }
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR("%s: no open peer to configure", __func__);
+    return false;
+  }
+
+  // Use the current sink codec
+  const tBTA_AV_CO_SINK* p_sink = p_peer->p_sink;
+  if (p_sink == nullptr) {
+    APPL_TRACE_ERROR("%s: cannot find peer SEP to configure", __func__);
+    return false;
+  }
+
+  tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
+  bta_av_co_get_peer_params(&peer_params);
+  if (!bta_av_co_cb.codecs->setCodecAudioConfig(
+          codec_audio_config, &peer_params, p_sink->codec_caps,
+          result_codec_config, &restart_output, &config_updated)) {
+    return false;
+  }
+
+  if (restart_output) {
+    uint8_t num_protect = 0;
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+    if (p_peer->cp_active) num_protect = AVDT_CP_INFO_LEN;
+#endif
+
+    bta_av_co_save_new_codec_config(p_peer, result_codec_config,
+                                    p_sink->num_protect, p_sink->protect_info);
+
+    APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(x%x)", __func__, p_peer->handle);
+    BTA_AvReconfig(p_peer->handle, true, p_sink->sep_info_idx,
+                   p_peer->codec_config, num_protect, bta_av_co_cp_scmst);
+  }
+
+  if (config_updated) {
+    // NOTE: Currently, the input is restarted by sending an upcall
+    // and informing the Media Framework about the change.
+    btif_dispatch_sm_event(BTIF_AV_SOURCE_CONFIG_UPDATED_EVT, NULL, 0);
+  }
+
+  return true;
+}
+
+A2dpCodecs* bta_av_get_a2dp_codecs(void) { return bta_av_co_cb.codecs; }
+
+A2dpCodecConfig* bta_av_get_a2dp_current_codec(void) {
+  A2dpCodecConfig* current_codec;
+
+  mutex_global_lock();
+  if (bta_av_co_cb.codecs == nullptr) {
+    mutex_global_unlock();
+    return nullptr;
+  }
+  current_codec = bta_av_co_cb.codecs->getCurrentCodecConfig();
+  mutex_global_unlock();
+
+  return current_codec;
+}
+
 void bta_av_co_init(void) {
   APPL_TRACE_DEBUG("%s", __func__);
 
   /* Reset the control block */
-  memset(&bta_av_co_cb, 0, sizeof(bta_av_co_cb));
+  bta_av_co_cb.reset();
 
 #if (BTA_AV_CO_CP_SCMS_T == TRUE)
   bta_av_co_cp_set_flag(AVDT_CP_SCMS_COPY_NEVER);
@@ -1028,5 +1203,10 @@
 #endif
 
   /* Reset the current config */
-  bta_av_co_audio_codec_reset();
+  /* Protect access to bta_av_co_cb.codec_config */
+  mutex_global_lock();
+  bta_av_co_cb.codecs = new A2dpCodecs();
+  bta_av_co_cb.codecs->init();
+  A2DP_InitDefaultCodec(bta_av_co_cb.codec_config);
+  mutex_global_unlock();
 }
diff --git a/btif/include/btif_a2dp_source.h b/btif/include/btif_a2dp_source.h
index d076cbe..2f1763d 100644
--- a/btif/include/btif_a2dp_source.h
+++ b/btif/include/btif_a2dp_source.h
@@ -55,6 +55,19 @@
 // Process a request to stop the A2DP audio encoding task.
 void btif_a2dp_source_stop_audio_req(void);
 
+// Process a request to update the A2DP audio encoder with user preferred
+// codec configuration.
+// |codec_user_config| contains the preferred codec user configuration.
+void btif_a2dp_source_encoder_user_config_update_req(
+    const btav_a2dp_codec_config_t& codec_user_config);
+
+// Process a request to update the A2DP audio encoding with new audio
+// configuration feeding parameters stored in |codec_audio_config|.
+// The fields that are used are: |codec_audio_config.sample_rate|,
+// |codec_audio_config.bits_per_sample| and |codec_audio_config.channel_mode|.
+void btif_a2dp_source_feeding_update_req(
+    const btav_a2dp_codec_config_t& codec_audio_config);
+
 // Process 'idle' request from the BTIF state machine during initialization.
 void btif_a2dp_source_on_idle(void);
 
diff --git a/btif/include/btif_av.h b/btif/include/btif_av.h
index 7c54c94..6349701 100644
--- a/btif/include/btif_av.h
+++ b/btif/include/btif_av.h
@@ -43,6 +43,8 @@
   BTIF_AV_START_STREAM_REQ_EVT,
   BTIF_AV_STOP_STREAM_REQ_EVT,
   BTIF_AV_SUSPEND_STREAM_REQ_EVT,
+  BTIF_AV_SOURCE_CONFIG_REQ_EVT,
+  BTIF_AV_SOURCE_CONFIG_UPDATED_EVT,
   BTIF_AV_SINK_CONFIG_REQ_EVT,
   BTIF_AV_OFFLOAD_START_REQ_EVT,
   BTIF_AV_CLEANUP_REQ_EVT,
diff --git a/btif/include/btif_av_co.h b/btif/include/btif_av_co.h
index 9427747..13d796b 100644
--- a/btif/include/btif_av_co.h
+++ b/btif/include/btif_av_co.h
@@ -20,25 +20,16 @@
 #define BTIF_AV_CO_H
 
 #include "btif/include/btif_a2dp_source.h"
-#include "stack/include/a2dp_api.h"
+#include "stack/include/a2dp_codec_api.h"
 
-/*******************************************************************************
- *  Constants & Macros
- ******************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
 
-/*******************************************************************************
- *  Functions
- ******************************************************************************/
-
-// Prepares a message to initialize the encoder. The prepared message is
-// stored in |p_init_params|.
-// |p_init_params| cannot be null.
-void bta_av_co_audio_encoder_init(tA2DP_ENCODER_INIT_PARAMS* p_init_params);
-
-// Gets the current A2DP encoder's feeding parameters. The result
-// is stored in |p_feeding_params|.
-void bta_av_co_get_encoder_feeding_parameters(
-    tA2DP_FEEDING_PARAMS* p_feeding_params);
+// Gets the A2DP peer parameters that are used to initialize the encoder.
+// The parameters are stored in |p_peer_params|.
+// |p_peer_params| cannot be null.
+void bta_av_co_get_peer_params(tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params);
 
 // Gets the current A2DP encoder interface that can be used to encode and
 // prepare A2DP packets for transmission - see |tA2DP_ENCODER_INTERFACE|.
@@ -46,15 +37,33 @@
 // otherwise NULL.
 const tA2DP_ENCODER_INTERFACE* bta_av_co_get_encoder_interface(void);
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_init
- **
- ** Description      Initialization
- **
- ** Returns          Nothing
- **
- ******************************************************************************/
+// Sets the user preferred codec configuration.
+// |codec_user_config| contains the preferred codec configuration.
+// Returns true on success, otherwise false.
+bool bta_av_co_set_codec_user_config(
+    const btav_a2dp_codec_config_t& codec_user_config);
+
+// Sets the Audio HAL selected audio feeding parameters.
+// Those parameters are applied only to the currently selected codec.
+// |codec_audio_config| contains the selected audio feeding configuration.
+// Returns true on success, otherwise false.
+bool bta_av_co_set_codec_audio_config(
+    const btav_a2dp_codec_config_t& codec_audio_config);
+
+// Initializes the control block.
 void bta_av_co_init(void);
 
-#endif /* BTIF_AV_CO_H */
+// Gets the initialized A2DP codecs.
+// Returns a pointer to the |A2dpCodecs| object with the initialized A2DP
+// codecs, or nullptr if no codecs are initialized.
+A2dpCodecs* bta_av_get_a2dp_codecs(void);
+
+// Gets the current A2DP codec.
+// Returns a pointer to the current |A2dpCodec| if valid, otherwise nullptr.
+A2dpCodecConfig* bta_av_get_a2dp_current_codec(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // BTIF_AV_CO_H
diff --git a/btif/src/bluetooth.cc b/btif/src/bluetooth.cc
index bc9ec57..20c3209 100644
--- a/btif/src/bluetooth.cc
+++ b/btif/src/bluetooth.cc
@@ -84,8 +84,8 @@
 /* handsfree profile - client */
 extern bthf_client_interface_t* btif_hf_client_get_interface();
 /* advanced audio profile */
-extern btav_interface_t* btif_av_get_src_interface();
-extern btav_interface_t* btif_av_get_sink_interface();
+extern btav_source_interface_t* btif_av_get_src_interface();
+extern btav_sink_interface_t* btif_av_get_sink_interface();
 /*rfc l2cap*/
 extern btsock_interface_t* btif_sock_get_interface();
 /* hid host profile */
diff --git a/btif/src/btif_a2dp_control.cc b/btif/src/btif_a2dp_control.cc
index 7760329..555f3f3 100644
--- a/btif/src/btif_a2dp_control.cc
+++ b/btif/src/btif_a2dp_control.cc
@@ -176,20 +176,81 @@
     }
 
     case A2DP_CTRL_GET_OUTPUT_AUDIO_CONFIG: {
-      tA2DP_FEEDING_PARAMS feeding_params;
+      btav_a2dp_codec_config_t codec_config;
+      btav_a2dp_codec_config_t codec_capability;
+      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+      codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+      codec_config.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+      codec_capability.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+      codec_capability.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+      codec_capability.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
 
-      bta_av_co_get_encoder_feeding_parameters(&feeding_params);
-      tA2DP_SAMPLE_RATE sample_rate = feeding_params.sample_rate;
-      tA2DP_CHANNEL_COUNT channel_count = feeding_params.channel_count;
-      tA2DP_BITS_PER_SAMPLE bits_per_sample = feeding_params.bits_per_sample;
+      A2dpCodecConfig* current_codec = bta_av_get_a2dp_current_codec();
+      if (current_codec != nullptr) {
+        codec_config = current_codec->getCodecConfig();
+        codec_capability = current_codec->getCodecCapability();
+      }
 
       btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
-      UIPC_Send(UIPC_CH_ID_AV_CTRL, 0, reinterpret_cast<uint8_t*>(&sample_rate),
-                sizeof(tA2DP_SAMPLE_RATE));
-      UIPC_Send(UIPC_CH_ID_AV_CTRL, 0, &channel_count,
-                sizeof(tA2DP_CHANNEL_COUNT));
-      UIPC_Send(UIPC_CH_ID_AV_CTRL, 0, &bits_per_sample,
-                sizeof(tA2DP_BITS_PER_SAMPLE));
+      // Send the current codec config
+      UIPC_Send(UIPC_CH_ID_AV_CTRL, 0,
+                reinterpret_cast<const uint8_t*>(&codec_config.sample_rate),
+                sizeof(btav_a2dp_codec_sample_rate_t));
+      UIPC_Send(UIPC_CH_ID_AV_CTRL, 0,
+                reinterpret_cast<const uint8_t*>(&codec_config.bits_per_sample),
+                sizeof(btav_a2dp_codec_bits_per_sample_t));
+      UIPC_Send(UIPC_CH_ID_AV_CTRL, 0,
+                reinterpret_cast<const uint8_t*>(&codec_config.channel_mode),
+                sizeof(btav_a2dp_codec_channel_mode_t));
+      // Send the current codec capability
+      UIPC_Send(UIPC_CH_ID_AV_CTRL, 0,
+                reinterpret_cast<const uint8_t*>(&codec_capability.sample_rate),
+                sizeof(btav_a2dp_codec_sample_rate_t));
+      UIPC_Send(UIPC_CH_ID_AV_CTRL, 0, reinterpret_cast<const uint8_t*>(
+                                           &codec_capability.bits_per_sample),
+                sizeof(btav_a2dp_codec_bits_per_sample_t));
+      UIPC_Send(UIPC_CH_ID_AV_CTRL, 0, reinterpret_cast<const uint8_t*>(
+                                           &codec_capability.channel_mode),
+                sizeof(btav_a2dp_codec_channel_mode_t));
+      break;
+    }
+
+    case A2DP_CTRL_SET_OUTPUT_AUDIO_CONFIG: {
+      btav_a2dp_codec_config_t codec_config;
+      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+      codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+      codec_config.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+
+      btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
+      // Send the current codec config
+      if (UIPC_Read(UIPC_CH_ID_AV_CTRL, 0,
+                    reinterpret_cast<uint8_t*>(&codec_config.sample_rate),
+                    sizeof(btav_a2dp_codec_sample_rate_t)) !=
+          sizeof(btav_a2dp_codec_sample_rate_t)) {
+        APPL_TRACE_ERROR("Error reading sample rate from audio HAL");
+        break;
+      }
+      if (UIPC_Read(UIPC_CH_ID_AV_CTRL, 0,
+                    reinterpret_cast<uint8_t*>(&codec_config.bits_per_sample),
+                    sizeof(btav_a2dp_codec_bits_per_sample_t)) !=
+          sizeof(btav_a2dp_codec_bits_per_sample_t)) {
+        APPL_TRACE_ERROR("Error reading bits per sample from audio HAL");
+        break;
+      }
+      if (UIPC_Read(UIPC_CH_ID_AV_CTRL, 0,
+                    reinterpret_cast<uint8_t*>(&codec_config.channel_mode),
+                    sizeof(btav_a2dp_codec_channel_mode_t)) !=
+          sizeof(btav_a2dp_codec_channel_mode_t)) {
+        APPL_TRACE_ERROR("Error reading channel mode from audio HAL");
+        break;
+      }
+      APPL_TRACE_DEBUG(
+          "%s: A2DP_CTRL_SET_OUTPUT_AUDIO_CONFIG: "
+          "sample_rate=0x%x bits_per_sample=0x%x "
+          "channel_mode=0x%x",
+          __func__, codec_config.sample_rate, codec_config.bits_per_sample,
+          codec_config.channel_mode);
+      btif_a2dp_source_feeding_update_req(codec_config);
       break;
     }
 
diff --git a/btif/src/btif_a2dp_source.cc b/btif/src/btif_a2dp_source.cc
index bd032c8..b6077fc 100644
--- a/btif/src/btif_a2dp_source.cc
+++ b/btif/src/btif_a2dp_source.cc
@@ -62,21 +62,28 @@
   BTIF_MEDIA_AUDIO_TX_STOP,
   BTIF_MEDIA_AUDIO_TX_FLUSH,
   BTIF_MEDIA_SOURCE_ENCODER_INIT,
-  BTIF_MEDIA_AUDIO_FEEDING_INIT
+  BTIF_MEDIA_SOURCE_ENCODER_USER_CONFIG_UPDATE,
+  BTIF_MEDIA_AUDIO_FEEDING_UPDATE
 };
 
-/* tBTIF_A2DP_AUDIO_FEEDING_INIT msg structure */
-typedef struct {
-  BT_HDR hdr;
-  tA2DP_FEEDING_PARAMS feeding_params;
-} tBTIF_A2DP_AUDIO_FEEDING_INIT;
-
 /* tBTIF_A2DP_SOURCE_ENCODER_INIT msg structure */
 typedef struct {
   BT_HDR hdr;
-  tA2DP_ENCODER_INIT_PARAMS init_params;
+  tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
 } tBTIF_A2DP_SOURCE_ENCODER_INIT;
 
+/* tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE msg structure */
+typedef struct {
+  BT_HDR hdr;
+  btav_a2dp_codec_config_t user_config;
+} tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE;
+
+/* tBTIF_A2DP_AUDIO_FEEDING_UPDATE msg structure */
+typedef struct {
+  BT_HDR hdr;
+  btav_a2dp_codec_config_t feeding_params;
+} tBTIF_A2DP_AUDIO_FEEDING_UPDATE;
+
 typedef struct {
   // Counter for total updates
   size_t total_updates;
@@ -142,7 +149,6 @@
   fixed_queue_t* cmd_msg_queue;
   fixed_queue_t* tx_audio_queue;
   bool tx_flush; /* Discards any outgoing data when true */
-  bool is_streaming;
   alarm_t* media_alarm;
   const tA2DP_ENCODER_INTERFACE* encoder_interface;
   period_ms_t encoder_interval_ms; /* Local copy of the encoder interval */
@@ -159,10 +165,9 @@
 static void btif_a2dp_source_audio_tx_stop_event(void);
 static void btif_a2dp_source_audio_tx_flush_event(BT_HDR* p_msg);
 static void btif_a2dp_source_encoder_init_event(BT_HDR* p_msg);
-static void btif_a2dp_source_audio_feeding_init_event(BT_HDR* p_msg);
+static void btif_a2dp_source_encoder_user_config_update_event(BT_HDR* p_msg);
+static void btif_a2dp_source_audio_feeding_update_event(BT_HDR* p_msg);
 static void btif_a2dp_source_encoder_init(void);
-UNUSED_ATTR static void btif_a2dp_source_feeding_init_req(
-    tBTIF_A2DP_AUDIO_FEEDING_INIT* p_msg);
 static void btif_a2dp_source_encoder_init_req(
     tBTIF_A2DP_SOURCE_ENCODER_INIT* p_msg);
 static bool btif_a2dp_source_audio_tx_flush_req(void);
@@ -181,7 +186,8 @@
     CASE_RETURN_STR(BTIF_MEDIA_AUDIO_TX_STOP)
     CASE_RETURN_STR(BTIF_MEDIA_AUDIO_TX_FLUSH)
     CASE_RETURN_STR(BTIF_MEDIA_SOURCE_ENCODER_INIT)
-    CASE_RETURN_STR(BTIF_MEDIA_AUDIO_FEEDING_INIT)
+    CASE_RETURN_STR(BTIF_MEDIA_SOURCE_ENCODER_USER_CONFIG_UPDATE)
+    CASE_RETURN_STR(BTIF_MEDIA_AUDIO_FEEDING_UPDATE)
     default:
       break;
   }
@@ -244,7 +250,6 @@
   APPL_TRACE_EVENT("## A2DP SOURCE STOP MEDIA THREAD ##");
 
   // Stop the timer
-  btif_a2dp_source_cb.is_streaming = FALSE;
   alarm_free(btif_a2dp_source_cb.media_alarm);
   btif_a2dp_source_cb.media_alarm = NULL;
 
@@ -274,7 +279,7 @@
 }
 
 bool btif_a2dp_source_is_streaming(void) {
-  return btif_a2dp_source_cb.is_streaming;
+  return alarm_is_scheduled(btif_a2dp_source_cb.media_alarm);
 }
 
 static void btif_a2dp_source_command_ready(fixed_queue_t* queue,
@@ -297,8 +302,11 @@
     case BTIF_MEDIA_SOURCE_ENCODER_INIT:
       btif_a2dp_source_encoder_init_event(p_msg);
       break;
-    case BTIF_MEDIA_AUDIO_FEEDING_INIT:
-      btif_a2dp_source_audio_feeding_init_event(p_msg);
+    case BTIF_MEDIA_SOURCE_ENCODER_USER_CONFIG_UPDATE:
+      btif_a2dp_source_encoder_user_config_update_event(p_msg);
+      break;
+    case BTIF_MEDIA_AUDIO_FEEDING_UPDATE:
+      btif_a2dp_source_audio_feeding_update_event(p_msg);
       break;
     default:
       APPL_TRACE_ERROR("ERROR in %s unknown event %d", __func__, p_msg->event);
@@ -317,21 +325,6 @@
   /* Init the encoding task */
   btif_a2dp_source_encoder_init();
 
-#if 0
-  // TODO: The feeding parameters setup mechanism below to-be reused for
-  // other purposes.
-  /* For now hardcode 44.1 khz 16 bit stereo PCM format */
-  tA2DP_FEEDING_PARAMS feeding_params;
-  tBTIF_A2DP_AUDIO_FEEDING_INIT mfeed;
-  feeding_params.sample_rate = BTIF_A2DP_SRC_SAMPLING_RATE;
-  feeding_params.channel_count = BTIF_A2DP_SRC_NUM_CHANNELS;
-  feeding_params.bits_per_sample = BTIF_A2DP_SRC_BIT_DEPTH;
-  mfeed.feeding_params = feeding_params;
-
-  /* Send message to Media task to configure transcoding */
-  btif_a2dp_source_feeding_init_req(&mfeed);
-#endif
-
   mutex_global_unlock();
 }
 
@@ -370,7 +363,7 @@
 
   APPL_TRACE_DEBUG("%s", __func__);
 
-  bta_av_co_audio_encoder_init(&msg.init_params);
+  bta_av_co_get_peer_params(&msg.peer_params);
   btif_a2dp_source_encoder_init_req(&msg);
 }
 
@@ -398,27 +391,65 @@
     return;
   }
 
+  A2dpCodecConfig* a2dp_codec_config = bta_av_get_a2dp_current_codec();
+  if (a2dp_codec_config == nullptr) {
+    APPL_TRACE_ERROR("%s: Cannot stream audio: current codec is not set",
+                     __func__);
+    return;
+  }
+
   btif_a2dp_source_cb.encoder_interface->encoder_init(
-      btif_av_is_peer_edr(), btif_av_peer_supports_3mbps(),
-      &p_encoder_init->init_params, btif_a2dp_source_read_callback,
-      btif_a2dp_source_enqueue_callback);
+      &p_encoder_init->peer_params, a2dp_codec_config,
+      btif_a2dp_source_read_callback, btif_a2dp_source_enqueue_callback);
 
   // Save a local copy of the encoder_interval_ms
   btif_a2dp_source_cb.encoder_interval_ms =
       btif_a2dp_source_cb.encoder_interface->get_encoder_interval_ms();
 }
 
-static void btif_a2dp_source_feeding_init_req(
-    tBTIF_A2DP_AUDIO_FEEDING_INIT* p_msg) {
-  tBTIF_A2DP_AUDIO_FEEDING_INIT* p_buf =
-      (tBTIF_A2DP_AUDIO_FEEDING_INIT*)osi_malloc(
-          sizeof(tBTIF_A2DP_AUDIO_FEEDING_INIT));
+void btif_a2dp_source_encoder_user_config_update_req(
+    const btav_a2dp_codec_config_t& codec_user_config) {
+  tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE* p_buf =
+      (tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE*)osi_malloc(
+          sizeof(tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE));
 
-  memcpy(p_buf, p_msg, sizeof(tBTIF_A2DP_AUDIO_FEEDING_INIT));
-  p_buf->hdr.event = BTIF_MEDIA_AUDIO_FEEDING_INIT;
+  p_buf->user_config = codec_user_config;
+  p_buf->hdr.event = BTIF_MEDIA_SOURCE_ENCODER_USER_CONFIG_UPDATE;
   fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
 }
 
+static void btif_a2dp_source_encoder_user_config_update_event(BT_HDR* p_msg) {
+  tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE* p_user_config =
+      (tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE*)p_msg;
+
+  APPL_TRACE_DEBUG("%s", __func__);
+  if (!bta_av_co_set_codec_user_config(p_user_config->user_config)) {
+    APPL_TRACE_ERROR("%s: cannot update codec user configuration", __func__);
+  }
+}
+
+void btif_a2dp_source_feeding_update_req(
+    const btav_a2dp_codec_config_t& codec_audio_config) {
+  tBTIF_A2DP_AUDIO_FEEDING_UPDATE* p_buf =
+      (tBTIF_A2DP_AUDIO_FEEDING_UPDATE*)osi_malloc(
+          sizeof(tBTIF_A2DP_AUDIO_FEEDING_UPDATE));
+
+  p_buf->feeding_params = codec_audio_config;
+  p_buf->hdr.event = BTIF_MEDIA_AUDIO_FEEDING_UPDATE;
+  fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
+}
+
+static void btif_a2dp_source_audio_feeding_update_event(BT_HDR* p_msg) {
+  tBTIF_A2DP_AUDIO_FEEDING_UPDATE* p_feeding =
+      (tBTIF_A2DP_AUDIO_FEEDING_UPDATE*)p_msg;
+
+  APPL_TRACE_DEBUG("%s", __func__);
+  if (!bta_av_co_set_codec_audio_config(p_feeding->feeding_params)) {
+    APPL_TRACE_ERROR("%s: cannot update codec audio feeding parameters",
+                     __func__);
+  }
+}
+
 void btif_a2dp_source_on_idle(void) {
   if (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_OFF) return;
 
@@ -483,22 +514,11 @@
   btif_a2dp_source_cb.tx_flush = enable;
 }
 
-static void btif_a2dp_source_audio_feeding_init_event(BT_HDR* p_msg) {
-  tBTIF_A2DP_AUDIO_FEEDING_INIT* p_feeding =
-      (tBTIF_A2DP_AUDIO_FEEDING_INIT*)p_msg;
-
-  APPL_TRACE_DEBUG("%s", __func__);
-
-  CHECK(btif_a2dp_source_cb.encoder_interface != NULL);
-  btif_a2dp_source_cb.encoder_interface->feeding_init(
-      &p_feeding->feeding_params);
-}
-
 static void btif_a2dp_source_audio_tx_start_event(void) {
   APPL_TRACE_DEBUG(
-      "%s media_alarm is %srunning, is_streaming %s", __func__,
+      "%s media_alarm is %srunning, streaming %s", __func__,
       alarm_is_scheduled(btif_a2dp_source_cb.media_alarm) ? "" : "not ",
-      (btif_a2dp_source_cb.is_streaming) ? "true" : "false");
+      btif_a2dp_source_is_streaming() ? "true" : "false");
 
   /* Reset the media feeding state */
   CHECK(btif_a2dp_source_cb.encoder_interface != NULL);
@@ -523,14 +543,13 @@
 
 static void btif_a2dp_source_audio_tx_stop_event(void) {
   APPL_TRACE_DEBUG(
-      "%s media_alarm is %srunning, is_streaming %s", __func__,
+      "%s media_alarm is %srunning, streaming %s", __func__,
       alarm_is_scheduled(btif_a2dp_source_cb.media_alarm) ? "" : "not ",
-      (btif_a2dp_source_cb.is_streaming) ? "true" : "false");
+      btif_a2dp_source_is_streaming() ? "true" : "false");
 
-  const bool send_ack = btif_a2dp_source_cb.is_streaming;
+  const bool send_ack = btif_a2dp_source_is_streaming();
 
   /* Stop the timer first */
-  btif_a2dp_source_cb.is_streaming = false;
   alarm_free(btif_a2dp_source_cb.media_alarm);
   btif_a2dp_source_cb.media_alarm = NULL;
 
diff --git a/btif/src/btif_av.cc b/btif/src/btif_av.cc
index c03622a..bcfcbc4 100644
--- a/btif/src/btif_av.cc
+++ b/btif/src/btif_av.cc
@@ -94,8 +94,8 @@
 /*****************************************************************************
  *  Static variables
  *****************************************************************************/
-static btav_callbacks_t* bt_av_src_callbacks = NULL;
-static btav_callbacks_t* bt_av_sink_callbacks = NULL;
+static btav_source_callbacks_t* bt_av_src_callbacks = NULL;
+static btav_sink_callbacks_t* bt_av_sink_callbacks = NULL;
 static btif_av_cb_t btif_av_cb = {0, {{0}}, 0, 0, 0, 0};
 static alarm_t* av_open_on_rc_timer = NULL;
 
@@ -197,6 +197,8 @@
     CASE_RETURN_STR(BTIF_AV_START_STREAM_REQ_EVT)
     CASE_RETURN_STR(BTIF_AV_STOP_STREAM_REQ_EVT)
     CASE_RETURN_STR(BTIF_AV_SUSPEND_STREAM_REQ_EVT)
+    CASE_RETURN_STR(BTIF_AV_SOURCE_CONFIG_REQ_EVT)
+    CASE_RETURN_STR(BTIF_AV_SOURCE_CONFIG_UPDATED_EVT)
     CASE_RETURN_STR(BTIF_AV_SINK_CONFIG_REQ_EVT)
     CASE_RETURN_STR(BTIF_AV_OFFLOAD_START_REQ_EVT)
     default:
@@ -283,6 +285,34 @@
   }
 }
 
+static void btif_update_source_codec(void* p_data) {
+  btav_a2dp_codec_config_t req;
+  // copy to avoid alignment problems
+  memcpy(&req, p_data, sizeof(req));
+
+  BTIF_TRACE_DEBUG("BTIF_AV_SOURCE_CONFIG_REQ_EVT");
+  btif_a2dp_source_encoder_user_config_update_req(req);
+}
+
+static void btif_report_source_codec_state(UNUSED_ATTR void* p_data) {
+  btav_a2dp_codec_config_t codec_config;
+  std::vector<btav_a2dp_codec_config_t> codec_capabilities;
+
+  A2dpCodecs* a2dp_codecs = bta_av_get_a2dp_codecs();
+  if (a2dp_codecs == nullptr) return;
+  if (!a2dp_codecs->getCodecConfigAndCapabilities(&codec_config,
+                                                  &codec_capabilities)) {
+    BTIF_TRACE_WARNING(
+        "BTIF_AV_SOURCE_CONFIG_UPDATED_EVT failed: "
+        "cannot get codec config and capabilities");
+    return;
+  }
+  if (bt_av_src_callbacks != NULL) {
+    HAL_CBACK(bt_av_src_callbacks, audio_config_cb, codec_config,
+              codec_capabilities);
+  }
+}
+
 /*****************************************************************************
  *
  * Function     btif_av_state_idle_handler
@@ -368,6 +398,14 @@
       btif_rc_handler(event, (tBTA_AV*)p_data);
       break;
 
+    case BTIF_AV_SOURCE_CONFIG_REQ_EVT:
+      btif_update_source_codec(p_data);
+      break;
+
+    case BTIF_AV_SOURCE_CONFIG_UPDATED_EVT:
+      btif_report_source_codec_state(p_data);
+      break;
+
     /*
      * In case Signalling channel is not down
      * and remote started Streaming Procedure
@@ -537,6 +575,14 @@
       btif_queue_advance();
     } break;
 
+    case BTIF_AV_SOURCE_CONFIG_REQ_EVT:
+      btif_update_source_codec(p_data);
+      break;
+
+    case BTIF_AV_SOURCE_CONFIG_UPDATED_EVT:
+      btif_report_source_codec_state(p_data);
+      break;
+
     case BTIF_AV_SINK_CONFIG_REQ_EVT: {
       btif_av_sink_config_req_t req;
       // copy to avoid alignment problems
@@ -646,6 +692,14 @@
     case BTIF_SM_EXIT_EVT:
       break;
 
+    case BTIF_AV_SOURCE_CONFIG_REQ_EVT:
+      btif_update_source_codec(p_data);
+      break;
+
+    case BTIF_AV_SOURCE_CONFIG_UPDATED_EVT:
+      btif_report_source_codec_state(p_data);
+      break;
+
     case BTA_AV_CLOSE_EVT:
 
       /* inform the application that we are disconnecting */
@@ -768,6 +822,14 @@
 
     } break;
 
+    case BTIF_AV_SOURCE_CONFIG_REQ_EVT:
+      btif_update_source_codec(p_data);
+      break;
+
+    case BTIF_AV_SOURCE_CONFIG_UPDATED_EVT:
+      btif_report_source_codec_state(p_data);
+      break;
+
     case BTIF_AV_DISCONNECT_REQ_EVT:
       BTA_AvClose(btif_av_cb.bta_handle);
       if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
@@ -884,6 +946,14 @@
         btif_a2dp_on_started(NULL, true);
       break;
 
+    case BTIF_AV_SOURCE_CONFIG_REQ_EVT:
+      btif_update_source_codec(p_data);
+      break;
+
+    case BTIF_AV_SOURCE_CONFIG_UPDATED_EVT:
+      btif_report_source_codec_state(p_data);
+      break;
+
     /* fixme -- use suspend = true always to work around issue with BTA AV */
     case BTIF_AV_STOP_STREAM_REQ_EVT:
     case BTIF_AV_SUSPEND_STREAM_REQ_EVT:
@@ -1190,7 +1260,7 @@
  *
  ******************************************************************************/
 
-static bt_status_t init_src(btav_callbacks_t* callbacks) {
+static bt_status_t init_src(btav_source_callbacks_t* callbacks) {
   BTIF_TRACE_EVENT("%s()", __func__);
 
   bt_status_t status = btif_av_init(BTA_A2DP_SOURCE_SERVICE_ID);
@@ -1209,7 +1279,7 @@
  *
  ******************************************************************************/
 
-static bt_status_t init_sink(btav_callbacks_t* callbacks) {
+static bt_status_t init_sink(btav_sink_callbacks_t* callbacks) {
   BTIF_TRACE_EVENT("%s()", __func__);
 
   bt_status_t status = btif_av_init(BTA_A2DP_SINK_SERVICE_ID);
@@ -1303,6 +1373,28 @@
                                (char*)bd_addr, sizeof(bt_bdaddr_t), NULL);
 }
 
+static bt_status_t codec_config_src(
+    std::vector<btav_a2dp_codec_config_t> codec_preferences) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  CHECK_BTAV_INIT();
+
+  for (auto cp : codec_preferences) {
+    BTIF_TRACE_DEBUG(
+        "%s: codec_type=%d codec_priority=%d "
+        "sample_rate=0x%x bits_per_sample=0x%x "
+        "channel_mode=0x%x codec_specific_1=%d "
+        "codec_specific_2=%d codec_specific_3=%d "
+        "codec_specific_4=%d",
+        __func__, cp.codec_type, cp.codec_priority, cp.sample_rate,
+        cp.bits_per_sample, cp.channel_mode, cp.codec_specific_1,
+        cp.codec_specific_2, cp.codec_specific_3, cp.codec_specific_4);
+    btif_transfer_context(btif_av_handle_event, BTIF_AV_SOURCE_CONFIG_REQ_EVT,
+                          reinterpret_cast<char*>(&cp), sizeof(cp), NULL);
+  }
+
+  return BT_STATUS_SUCCESS;
+}
+
 /*******************************************************************************
  *
  * Function         cleanup
@@ -1346,18 +1438,17 @@
   }
 }
 
-static const btav_interface_t bt_av_src_interface = {
-    sizeof(btav_interface_t),
+static const btav_source_interface_t bt_av_src_interface = {
+    sizeof(btav_source_interface_t),
     init_src,
     src_connect_sink,
     disconnect,
+    codec_config_src,
     cleanup_src,
-    NULL,
-    NULL,
 };
 
-static const btav_interface_t bt_av_sink_interface = {
-    sizeof(btav_interface_t),
+static const btav_sink_interface_t bt_av_sink_interface = {
+    sizeof(btav_sink_interface_t),
     init_sink,
     sink_connect_src,
     disconnect,
@@ -1540,10 +1631,10 @@
  *
  * Description      Get the AV callback interface for A2DP source profile
  *
- * Returns          btav_interface_t
+ * Returns          btav_source_interface_t
  *
  ******************************************************************************/
-const btav_interface_t* btif_av_get_src_interface(void) {
+const btav_source_interface_t* btif_av_get_src_interface(void) {
   BTIF_TRACE_EVENT("%s", __func__);
   return &bt_av_src_interface;
 }
@@ -1554,10 +1645,10 @@
  *
  * Description      Get the AV callback interface for A2DP sink profile
  *
- * Returns          btav_interface_t
+ * Returns          btav_sink_interface_t
  *
  ******************************************************************************/
-const btav_interface_t* btif_av_get_sink_interface(void) {
+const btav_sink_interface_t* btif_av_get_sink_interface(void) {
   BTIF_TRACE_EVENT("%s", __func__);
   return &bt_av_sink_interface;
 }
diff --git a/embdrv/sbc/decoder/Android.mk b/embdrv/sbc/decoder/Android.mk
index 4a967c0..267de82 100644
--- a/embdrv/sbc/decoder/Android.mk
+++ b/embdrv/sbc/decoder/Android.mk
@@ -6,7 +6,7 @@
 
 LOCAL_CPP_EXTENSION := .cc
 
-# sbc decoder
+# SBC decoder
 LOCAL_SRC_FILES := \
         ./srce/alloc.c \
         ./srce/bitalloc.c \
@@ -21,12 +21,12 @@
         ./srce/oi_codec_version.c \
         ./srce/synthesis-sbc.c \
         ./srce/synthesis-dct8.c \
-        ./srce/synthesis-8-generated.c \
+        ./srce/synthesis-8-generated.c
 
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
 	$(LOCAL_PATH)/srce
 
-LOCAL_MODULE := libbt-qcom_sbc_decoder
+LOCAL_MODULE := libbt-sbc-decoder
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_CLASS := STATIC_LIBRARIES
 
diff --git a/embdrv/sbc/encoder/Android.mk b/embdrv/sbc/encoder/Android.mk
new file mode 100644
index 0000000..e5eb406
--- /dev/null
+++ b/embdrv/sbc/encoder/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Bluetooth SBC encoder static library for target
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+# SBC encoder
+LOCAL_SRC_FILES := \
+        ./srce/sbc_analysis.c \
+        ./srce/sbc_dct.c \
+        ./srce/sbc_dct_coeffs.c \
+        ./srce/sbc_enc_bit_alloc_mono.c \
+        ./srce/sbc_enc_bit_alloc_ste.c \
+        ./srce/sbc_enc_coeffs.c \
+        ./srce/sbc_encoder.c \
+        ./srce/sbc_packing.c
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+        $(LOCAL_PATH)/../../../include \
+        $(LOCAL_PATH)/../../../stack/include \
+        $(LOCAL_PATH)/srce \
+        $(bluetooth_C_INCLUDES)
+
+LOCAL_MODULE := libbt-sbc-encoder
+# LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+LOCAL_CFLAGS += $(bluetooth_CFLAGS)
+LOCAL_CONLYFLAGS += $(bluetooth_CONLYFLAGS)
+LOCAL_CPPFLAGS += $(bluetooth_CPPFLAGS)
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/embdrv/sbc/encoder/include/sbc_encoder.h b/embdrv/sbc/encoder/include/sbc_encoder.h
index 11b7bc7..e53a08a 100644
--- a/embdrv/sbc/encoder/include/sbc_encoder.h
+++ b/embdrv/sbc/encoder/include/sbc_encoder.h
@@ -27,9 +27,7 @@
 
 #define ENCODER_VERSION "0025"
 
-#ifdef BUILDCFG
 #include "bt_target.h"
-#endif
 
 /*DEFINES*/
 #ifndef FALSE
diff --git a/embdrv/sbc/encoder/include/sbc_types.h b/embdrv/sbc/encoder/include/sbc_types.h
index ffa3f32..0069a2a 100644
--- a/embdrv/sbc/encoder/include/sbc_types.h
+++ b/embdrv/sbc/encoder/include/sbc_types.h
@@ -27,10 +27,7 @@
 
 #include <stdint.h>
 
-#ifdef BUILDCFG
 #include "bt_target.h"
-#endif
-
 #include "bt_types.h"
 
 #define abs32(x) (((x) >= 0) ? (x) : (-(x)))
diff --git a/embdrv/sbc/encoder/srce/sbc_encoder.c b/embdrv/sbc/encoder/srce/sbc_encoder.c
index 6d334b9..978fa71 100644
--- a/embdrv/sbc/encoder/srce/sbc_encoder.c
+++ b/embdrv/sbc/encoder/srce/sbc_encoder.c
@@ -254,8 +254,5 @@
       EncMaxShiftCounter = ((ENC_VX_BUFFER_SIZE - 8 * 10 * 2) >> 4) << 3;
   }
 
-  APPL_TRACE_EVENT("SBC_Encoder_Init : bitrate %d, bitpool %d",
-                   pstrEncParams->u16BitRate, pstrEncParams->s16BitPool);
-
   SbcAnalysisInit();
 }
diff --git a/include/bt_target.h b/include/bt_target.h
index bb42b93..fa2d6aa 100644
--- a/include/bt_target.h
+++ b/include/bt_target.h
@@ -119,18 +119,6 @@
 #define BTA_AV_CO_CP_SCMS_T FALSE
 #endif
 
-#ifndef BTIF_A2DP_SRC_SAMPLING_RATE
-#define BTIF_A2DP_SRC_SAMPLING_RATE 44100
-#endif
-
-#ifndef BTIF_A2DP_SRC_BIT_DEPTH
-#define BTIF_A2DP_SRC_BIT_DEPTH 16
-#endif
-
-#ifndef BTIF_A2DP_SRC_NUM_CHANNELS
-#define BTIF_A2DP_SRC_NUM_CHANNELS 2
-#endif
-
 /* This feature is used to enable interleaved scan */
 #ifndef BTA_HOST_INTERLEAVE_SEARCH
 #define BTA_HOST_INTERLEAVE_SEARCH FALSE
diff --git a/main/Android.mk b/main/Android.mk
index 3f526bb..4f95ef4 100644
--- a/main/Android.mk
+++ b/main/Android.mk
@@ -15,17 +15,6 @@
     bte_main.cc \
     stack_config.cc
 
-# sbc encoder
-LOCAL_SRC_FILES += \
-    ../embdrv/sbc/encoder/srce/sbc_analysis.c \
-    ../embdrv/sbc/encoder/srce/sbc_dct.c \
-    ../embdrv/sbc/encoder/srce/sbc_dct_coeffs.c \
-    ../embdrv/sbc/encoder/srce/sbc_enc_bit_alloc_mono.c \
-    ../embdrv/sbc/encoder/srce/sbc_enc_bit_alloc_ste.c \
-    ../embdrv/sbc/encoder/srce/sbc_enc_coeffs.c \
-    ../embdrv/sbc/encoder/srce/sbc_encoder.c \
-    ../embdrv/sbc/encoder/srce/sbc_packing.c \
-
 LOCAL_SRC_FILES += \
     ../udrv/ulinux/uipc.cc
 
@@ -67,7 +56,8 @@
     libtinyxml2
 
 LOCAL_STATIC_LIBRARIES := \
-    libbt-qcom_sbc_decoder
+    libbt-sbc-decoder \
+    libbt-sbc-encoder
 
 LOCAL_WHOLE_STATIC_LIBRARIES := \
     libbt-bta \
diff --git a/stack/Android.mk b/stack/Android.mk
index b412128..64a5ca2 100644
--- a/stack/Android.mk
+++ b/stack/Android.mk
@@ -34,6 +34,7 @@
 
 LOCAL_SRC_FILES := \
     ./a2dp/a2dp_api.cc \
+    ./a2dp/a2dp_codec_config.cc \
     ./a2dp/a2dp_sbc.cc \
     ./a2dp/a2dp_sbc_encoder.cc \
     ./a2dp/a2dp_sbc_up_sample.cc \
@@ -156,7 +157,7 @@
 
 LOCAL_MODULE := libbt-stack
 LOCAL_STATIC_LIBRARIES := libbt-hci
-LOCAL_SHARED_LIBRARIES := libcutils libchrome
+LOCAL_SHARED_LIBRARIES := libcutils liblog libchrome
 
 LOCAL_CFLAGS += $(bluetooth_CFLAGS)
 LOCAL_CONLYFLAGS += $(bluetooth_CONLYFLAGS)
@@ -177,8 +178,8 @@
                    $(bluetooth_C_INCLUDES)
 
 LOCAL_SRC_FILES := test/stack_a2dp_test.cc
-LOCAL_SHARED_LIBRARIES :=
-LOCAL_STATIC_LIBRARIES := libbt-stack liblog
+LOCAL_SHARED_LIBRARIES := libc liblog libchrome
+LOCAL_STATIC_LIBRARIES := libbt-stack libbt-sbc-encoder libosi
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE := net_test_stack
 
diff --git a/stack/a2dp/a2dp_api.cc b/stack/a2dp/a2dp_api.cc
index ac458ed..b8fe869 100644
--- a/stack/a2dp/a2dp_api.cc
+++ b/stack/a2dp/a2dp_api.cc
@@ -25,19 +25,16 @@
 #define LOG_TAG "a2dp_api"
 
 #include "a2dp_api.h"
+
 #include <string.h>
+
 #include "a2dp_int.h"
-#include "a2dp_sbc.h"
-#include "a2dp_vendor.h"
 #include "avdt_api.h"
 #include "bt_common.h"
 #include "bt_target.h"
 #include "osi/include/log.h"
 #include "sdpdefs.h"
 
-/* The Media Type offset within the codec info byte array */
-#define A2DP_MEDIA_TYPE_OFFSET 1
-
 /*****************************************************************************
  *  Global data
  ****************************************************************************/
@@ -353,7 +350,7 @@
  *****************************************************************************/
 uint8_t A2DP_BitsSet(uint8_t num) {
   uint8_t count;
-  bool res;
+  uint8_t res;
   if (num == 0)
     res = A2DP_SET_ZERO_BIT;
   else {
@@ -386,447 +383,3 @@
   a2dp_cb.trace_level = BT_TRACE_LEVEL_NONE;
 #endif
 }
-
-tA2DP_CODEC_TYPE A2DP_GetCodecType(const uint8_t* p_codec_info) {
-  return (tA2DP_CODEC_TYPE)(p_codec_info[AVDT_CODEC_TYPE_INDEX]);
-}
-
-bool A2DP_IsSourceCodecValid(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_IsSourceCodecValidSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_IsVendorSourceCodecValid(p_codec_info);
-    default:
-      break;
-  }
-
-  return false;
-}
-
-bool A2DP_IsSinkCodecValid(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_IsSinkCodecValidSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_IsVendorSinkCodecValid(p_codec_info);
-    default:
-      break;
-  }
-
-  return false;
-}
-
-bool A2DP_IsPeerSourceCodecValid(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_IsPeerSourceCodecValidSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_IsVendorPeerSourceCodecValid(p_codec_info);
-    default:
-      break;
-  }
-
-  return false;
-}
-
-bool A2DP_IsPeerSinkCodecValid(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_IsPeerSinkCodecValidSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_IsVendorPeerSinkCodecValid(p_codec_info);
-    default:
-      break;
-  }
-
-  return false;
-}
-
-bool A2DP_IsSinkCodecSupported(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_IsSinkCodecSupportedSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_IsVendorSinkCodecSupported(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return false;
-}
-
-bool A2DP_IsPeerSourceCodecSupported(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_IsPeerSourceCodecSupportedSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_IsVendorPeerSourceCodecSupported(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return false;
-}
-
-void A2DP_InitDefaultCodec(uint8_t* p_codec_info) {
-  A2DP_InitDefaultCodecSbc(p_codec_info);
-}
-
-tA2DP_STATUS A2DP_InitSource2SinkCodec(const uint8_t* p_sink_caps,
-                                       uint8_t* p_result_codec_config) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_sink_caps);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_InitSource2SinkCodecSbc(p_sink_caps, p_result_codec_config);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorInitSource2SinkCodec(p_sink_caps,
-                                             p_result_codec_config);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return A2DP_NS_CODEC_TYPE;
-}
-
-tA2DP_STATUS A2DP_BuildSrc2SinkConfig(const uint8_t* p_src_cap,
-                                      uint8_t* p_pref_cfg) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_src_cap);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_BuildSrc2SinkConfigSbc(p_src_cap, p_pref_cfg);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorBuildSrc2SinkConfig(p_src_cap, p_pref_cfg);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return A2DP_NS_CODEC_TYPE;
-}
-
-bool A2DP_UsesRtpHeader(bool content_protection_enabled,
-                        const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  if (codec_type != A2DP_MEDIA_CT_NON_A2DP) return true;
-
-  return A2DP_VendorUsesRtpHeader(content_protection_enabled, p_codec_info);
-}
-
-tA2DP_CODEC_SEP_INDEX A2DP_SourceCodecSepIndex(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_SourceCodecSepIndexSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorSourceCodecSepIndex(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return A2DP_CODEC_SEP_INDEX_MAX;
-}
-
-const char* A2DP_CodecSepIndexStr(tA2DP_CODEC_SEP_INDEX codec_sep_index) {
-  switch (codec_sep_index) {
-    case A2DP_CODEC_SEP_INDEX_SOURCE_SBC:
-      return A2DP_CodecSepIndexStrSbc();
-    case A2DP_CODEC_SEP_INDEX_SINK_SBC:
-      return A2DP_CodecSepIndexStrSbcSink();
-    default:
-      break;
-  }
-
-  if (codec_sep_index < A2DP_CODEC_SEP_INDEX_MAX)
-    return A2DP_VendorCodecSepIndexStr(codec_sep_index);
-
-  return "UNKNOWN CODEC SEP INDEX";
-}
-
-bool A2DP_InitCodecConfig(tA2DP_CODEC_SEP_INDEX codec_sep_index,
-                          tAVDT_CFG* p_cfg) {
-  LOG_VERBOSE(LOG_TAG, "%s: codec %s", __func__,
-              A2DP_CodecSepIndexStr(codec_sep_index));
-
-  /* Default: no content protection info */
-  p_cfg->num_protect = 0;
-  p_cfg->protect_info[0] = 0;
-
-  switch (codec_sep_index) {
-    case A2DP_CODEC_SEP_INDEX_SOURCE_SBC:
-      return A2DP_InitCodecConfigSbc(p_cfg);
-    case A2DP_CODEC_SEP_INDEX_SINK_SBC:
-      return A2DP_InitCodecConfigSbcSink(p_cfg);
-    default:
-      break;
-  }
-
-  if (codec_sep_index < A2DP_CODEC_SEP_INDEX_MAX)
-    return A2DP_VendorInitCodecConfig(codec_sep_index, p_cfg);
-
-  return false;
-}
-
-uint8_t A2DP_GetMediaType(const uint8_t* p_codec_info) {
-  uint8_t media_type = (p_codec_info[A2DP_MEDIA_TYPE_OFFSET] >> 4) & 0x0f;
-  return media_type;
-}
-
-const char* A2DP_CodecName(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_CodecNameSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorCodecName(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return "UNKNOWN CODEC";
-}
-
-bool A2DP_CodecTypeEquals(const uint8_t* p_codec_info_a,
-                          const uint8_t* p_codec_info_b) {
-  tA2DP_CODEC_TYPE codec_type_a = A2DP_GetCodecType(p_codec_info_a);
-  tA2DP_CODEC_TYPE codec_type_b = A2DP_GetCodecType(p_codec_info_b);
-
-  if (codec_type_a != codec_type_b) return false;
-
-  switch (codec_type_a) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_CodecTypeEqualsSbc(p_codec_info_a, p_codec_info_b);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorCodecTypeEquals(p_codec_info_a, p_codec_info_b);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type_a);
-  return false;
-}
-
-bool A2DP_CodecEquals(const uint8_t* p_codec_info_a,
-                      const uint8_t* p_codec_info_b) {
-  tA2DP_CODEC_TYPE codec_type_a = A2DP_GetCodecType(p_codec_info_a);
-  tA2DP_CODEC_TYPE codec_type_b = A2DP_GetCodecType(p_codec_info_b);
-
-  if (codec_type_a != codec_type_b) return false;
-
-  switch (codec_type_a) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_CodecEqualsSbc(p_codec_info_a, p_codec_info_b);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorCodecEquals(p_codec_info_a, p_codec_info_b);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type_a);
-  return false;
-}
-
-int A2DP_GetTrackSampleRate(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_GetTrackSampleRateSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorGetTrackSampleRate(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return -1;
-}
-
-int A2DP_GetTrackChannelCount(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_GetTrackChannelCountSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorGetTrackChannelCount(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return -1;
-}
-
-int A2DP_GetTrackBitsPerSample(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_GetTrackBitsPerSampleSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorGetTrackBitsPerSample(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return -1;
-}
-
-int A2DP_GetSinkTrackChannelType(const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_GetSinkTrackChannelTypeSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorGetSinkTrackChannelType(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return -1;
-}
-
-int A2DP_GetSinkFramesCountToProcess(uint64_t time_interval_ms,
-                                     const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_GetSinkFramesCountToProcessSbc(time_interval_ms,
-                                                 p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorGetSinkFramesCountToProcess(time_interval_ms,
-                                                    p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return -1;
-}
-
-bool A2DP_GetPacketTimestamp(const uint8_t* p_codec_info, const uint8_t* p_data,
-                             uint32_t* p_timestamp) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_GetPacketTimestampSbc(p_codec_info, p_data, p_timestamp);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorGetPacketTimestamp(p_codec_info, p_data, p_timestamp);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return false;
-}
-
-bool A2DP_BuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf,
-                           uint16_t frames_per_packet) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_BuildCodecHeaderSbc(p_codec_info, p_buf, frames_per_packet);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorBuildCodecHeader(p_codec_info, p_buf,
-                                         frames_per_packet);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return false;
-}
-
-const tA2DP_ENCODER_INTERFACE* A2DP_GetEncoderInterface(
-    const uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_GetEncoderInterfaceSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorGetEncoderInterface(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return NULL;
-}
-
-bool A2DP_AdjustCodec(uint8_t* p_codec_info) {
-  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
-
-  switch (codec_type) {
-    case A2DP_MEDIA_CT_SBC:
-      return A2DP_AdjustCodecSbc(p_codec_info);
-    case A2DP_MEDIA_CT_NON_A2DP:
-      return A2DP_VendorAdjustCodec(p_codec_info);
-    default:
-      break;
-  }
-
-  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
-  return false;
-}
diff --git a/stack/a2dp/a2dp_codec_config.cc b/stack/a2dp/a2dp_codec_config.cc
new file mode 100644
index 0000000..73e1f67
--- /dev/null
+++ b/stack/a2dp/a2dp_codec_config.cc
@@ -0,0 +1,950 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+/**
+ * A2DP Codecs Configuration
+ */
+
+#define LOG_TAG "a2dp_codec"
+
+#include "a2dp_codec_api.h"
+
+#include <base/logging.h>
+
+#include "a2dp_sbc.h"
+#include "a2dp_vendor.h"
+#include "osi/include/log.h"
+
+/* The Media Type offset within the codec info byte array */
+#define A2DP_MEDIA_TYPE_OFFSET 1
+
+A2dpCodecConfig::A2dpCodecConfig(btav_a2dp_codec_index_t codec_index,
+                                 const std::string& name)
+    : codec_index_(codec_index), name_(name) {
+  setDefaultCodecPriority();
+
+  memset(&codec_config_, 0, sizeof(codec_config_));
+  codec_config_.codec_type = codec_index_;
+
+  memset(&codec_capability_, 0, sizeof(codec_capability_));
+  codec_capability_.codec_type = codec_index_;
+
+  memset(&codec_user_config_, 0, sizeof(codec_user_config_));
+  codec_user_config_.codec_type = codec_index_;
+
+  memset(&codec_audio_config_, 0, sizeof(codec_audio_config_));
+  codec_audio_config_.codec_type = codec_index_;
+
+  memset(ota_codec_config_, 0, sizeof(ota_codec_config_));
+  memset(ota_codec_peer_capability_, 0, sizeof(ota_codec_peer_capability_));
+  memset(ota_codec_peer_config_, 0, sizeof(ota_codec_peer_config_));
+}
+
+A2dpCodecConfig::~A2dpCodecConfig() {}
+
+void A2dpCodecConfig::setCodecPriority(
+    btav_a2dp_codec_priority_t codec_priority) {
+  if (codec_priority == 0) {
+    // Compute the default codec priority
+    setDefaultCodecPriority();
+  } else {
+    codec_priority_ = codec_priority;
+  }
+}
+
+void A2dpCodecConfig::setDefaultCodecPriority() {
+  // Compute the default codec priority
+  codec_priority_ = 1000 * codec_index_ + 1;
+}
+
+A2dpCodecConfig* A2dpCodecConfig::createCodec(
+    btav_a2dp_codec_index_t codec_index) {
+  LOG_DEBUG(LOG_TAG, "%s: codec %s", __func__, A2DP_CodecIndexStr(codec_index));
+
+  A2dpCodecConfig* codec_config = nullptr;
+  switch (codec_index) {
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
+      codec_config = new A2dpCodecConfigSbc();
+      break;
+    case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
+      codec_config = new A2dpCodecConfigSbcSink();
+      break;
+    case BTAV_A2DP_CODEC_INDEX_MAX:
+      break;
+  }
+
+  if (codec_config != nullptr) {
+    if (!codec_config->init()) {
+      delete codec_config;
+      codec_config = nullptr;
+    }
+  }
+
+  return codec_config;
+}
+
+bool A2dpCodecConfig::isValid() const { return true; }
+
+bool A2dpCodecConfig::copyOutOtaCodecConfig(uint8_t* p_codec_info) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+
+  // TODO: We should use a mechanism to verify codec config,
+  // not codec capability.
+  if (!A2DP_IsSourceCodecValid(ota_codec_config_)) {
+    return false;
+  }
+  memcpy(p_codec_info, ota_codec_config_, sizeof(ota_codec_config_));
+  return true;
+}
+
+btav_a2dp_codec_config_t A2dpCodecConfig::getCodecConfig() {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+
+  // TODO: We should check whether the codec config is valid
+  return codec_config_;
+}
+
+btav_a2dp_codec_config_t A2dpCodecConfig::getCodecCapability() {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+
+  // TODO: We should check whether the codec capability is valid
+  return codec_capability_;
+}
+
+btav_a2dp_codec_config_t A2dpCodecConfig::getCodecUserConfig() {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+
+  return codec_user_config_;
+}
+
+btav_a2dp_codec_config_t A2dpCodecConfig::getCodecAudioConfig() {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+
+  return codec_audio_config_;
+}
+
+uint8_t A2dpCodecConfig::getAudioBitsPerSample() {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+
+  switch (codec_config_.bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      return 16;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+      return 24;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+      return 32;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+      break;
+  }
+  return 0;
+}
+
+bool A2dpCodecConfig::isCodecConfigEmpty(
+    const btav_a2dp_codec_config_t& codec_config) {
+  return (
+      (codec_config.codec_priority == 0) &&
+      (codec_config.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) &&
+      (codec_config.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) &&
+      (codec_config.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) &&
+      (codec_config.codec_specific_1 == 0) &&
+      (codec_config.codec_specific_2 == 0) &&
+      (codec_config.codec_specific_3 == 0) &&
+      (codec_config.codec_specific_4 == 0));
+}
+
+bool A2dpCodecConfig::setCodecUserConfig(
+    const btav_a2dp_codec_config_t& codec_user_config,
+    const btav_a2dp_codec_config_t& codec_audio_config,
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    const uint8_t* p_peer_codec_info, bool is_capability,
+    uint8_t* p_result_codec_config, bool* p_restart_input,
+    bool* p_restart_output, bool* p_config_updated) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  *p_restart_input = false;
+  *p_restart_output = false;
+  *p_config_updated = false;
+
+  // Save copies of the current codec config, and the OTA codec config, so they
+  // can be compared for changes.
+  btav_a2dp_codec_config_t saved_codec_config = getCodecConfig();
+  uint8_t saved_ota_codec_config[AVDT_CODEC_SIZE];
+  memcpy(saved_ota_codec_config, ota_codec_config_, sizeof(ota_codec_config_));
+
+  btav_a2dp_codec_config_t saved_codec_user_config = codec_user_config_;
+  codec_user_config_ = codec_user_config;
+  btav_a2dp_codec_config_t saved_codec_audio_config = codec_audio_config_;
+  codec_audio_config_ = codec_audio_config;
+  bool success =
+      setCodecConfig(p_peer_codec_info, is_capability, p_result_codec_config);
+  if (!success) {
+    // Restore the local copy of the user and audio config
+    codec_user_config_ = saved_codec_user_config;
+    codec_audio_config_ = saved_codec_audio_config;
+    return false;
+  }
+
+  //
+  // The input (audio data) should be restarted if the audio format has changed
+  //
+  btav_a2dp_codec_config_t new_codec_config = getCodecConfig();
+  if ((saved_codec_config.sample_rate != new_codec_config.sample_rate) ||
+      (saved_codec_config.bits_per_sample !=
+       new_codec_config.bits_per_sample) ||
+      (saved_codec_config.channel_mode != new_codec_config.channel_mode)) {
+    *p_restart_input = true;
+  }
+
+  //
+  // The output (the connection) should be restarted if OTA codec config
+  // has changed.
+  //
+  if (!A2DP_CodecEquals(saved_ota_codec_config, p_result_codec_config)) {
+    *p_restart_output = true;
+  }
+
+  bool encoder_restart_input = *p_restart_input;
+  bool encoder_restart_output = *p_restart_output;
+  bool encoder_config_updated = *p_config_updated;
+  if (updateEncoderUserConfig(p_peer_params, &encoder_restart_input,
+                              &encoder_restart_output,
+                              &encoder_config_updated)) {
+    if (encoder_restart_input) *p_restart_input = true;
+    if (encoder_restart_output) *p_restart_output = true;
+    if (encoder_config_updated) *p_config_updated = true;
+  }
+  if (*p_restart_input || *p_restart_output) *p_config_updated = true;
+
+  return true;
+}
+
+//
+// Compares two codecs |lhs| and |rhs| based on their priority.
+// Returns true if |lhs| has higher priority (larger priority value).
+// If |lhs| and |rhs| have same priority, the unique codec index is used
+// as a tie-breaker: larger codec index value means higher priority.
+//
+static bool compare_codec_priority(const A2dpCodecConfig* lhs,
+                                   const A2dpCodecConfig* rhs) {
+  if (lhs->codecPriority() > rhs->codecPriority()) return true;
+  if (lhs->codecPriority() < rhs->codecPriority()) return false;
+  return (lhs->codecIndex() > rhs->codecIndex());
+}
+
+A2dpCodecs::A2dpCodecs() : current_codec_config_(nullptr) {}
+
+A2dpCodecs::~A2dpCodecs() {
+  std::unique_lock<std::recursive_mutex> lock(codec_mutex_);
+  for (const auto& iter : indexed_codecs_) {
+    delete iter.second;
+  }
+  lock.unlock();
+}
+
+bool A2dpCodecs::init() {
+  LOG_DEBUG(LOG_TAG, "%s", __func__);
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+
+  for (int i = BTAV_A2DP_CODEC_INDEX_MIN; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
+    btav_a2dp_codec_index_t codec_index =
+        static_cast<btav_a2dp_codec_index_t>(i);
+    A2dpCodecConfig* codec_config = A2dpCodecConfig::createCodec(codec_index);
+    if (codec_config == nullptr) continue;
+
+    indexed_codecs_.insert(std::make_pair(codec_index, codec_config));
+
+    if (codec_index < BTAV_A2DP_CODEC_INDEX_SOURCE_MAX) {
+      ordered_source_codecs_.push_back(codec_config);
+      ordered_source_codecs_.sort(compare_codec_priority);
+    } else {
+      ordered_sink_codecs_.push_back(codec_config);
+      ordered_sink_codecs_.sort(compare_codec_priority);
+    }
+  }
+
+  if (ordered_source_codecs_.empty()) {
+    LOG_ERROR(LOG_TAG, "%s: no Source codecs were initialized", __func__);
+  } else {
+    for (auto iter : ordered_source_codecs_) {
+      LOG_INFO(LOG_TAG, "%s: initialized Source codec %s", __func__,
+               iter->name().c_str());
+    }
+  }
+  if (ordered_sink_codecs_.empty()) {
+    LOG_ERROR(LOG_TAG, "%s: no Sink codecs were initialized", __func__);
+  } else {
+    for (auto iter : ordered_sink_codecs_) {
+      LOG_INFO(LOG_TAG, "%s: initialized Sink codec %s", __func__,
+               iter->name().c_str());
+    }
+  }
+
+  return (!ordered_source_codecs_.empty() && !ordered_sink_codecs_.empty());
+}
+
+A2dpCodecConfig* A2dpCodecs::findSourceCodecConfig(
+    const uint8_t* p_codec_info) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  btav_a2dp_codec_index_t codec_index = A2DP_SourceCodecIndex(p_codec_info);
+  if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) return nullptr;
+
+  auto iter = indexed_codecs_.find(codec_index);
+  if (iter == indexed_codecs_.end()) return nullptr;
+  return iter->second;
+}
+
+bool A2dpCodecs::setCodecConfig(const uint8_t* p_peer_codec_info,
+                                bool is_capability,
+                                uint8_t* p_result_codec_config) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  A2dpCodecConfig* a2dp_codec_config = findSourceCodecConfig(p_peer_codec_info);
+  if (a2dp_codec_config == nullptr) return false;
+  if (!a2dp_codec_config->setCodecConfig(p_peer_codec_info, is_capability,
+                                         p_result_codec_config)) {
+    return false;
+  }
+  current_codec_config_ = a2dp_codec_config;
+  return true;
+}
+
+bool A2dpCodecs::setCodecUserConfig(
+    const btav_a2dp_codec_config_t& codec_user_config,
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    const uint8_t* p_peer_sink_capabilities, uint8_t* p_result_codec_config,
+    bool* p_restart_input, bool* p_restart_output, bool* p_config_updated) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  btav_a2dp_codec_config_t codec_audio_config;
+  A2dpCodecConfig* a2dp_codec_config = nullptr;
+  A2dpCodecConfig* last_codec_config = current_codec_config_;
+  *p_restart_input = false;
+  *p_restart_output = false;
+  *p_config_updated = false;
+
+  if (codec_user_config.codec_type < BTAV_A2DP_CODEC_INDEX_MAX) {
+    auto iter = indexed_codecs_.find(codec_user_config.codec_type);
+    if (iter == indexed_codecs_.end()) goto fail;
+    a2dp_codec_config = iter->second;
+  } else {
+    // Update the default codec
+    a2dp_codec_config = current_codec_config_;
+  }
+  if (a2dp_codec_config == nullptr) goto fail;
+  current_codec_config_ = a2dp_codec_config;
+
+  // Reuse the existing codec audio config
+  codec_audio_config = a2dp_codec_config->getCodecAudioConfig();
+  if (!a2dp_codec_config->setCodecUserConfig(
+          codec_user_config, codec_audio_config, p_peer_params,
+          p_peer_sink_capabilities, true, p_result_codec_config,
+          p_restart_input, p_restart_output, p_config_updated)) {
+    goto fail;
+  }
+  CHECK(current_codec_config_ != nullptr);
+
+  // If the codec has changed because of priority, update the priorities
+  // and restart the connection
+  do {
+    btav_a2dp_codec_priority_t old_priority =
+        a2dp_codec_config->codecPriority();
+    btav_a2dp_codec_priority_t new_priority = codec_user_config.codec_priority;
+    if (old_priority == new_priority) break;  // No change in priority
+    *p_config_updated = true;
+    a2dp_codec_config->setCodecPriority(new_priority);
+    // Get the actual (recomputed) priority
+    new_priority = a2dp_codec_config->codecPriority();
+    if (old_priority > new_priority) {
+      // The priority has become lower. If this was the previous codec, restart
+      // the connection to re-elect a new codec.
+      if (a2dp_codec_config == last_codec_config) {
+        *p_restart_output = true;
+      }
+      break;
+    }
+    // The priority has become higher. If this was not the previous codec,
+    // and the new priority is higher than the current codec, restart the
+    // connection to re-elect a new codec.
+    if ((a2dp_codec_config == last_codec_config) ||
+        (last_codec_config == nullptr)) {
+      break;
+    }
+    if (new_priority < last_codec_config->codecPriority()) break;
+    last_codec_config->setDefaultCodecPriority();
+    *p_restart_output = true;
+
+  } while (false);
+  ordered_source_codecs_.sort(compare_codec_priority);
+
+  if (*p_restart_input || *p_restart_output) *p_config_updated = true;
+
+  return true;
+
+fail:
+  current_codec_config_ = last_codec_config;
+  return false;
+}
+
+bool A2dpCodecs::setCodecAudioConfig(
+    const btav_a2dp_codec_config_t& codec_audio_config,
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    const uint8_t* p_peer_sink_capabilities, uint8_t* p_result_codec_config,
+    bool* p_restart_output, bool* p_config_updated) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  btav_a2dp_codec_config_t codec_user_config;
+  A2dpCodecConfig* a2dp_codec_config = current_codec_config_;
+  *p_restart_output = false;
+  *p_config_updated = false;
+
+  if (a2dp_codec_config == nullptr) return false;
+
+  // Reuse the existing codec user config
+  codec_user_config = a2dp_codec_config->getCodecUserConfig();
+  bool restart_input = false;  // Flag ignored - input was just restarted
+  if (!a2dp_codec_config->setCodecUserConfig(
+          codec_user_config, codec_audio_config, p_peer_params,
+          p_peer_sink_capabilities, true, p_result_codec_config, &restart_input,
+          p_restart_output, p_config_updated)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool A2dpCodecs::setCodecOtaConfig(
+    const uint8_t* p_ota_codec_config,
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    uint8_t* p_result_codec_config, bool* p_restart_input,
+    bool* p_restart_output, bool* p_config_updated) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  btav_a2dp_codec_index_t codec_type;
+  btav_a2dp_codec_config_t codec_user_config;
+  btav_a2dp_codec_config_t codec_audio_config;
+  A2dpCodecConfig* a2dp_codec_config = nullptr;
+  A2dpCodecConfig* last_codec_config = current_codec_config_;
+  *p_restart_input = false;
+  *p_restart_output = false;
+  *p_config_updated = false;
+
+  // Check whether the current codec config is explicitly configured by
+  // user configuration. If yes, then the OTA codec configuration is ignored.
+  if (current_codec_config_ != nullptr) {
+    codec_user_config = current_codec_config_->getCodecUserConfig();
+    if (!A2dpCodecConfig::isCodecConfigEmpty(codec_user_config)) {
+      LOG_WARN(LOG_TAG,
+               "%s: ignoring peer OTA configuration for codec %s: "
+               "existing user configuration for current codec %s",
+               __func__, A2DP_CodecName(p_ota_codec_config),
+               current_codec_config_->name().c_str());
+      goto fail;
+    }
+  }
+
+  // Check whether the codec config for the same codec is explicitly configured
+  // by user configuration. If yes, then the OTA codec configuration is
+  // ignored.
+  codec_type = A2DP_SourceCodecIndex(p_ota_codec_config);
+  if (codec_type == BTAV_A2DP_CODEC_INDEX_MAX) {
+    LOG_WARN(LOG_TAG,
+             "%s: ignoring peer OTA codec configuration: "
+             "invalid codec",
+             __func__);
+    goto fail;  // Invalid codec
+  } else {
+    auto iter = indexed_codecs_.find(codec_type);
+    if (iter == indexed_codecs_.end()) {
+      LOG_WARN(LOG_TAG,
+               "%s: cannot find codec configuration for peer OTA codec %s",
+               __func__, A2DP_CodecName(p_ota_codec_config));
+      goto fail;
+    }
+    a2dp_codec_config = iter->second;
+  }
+  if (a2dp_codec_config == nullptr) goto fail;
+  codec_user_config = a2dp_codec_config->getCodecUserConfig();
+  if (!A2dpCodecConfig::isCodecConfigEmpty(codec_user_config)) {
+    LOG_WARN(LOG_TAG,
+             "%s: ignoring peer OTA configuration for codec %s: "
+             "existing user configuration for same codec",
+             __func__, A2DP_CodecName(p_ota_codec_config));
+    goto fail;
+  }
+  current_codec_config_ = a2dp_codec_config;
+
+  // Reuse the existing codec user config and codec audio config
+  codec_audio_config = a2dp_codec_config->getCodecAudioConfig();
+  if (!a2dp_codec_config->setCodecUserConfig(
+          codec_user_config, codec_audio_config, p_peer_params,
+          p_ota_codec_config, false, p_result_codec_config, p_restart_input,
+          p_restart_output, p_config_updated)) {
+    LOG_WARN(LOG_TAG,
+             "%s: cannot set codec configuration for peer OTA codec %s",
+             __func__, A2DP_CodecName(p_ota_codec_config));
+    goto fail;
+  }
+  CHECK(current_codec_config_ != nullptr);
+
+  if (*p_restart_input || *p_restart_output) *p_config_updated = true;
+
+  return true;
+
+fail:
+  current_codec_config_ = last_codec_config;
+  return false;
+}
+
+bool A2dpCodecs::getCodecConfigAndCapabilities(
+    btav_a2dp_codec_config_t* p_codec_config,
+    std::vector<btav_a2dp_codec_config_t>* p_codec_capabilities) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+
+  if (current_codec_config_ != nullptr) {
+    *p_codec_config = current_codec_config_->getCodecConfig();
+  } else {
+    btav_a2dp_codec_config_t codec_config;
+    memset(&codec_config, 0, sizeof(codec_config));
+    *p_codec_config = codec_config;
+  }
+
+  std::vector<btav_a2dp_codec_config_t> codec_capabilities;
+  for (auto codec : orderedSourceCodecs()) {
+    codec_capabilities.push_back(codec->getCodecCapability());
+  }
+  *p_codec_capabilities = codec_capabilities;
+
+  return true;
+}
+
+tA2DP_CODEC_TYPE A2DP_GetCodecType(const uint8_t* p_codec_info) {
+  return (tA2DP_CODEC_TYPE)(p_codec_info[AVDT_CODEC_TYPE_INDEX]);
+}
+
+bool A2DP_IsSourceCodecValid(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_IsSourceCodecValidSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_IsVendorSourceCodecValid(p_codec_info);
+    default:
+      break;
+  }
+
+  return false;
+}
+
+bool A2DP_IsSinkCodecValid(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_IsSinkCodecValidSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_IsVendorSinkCodecValid(p_codec_info);
+    default:
+      break;
+  }
+
+  return false;
+}
+
+bool A2DP_IsPeerSourceCodecValid(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_IsPeerSourceCodecValidSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_IsVendorPeerSourceCodecValid(p_codec_info);
+    default:
+      break;
+  }
+
+  return false;
+}
+
+bool A2DP_IsPeerSinkCodecValid(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_IsPeerSinkCodecValidSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_IsVendorPeerSinkCodecValid(p_codec_info);
+    default:
+      break;
+  }
+
+  return false;
+}
+
+bool A2DP_IsSinkCodecSupported(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_IsSinkCodecSupportedSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_IsVendorSinkCodecSupported(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return false;
+}
+
+bool A2DP_IsPeerSourceCodecSupported(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_IsPeerSourceCodecSupportedSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_IsVendorPeerSourceCodecSupported(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return false;
+}
+
+void A2DP_InitDefaultCodec(uint8_t* p_codec_info) {
+  A2DP_InitDefaultCodecSbc(p_codec_info);
+}
+
+tA2DP_STATUS A2DP_BuildSrc2SinkConfig(const uint8_t* p_src_cap,
+                                      uint8_t* p_pref_cfg) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_src_cap);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_BuildSrc2SinkConfigSbc(p_src_cap, p_pref_cfg);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorBuildSrc2SinkConfig(p_src_cap, p_pref_cfg);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return A2DP_NS_CODEC_TYPE;
+}
+
+bool A2DP_UsesRtpHeader(bool content_protection_enabled,
+                        const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  if (codec_type != A2DP_MEDIA_CT_NON_A2DP) return true;
+
+  return A2DP_VendorUsesRtpHeader(content_protection_enabled, p_codec_info);
+}
+
+uint8_t A2DP_GetMediaType(const uint8_t* p_codec_info) {
+  uint8_t media_type = (p_codec_info[A2DP_MEDIA_TYPE_OFFSET] >> 4) & 0x0f;
+  return media_type;
+}
+
+const char* A2DP_CodecName(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_CodecNameSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorCodecName(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return "UNKNOWN CODEC";
+}
+
+bool A2DP_CodecTypeEquals(const uint8_t* p_codec_info_a,
+                          const uint8_t* p_codec_info_b) {
+  tA2DP_CODEC_TYPE codec_type_a = A2DP_GetCodecType(p_codec_info_a);
+  tA2DP_CODEC_TYPE codec_type_b = A2DP_GetCodecType(p_codec_info_b);
+
+  if (codec_type_a != codec_type_b) return false;
+
+  switch (codec_type_a) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_CodecTypeEqualsSbc(p_codec_info_a, p_codec_info_b);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorCodecTypeEquals(p_codec_info_a, p_codec_info_b);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type_a);
+  return false;
+}
+
+bool A2DP_CodecEquals(const uint8_t* p_codec_info_a,
+                      const uint8_t* p_codec_info_b) {
+  tA2DP_CODEC_TYPE codec_type_a = A2DP_GetCodecType(p_codec_info_a);
+  tA2DP_CODEC_TYPE codec_type_b = A2DP_GetCodecType(p_codec_info_b);
+
+  if (codec_type_a != codec_type_b) return false;
+
+  switch (codec_type_a) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_CodecEqualsSbc(p_codec_info_a, p_codec_info_b);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorCodecEquals(p_codec_info_a, p_codec_info_b);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type_a);
+  return false;
+}
+
+int A2DP_GetTrackSampleRate(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_GetTrackSampleRateSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorGetTrackSampleRate(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return -1;
+}
+
+int A2DP_GetTrackBitsPerSample(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_GetTrackBitsPerSampleSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorGetTrackBitsPerSample(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return -1;
+}
+
+int A2DP_GetTrackChannelCount(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_GetTrackChannelCountSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorGetTrackChannelCount(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return -1;
+}
+
+int A2DP_GetSinkTrackChannelType(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_GetSinkTrackChannelTypeSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorGetSinkTrackChannelType(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return -1;
+}
+
+int A2DP_GetSinkFramesCountToProcess(uint64_t time_interval_ms,
+                                     const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_GetSinkFramesCountToProcessSbc(time_interval_ms,
+                                                 p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorGetSinkFramesCountToProcess(time_interval_ms,
+                                                    p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return -1;
+}
+
+bool A2DP_GetPacketTimestamp(const uint8_t* p_codec_info, const uint8_t* p_data,
+                             uint32_t* p_timestamp) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_GetPacketTimestampSbc(p_codec_info, p_data, p_timestamp);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorGetPacketTimestamp(p_codec_info, p_data, p_timestamp);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return false;
+}
+
+bool A2DP_BuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf,
+                           uint16_t frames_per_packet) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_BuildCodecHeaderSbc(p_codec_info, p_buf, frames_per_packet);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorBuildCodecHeader(p_codec_info, p_buf,
+                                         frames_per_packet);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return false;
+}
+
+const tA2DP_ENCODER_INTERFACE* A2DP_GetEncoderInterface(
+    const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_GetEncoderInterfaceSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorGetEncoderInterface(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return NULL;
+}
+
+bool A2DP_AdjustCodec(uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_AdjustCodecSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorAdjustCodec(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return false;
+}
+
+btav_a2dp_codec_index_t A2DP_SourceCodecIndex(const uint8_t* p_codec_info) {
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+
+  LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type);
+
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      return A2DP_SourceCodecIndexSbc(p_codec_info);
+    case A2DP_MEDIA_CT_NON_A2DP:
+      return A2DP_VendorSourceCodecIndex(p_codec_info);
+    default:
+      break;
+  }
+
+  LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type);
+  return BTAV_A2DP_CODEC_INDEX_MAX;
+}
+
+const char* A2DP_CodecIndexStr(btav_a2dp_codec_index_t codec_index) {
+  switch (codec_index) {
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
+      return A2DP_CodecIndexStrSbc();
+    case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
+      return A2DP_CodecIndexStrSbcSink();
+    default:
+      break;
+  }
+
+  if (codec_index < BTAV_A2DP_CODEC_INDEX_MAX)
+    return A2DP_VendorCodecIndexStr(codec_index);
+
+  return "UNKNOWN CODEC INDEX";
+}
+
+bool A2DP_InitCodecConfig(btav_a2dp_codec_index_t codec_index,
+                          tAVDT_CFG* p_cfg) {
+  LOG_VERBOSE(LOG_TAG, "%s: codec %s", __func__,
+              A2DP_CodecIndexStr(codec_index));
+
+  /* Default: no content protection info */
+  p_cfg->num_protect = 0;
+  p_cfg->protect_info[0] = 0;
+
+  switch (codec_index) {
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
+      return A2DP_InitCodecConfigSbc(p_cfg);
+    case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
+      return A2DP_InitCodecConfigSbcSink(p_cfg);
+    default:
+      break;
+  }
+
+  if (codec_index < BTAV_A2DP_CODEC_INDEX_MAX)
+    return A2DP_VendorInitCodecConfig(codec_index, p_cfg);
+
+  return false;
+}
diff --git a/stack/a2dp/a2dp_sbc.cc b/stack/a2dp/a2dp_sbc.cc
index f96413f..6eccf05 100644
--- a/stack/a2dp/a2dp_sbc.cc
+++ b/stack/a2dp/a2dp_sbc.cc
@@ -27,10 +27,12 @@
 
 #include "bt_target.h"
 
-#include <string.h>
-#include "a2dp_api.h"
-#include "a2dp_int.h"
 #include "a2dp_sbc.h"
+
+#include <string.h>
+
+#include <base/logging.h>
+#include "a2dp_int.h"
 #include "a2dp_sbc_encoder.h"
 #include "bt_utils.h"
 #include "embdrv/sbc/encoder/include/sbc_encoder.h"
@@ -93,7 +95,7 @@
 
 static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilitySbc(
     const tA2DP_SBC_CIE* p_cap, const uint8_t* p_codec_info,
-    bool is_peer_codec_info);
+    bool is_capability);
 static void A2DP_ParseMplHeaderSbc(uint8_t* p_src, bool* p_frag, bool* p_start,
                                    bool* p_last, uint8_t* p_num);
 
@@ -139,13 +141,13 @@
 
 // Parses the SBC Media Codec Capabilities byte sequence beginning from the
 // LOSC octet. The result is stored in |p_ie|. The byte sequence to parse is
-// |p_codec_info|. If |is_peer_src_codec_info| is true, the byte sequence is
-// for get capabilities response from a peer Source.
+// |p_codec_info|. If |is_capability| is true, the byte sequence contains
+// codec capability.
 // Returns A2DP_SUCCESS on success, otherwise the corresponding A2DP error
 // status code.
 static tA2DP_STATUS A2DP_ParseInfoSbc(tA2DP_SBC_CIE* p_ie,
                                       const uint8_t* p_codec_info,
-                                      bool is_peer_src_codec_info) {
+                                      bool is_capability) {
   tA2DP_STATUS status = A2DP_SUCCESS;
   uint8_t losc;
   uint8_t media_type;
@@ -184,7 +186,7 @@
     status = A2DP_BAD_MAX_BITPOOL;
   }
 
-  if (is_peer_src_codec_info) return status;
+  if (is_capability) return status;
 
   if (A2DP_BitsSet(p_ie->samp_freq) != A2DP_SET_ONE_BIT)
     status = A2DP_BAD_SAMP_FREQ;
@@ -254,44 +256,10 @@
   }
 }
 
-tA2DP_CODEC_SEP_INDEX A2DP_SourceCodecSepIndexSbc(const uint8_t* p_codec_info) {
-  return A2DP_CODEC_SEP_INDEX_SOURCE_SBC;
-}
-
-const char* A2DP_CodecSepIndexStrSbc(void) { return "SBC"; }
-
-const char* A2DP_CodecSepIndexStrSbcSink(void) { return "SBC SINK"; }
-
-bool A2DP_InitCodecConfigSbc(tAVDT_CFG* p_cfg) {
-  if (A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &a2dp_sbc_caps,
-                        p_cfg->codec_info) != A2DP_SUCCESS) {
-    return false;
-  }
-
-#if (BTA_AV_CO_CP_SCMS_T == TRUE)
-  /* Content protection info - support SCMS-T */
-  uint8_t* p = p_cfg->protect_info;
-  *p++ = AVDT_CP_LOSC;
-  UINT16_TO_STREAM(p, AVDT_CP_SCMS_T_ID);
-  p_cfg->num_protect = 1;
-#endif
-
-  return true;
-}
-
 const char* A2DP_CodecNameSbc(UNUSED_ATTR const uint8_t* p_codec_info) {
   return "SBC";
 }
 
-bool A2DP_InitCodecConfigSbcSink(tAVDT_CFG* p_cfg) {
-  if (A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &a2dp_sbc_sink_caps,
-                        p_cfg->codec_info) != A2DP_SUCCESS) {
-    return false;
-  }
-
-  return true;
-}
-
 bool A2DP_IsSourceCodecValidSbc(const uint8_t* p_codec_info) {
   tA2DP_SBC_CIE cfg_cie;
 
@@ -341,144 +309,20 @@
   }
 }
 
-tA2DP_STATUS A2DP_InitSource2SinkCodecSbc(const uint8_t* p_sink_caps,
-                                          uint8_t* p_result_codec_config) {
-  tA2DP_SBC_CIE sink_caps_cie;
-  tA2DP_SBC_CIE result_config_cie;
-
-  tA2DP_STATUS status = A2DP_ParseInfoSbc(&sink_caps_cie, p_sink_caps, false);
-  if (status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: can't parse peer's Sink capabilities: error = %d",
-              __func__, status);
-    return A2DP_FAIL;
-  }
-
-  /* Load the encoder */
-  if (!A2DP_LoadEncoderSbc()) {
-    LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
-    return A2DP_FAIL;
-  }
-
-  //
-  // Build the preferred configuration
-  //
-  memset(&result_config_cie, 0, sizeof(result_config_cie));
-  // Select the sample frequency
-  if (a2dp_sbc_caps.samp_freq & sink_caps_cie.samp_freq &
-      A2DP_SBC_IE_SAMP_FREQ_48) {
-    result_config_cie.samp_freq = A2DP_SBC_IE_SAMP_FREQ_48;
-  } else if (a2dp_sbc_caps.samp_freq & sink_caps_cie.samp_freq &
-             A2DP_SBC_IE_SAMP_FREQ_44) {
-    result_config_cie.samp_freq = A2DP_SBC_IE_SAMP_FREQ_44;
-  } else {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match sample frequency: source caps = 0x%x "
-              "sink caps = 0x%x",
-              __func__, a2dp_sbc_caps.samp_freq, sink_caps_cie.samp_freq);
-    return A2DP_BAD_SAMP_FREQ;
-  }
-  // Select the channel mode
-  if (a2dp_sbc_caps.ch_mode & sink_caps_cie.ch_mode & A2DP_SBC_IE_CH_MD_JOINT) {
-    result_config_cie.ch_mode = A2DP_SBC_IE_CH_MD_JOINT;
-  } else if (a2dp_sbc_caps.ch_mode & sink_caps_cie.ch_mode &
-             A2DP_SBC_IE_CH_MD_STEREO) {
-    result_config_cie.ch_mode = A2DP_SBC_IE_CH_MD_STEREO;
-  } else if (a2dp_sbc_caps.ch_mode & sink_caps_cie.ch_mode &
-             A2DP_SBC_IE_CH_MD_DUAL) {
-    result_config_cie.ch_mode = A2DP_SBC_IE_CH_MD_DUAL;
-  } else if (a2dp_sbc_caps.ch_mode & sink_caps_cie.ch_mode &
-             A2DP_SBC_IE_CH_MD_MONO) {
-    result_config_cie.ch_mode = A2DP_SBC_IE_CH_MD_MONO;
-  } else {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match channel mode: source caps = 0x%x "
-              "sink caps = 0x%x",
-              __func__, a2dp_sbc_caps.ch_mode, sink_caps_cie.ch_mode);
-    return A2DP_BAD_CH_MODE;
-  }
-  // Select the block length
-  if (a2dp_sbc_caps.block_len & sink_caps_cie.block_len &
-      A2DP_SBC_IE_BLOCKS_16) {
-    result_config_cie.block_len = A2DP_SBC_IE_BLOCKS_16;
-  } else if (a2dp_sbc_caps.block_len & sink_caps_cie.block_len &
-             A2DP_SBC_IE_BLOCKS_12) {
-    result_config_cie.block_len = A2DP_SBC_IE_BLOCKS_12;
-  } else if (a2dp_sbc_caps.block_len & sink_caps_cie.block_len &
-             A2DP_SBC_IE_BLOCKS_8) {
-    result_config_cie.block_len = A2DP_SBC_IE_BLOCKS_8;
-  } else if (a2dp_sbc_caps.block_len & sink_caps_cie.block_len &
-             A2DP_SBC_IE_BLOCKS_4) {
-    result_config_cie.block_len = A2DP_SBC_IE_BLOCKS_4;
-  } else {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match block length: source caps = 0x%x "
-              "sink caps = 0x%x",
-              __func__, a2dp_sbc_caps.block_len, sink_caps_cie.block_len);
-    return A2DP_BAD_BLOCK_LEN;
-  }
-  // Select the number of sub-bands
-  if (a2dp_sbc_caps.num_subbands & sink_caps_cie.num_subbands &
-      A2DP_SBC_IE_SUBBAND_8) {
-    result_config_cie.num_subbands = A2DP_SBC_IE_SUBBAND_8;
-  } else if (a2dp_sbc_caps.num_subbands & sink_caps_cie.num_subbands &
-             A2DP_SBC_IE_SUBBAND_4) {
-    result_config_cie.num_subbands = A2DP_SBC_IE_SUBBAND_4;
-  } else {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match number of sub-bands: source caps = 0x%x "
-              "sink caps = 0x%x",
-              __func__, a2dp_sbc_caps.num_subbands, sink_caps_cie.num_subbands);
-    return A2DP_BAD_SUBBANDS;
-  }
-  // Select the allocation method
-  if (a2dp_sbc_caps.alloc_method & sink_caps_cie.alloc_method &
-      A2DP_SBC_IE_ALLOC_MD_L) {
-    result_config_cie.alloc_method = A2DP_SBC_IE_ALLOC_MD_L;
-  } else if (a2dp_sbc_caps.alloc_method & sink_caps_cie.alloc_method &
-             A2DP_SBC_IE_ALLOC_MD_S) {
-    result_config_cie.alloc_method = A2DP_SBC_IE_ALLOC_MD_S;
-  } else {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match allocation method: source caps = 0x%x "
-              "sink caps = 0x%x",
-              __func__, a2dp_sbc_caps.alloc_method, sink_caps_cie.alloc_method);
-    return A2DP_BAD_ALLOC_METHOD;
-  }
-  // Select the min/max bitpool
-  result_config_cie.min_bitpool = a2dp_sbc_caps.min_bitpool;
-  if (result_config_cie.min_bitpool < sink_caps_cie.min_bitpool)
-    result_config_cie.min_bitpool = sink_caps_cie.min_bitpool;
-  result_config_cie.max_bitpool = a2dp_sbc_caps.max_bitpool;
-  if (result_config_cie.max_bitpool > sink_caps_cie.max_bitpool)
-    result_config_cie.max_bitpool = sink_caps_cie.max_bitpool;
-  if (result_config_cie.min_bitpool > result_config_cie.max_bitpool) {
-    LOG_ERROR(LOG_TAG,
-              "%s: cannot match min/max bitpool: "
-              "source caps min/max = 0x%x/0x%x sink caps min/max = 0x%x/0x%x",
-              __func__, a2dp_sbc_caps.min_bitpool, a2dp_sbc_caps.max_bitpool,
-              sink_caps_cie.min_bitpool, sink_caps_cie.max_bitpool);
-    return A2DP_BAD_ALLOC_METHOD;
-  }
-
-  return A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
-                           p_result_codec_config);
-}
-
 // Checks whether A2DP SBC codec configuration matches with a device's codec
 // capabilities. |p_cap| is the SBC codec configuration. |p_codec_info| is
-// the device's codec capabilities. |is_peer_src_codec_info| is true if
-// |p_codec_info| contains the codec capabilities for a peer device that
-// is acting as an A2DP source.
+// the device's codec capabilities. |is_capability| is true if
+// |p_codec_info| contains A2DP codec capability.
 // Returns A2DP_SUCCESS if the codec configuration matches with capabilities,
 // otherwise the corresponding A2DP error status code.
 static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilitySbc(
     const tA2DP_SBC_CIE* p_cap, const uint8_t* p_codec_info,
-    bool is_peer_src_codec_info) {
+    bool is_capability) {
   tA2DP_STATUS status;
   tA2DP_SBC_CIE cfg_cie;
 
   /* parse configuration */
-  status = A2DP_ParseInfoSbc(&cfg_cie, p_codec_info, is_peer_src_codec_info);
+  status = A2DP_ParseInfoSbc(&cfg_cie, p_codec_info, is_capability);
   if (status != A2DP_SUCCESS) {
     LOG_ERROR(LOG_TAG, "%s: parsing failed %d", __func__, status);
     return status;
@@ -592,13 +436,13 @@
 
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status =
-      A2DP_ParseInfoSbc(&sbc_cie_a, p_codec_info_a, false);
+      A2DP_ParseInfoSbc(&sbc_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
     LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
               a2dp_status);
     return false;
   }
-  a2dp_status = A2DP_ParseInfoSbc(&sbc_cie_b, p_codec_info_b, false);
+  a2dp_status = A2DP_ParseInfoSbc(&sbc_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
     LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
               a2dp_status);
@@ -618,13 +462,13 @@
 
   // Check whether the codec info contains valid data
   tA2DP_STATUS a2dp_status =
-      A2DP_ParseInfoSbc(&sbc_cie_a, p_codec_info_a, false);
+      A2DP_ParseInfoSbc(&sbc_cie_a, p_codec_info_a, true);
   if (a2dp_status != A2DP_SUCCESS) {
     LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
               a2dp_status);
     return false;
   }
-  a2dp_status = A2DP_ParseInfoSbc(&sbc_cie_b, p_codec_info_b, false);
+  a2dp_status = A2DP_ParseInfoSbc(&sbc_cie_b, p_codec_info_b, true);
   if (a2dp_status != A2DP_SUCCESS) {
     LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
               a2dp_status);
@@ -672,6 +516,19 @@
   return -1;
 }
 
+int A2DP_GetTrackBitsPerSampleSbc(const uint8_t* p_codec_info) {
+  tA2DP_SBC_CIE sbc_cie;
+
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return -1;
+  }
+
+  return 16;  // For SBC we always use 16 bits per audio sample
+}
+
 int A2DP_GetTrackChannelCountSbc(const uint8_t* p_codec_info) {
   tA2DP_SBC_CIE sbc_cie;
 
@@ -696,19 +553,6 @@
   return -1;
 }
 
-int A2DP_GetTrackBitsPerSampleSbc(const uint8_t* p_codec_info) {
-  tA2DP_SBC_CIE sbc_cie;
-
-  tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
-  if (a2dp_status != A2DP_SUCCESS) {
-    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
-              a2dp_status);
-    return -1;
-  }
-
-  return 16;  // For SBC we always use 16 bits per audio sample
-}
-
 int A2DP_GetNumberOfSubbandsSbc(const uint8_t* p_codec_info) {
   tA2DP_SBC_CIE sbc_cie;
 
@@ -834,7 +678,7 @@
 int A2DP_GetMinBitpoolSbc(const uint8_t* p_codec_info) {
   tA2DP_SBC_CIE sbc_cie;
 
-  tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, true);
   if (a2dp_status != A2DP_SUCCESS) {
     LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
               a2dp_status);
@@ -847,7 +691,7 @@
 int A2DP_GetMaxBitpoolSbc(const uint8_t* p_codec_info) {
   tA2DP_SBC_CIE sbc_cie;
 
-  tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, true);
   if (a2dp_status != A2DP_SUCCESS) {
     LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
               a2dp_status);
@@ -1038,62 +882,68 @@
 
   LOG_DEBUG(LOG_TAG, "%s", __func__);
 
-  a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, false);
+  a2dp_status = A2DP_ParseInfoSbc(&sbc_cie, p_codec_info, true);
   if (a2dp_status != A2DP_SUCCESS) {
     LOG_ERROR(LOG_TAG, "%s: A2DP_ParseInfoSbc fail:%d", __func__, a2dp_status);
     return;
   }
 
-  if (sbc_cie.samp_freq == A2DP_SBC_IE_SAMP_FREQ_16) {
-    LOG_DEBUG(LOG_TAG, "\tsamp_freq:%d (16000)", sbc_cie.samp_freq);
-  } else if (sbc_cie.samp_freq == A2DP_SBC_IE_SAMP_FREQ_32) {
-    LOG_DEBUG(LOG_TAG, "\tsamp_freq:%d (32000)", sbc_cie.samp_freq);
-  } else if (sbc_cie.samp_freq == A2DP_SBC_IE_SAMP_FREQ_44) {
-    LOG_DEBUG(LOG_TAG, "\tsamp_freq:%d (44.100)", sbc_cie.samp_freq);
-  } else if (sbc_cie.samp_freq == A2DP_SBC_IE_SAMP_FREQ_48) {
-    LOG_DEBUG(LOG_TAG, "\tsamp_freq:%d (48000)", sbc_cie.samp_freq);
-  } else {
-    LOG_DEBUG(LOG_TAG, "\tBAD samp_freq:%d", sbc_cie.samp_freq);
+  LOG_DEBUG(LOG_TAG, "\tsamp_freq: 0x%x", sbc_cie.samp_freq);
+  if (sbc_cie.samp_freq & A2DP_SBC_IE_SAMP_FREQ_16) {
+    LOG_DEBUG(LOG_TAG, "\tsamp_freq: (16000)");
+  }
+  if (sbc_cie.samp_freq & A2DP_SBC_IE_SAMP_FREQ_32) {
+    LOG_DEBUG(LOG_TAG, "\tsamp_freq: (32000)");
+  }
+  if (sbc_cie.samp_freq & A2DP_SBC_IE_SAMP_FREQ_44) {
+    LOG_DEBUG(LOG_TAG, "\tsamp_freq: (44100)");
+  }
+  if (sbc_cie.samp_freq & A2DP_SBC_IE_SAMP_FREQ_48) {
+    LOG_DEBUG(LOG_TAG, "\tsamp_freq: (48000)");
   }
 
-  if (sbc_cie.ch_mode == A2DP_SBC_IE_CH_MD_MONO) {
-    LOG_DEBUG(LOG_TAG, "\tch_mode:%d (Mono)", sbc_cie.ch_mode);
-  } else if (sbc_cie.ch_mode == A2DP_SBC_IE_CH_MD_DUAL) {
-    LOG_DEBUG(LOG_TAG, "\tch_mode:%d (Dual)", sbc_cie.ch_mode);
-  } else if (sbc_cie.ch_mode == A2DP_SBC_IE_CH_MD_STEREO) {
-    LOG_DEBUG(LOG_TAG, "\tch_mode:%d (Stereo)", sbc_cie.ch_mode);
-  } else if (sbc_cie.ch_mode == A2DP_SBC_IE_CH_MD_JOINT) {
-    LOG_DEBUG(LOG_TAG, "\tch_mode:%d (Joint)", sbc_cie.ch_mode);
-  } else {
-    LOG_DEBUG(LOG_TAG, "\tBAD ch_mode:%d", sbc_cie.ch_mode);
+  LOG_DEBUG(LOG_TAG, "\tch_mode: 0x%x", sbc_cie.ch_mode);
+  if (sbc_cie.ch_mode & A2DP_SBC_IE_CH_MD_MONO) {
+    LOG_DEBUG(LOG_TAG, "\tch_mode: (Mono)");
+  }
+  if (sbc_cie.ch_mode & A2DP_SBC_IE_CH_MD_DUAL) {
+    LOG_DEBUG(LOG_TAG, "\tch_mode: (Dual)");
+  }
+  if (sbc_cie.ch_mode & A2DP_SBC_IE_CH_MD_STEREO) {
+    LOG_DEBUG(LOG_TAG, "\tch_mode: (Stereo)");
+  }
+  if (sbc_cie.ch_mode & A2DP_SBC_IE_CH_MD_JOINT) {
+    LOG_DEBUG(LOG_TAG, "\tch_mode: (Joint)");
   }
 
-  if (sbc_cie.block_len == A2DP_SBC_IE_BLOCKS_4) {
-    LOG_DEBUG(LOG_TAG, "\tblock_len:%d (4)", sbc_cie.block_len);
-  } else if (sbc_cie.block_len == A2DP_SBC_IE_BLOCKS_8) {
-    LOG_DEBUG(LOG_TAG, "\tblock_len:%d (8)", sbc_cie.block_len);
-  } else if (sbc_cie.block_len == A2DP_SBC_IE_BLOCKS_12) {
-    LOG_DEBUG(LOG_TAG, "\tblock_len:%d (12)", sbc_cie.block_len);
-  } else if (sbc_cie.block_len == A2DP_SBC_IE_BLOCKS_16) {
-    LOG_DEBUG(LOG_TAG, "\tblock_len:%d (16)", sbc_cie.block_len);
-  } else {
-    LOG_DEBUG(LOG_TAG, "\tBAD block_len:%d", sbc_cie.block_len);
+  LOG_DEBUG(LOG_TAG, "\tblock_len: 0x%x", sbc_cie.block_len);
+  if (sbc_cie.block_len & A2DP_SBC_IE_BLOCKS_4) {
+    LOG_DEBUG(LOG_TAG, "\tblock_len: (4)");
+  }
+  if (sbc_cie.block_len & A2DP_SBC_IE_BLOCKS_8) {
+    LOG_DEBUG(LOG_TAG, "\tblock_len: (8)");
+  }
+  if (sbc_cie.block_len & A2DP_SBC_IE_BLOCKS_12) {
+    LOG_DEBUG(LOG_TAG, "\tblock_len: (12)");
+  }
+  if (sbc_cie.block_len & A2DP_SBC_IE_BLOCKS_16) {
+    LOG_DEBUG(LOG_TAG, "\tblock_len: (16)");
   }
 
-  if (sbc_cie.num_subbands == A2DP_SBC_IE_SUBBAND_4) {
-    LOG_DEBUG(LOG_TAG, "\tnum_subbands:%d (4)", sbc_cie.num_subbands);
-  } else if (sbc_cie.num_subbands == A2DP_SBC_IE_SUBBAND_8) {
-    LOG_DEBUG(LOG_TAG, "\tnum_subbands:%d (8)", sbc_cie.num_subbands);
-  } else {
-    LOG_DEBUG(LOG_TAG, "\tBAD num_subbands:%d", sbc_cie.num_subbands);
+  LOG_DEBUG(LOG_TAG, "\tnum_subbands: 0x%x", sbc_cie.num_subbands);
+  if (sbc_cie.num_subbands & A2DP_SBC_IE_SUBBAND_4) {
+    LOG_DEBUG(LOG_TAG, "\tnum_subbands: (4)");
+  }
+  if (sbc_cie.num_subbands & A2DP_SBC_IE_SUBBAND_8) {
+    LOG_DEBUG(LOG_TAG, "\tnum_subbands: (8)");
   }
 
-  if (sbc_cie.alloc_method == A2DP_SBC_IE_ALLOC_MD_S) {
-    LOG_DEBUG(LOG_TAG, "\talloc_method:%d (SNR)", sbc_cie.alloc_method);
-  } else if (sbc_cie.alloc_method == A2DP_SBC_IE_ALLOC_MD_L) {
-    LOG_DEBUG(LOG_TAG, "\talloc_method:%d (Loundess)", sbc_cie.alloc_method);
-  } else {
-    LOG_DEBUG(LOG_TAG, "\tBAD alloc_method:%d", sbc_cie.alloc_method);
+  LOG_DEBUG(LOG_TAG, "\talloc_method: 0x%x)", sbc_cie.alloc_method);
+  if (sbc_cie.alloc_method & A2DP_SBC_IE_ALLOC_MD_S) {
+    LOG_DEBUG(LOG_TAG, "\talloc_method: (SNR)");
+  }
+  if (sbc_cie.alloc_method & A2DP_SBC_IE_ALLOC_MD_L) {
+    LOG_DEBUG(LOG_TAG, "\talloc_method: (Loundess)");
   }
 
   LOG_DEBUG(LOG_TAG, "\tBit pool Min:%d Max:%d", sbc_cie.min_bitpool,
@@ -1110,7 +960,7 @@
 bool A2DP_AdjustCodecSbc(uint8_t* p_codec_info) {
   tA2DP_SBC_CIE cfg_cie;
 
-  if (A2DP_ParseInfoSbc(&cfg_cie, p_codec_info, false) != A2DP_SUCCESS)
+  if (A2DP_ParseInfoSbc(&cfg_cie, p_codec_info, true) != A2DP_SUCCESS)
     return false;
 
   // Updated the max bitpool
@@ -1123,3 +973,606 @@
   return (A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &cfg_cie, p_codec_info) ==
           A2DP_SUCCESS);
 }
+
+btav_a2dp_codec_index_t A2DP_SourceCodecIndexSbc(
+    UNUSED_ATTR const uint8_t* p_codec_info) {
+  return BTAV_A2DP_CODEC_INDEX_SOURCE_SBC;
+}
+
+const char* A2DP_CodecIndexStrSbc(void) { return "SBC"; }
+
+const char* A2DP_CodecIndexStrSbcSink(void) { return "SBC SINK"; }
+
+bool A2DP_InitCodecConfigSbc(tAVDT_CFG* p_cfg) {
+  if (A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &a2dp_sbc_caps,
+                        p_cfg->codec_info) != A2DP_SUCCESS) {
+    return false;
+  }
+
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+  /* Content protection info - support SCMS-T */
+  uint8_t* p = p_cfg->protect_info;
+  *p++ = AVDT_CP_LOSC;
+  UINT16_TO_STREAM(p, AVDT_CP_SCMS_T_ID);
+  p_cfg->num_protect = 1;
+#endif
+
+  return true;
+}
+
+bool A2DP_InitCodecConfigSbcSink(tAVDT_CFG* p_cfg) {
+  if (A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &a2dp_sbc_sink_caps,
+                        p_cfg->codec_info) != A2DP_SUCCESS) {
+    return false;
+  }
+
+  return true;
+}
+
+UNUSED_ATTR static void build_codec_config(const tA2DP_SBC_CIE& config_cie,
+                                           btav_a2dp_codec_config_t* result) {
+  if (config_cie.samp_freq & A2DP_SBC_IE_SAMP_FREQ_44)
+    result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+  if (config_cie.samp_freq & A2DP_SBC_IE_SAMP_FREQ_48)
+    result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+
+  result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+
+  if (config_cie.ch_mode & A2DP_SBC_IE_CH_MD_MONO)
+    result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+
+  if (config_cie.ch_mode & (A2DP_SBC_IE_CH_MD_STEREO | A2DP_SBC_IE_CH_MD_JOINT |
+                            A2DP_SBC_IE_CH_MD_DUAL)) {
+    result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+  }
+}
+
+A2dpCodecConfigSbc::A2dpCodecConfigSbc()
+    : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC, "SBC") {}
+
+A2dpCodecConfigSbc::~A2dpCodecConfigSbc() {}
+
+bool A2dpCodecConfigSbc::init() {
+  if (!isValid()) return false;
+
+  // Load the encoder
+  if (!A2DP_LoadEncoderSbc()) {
+    LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Selects the best sample rate from |samp_freq|.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_sample_rate(uint8_t samp_freq, tA2DP_SBC_CIE* p_result,
+                                    btav_a2dp_codec_config_t* p_codec_config) {
+  if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_48) {
+    p_result->samp_freq = A2DP_SBC_IE_SAMP_FREQ_48;
+    p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+    return true;
+  }
+  if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_44) {
+    p_result->samp_freq = A2DP_SBC_IE_SAMP_FREQ_44;
+    p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+    return true;
+  }
+  return false;
+}
+
+//
+// Selects the audio sample rate from |p_codec_audio_config|.
+// |samp_freq| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_sample_rate(
+    const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t samp_freq,
+    tA2DP_SBC_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+      if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_44) {
+        p_result->samp_freq = A2DP_SBC_IE_SAMP_FREQ_44;
+        p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_48) {
+        p_result->samp_freq = A2DP_SBC_IE_SAMP_FREQ_48;
+        p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
+      break;
+  }
+
+  return false;
+}
+
+//
+// Selects the best bits per sample.
+// The result is stored in |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_bits_per_sample(
+    btav_a2dp_codec_config_t* p_codec_config) {
+  p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+  return true;
+}
+
+//
+// Selects the audio bits per sample from |p_codec_audio_config|.
+// The result is stored in |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_bits_per_sample(
+    const btav_a2dp_codec_config_t* p_codec_audio_config,
+    btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+      return true;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+      break;
+  }
+  return false;
+}
+
+//
+// Selects the best channel mode from |ch_mode|.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_channel_mode(uint8_t ch_mode, tA2DP_SBC_CIE* p_result,
+                                     btav_a2dp_codec_config_t* p_codec_config) {
+  if (ch_mode & A2DP_SBC_IE_CH_MD_JOINT) {
+    p_result->ch_mode = A2DP_SBC_IE_CH_MD_JOINT;
+    p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+    return true;
+  }
+  if (ch_mode & A2DP_SBC_IE_CH_MD_STEREO) {
+    p_result->ch_mode = A2DP_SBC_IE_CH_MD_STEREO;
+    p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+    return true;
+  }
+  if (ch_mode & A2DP_SBC_IE_CH_MD_DUAL) {
+    p_result->ch_mode = A2DP_SBC_IE_CH_MD_DUAL;
+    p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+    return true;
+  }
+  if (ch_mode & A2DP_SBC_IE_CH_MD_MONO) {
+    p_result->ch_mode = A2DP_SBC_IE_CH_MD_MONO;
+    p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+    return true;
+  }
+  return false;
+}
+
+//
+// Selects the audio channel mode from |p_codec_audio_config|.
+// |ch_mode| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_channel_mode(
+    const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t ch_mode,
+    tA2DP_SBC_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->channel_mode) {
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+      if (ch_mode & A2DP_SBC_IE_CH_MD_MONO) {
+        p_result->ch_mode = A2DP_SBC_IE_CH_MD_MONO;
+        p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+      if (ch_mode & A2DP_SBC_IE_CH_MD_JOINT) {
+        p_result->ch_mode = A2DP_SBC_IE_CH_MD_JOINT;
+        p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+        return true;
+      }
+      if (ch_mode & A2DP_SBC_IE_CH_MD_STEREO) {
+        p_result->ch_mode = A2DP_SBC_IE_CH_MD_STEREO;
+        p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+        return true;
+      }
+      if (ch_mode & A2DP_SBC_IE_CH_MD_DUAL) {
+        p_result->ch_mode = A2DP_SBC_IE_CH_MD_DUAL;
+        p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
+      break;
+  }
+
+  return false;
+}
+
+bool A2dpCodecConfigSbc::setCodecConfig(const uint8_t* p_peer_codec_info,
+                                        bool is_capability,
+                                        uint8_t* p_result_codec_config) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  tA2DP_SBC_CIE sink_info_cie;
+  tA2DP_SBC_CIE result_config_cie;
+  uint8_t samp_freq;
+  uint8_t ch_mode;
+  uint8_t block_len;
+  uint8_t num_subbands;
+  uint8_t alloc_method;
+
+  // Save the internal state
+  btav_a2dp_codec_config_t saved_codec_config = codec_config_;
+  btav_a2dp_codec_config_t saved_codec_capability = codec_capability_;
+  btav_a2dp_codec_config_t saved_codec_user_config = codec_user_config_;
+  btav_a2dp_codec_config_t saved_codec_audio_config = codec_audio_config_;
+  uint8_t saved_ota_codec_config[AVDT_CODEC_SIZE];
+  uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE];
+  uint8_t saved_ota_codec_peer_config[AVDT_CODEC_SIZE];
+  memcpy(saved_ota_codec_config, ota_codec_config_, sizeof(ota_codec_config_));
+  memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_,
+         sizeof(ota_codec_peer_capability_));
+  memcpy(saved_ota_codec_peer_config, ota_codec_peer_config_,
+         sizeof(ota_codec_peer_config_));
+
+  tA2DP_STATUS status =
+      A2DP_ParseInfoSbc(&sink_info_cie, p_peer_codec_info, is_capability);
+  if (status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: can't parse peer's Sink capabilities: error = %d",
+              __func__, status);
+    goto fail;
+  }
+
+  //
+  // Build the preferred configuration
+  //
+  memset(&result_config_cie, 0, sizeof(result_config_cie));
+
+  //
+  // Select the sample frequency
+  //
+  samp_freq = a2dp_sbc_caps.samp_freq & sink_info_cie.samp_freq;
+  codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+  switch (codec_user_config_.sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+      if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_44) {
+        result_config_cie.samp_freq = A2DP_SBC_IE_SAMP_FREQ_44;
+        codec_capability_.sample_rate = codec_user_config_.sample_rate;
+        codec_config_.sample_rate = codec_user_config_.sample_rate;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_48) {
+        result_config_cie.samp_freq = A2DP_SBC_IE_SAMP_FREQ_48;
+        codec_capability_.sample_rate = codec_user_config_.sample_rate;
+        codec_config_.sample_rate = codec_user_config_.sample_rate;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
+      codec_capability_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+      codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+      break;
+  }
+
+  // Select the sample frequency if there is no user preference
+  do {
+    if (codec_config_.sample_rate != BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) break;
+
+    // Compute the common capability
+    if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_44)
+      codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+    if (samp_freq & A2DP_SBC_IE_SAMP_FREQ_48)
+      codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+
+    // No user preference - try the codec audio config
+    if (select_audio_sample_rate(&codec_audio_config_, samp_freq,
+                                 &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_sample_rate(
+            a2dp_sbc_default_config.samp_freq & sink_info_cie.samp_freq,
+            &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    if (select_best_sample_rate(samp_freq, &result_config_cie,
+                                &codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match sample frequency: source caps = 0x%x "
+              "sink info = 0x%x",
+              __func__, a2dp_sbc_caps.samp_freq, sink_info_cie.samp_freq);
+    goto fail;
+  }
+
+  //
+  // Select the bits per sample
+  //
+  // NOTE: this information is NOT included in the SBC A2DP codec description
+  // that is sent OTA.
+  codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+  switch (codec_user_config_.bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample;
+      codec_config_.bits_per_sample = codec_user_config_.bits_per_sample;
+      break;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+      codec_capability_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+      codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+      break;
+  }
+
+  // Select the bits per sample if there is no user preference
+  do {
+    if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE)
+      break;
+
+    // Compute the common capability
+    codec_capability_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+
+    // No user preference - try the codec audio config
+    if (select_audio_bits_per_sample(&codec_audio_config_, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_bits_per_sample(&codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    // TODO: no-op - temporary kept here for consistency
+    if (select_best_bits_per_sample(&codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match bits per sample: user preference = 0x%x",
+              __func__, codec_user_config_.bits_per_sample);
+    goto fail;
+  }
+
+  //
+  // Select the channel mode
+  //
+  ch_mode = a2dp_sbc_caps.ch_mode & sink_info_cie.ch_mode;
+  codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+  switch (codec_user_config_.channel_mode) {
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+      if (ch_mode & A2DP_SBC_IE_CH_MD_MONO) {
+        result_config_cie.ch_mode = A2DP_SBC_IE_CH_MD_MONO;
+        codec_capability_.channel_mode = codec_user_config_.channel_mode;
+        codec_config_.channel_mode = codec_user_config_.channel_mode;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+      if (ch_mode & A2DP_SBC_IE_CH_MD_JOINT) {
+        result_config_cie.ch_mode = A2DP_SBC_IE_CH_MD_JOINT;
+        codec_capability_.channel_mode = codec_user_config_.channel_mode;
+        codec_config_.channel_mode = codec_user_config_.channel_mode;
+        break;
+      }
+      if (ch_mode & A2DP_SBC_IE_CH_MD_STEREO) {
+        result_config_cie.ch_mode = A2DP_SBC_IE_CH_MD_STEREO;
+        codec_capability_.channel_mode = codec_user_config_.channel_mode;
+        codec_config_.channel_mode = codec_user_config_.channel_mode;
+        break;
+      }
+      if (ch_mode & A2DP_SBC_IE_CH_MD_DUAL) {
+        result_config_cie.ch_mode = A2DP_SBC_IE_CH_MD_DUAL;
+        codec_capability_.channel_mode = codec_user_config_.channel_mode;
+        codec_config_.channel_mode = codec_user_config_.channel_mode;
+        break;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
+      codec_capability_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+      codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+      break;
+  }
+
+  // Select the channel mode if there is no user preference
+  do {
+    if (codec_config_.channel_mode != BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) break;
+
+    // Compute the common capability
+    if (ch_mode & A2DP_SBC_IE_CH_MD_MONO)
+      codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+    if (ch_mode & (A2DP_SBC_IE_CH_MD_JOINT | A2DP_SBC_IE_CH_MD_STEREO |
+                   A2DP_SBC_IE_CH_MD_DUAL)) {
+      codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+    }
+
+    // No user preference - use the codec audio config
+    if (select_audio_channel_mode(&codec_audio_config_, ch_mode,
+                                  &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_channel_mode(
+            a2dp_sbc_default_config.ch_mode & sink_info_cie.ch_mode,
+            &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    if (select_best_channel_mode(ch_mode, &result_config_cie, &codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match channel mode: source caps = 0x%x "
+              "sink info = 0x%x",
+              __func__, a2dp_sbc_caps.ch_mode, sink_info_cie.ch_mode);
+    goto fail;
+  }
+
+  //
+  // Select the block length
+  //
+  block_len = a2dp_sbc_caps.block_len & sink_info_cie.block_len;
+  if (block_len & A2DP_SBC_IE_BLOCKS_16) {
+    result_config_cie.block_len = A2DP_SBC_IE_BLOCKS_16;
+  } else if (block_len & A2DP_SBC_IE_BLOCKS_12) {
+    result_config_cie.block_len = A2DP_SBC_IE_BLOCKS_12;
+  } else if (block_len & A2DP_SBC_IE_BLOCKS_8) {
+    result_config_cie.block_len = A2DP_SBC_IE_BLOCKS_8;
+  } else if (block_len & A2DP_SBC_IE_BLOCKS_4) {
+    result_config_cie.block_len = A2DP_SBC_IE_BLOCKS_4;
+  } else {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match block length: source caps = 0x%x "
+              "sink info = 0x%x",
+              __func__, a2dp_sbc_caps.block_len, sink_info_cie.block_len);
+    goto fail;
+  }
+
+  //
+  // Select the number of sub-bands
+  //
+  num_subbands = a2dp_sbc_caps.num_subbands & sink_info_cie.num_subbands;
+  if (num_subbands & A2DP_SBC_IE_SUBBAND_8) {
+    result_config_cie.num_subbands = A2DP_SBC_IE_SUBBAND_8;
+  } else if (num_subbands & A2DP_SBC_IE_SUBBAND_4) {
+    result_config_cie.num_subbands = A2DP_SBC_IE_SUBBAND_4;
+  } else {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match number of sub-bands: source caps = 0x%x "
+              "sink info = 0x%x",
+              __func__, a2dp_sbc_caps.num_subbands, sink_info_cie.num_subbands);
+    goto fail;
+  }
+
+  //
+  // Select the allocation method
+  //
+  alloc_method = a2dp_sbc_caps.alloc_method & sink_info_cie.alloc_method;
+  if (alloc_method & A2DP_SBC_IE_ALLOC_MD_L) {
+    result_config_cie.alloc_method = A2DP_SBC_IE_ALLOC_MD_L;
+  } else if (alloc_method & A2DP_SBC_IE_ALLOC_MD_S) {
+    result_config_cie.alloc_method = A2DP_SBC_IE_ALLOC_MD_S;
+  } else {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match allocation method: source caps = 0x%x "
+              "sink info = 0x%x",
+              __func__, a2dp_sbc_caps.alloc_method, sink_info_cie.alloc_method);
+    goto fail;
+  }
+
+  //
+  // Select the min/max bitpool
+  //
+  result_config_cie.min_bitpool = a2dp_sbc_caps.min_bitpool;
+  if (result_config_cie.min_bitpool < sink_info_cie.min_bitpool)
+    result_config_cie.min_bitpool = sink_info_cie.min_bitpool;
+  result_config_cie.max_bitpool = a2dp_sbc_caps.max_bitpool;
+  if (result_config_cie.max_bitpool > sink_info_cie.max_bitpool)
+    result_config_cie.max_bitpool = sink_info_cie.max_bitpool;
+  if (result_config_cie.min_bitpool > result_config_cie.max_bitpool) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match min/max bitpool: "
+              "source caps min/max = 0x%x/0x%x sink info min/max = 0x%x/0x%x",
+              __func__, a2dp_sbc_caps.min_bitpool, a2dp_sbc_caps.max_bitpool,
+              sink_info_cie.min_bitpool, sink_info_cie.max_bitpool);
+    goto fail;
+  }
+
+  if (A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+                        p_result_codec_config) != A2DP_SUCCESS) {
+    goto fail;
+  }
+
+  //
+  // Copy the codec-specific fields if they are not zero
+  //
+  if (codec_user_config_.codec_specific_1 != 0)
+    codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+  if (codec_user_config_.codec_specific_2 != 0)
+    codec_config_.codec_specific_2 = codec_user_config_.codec_specific_2;
+  if (codec_user_config_.codec_specific_3 != 0)
+    codec_config_.codec_specific_3 = codec_user_config_.codec_specific_3;
+  if (codec_user_config_.codec_specific_4 != 0)
+    codec_config_.codec_specific_4 = codec_user_config_.codec_specific_4;
+
+  // Create a local copy of the peer codec capability/config, and the
+  // result codec config.
+  if (is_capability) {
+    status = A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+                               ota_codec_peer_capability_);
+  } else {
+    status = A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+                               ota_codec_peer_config_);
+  }
+  CHECK(status == A2DP_SUCCESS);
+  status = A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+                             ota_codec_config_);
+  CHECK(status == A2DP_SUCCESS);
+  return true;
+
+fail:
+  // Restore the internal state
+  codec_config_ = saved_codec_config;
+  codec_capability_ = saved_codec_capability;
+  codec_user_config_ = saved_codec_user_config;
+  codec_audio_config_ = saved_codec_audio_config;
+  memcpy(ota_codec_config_, saved_ota_codec_config, sizeof(ota_codec_config_));
+  memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability,
+         sizeof(ota_codec_peer_capability_));
+  memcpy(ota_codec_peer_config_, saved_ota_codec_peer_config,
+         sizeof(ota_codec_peer_config_));
+  return false;
+}
+
+A2dpCodecConfigSbcSink::A2dpCodecConfigSbcSink()
+    : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SINK_SBC, "SBC(Sink)") {}
+
+A2dpCodecConfigSbcSink::~A2dpCodecConfigSbcSink() {}
+
+bool A2dpCodecConfigSbcSink::init() {
+  if (!isValid()) return false;
+
+  return true;
+}
+
+bool A2dpCodecConfigSbcSink::setCodecConfig(
+    UNUSED_ATTR const uint8_t* p_peer_codec_info,
+    UNUSED_ATTR bool is_capability,
+    UNUSED_ATTR uint8_t* p_result_codec_config) {
+  // TODO: This method applies only to Source codecs
+  return false;
+}
+
+bool A2dpCodecConfigSbcSink::updateEncoderUserConfig(
+    UNUSED_ATTR const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    UNUSED_ATTR bool* p_restart_input, UNUSED_ATTR bool* p_restart_output,
+    UNUSED_ATTR bool* p_config_updated) {
+  // TODO: This method applies only to Source codecs
+  return false;
+}
diff --git a/stack/a2dp/a2dp_sbc_encoder.cc b/stack/a2dp/a2dp_sbc_encoder.cc
index e72c80b..8771f63 100644
--- a/stack/a2dp/a2dp_sbc_encoder.cc
+++ b/stack/a2dp/a2dp_sbc_encoder.cc
@@ -19,13 +19,13 @@
 
 #define LOG_TAG "a2dp_sbc_encoder"
 
+#include "a2dp_sbc_encoder.h"
+
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
 
-#include "a2dp_api.h"
 #include "a2dp_sbc.h"
-#include "a2dp_sbc_encoder.h"
 #include "a2dp_sbc_up_sample.h"
 #include "bt_common.h"
 #include "embdrv/sbc/encoder/include/sbc_encoder.h"
@@ -104,6 +104,7 @@
   uint8_t tx_sbc_frames;
   bool is_peer_edr;         /* True if the peer device supports EDR */
   bool peer_supports_3mbps; /* True if the peer device supports 3Mbps EDR */
+  uint16_t peer_mtu;        /* MTU of the A2DP peer */
   uint32_t timestamp;       /* Timestamp for the A2DP frames */
   SBC_ENC_PARAMS sbc_encoder_params;
   tA2DP_FEEDING_PARAMS feeding_params;
@@ -115,8 +116,11 @@
 
 static tA2DP_SBC_ENCODER_CB a2dp_sbc_encoder_cb;
 
-static void a2dp_sbc_encoder_update(
-    const tA2DP_ENCODER_INIT_PARAMS* p_init_params);
+static void a2dp_sbc_encoder_update(uint16_t peer_mtu,
+                                    A2dpCodecConfig* a2dp_codec_config,
+                                    bool* p_restart_input,
+                                    bool* p_restart_output,
+                                    bool* p_config_updated);
 static bool a2dp_sbc_read_feeding(void);
 static void a2dp_sbc_encode_frames(uint8_t nb_frame);
 static void a2dp_sbc_get_num_frame_iteration(uint8_t* num_of_iterations,
@@ -135,8 +139,8 @@
   // Nothing to do - the library is statically linked
 }
 
-void a2dp_sbc_encoder_init(bool is_peer_edr, bool peer_supports_3mbps,
-                           const tA2DP_ENCODER_INIT_PARAMS* p_init_params,
+void a2dp_sbc_encoder_init(const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+                           A2dpCodecConfig* a2dp_codec_config,
                            a2dp_source_read_callback_t read_callback,
                            a2dp_source_enqueue_callback_t enqueue_callback) {
   memset(&a2dp_sbc_encoder_cb, 0, sizeof(a2dp_sbc_encoder_cb));
@@ -145,34 +149,80 @@
 
   a2dp_sbc_encoder_cb.read_callback = read_callback;
   a2dp_sbc_encoder_cb.enqueue_callback = enqueue_callback;
-  a2dp_sbc_encoder_cb.is_peer_edr = is_peer_edr;
-  a2dp_sbc_encoder_cb.peer_supports_3mbps = peer_supports_3mbps;
+  a2dp_sbc_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+  a2dp_sbc_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps;
+  a2dp_sbc_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
   a2dp_sbc_encoder_cb.timestamp = 0;
 
-  a2dp_sbc_encoder_update(p_init_params);
+  // NOTE: Ignore the restart_input / restart_output flags - this initization
+  // happens when the connection is (re)started.
+  bool restart_input = false;
+  bool restart_output = false;
+  bool config_updated = false;
+  a2dp_sbc_encoder_update(a2dp_sbc_encoder_cb.peer_mtu, a2dp_codec_config,
+                          &restart_input, &restart_output, &config_updated);
+}
+
+bool A2dpCodecConfigSbc::updateEncoderUserConfig(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, bool* p_restart_input,
+    bool* p_restart_output, bool* p_config_updated) {
+  a2dp_sbc_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+  a2dp_sbc_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps;
+  a2dp_sbc_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+  a2dp_sbc_encoder_cb.timestamp = 0;
+
+  if (a2dp_sbc_encoder_cb.peer_mtu == 0) {
+    LOG_ERROR(LOG_TAG,
+              "%s: Cannot update the codec encoder for %s: "
+              "invalid peer MTU",
+              __func__, name().c_str());
+    return false;
+  }
+
+  a2dp_sbc_encoder_update(a2dp_sbc_encoder_cb.peer_mtu, this, p_restart_input,
+                          p_restart_output, p_config_updated);
+  return true;
 }
 
 // Update the A2DP SBC encoder.
-// The encoder initialization parameters are in |p_init_params|.
-static void a2dp_sbc_encoder_update(
-    const tA2DP_ENCODER_INIT_PARAMS* p_init_params) {
+// |peer_mtu| is the peer MTU.
+// |a2dp_codec_config| is the A2DP codec to use for the update.
+static void a2dp_sbc_encoder_update(uint16_t peer_mtu,
+                                    A2dpCodecConfig* a2dp_codec_config,
+                                    bool* p_restart_input,
+                                    bool* p_restart_output,
+                                    bool* p_config_updated) {
   SBC_ENC_PARAMS* p_encoder_params = &a2dp_sbc_encoder_cb.sbc_encoder_params;
-  const uint8_t* p_codec_info = p_init_params->codec_config;
+  uint8_t codec_info[AVDT_CODEC_SIZE];
   uint16_t s16SamplingFreq;
   int16_t s16BitPool = 0;
   int16_t s16BitRate;
   int16_t s16FrameLen;
   uint8_t protect = 0;
-  int min_bitpool = A2DP_GetMinBitpoolSbc(p_codec_info);
-  int max_bitpool = A2DP_GetMaxBitpoolSbc(p_codec_info);
+  int min_bitpool;
+  int max_bitpool;
+
+  *p_restart_input = false;
+  *p_restart_output = false;
+  *p_config_updated = false;
+  if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
+    LOG_ERROR(LOG_TAG,
+              "%s: Cannot update the codec encoder for %s: "
+              "invalid codec config",
+              __func__, a2dp_codec_config->name().c_str());
+    return;
+  }
+  const uint8_t* p_codec_info = codec_info;
+  min_bitpool = A2DP_GetMinBitpoolSbc(p_codec_info);
+  max_bitpool = A2DP_GetMaxBitpoolSbc(p_codec_info);
 
   // The feeding parameters
   a2dp_sbc_encoder_cb.feeding_params.sample_rate =
       A2DP_GetTrackSampleRateSbc(p_codec_info);
-  a2dp_sbc_encoder_cb.feeding_params.channel_count =
-      A2DP_GetTrackChannelCountSbc(p_codec_info);
   a2dp_sbc_encoder_cb.feeding_params.bits_per_sample =
       A2DP_GetTrackBitsPerSampleSbc(p_codec_info);
+  a2dp_sbc_encoder_cb.feeding_params.channel_count =
+      A2DP_GetTrackChannelCountSbc(p_codec_info);
 
   // The codec parameters
   p_encoder_params->s16ChannelMode = A2DP_GetChannelModeCodeSbc(p_codec_info);
@@ -204,10 +254,10 @@
   }
 
   uint16_t mtu_size = A2DP_SBC_BUFFER_SIZE - A2DP_SBC_OFFSET - sizeof(BT_HDR);
-  if (mtu_size < p_init_params->peer_mtu) {
+  if (mtu_size < peer_mtu) {
     a2dp_sbc_encoder_cb.TxAaMtuSize = mtu_size;
   } else {
-    a2dp_sbc_encoder_cb.TxAaMtuSize = p_init_params->peer_mtu;
+    a2dp_sbc_encoder_cb.TxAaMtuSize = peer_mtu;
   }
 
   if (p_encoder_params->s16SamplingFreq == SBC_sf16000)
@@ -223,8 +273,8 @@
   p_encoder_params->u16BitRate = a2dp_sbc_source_rate();
 
   LOG_DEBUG(LOG_TAG, "%s: MTU=%d, peer_mtu=%d min_bitpool=%d max_bitpool=%d",
-            __func__, a2dp_sbc_encoder_cb.TxAaMtuSize, p_init_params->peer_mtu,
-            min_bitpool, max_bitpool);
+            __func__, a2dp_sbc_encoder_cb.TxAaMtuSize, peer_mtu, min_bitpool,
+            max_bitpool);
   LOG_DEBUG(LOG_TAG,
             "%s: ChannelMode=%d, NumOfSubBands=%d, NumOfBlocks=%d, "
             "AllocationMethod=%d, BitRate=%d, SamplingFreq=%d BitPool=%d",
@@ -334,9 +384,9 @@
 
   LOG_DEBUG(
       LOG_TAG,
-      "%s: PCM feeding: sample_rate:%d channel_count:%d bits_per_sample:%d",
-      __func__, p_feeding_params->sample_rate, p_feeding_params->channel_count,
-      p_feeding_params->bits_per_sample);
+      "%s: PCM feeding: sample_rate:%d bits_per_sample:%d channel_count:%d",
+      __func__, p_feeding_params->sample_rate,
+      p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
 
   /* Save the feeding information */
   memcpy(&a2dp_sbc_encoder_cb.feeding_params, p_feeding_params,
diff --git a/stack/a2dp/a2dp_vendor.cc b/stack/a2dp/a2dp_vendor.cc
index 2e8bec9..387f445 100644
--- a/stack/a2dp/a2dp_vendor.cc
+++ b/stack/a2dp/a2dp_vendor.cc
@@ -25,43 +25,6 @@
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 
-tA2DP_CODEC_SEP_INDEX A2DP_VendorSourceCodecSepIndex(
-    const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
-
-  // Add checks based on <vendor_id, codec_id>
-
-  return A2DP_CODEC_SEP_INDEX_MAX;
-}
-
-const char* A2DP_VendorCodecSepIndexStr(tA2DP_CODEC_SEP_INDEX codec_sep_index) {
-  // Add checks based on codec_sep_index
-  switch (codec_sep_index) {
-    case A2DP_CODEC_SEP_INDEX_SOURCE_SBC:
-    case A2DP_CODEC_SEP_INDEX_SINK_SBC:
-      break;  // This is not a vendor-specific codec
-    case A2DP_CODEC_SEP_INDEX_MAX:
-      break;
-  }
-
-  return "UNKNOWN CODEC SEP INDEX";
-}
-
-bool A2DP_VendorInitCodecConfig(tA2DP_CODEC_SEP_INDEX codec_sep_index,
-                                UNUSED_ATTR tAVDT_CFG* p_cfg) {
-  // Add checks based on codec_sep_index
-  switch (codec_sep_index) {
-    case A2DP_CODEC_SEP_INDEX_SOURCE_SBC:
-    case A2DP_CODEC_SEP_INDEX_SINK_SBC:
-      break;  // This is not a vendor-specific codec
-    case A2DP_CODEC_SEP_INDEX_MAX:
-      break;
-  }
-
-  return false;
-}
-
 bool A2DP_IsVendorSourceCodecValid(UNUSED_ATTR const uint8_t* p_codec_info) {
   // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
   // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
@@ -122,17 +85,6 @@
   return false;
 }
 
-tA2DP_STATUS A2DP_VendorInitSource2SinkCodec(
-    UNUSED_ATTR const uint8_t* p_sink_caps,
-    UNUSED_ATTR uint8_t* p_result_codec_config) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_sink_caps);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_sink_caps);
-
-  // Add checks based on <vendor_id, codec_id>
-
-  return A2DP_NS_CODEC_TYPE;
-}
-
 tA2DP_STATUS A2DP_VendorBuildSrc2SinkConfig(
     UNUSED_ATTR const uint8_t* p_src_cap, UNUSED_ATTR uint8_t* p_pref_cfg) {
   // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
@@ -234,7 +186,7 @@
   return -1;
 }
 
-int A2DP_VendorGetTrackChannelCount(UNUSED_ATTR const uint8_t* p_codec_info) {
+int A2DP_VendorGetTrackBitsPerSample(UNUSED_ATTR const uint8_t* p_codec_info) {
   // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
   // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
 
@@ -243,7 +195,7 @@
   return -1;
 }
 
-int A2DP_VendorGetTrackBitsPerSample(UNUSED_ATTR const uint8_t* p_codec_info) {
+int A2DP_VendorGetTrackChannelCount(UNUSED_ATTR const uint8_t* p_codec_info) {
   // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
   // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
 
@@ -258,6 +210,7 @@
   // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
 
   // Add checks based on <vendor_id, codec_id>
+  // NOTE: Should be done only for local Sink codecs.
 
   return -1;
 }
@@ -314,3 +267,42 @@
 
   return false;
 }
+
+btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndex(
+    UNUSED_ATTR const uint8_t* p_codec_info) {
+  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Add checks based on <vendor_id, codec_id>
+
+  return BTAV_A2DP_CODEC_INDEX_MAX;
+}
+
+const char* A2DP_VendorCodecIndexStr(btav_a2dp_codec_index_t codec_index) {
+  // Add checks based on codec_index
+  switch (codec_index) {
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
+    case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
+      break;  // This is not a vendor-specific codec
+    // Add a switch statement for each vendor-specific codec
+    case BTAV_A2DP_CODEC_INDEX_MAX:
+      break;
+  }
+
+  return "UNKNOWN CODEC INDEX";
+}
+
+bool A2DP_VendorInitCodecConfig(btav_a2dp_codec_index_t codec_index,
+                                tAVDT_CFG* p_cfg) {
+  // Add checks based on codec_index
+  switch (codec_index) {
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
+    case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
+      break;  // This is not a vendor-specific codec
+    // Add a switch statement for each vendor-specific codec
+    case BTAV_A2DP_CODEC_INDEX_MAX:
+      break;
+  }
+
+  return false;
+}
diff --git a/stack/avdt/avdt_ccb.cc b/stack/avdt/avdt_ccb.cc
index 9c09ab5..c1f1469 100644
--- a/stack/avdt/avdt_ccb.cc
+++ b/stack/avdt/avdt_ccb.cc
@@ -371,8 +371,9 @@
   int i;
 
 #if (AVDT_DEBUG == TRUE)
-  AVDT_TRACE_EVENT("CCB ccb=%d event=%s state=%s", avdt_ccb_to_idx(p_ccb),
-                   avdt_ccb_evt_str[event], avdt_ccb_st_str[p_ccb->state]);
+  AVDT_TRACE_EVENT("%s: CCB ccb=%d event=%s state=%s", __func__,
+                   avdt_ccb_to_idx(p_ccb), avdt_ccb_evt_str[event],
+                   avdt_ccb_st_str[p_ccb->state]);
 #endif
 
   /* look up the state table for the current state */
diff --git a/stack/avdt/avdt_int.h b/stack/avdt/avdt_int.h
index 5a90433..8d45418 100644
--- a/stack/avdt/avdt_int.h
+++ b/stack/avdt/avdt_int.h
@@ -34,7 +34,7 @@
 #include "osi/include/fixed_queue.h"
 
 #ifndef AVDT_DEBUG
-#define AVDT_DEBUG false
+#define AVDT_DEBUG FALSE
 #endif
 
 /*****************************************************************************
diff --git a/stack/avdt/avdt_scb.cc b/stack/avdt/avdt_scb.cc
index 4cb456c..777cc73 100644
--- a/stack/avdt/avdt_scb.cc
+++ b/stack/avdt/avdt_scb.cc
@@ -772,8 +772,8 @@
   int i;
 
 #if (AVDT_DEBUG == TRUE)
-  AVDT_TRACE_EVENT("SCB hdl=%d event=%d/%s state=%s", avdt_scb_to_hdl(p_scb),
-                   event, avdt_scb_evt_str[event],
+  AVDT_TRACE_EVENT("%s: SCB hdl=%d event=%d/%s state=%s", __func__,
+                   avdt_scb_to_hdl(p_scb), event, avdt_scb_evt_str[event],
                    avdt_scb_st_str[p_scb->state]);
 #endif
   /* set current event */
diff --git a/stack/avdt/avdt_scb_act.cc b/stack/avdt/avdt_scb_act.cc
index ebe0582..8832d44 100644
--- a/stack/avdt/avdt_scb_act.cc
+++ b/stack/avdt/avdt_scb_act.cc
@@ -24,7 +24,7 @@
  ******************************************************************************/
 
 #include <string.h>
-#include "a2dp_api.h"
+#include "a2dp_codec_api.h"
 #include "avdt_api.h"
 #include "avdt_int.h"
 #include "avdtc_api.h"
diff --git a/stack/avrc/avrc_api.cc b/stack/avrc/avrc_api.cc
index bc6234d..f523479 100644
--- a/stack/avrc/avrc_api.cc
+++ b/stack/avrc/avrc_api.cc
@@ -924,7 +924,7 @@
     AVRC_build_error_packet(p_pkt);
   }
 
-  p_fcb->frag_enabled = FALSE;
+  p_fcb->frag_enabled = false;
   osi_free_and_reset((void**)&p_fcb->p_fmsg);
 
   return AVCT_MsgReq(handle, label, AVCT_RSP, p_pkt);
diff --git a/stack/avrc/avrc_bld_tg.cc b/stack/avrc/avrc_bld_tg.cc
index 731eb8d..3e716be 100644
--- a/stack/avrc/avrc_bld_tg.cc
+++ b/stack/avrc/avrc_bld_tg.cc
@@ -920,7 +920,7 @@
   uint8_t *p_item_start, *p_attr_count;
   uint16_t item_count;
   uint16_t mtu;
-  bool multi_items_add_fail = FALSE;
+  bool multi_items_add_fail = false;
   AVRC_TRACE_API("%s", __func__);
 
   /* make sure the given buffer can accomodate this response */
@@ -955,7 +955,7 @@
   /* min len required = item_type(1) + item len(2) + min item (14) = 17 */
   for (xx = 0;
        xx < p_rsp->item_count && len_left > AVRC_MIN_LEN_GET_FOLDER_ITEMS_RSP &&
-       multi_items_add_fail == FALSE;
+       multi_items_add_fail == false;
        xx++) {
     p_item_start = p_data;
     UINT8_TO_BE_STREAM(p_data, p_item_list[xx].item_type);
@@ -1058,7 +1058,7 @@
       /* fill in variable item lenth */
       UINT16_TO_BE_STREAM(p_item_len, item_len);
     } else {
-      if (multi_items_add_fail == FALSE) {
+      if (multi_items_add_fail == false) {
         /* some item is not added properly - set an error status */
         if (len_left < item_len)
           status = AVRC_STS_INTERNAL_ERR;
@@ -1066,7 +1066,7 @@
           status = AVRC_STS_BAD_PARAM;
       }
     }
-    if (multi_items_add_fail == FALSE) {
+    if (multi_items_add_fail == false) {
       len += item_len;
       len += 3; /* the item_type(1) and item_len(2) */
     }
diff --git a/stack/include/a2dp_api.h b/stack/include/a2dp_api.h
index e2a4c3f..2a27de5 100644
--- a/stack/include/a2dp_api.h
+++ b/stack/include/a2dp_api.h
@@ -16,25 +16,19 @@
  *
  ******************************************************************************/
 
-/******************************************************************************
- *
- *  Interface to A2DP Application Programming Interface
- *
- ******************************************************************************/
+//
+// A2DP API
+//
+
 #ifndef A2DP_API_H
 #define A2DP_API_H
 
-#include <stddef.h>
+#include <inttypes.h>
 
-#include "audio_a2dp_hw/audio_a2dp_hw.h"
-#include "avdt_api.h"
-#include "osi/include/time.h"
+#include "a2dp_constants.h"
+#include "a2dp_error_codes.h"
 #include "sdp_api.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /*****************************************************************************
  *  constants
  ****************************************************************************/
@@ -47,73 +41,6 @@
 #define MAX_PCM_FRAME_NUM_PER_TICK 14
 #endif
 
-/* Profile supported features */
-#define A2DP_SUPF_PLAYER 0x0001
-#define A2DP_SUPF_MIC 0x0002
-#define A2DP_SUPF_TUNER 0x0004
-#define A2DP_SUPF_MIXER 0x0008
-
-#define A2DP_SUPF_HEADPHONE 0x0001
-#define A2DP_SUPF_SPEAKER 0x0002
-#define A2DP_SUPF_RECORDER 0x0004
-#define A2DP_SUPF_AMP 0x0008
-
-/* AV Media Codec Type (Audio Codec ID) */
-#define A2DP_MEDIA_CT_SBC 0x00 /* SBC media codec type */
-#define A2DP_MEDIA_CT_NON_A2DP \
-  0xFF /* Non-A2DP media codec type (vendor-specific codec) */
-
-typedef uint8_t tA2DP_CODEC_TYPE; /* A2DP Codec type: A2DP_MEDIA_CT_* */
-
-#define A2DP_SUCCESS 0           /* Success */
-#define A2DP_FAIL 0x0A           /* Failed */
-#define A2DP_BUSY 0x0B           /* A2DP_FindService is already in progress */
-#define A2DP_INVALID_PARAMS 0x0C /* bad parameters */
-#define A2DP_WRONG_CODEC 0x0D    /* wrong codec info */
-#define A2DP_BAD_CODEC_TYPE 0xC1 /* Media Codec Type is not valid  */
-#define A2DP_NS_CODEC_TYPE 0xC2  /* Media Codec Type is not supported */
-#define A2DP_BAD_SAMP_FREQ                                             \
-  0xC3 /* Sampling Frequency is not valid or multiple values have been \
-          selected  */
-#define A2DP_NS_SAMP_FREQ 0xC4 /* Sampling Frequency is not supported  */
-#define A2DP_BAD_CH_MODE \
-  0xC5 /* Channel Mode is not valid or multiple values have been selected  */
-#define A2DP_NS_CH_MODE 0xC6 /* Channel Mode is not supported */
-#define A2DP_BAD_SUBBANDS \
-  0xC7 /* None or multiple values have been selected for Number of Subbands */
-#define A2DP_NS_SUBBANDS 0xC8 /* Number of Subbands is not supported */
-#define A2DP_BAD_ALLOC_METHOD \
-  0xC9 /* None or multiple values have been selected for Allocation Method */
-#define A2DP_NS_ALLOC_METHOD 0xCA /* Allocation Method is not supported */
-#define A2DP_BAD_MIN_BITPOOL 0xCB /* Minimum Bitpool Value is not valid */
-#define A2DP_NS_MIN_BITPOOL 0xCC  /* Minimum Bitpool Value is not supported */
-#define A2DP_BAD_MAX_BITPOOL 0xCD /* Maximum Bitpool Value is not valid */
-#define A2DP_NS_MAX_BITPOOL 0xCE  /* Maximum Bitpool Value is not supported */
-#define A2DP_BAD_LAYER \
-  0xCF /* None or multiple values have been selected for Layer */
-#define A2DP_NS_LAYER 0xD0 /* Layer is not supported */
-#define A2DP_NS_CRC 0xD1   /* CRC is not supported */
-#define A2DP_NS_MPF 0xD2   /* MPF-2 is not supported */
-#define A2DP_NS_VBR 0xD3   /* VBR is not supported */
-#define A2DP_BAD_BIT_RATE \
-  0xD4 /* None or multiple values have been selected for Bit Rate */
-#define A2DP_NS_BIT_RATE 0xD5 /* Bit Rate is not supported */
-#define A2DP_BAD_OBJ_TYPE                                                   \
-  0xD6 /* Either 1) Object type is not valid (b3-b0) or 2) None or multiple \
-          values have been selected for Object Type */
-#define A2DP_NS_OBJ_TYPE 0xD7 /* Object type is not supported */
-#define A2DP_BAD_CHANNEL \
-  0xD8 /* None or multiple values have been selected for Channels */
-#define A2DP_NS_CHANNEL 0xD9 /* Channels is not supported */
-#define A2DP_BAD_BLOCK_LEN \
-  0xDD /* None or multiple values have been selected for Block Length */
-#define A2DP_BAD_CP_TYPE 0xE0 /* The requested CP Type is not supported. */
-#define A2DP_BAD_CP_FORMAT                                            \
-  0xE1 /* The format of Content Protection Service Capability/Content \
-          Protection Scheme Dependent Data is not correct. */
-
-typedef uint8_t tA2DP_STATUS;
-
 /* the return values from A2DP_BitsSet() */
 #define A2DP_SET_ONE_BIT 1   /* one and only one bit is set */
 #define A2DP_SET_ZERO_BIT 0  /* all bits clear */
@@ -157,99 +84,6 @@
 /* This is the callback to notify the result of the SDP discovery process. */
 typedef void(tA2DP_FIND_CBACK)(bool found, tA2DP_Service* p_service);
 
-/*
- * Enum values for each supported codec per SEP.
- * There should be a separate entry for each codec that is supported
- * for encoding (SRC), and for decoding purpose (SINK).
- */
-typedef enum {
-  A2DP_CODEC_SEP_INDEX_SOURCE_MIN = 0,
-  A2DP_CODEC_SEP_INDEX_SOURCE_SBC = 0,
-  /* Add an entry for each new source codec here */
-  A2DP_CODEC_SEP_INDEX_SOURCE_MAX,
-
-  A2DP_CODEC_SEP_INDEX_SINK_MIN = A2DP_CODEC_SEP_INDEX_SOURCE_MAX,
-  A2DP_CODEC_SEP_INDEX_SINK_SBC = A2DP_CODEC_SEP_INDEX_SINK_MIN,
-  /* Add an entry for each new sink codec here */
-  A2DP_CODEC_SEP_INDEX_SINK_MAX,
-
-  A2DP_CODEC_SEP_INDEX_MIN = A2DP_CODEC_SEP_INDEX_SOURCE_MIN,
-  A2DP_CODEC_SEP_INDEX_MAX = A2DP_CODEC_SEP_INDEX_SINK_MAX
-} tA2DP_CODEC_SEP_INDEX;
-
-/**
- * Structure used to configure the A2DP feeding.
- */
-typedef struct {
-  tA2DP_SAMPLE_RATE sample_rate;          // 44100, 48000, etc
-  tA2DP_CHANNEL_COUNT channel_count;      // 1 for mono or 2 for stereo
-  tA2DP_BITS_PER_SAMPLE bits_per_sample;  // 8, 16, 24, 32
-} tA2DP_FEEDING_PARAMS;
-
-/**
- * Structure used to initialize the A2DP encoder.
- */
-typedef struct {
-  uint8_t codec_config[AVDT_CODEC_SIZE];  // Current codec config
-  uint16_t peer_mtu;                      // MTU of the A2DP peer
-} tA2DP_ENCODER_INIT_PARAMS;
-
-// Prototype for a callback to read audio data for encoding.
-// |p_buf| is the buffer to store the data. |len| is the number of octets to
-// read.
-// Returns the number of octets read.
-typedef uint32_t (*a2dp_source_read_callback_t)(uint8_t* p_buf, uint32_t len);
-
-// Prototype for a callback to enqueue A2DP Source packets for transmission.
-// |p_buf| is the buffer with the audio data to enqueue. The callback is
-// responsible for freeing |p_buf|.
-// |frames_n| is the number of audio frames in |p_buf| - it is used for
-// statistics purpose.
-// Returns true if the packet was enqueued, otherwise false.
-typedef bool (*a2dp_source_enqueue_callback_t)(BT_HDR* p_buf, size_t frames_n);
-
-//
-// A2DP encoder callbacks interface.
-//
-typedef struct {
-  // Initialize the A2DP encoder.
-  // If |is_peer_edr| is true, the A2DP peer device supports EDR.
-  // If |peer_supports_3mbps| is true, the A2DP peer device supports 3Mbps
-  // EDR.
-  // The encoder initialization parameters are in |p_init_params|.
-  // |enqueue_callback} is the callback for enqueueing the encoded audio
-  // data.
-  void (*encoder_init)(bool is_peer_edr, bool peer_supports_3mbps,
-                       const tA2DP_ENCODER_INIT_PARAMS* p_init_params,
-                       a2dp_source_read_callback_t read_callback,
-                       a2dp_source_enqueue_callback_t enqueue_callback);
-
-  // Cleanup the A2DP encoder.
-  void (*encoder_cleanup)(void);
-
-  // Initialize the feeding for the A2DP encoder.
-  // The feeding initialization parameters are in |p_feeding_params|.
-  void (*feeding_init)(const tA2DP_FEEDING_PARAMS* p_feeding_params);
-
-  // Reset the feeding for the A2DP encoder.
-  void (*feeding_reset)(void);
-
-  // Flush the feeding for the A2DP encoder.
-  void (*feeding_flush)(void);
-
-  // Get the A2DP encoder interval (in milliseconds).
-  period_ms_t (*get_encoder_interval_ms)(void);
-
-  // Prepare and send A2DP encoded frames.
-  // |timestamp_us| is the current timestamp (in microseconds).
-  void (*send_frames)(uint64_t timestamp_us);
-
-  // Dump codec-related statistics.
-  // |fd| is the file descriptor to use to dump the statistics information
-  // in user-friendly test format.
-  void (*debug_codec_dump)(int fd);
-} tA2DP_ENCODER_INTERFACE;
-
 /*****************************************************************************
  *  external function declarations
  ****************************************************************************/
@@ -362,176 +196,4 @@
 // Initializes the A2DP control block.
 void A2DP_Init(void);
 
-// Gets the A2DP codec type.
-// |p_codec_info| contains information about the codec capabilities.
-tA2DP_CODEC_TYPE A2DP_GetCodecType(const uint8_t* p_codec_info);
-
-// Checks whether the codec capabilities contain a valid A2DP Source codec.
-// NOTE: only codecs that are implemented are considered valid.
-// Returns true if |p_codec_info| contains information about a valid codec,
-// otherwise false.
-bool A2DP_IsSourceCodecValid(const uint8_t* p_codec_info);
-
-// Checks whether the codec capabilities contain a valid A2DP Sink codec.
-// NOTE: only codecs that are implemented are considered valid.
-// Returns true if |p_codec_info| contains information about a valid codec,
-// otherwise false.
-bool A2DP_IsSinkCodecValid(const uint8_t* p_codec_info);
-
-// Checks whether the codec capabilities contain a valid peer A2DP Source
-// codec.
-// NOTE: only codecs that are implemented are considered valid.
-// Returns true if |p_codec_info| contains information about a valid codec,
-// otherwise false.
-bool A2DP_IsPeerSourceCodecValid(const uint8_t* p_codec_info);
-
-// Checks whether the codec capabilities contain a valid peer A2DP Sink codec.
-// NOTE: only codecs that are implemented are considered valid.
-// Returns true if |p_codec_info| contains information about a valid codec,
-// otherwise false.
-bool A2DP_IsPeerSinkCodecValid(const uint8_t* p_codec_info);
-
-// Checks whether an A2DP Sink codec is supported.
-// |p_codec_info| contains information about the codec capabilities.
-// Returns true if the A2DP Sink codec is supported, otherwise false.
-bool A2DP_IsSinkCodecSupported(const uint8_t* p_codec_info);
-
-// Checks whether an A2DP Source codec for a peer Source device is supported.
-// |p_codec_info| contains information about the codec capabilities of the
-// peer device.
-// Returns true if the A2DP Source codec for a peer Source device is supported,
-// otherwise false.
-bool A2DP_IsPeerSourceCodecSupported(const uint8_t* p_codec_info);
-
-// Initialize state with the default A2DP codec.
-// The initialized state with the codec capabilities is stored in
-// |p_codec_info|.
-void A2DP_InitDefaultCodec(uint8_t* p_codec_info);
-
-// Initializes A2DP Source-to-Sink codec config from Sink codec capability.
-// |p_sink_caps| is the A2DP Sink codec capabilities to use.
-// The selected codec configuration is stored in |p_result_codec_config|.
-// Returns |A2DP_SUCCESS| on success, otherwise the corresponding A2DP error
-// status code.
-tA2DP_STATUS A2DP_InitSource2SinkCodec(const uint8_t* p_sink_caps,
-                                       uint8_t* p_result_codec_config);
-
-// Builds A2DP preferred Sink capability from Source capability.
-// |p_src_cap| is the Source capability to use.
-// |p_pref_cfg| is the result Sink capability to store.
-// Returns |A2DP_SUCCESS| on success, otherwise the corresponding A2DP error
-// status code.
-tA2DP_STATUS A2DP_BuildSrc2SinkConfig(const uint8_t* p_src_cap,
-                                      uint8_t* p_pref_cfg);
-
-// Checks whether the A2DP data packets should contain RTP header.
-// |content_protection_enabled| is true if Content Protection is
-// enabled. |p_codec_info| contains information about the codec capabilities.
-// Returns true if the A2DP data packets should contain RTP header, otherwise
-// false.
-bool A2DP_UsesRtpHeader(bool content_protection_enabled,
-                        const uint8_t* p_codec_info);
-
-// Gets the A2DP Source codec SEP index for a given |p_codec_info|.
-// Returns the corresponding |tA2DP_CODEC_SEP_INDEX| on success,
-// otherwise |A2DP_CODEC_SEP_INDEX_MAX|.
-tA2DP_CODEC_SEP_INDEX A2DP_SourceCodecSepIndex(const uint8_t* p_codec_info);
-
-// Gets the A2DP codec name for a given |codec_sep_index|.
-const char* A2DP_CodecSepIndexStr(tA2DP_CODEC_SEP_INDEX codec_sep_index);
-
-// Initializes A2DP codec-specific information into |tAVDT_CFG| configuration
-// entry pointed by |p_cfg|. The selected codec is defined by
-// |codec_sep_index|.
-// Returns true on success, otherwise false.
-bool A2DP_InitCodecConfig(tA2DP_CODEC_SEP_INDEX codec_sep_index,
-                          tAVDT_CFG* p_cfg);
-
-// Gets the |AVDT_MEDIA_TYPE_*| media type from the codec capability
-// in |p_codec_info|.
-uint8_t A2DP_GetMediaType(const uint8_t* p_codec_info);
-
-// Gets the A2DP codec name for a given |p_codec_info|.
-const char* A2DP_CodecName(const uint8_t* p_codec_info);
-
-// Checks whether two A2DP codecs |p_codec_info_a| and |p_codec_info_b| have
-// the same type.
-// Returns true if the two codecs have the same type, otherwise false.
-// If the codec type is not recognized, the return value is false.
-bool A2DP_CodecTypeEquals(const uint8_t* p_codec_info_a,
-                          const uint8_t* p_codec_info_b);
-
-// Checks whether two A2DP codecs p_codec_info_a| and |p_codec_info_b| are
-// exactly the same.
-// Returns true if the two codecs are exactly the same, otherwise false.
-// If the codec type is not recognized, the return value is false.
-bool A2DP_CodecEquals(const uint8_t* p_codec_info_a,
-                      const uint8_t* p_codec_info_b);
-
-// Gets the track sample rate value for the A2DP codec.
-// |p_codec_info| is a pointer to the codec_info to decode.
-// Returns the track sample rate on success, or -1 if |p_codec_info|
-// contains invalid codec information.
-int A2DP_GetTrackSampleRate(const uint8_t* p_codec_info);
-
-// Gets the channel count for the A2DP codec.
-// |p_codec_info| is a pointer to the codec_info to decode.
-// Returns the channel count on success, or -1 if |p_codec_info|
-// contains invalid codec information.
-int A2DP_GetTrackChannelCount(const uint8_t* p_codec_info);
-
-// Gets the bits per audio sample for the A2DP codec.
-// |p_codec_info| is a pointer to the codec_info to decode.
-// Returns the bits per audio sample on success, or -1 if |p_codec_info|
-// contains invalid codec information.
-int A2DP_GetTrackBitsPerSample(const uint8_t* p_codec_info);
-
-// Gets the channel type for the A2DP Sink codec:
-// 1 for mono, or 3 for dual/stereo/joint.
-// |p_codec_info| is a pointer to the codec_info to decode.
-// Returns the channel type on success, or -1 if |p_codec_info|
-// contains invalid codec information.
-int A2DP_GetSinkTrackChannelType(const uint8_t* p_codec_info);
-
-// Computes the number of frames to process in a time window for the A2DP
-// Sink codec. |time_interval_ms| is the time interval (in milliseconds).
-// |p_codec_info| is a pointer to the codec_info to decode.
-// Returns the number of frames to process on success, or -1 if |p_codec_info|
-// contains invalid codec information.
-int A2DP_GetSinkFramesCountToProcess(uint64_t time_interval_ms,
-                                     const uint8_t* p_codec_info);
-
-// Gets the A2DP audio data timestamp from an audio packet.
-// |p_codec_info| contains the codec information.
-// |p_data| contains the audio data.
-// The timestamp is stored in |p_timestamp|.
-// Returns true on success, otherwise false.
-bool A2DP_GetPacketTimestamp(const uint8_t* p_codec_info, const uint8_t* p_data,
-                             uint32_t* p_timestamp);
-
-// Builds A2DP codec header for audio data.
-// |p_codec_info| contains the codec information.
-// |p_buf| contains the audio data.
-// |frames_per_packet| is the number of frames in this packet.
-// Returns true on success, otherwise false.
-bool A2DP_BuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf,
-                           uint16_t frames_per_packet);
-
-// Gets the A2DP encoder interface that can be used to encode and prepare
-// A2DP packets for transmission - see |tA2DP_ENCODER_INTERFACE|.
-// |p_codec_info| contains the codec information.
-// Returns the A2DP encoder interface if the |p_codec_info| is valid and
-// supported, otherwise NULL.
-const tA2DP_ENCODER_INTERFACE* A2DP_GetEncoderInterface(
-    const uint8_t* p_codec_info);
-
-// Adjusts the A2DP codec, based on local support and Bluetooth specification.
-// |p_codec_info| contains the codec information to adjust.
-// Returns true if |p_codec_info| is valid and supported, otherwise false.
-bool A2DP_AdjustCodec(uint8_t* p_codec_info);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* A2DP_API_H */
+#endif  // A2DP_API_H
diff --git a/stack/include/a2dp_codec_api.h b/stack/include/a2dp_codec_api.h
new file mode 100644
index 0000000..3cce872
--- /dev/null
+++ b/stack/include/a2dp_codec_api.h
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+//
+// A2DP Codecs API
+//
+
+#ifndef A2DP_CODEC_API_H
+#define A2DP_CODEC_API_H
+
+#include <stddef.h>
+#include <string.h>
+#include <functional>
+#include <list>
+#include <map>
+#include <mutex>
+#include <string>
+
+#include <hardware/bt_av.h>
+
+#include "a2dp_api.h"
+#include "audio_a2dp_hw/audio_a2dp_hw.h"
+#include "avdt_api.h"
+#include "osi/include/time.h"
+
+/**
+ * Structure used to initialize the A2DP encoder with A2DP peer information
+ */
+typedef struct {
+  bool is_peer_edr;          // True if the A2DP peer supports EDR
+  bool peer_supports_3mbps;  // True if the A2DP peer supports 3 Mbps EDR
+  uint16_t peer_mtu;         // MTU of the A2DP peer
+} tA2DP_ENCODER_INIT_PEER_PARAMS;
+
+class A2dpCodecConfig {
+  friend class A2dpCodecs;
+
+ public:
+  // Creates a codec entry. The selected codec is defined by |codec_index|,
+  // Returns the codec entry on success, otherwise nullptr.
+  static A2dpCodecConfig* createCodec(btav_a2dp_codec_index_t codec_index);
+
+  virtual ~A2dpCodecConfig() = 0;
+
+  // Gets the pre-defined codec index.
+  btav_a2dp_codec_index_t codecIndex() const { return codec_index_; }
+
+  // Gets the codec name.
+  const std::string& name() const { return name_; }
+
+  // Gets the current priority of the codec.
+  btav_a2dp_codec_priority_t codecPriority() const { return codec_priority_; }
+
+  // Copies out the current OTA codec config to |p_codec_info|.
+  // Returns true if the current codec config is valied and copied,
+  // otherwise false.
+  bool copyOutOtaCodecConfig(uint8_t* p_codec_info);
+
+  // Gets the current codec configuration.
+  // Returns a copy of the current codec configuration.
+  btav_a2dp_codec_config_t getCodecConfig();
+
+  // Gets the current codec capability.
+  // The capability is computed by intersecting the local codec's capability
+  // and the peer's codec capability. However, if there is an explicit user
+  // configuration for some of the parameters, the result codec configuration
+  // and capability is restricted to the user's configuration choice.
+  // Returns a copy of the current codec capability.
+  btav_a2dp_codec_config_t getCodecCapability();
+
+  // Gets the current codec user configuration.
+  // Returns a copy of the current codec user configuration.
+  btav_a2dp_codec_config_t getCodecUserConfig();
+
+  // Gets the current codec audio configuration.
+  // Returns a copy of the current codec audio configuration.
+  btav_a2dp_codec_config_t getCodecAudioConfig();
+
+  // Gets the number of bits per sample of the current codec configuration,
+  // or 0 if not configured.
+  uint8_t getAudioBitsPerSample();
+
+  // Checks whether |codec_config| is empty and contains no configuration.
+  // Returns true if |codec_config| is empty, otherwise false.
+  static bool isCodecConfigEmpty(const btav_a2dp_codec_config_t& codec_config);
+
+ protected:
+  // Sets the current priority of the codec to |codec_priority|.
+  // If |codec_priority| is 0, the priority is reset to its default value.
+  void setCodecPriority(btav_a2dp_codec_priority_t codec_priority);
+
+  // Sets the current priority of the codec to its default value.
+  void setDefaultCodecPriority();
+
+  // Sets the A2DP Source-to-Sink codec configuration to be used
+  // with a peer Sink device.
+  // |p_peer_codec_info| is the peer's A2DP Sink codec information
+  // to use. If |is_capability| is true, then |p_peer_codec_info| contains the
+  // peer's A2DP Sink codec capability, otherwise it contains the peer's
+  // preferred A2DP codec configuration to use.
+  // The result codec configuration is stored in |p_result_codec_config|.
+  // See |A2dpCodecs.setCodecConfig| for detailed description of
+  // the actual mechanism used to compute the configuration.
+  // Returns true on success, othewise false.
+  virtual bool setCodecConfig(const uint8_t* p_peer_codec_info,
+                              bool is_capability,
+                              uint8_t* p_result_codec_config) = 0;
+
+  // Sets the user prefered codec configuration.
+  // |codec_user_config| contains the preferred codec user configuration.
+  // |codec_audio_config| contains the selected audio feeding configuration.
+  // |p_peer_params| contains the A2DP peer information.
+  // |p_peer_codec_info| is the peer's A2DP Sink codec information
+  // to use. If |is_capability| is true, then |p_peer_codec_info| contains the
+  // peer's A2DP Sink codec capability, otherwise it contains the peer's
+  // preferred A2DP codec configuration to use.
+  // If there is a change in the codec configuration that requires restarting
+  // if the audio input stream, flag |p_restart_input| is set to true.
+  // If there is a change in the encoder configuration that requires restarting
+  // of the A2DP connection, the new codec configuration is stored in
+  // |p_result_codec_config|, and flag |p_restart_output| is set to true.
+  // If there is any change in the codec configuration, flag |p_config_updated|
+  // is set to true.
+  // Returns true on success, otherwise false.
+  virtual bool setCodecUserConfig(
+      const btav_a2dp_codec_config_t& codec_user_config,
+      const btav_a2dp_codec_config_t& codec_audio_config,
+      const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+      const uint8_t* p_peer_codec_info, bool is_capability,
+      uint8_t* p_result_codec_config, bool* p_restart_input,
+      bool* p_restart_output, bool* p_config_updated);
+
+  // Updates the encoder with the user prefered codec configuration.
+  // |p_peer_params| contains the A2DP peer information.
+  // If there is a change in the encoder configuration that requires restarting
+  // the audio input stream, flag |p_restart_input| is set to true.
+  // If there is a change in the encoder configuration that requires restarting
+  // of the A2DP connection, flag |p_restart_output| is set to true.
+  // If there is any change in the codec configuration, flag |p_config_updated|
+  // is set to true.
+  // Returns true on success, otherwise false.
+  virtual bool updateEncoderUserConfig(
+      const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+      bool* p_restart_input, bool* p_restart_output,
+      bool* p_config_updated) = 0;
+
+  // Constructor where |codec_index| is the unique index that identifies the
+  // codec. The user-friendly name is |name|.
+  A2dpCodecConfig(btav_a2dp_codec_index_t codec_index, const std::string& name);
+
+  // Initializes the codec entry.
+  // Returns true on success, otherwise false.
+  virtual bool init() = 0;
+
+  // Checks whether the internal state is valid
+  virtual bool isValid() const;
+
+  std::recursive_mutex codec_mutex_;
+  const btav_a2dp_codec_index_t codec_index_;  // The unique codec index
+  const std::string name_;                     // The codec name
+  btav_a2dp_codec_priority_t codec_priority_;  // Codec priority: must be unique
+
+  btav_a2dp_codec_config_t codec_config_;
+  btav_a2dp_codec_config_t codec_capability_;
+
+  // The optional user configuration. The values (if set) are used
+  // as a preference when there is a choice. If a particular value
+  // is not supported by the local or remote device, it is ignored.
+  btav_a2dp_codec_config_t codec_user_config_;
+
+  // The selected audio feeding configuration.
+  btav_a2dp_codec_config_t codec_audio_config_;
+
+  uint8_t ota_codec_config_[AVDT_CODEC_SIZE];
+  uint8_t ota_codec_peer_capability_[AVDT_CODEC_SIZE];
+  uint8_t ota_codec_peer_config_[AVDT_CODEC_SIZE];
+};
+
+class A2dpCodecs {
+ public:
+  A2dpCodecs();
+  ~A2dpCodecs();
+
+  // Initializes all supported codecs.
+  // Returns true if at least one Source codec and one Sink codec were
+  // initialized, otherwise false.
+  bool init();
+
+  // Finds the Source codec that corresponds to the A2DP over-the-air
+  // |p_codec_info| information.
+  // Returns the Source codec if found, otherwise nullptr.
+  A2dpCodecConfig* findSourceCodecConfig(const uint8_t* p_codec_info);
+
+  // Gets the codec config that is currently selected.
+  // Returns the codec config that is currently selected, or nullptr if
+  // no codec is selected.
+  A2dpCodecConfig* getCurrentCodecConfig() const {
+    return current_codec_config_;
+  }
+
+  // Gets the list of Source codecs ordered by priority: higher priority first.
+  const std::list<A2dpCodecConfig*> orderedSourceCodecs() const {
+    return ordered_source_codecs_;
+  }
+
+  // Gets the list of Sink codecs ordered by priority: higher priority first.
+  const std::list<A2dpCodecConfig*> orderedSinkCodecs() const {
+    return ordered_sink_codecs_;
+  }
+
+  // Sets the A2DP Source-to-Sink codec configuration to be used
+  // with a peer Sink device.
+  // |p_peer_codec_info| is the peer's A2DP Sink codec information
+  // to use. If |is_capability| is true, then |p_peer_codec_info| contains the
+  // peer's A2DP Sink codec capability, otherwise it contains the peer's
+  // preferred A2DP codec configuration to use.
+  //
+  // The codec configuration is built by considering the optional user
+  // configuration, the local codec capabilities, the peer's codec
+  // capabilities, and the codec's locally-defined default values.
+  // For each codec parameter:
+  //
+  // 1. If it is user-configurable parameter (sample rate, bits per sample,
+  //    channel mode, and some codec-specific parameters),
+  //    if the user has an explicit preference, and that preference
+  //    is supported by both the local and remote device, this is the
+  //    parameter value that is used.
+  // 2. Otherwise, if the explicit internal default value is supported
+  //    by both the local and remote device, this is the parameter value
+  //    that is used.
+  // 3. Otherwise, the best match is chosen among all values supported by
+  //    the local and remote device.
+  //
+  // In addition, the codec's internal state is updated to reflect
+  // the capabilities that are advertised to the upstream audio source
+  // (Media Framework) to make run-time audio parameter choices:
+  // 4. If the user-configurable parameter was selected, this is the
+  //    only parameter value that is advertised to the Media Framework.
+  // 5. Otherwise, all values supported by both the local and remote
+  //    devices are advertised to the Media Framework.
+  //
+  // The result codec configuration is stored in |p_result_codec_config|.
+  // Returns true on success, othewise false.
+  bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability,
+                      uint8_t* p_result_codec_config);
+
+  // Sets the user prefered codec configuration.
+  // |codec_user_config| contains the preferred codec configuration.
+  // |p_peer_params| contains the A2DP peer information.
+  // |p_peer_sink_capabilities| is the peer's A2DP Sink codec capabilities
+  // to use.
+  // If there is a change in the encoder configuration that requires restarting
+  // the audio input stream, flag |p_restart_input| is set to true.
+  // If there is a change in the encoder configuration that requires restarting
+  // of the A2DP connection, flag |p_restart_output| is set to true, and the
+  // new codec is stored in |p_result_codec_config|.
+  // If there is any change in the codec configuration, flag |p_config_updated|
+  // is set to true.
+  // Returns true on success, otherwise false.
+  bool setCodecUserConfig(const btav_a2dp_codec_config_t& codec_user_config,
+                          const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+                          const uint8_t* p_peer_sink_capabilities,
+                          uint8_t* p_result_codec_config, bool* p_restart_input,
+                          bool* p_restart_output, bool* p_config_updated);
+
+  // Sets the Audio HAL selected audio feeding parameters.
+  // Those parameters are applied only to the currently selected codec.
+  // |codec_audio_config| contains the selected audio feeding configuration.
+  // |p_peer_params| contains the A2DP peer information.
+  // |p_peer_sink_capabilities| is the peer's A2DP Sink codec capabilities
+  // to use.
+  // If there is a change in the encoder configuration that requires restarting
+  // of the A2DP connection, flag |p_restart_output| is set to true, and the
+  // new codec is stored in |p_result_codec_config|.
+  // If there is any change in the codec configuration, flag |p_config_updated|
+  // is set to true.
+  // Returns true on success, otherwise false.
+  bool setCodecAudioConfig(const btav_a2dp_codec_config_t& codec_audio_config,
+                           const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+                           const uint8_t* p_peer_sink_capabilities,
+                           uint8_t* p_result_codec_config,
+                           bool* p_restart_output, bool* p_config_updated);
+
+  // Sets the Over-The-Air preferred codec configuration.
+  // The OTA prefered codec configuration is ignored if the current
+  // codec configuration contains explicit user configuration, or if the
+  // codec configuration for the same codec contains explicit user
+  // configuration.
+  // |p_ota_codec_config| contains the received OTA A2DP codec configuration
+  // from the remote peer. Note: this is not the peer codec capability,
+  // but the codec configuration that the peer would like to use.
+  // |p_peer_params| contains the A2DP peer information.
+  // If there is a change in the encoder configuration that requires restarting
+  // the audio input stream, flag |p_restart_input| is set to true.
+  // If there is a change in the encoder configuration that requires restarting
+  // of the A2DP connection, flag |p_restart_output| is set to true, and the
+  // new codec is stored in |p_result_codec_config|.
+  // If there is any change in the codec configuration, flag |p_config_updated|
+  // is set to true.
+  // Returns true on success, otherwise false.
+  bool setCodecOtaConfig(const uint8_t* p_ota_codec_config,
+                         const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+                         uint8_t* p_result_codec_config, bool* p_restart_input,
+                         bool* p_restart_output, bool* p_config_updated);
+
+  // Gets the current codec configuration and the capabilities of
+  // all configured codecs.
+  // The codec configuration is stored in |p_codec_config|.
+  // The codecs capabilities are stored in |p_codec_capabilities|.
+  // Returns true on success, otherwise false.
+  bool getCodecConfigAndCapabilities(
+      btav_a2dp_codec_config_t* p_codec_config,
+      std::vector<btav_a2dp_codec_config_t>* p_codec_capabilities);
+
+ private:
+  struct CompareBtBdaddr
+      : public std::binary_function<bt_bdaddr_t, bt_bdaddr_t, bool> {
+    bool operator()(const bt_bdaddr_t& lhs, const bt_bdaddr_t& rhs) const {
+      return (memcmp(&lhs, &rhs, sizeof(lhs)) < 0);
+    }
+  };
+  typedef std::map<btav_a2dp_codec_index_t, A2dpCodecConfig*> IndexedCodecs;
+
+  std::recursive_mutex codec_mutex_;
+  A2dpCodecConfig* current_codec_config_;  // Currently selected codec
+  IndexedCodecs indexed_codecs_;           // The codecs indexed by codec index
+
+  // A2DP Source codecs ordered by priority
+  std::list<A2dpCodecConfig*> ordered_source_codecs_;
+
+  // A2DP Sink codecs ordered by priority
+  std::list<A2dpCodecConfig*> ordered_sink_codecs_;
+
+  std::map<bt_bdaddr_t, IndexedCodecs*, CompareBtBdaddr> peer_codecs_;
+};
+
+/**
+ * Structure used to configure the A2DP feeding.
+ */
+typedef struct {
+  tA2DP_SAMPLE_RATE sample_rate;          // 44100, 48000, etc
+  tA2DP_BITS_PER_SAMPLE bits_per_sample;  // 8, 16, 24, 32
+  tA2DP_CHANNEL_COUNT channel_count;      // 1 for mono or 2 for stereo
+} tA2DP_FEEDING_PARAMS;
+
+// Prototype for a callback to read audio data for encoding.
+// |p_buf| is the buffer to store the data. |len| is the number of octets to
+// read.
+// Returns the number of octets read.
+typedef uint32_t (*a2dp_source_read_callback_t)(uint8_t* p_buf, uint32_t len);
+
+// Prototype for a callback to enqueue A2DP Source packets for transmission.
+// |p_buf| is the buffer with the audio data to enqueue. The callback is
+// responsible for freeing |p_buf|.
+// |frames_n| is the number of audio frames in |p_buf| - it is used for
+// statistics purpose.
+// Returns true if the packet was enqueued, otherwise false.
+typedef bool (*a2dp_source_enqueue_callback_t)(BT_HDR* p_buf, size_t frames_n);
+
+//
+// A2DP encoder callbacks interface.
+//
+typedef struct {
+  // Initialize the A2DP encoder.
+  // |p_peer_params| contains the A2DP peer information
+  // The current A2DP codec config is in |a2dp_codec_config|.
+  // |read_callback| is the callback for reading the input audio data.
+  // |enqueue_callback| is the callback for enqueueing the encoded audio data.
+  void (*encoder_init)(const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+                       A2dpCodecConfig* a2dp_codec_config,
+                       a2dp_source_read_callback_t read_callback,
+                       a2dp_source_enqueue_callback_t enqueue_callback);
+
+  // Cleanup the A2DP encoder.
+  void (*encoder_cleanup)(void);
+
+  // Initialize the feeding for the A2DP encoder.
+  // The feeding initialization parameters are in |p_feeding_params|.
+  void (*feeding_init)(const tA2DP_FEEDING_PARAMS* p_feeding_params);
+
+  // Reset the feeding for the A2DP encoder.
+  void (*feeding_reset)(void);
+
+  // Flush the feeding for the A2DP encoder.
+  void (*feeding_flush)(void);
+
+  // Get the A2DP encoder interval (in milliseconds).
+  period_ms_t (*get_encoder_interval_ms)(void);
+
+  // Prepare and send A2DP encoded frames.
+  // |timestamp_us| is the current timestamp (in microseconds).
+  void (*send_frames)(uint64_t timestamp_us);
+
+  // Dump codec-related statistics.
+  // |fd| is the file descriptor to use to dump the statistics information
+  // in user-friendly test format.
+  void (*debug_codec_dump)(int fd);
+} tA2DP_ENCODER_INTERFACE;
+
+// Gets the A2DP codec type.
+// |p_codec_info| contains information about the codec capabilities.
+tA2DP_CODEC_TYPE A2DP_GetCodecType(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid A2DP Source codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid codec,
+// otherwise false.
+bool A2DP_IsSourceCodecValid(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid A2DP Sink codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid codec,
+// otherwise false.
+bool A2DP_IsSinkCodecValid(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP Source
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid codec,
+// otherwise false.
+bool A2DP_IsPeerSourceCodecValid(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP Sink codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid codec,
+// otherwise false.
+bool A2DP_IsPeerSinkCodecValid(const uint8_t* p_codec_info);
+
+// Checks whether an A2DP Sink codec is supported.
+// |p_codec_info| contains information about the codec capabilities.
+// Returns true if the A2DP Sink codec is supported, otherwise false.
+bool A2DP_IsSinkCodecSupported(const uint8_t* p_codec_info);
+
+// Checks whether an A2DP Source codec for a peer Source device is supported.
+// |p_codec_info| contains information about the codec capabilities of the
+// peer device.
+// Returns true if the A2DP Source codec for a peer Source device is supported,
+// otherwise false.
+bool A2DP_IsPeerSourceCodecSupported(const uint8_t* p_codec_info);
+
+// Initialize state with the default A2DP codec.
+// The initialized state with the codec capabilities is stored in
+// |p_codec_info|.
+void A2DP_InitDefaultCodec(uint8_t* p_codec_info);
+
+// Builds A2DP preferred Sink capability from Source capability.
+// |p_src_cap| is the Source capability to use.
+// |p_pref_cfg| is the result Sink capability to store.
+// Returns |A2DP_SUCCESS| on success, otherwise the corresponding A2DP error
+// status code.
+tA2DP_STATUS A2DP_BuildSrc2SinkConfig(const uint8_t* p_src_cap,
+                                      uint8_t* p_pref_cfg);
+
+// Checks whether the A2DP data packets should contain RTP header.
+// |content_protection_enabled| is true if Content Protection is
+// enabled. |p_codec_info| contains information about the codec capabilities.
+// Returns true if the A2DP data packets should contain RTP header, otherwise
+// false.
+bool A2DP_UsesRtpHeader(bool content_protection_enabled,
+                        const uint8_t* p_codec_info);
+
+// Gets the |AVDT_MEDIA_TYPE_*| media type from the codec capability
+// in |p_codec_info|.
+uint8_t A2DP_GetMediaType(const uint8_t* p_codec_info);
+
+// Gets the A2DP codec name for a given |p_codec_info|.
+const char* A2DP_CodecName(const uint8_t* p_codec_info);
+
+// Checks whether two A2DP codecs |p_codec_info_a| and |p_codec_info_b| have
+// the same type.
+// Returns true if the two codecs have the same type, otherwise false.
+// If the codec type is not recognized, the return value is false.
+bool A2DP_CodecTypeEquals(const uint8_t* p_codec_info_a,
+                          const uint8_t* p_codec_info_b);
+
+// Checks whether two A2DP codecs p_codec_info_a| and |p_codec_info_b| are
+// exactly the same.
+// Returns true if the two codecs are exactly the same, otherwise false.
+// If the codec type is not recognized, the return value is false.
+bool A2DP_CodecEquals(const uint8_t* p_codec_info_a,
+                      const uint8_t* p_codec_info_b);
+
+// Gets the track sample rate value for the A2DP codec.
+// |p_codec_info| is a pointer to the codec_info to decode.
+// Returns the track sample rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackSampleRate(const uint8_t* p_codec_info);
+
+// Gets the bits per audio sample for the A2DP codec.
+// |p_codec_info| is a pointer to the codec_info to decode.
+// Returns the bits per audio sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackBitsPerSample(const uint8_t* p_codec_info);
+
+// Gets the channel count for the A2DP codec.
+// |p_codec_info| is a pointer to the codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackChannelCount(const uint8_t* p_codec_info);
+
+// Gets the channel type for the A2DP Sink codec:
+// 1 for mono, or 3 for dual/stereo/joint.
+// |p_codec_info| is a pointer to the codec_info to decode.
+// Returns the channel type on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetSinkTrackChannelType(const uint8_t* p_codec_info);
+
+// Computes the number of frames to process in a time window for the A2DP
+// Sink codec. |time_interval_ms| is the time interval (in milliseconds).
+// |p_codec_info| is a pointer to the codec_info to decode.
+// Returns the number of frames to process on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetSinkFramesCountToProcess(uint64_t time_interval_ms,
+                                     const uint8_t* p_codec_info);
+
+// Gets the A2DP audio data timestamp from an audio packet.
+// |p_codec_info| contains the codec information.
+// |p_data| contains the audio data.
+// The timestamp is stored in |p_timestamp|.
+// Returns true on success, otherwise false.
+bool A2DP_GetPacketTimestamp(const uint8_t* p_codec_info, const uint8_t* p_data,
+                             uint32_t* p_timestamp);
+
+// Builds A2DP codec header for audio data.
+// |p_codec_info| contains the codec information.
+// |p_buf| contains the audio data.
+// |frames_per_packet| is the number of frames in this packet.
+// Returns true on success, otherwise false.
+bool A2DP_BuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf,
+                           uint16_t frames_per_packet);
+
+// Gets the A2DP encoder interface that can be used to encode and prepare
+// A2DP packets for transmission - see |tA2DP_ENCODER_INTERFACE|.
+// |p_codec_info| contains the codec information.
+// Returns the A2DP encoder interface if the |p_codec_info| is valid and
+// supported, otherwise NULL.
+const tA2DP_ENCODER_INTERFACE* A2DP_GetEncoderInterface(
+    const uint8_t* p_codec_info);
+
+// Adjusts the A2DP codec, based on local support and Bluetooth specification.
+// |p_codec_info| contains the codec information to adjust.
+// Returns true if |p_codec_info| is valid and supported, otherwise false.
+bool A2DP_AdjustCodec(uint8_t* p_codec_info);
+
+// Gets the A2DP Source codec index for a given |p_codec_info|.
+// Returns the corresponding |btav_a2dp_codec_index_t| on success,
+// otherwise |BTAV_A2DP_CODEC_INDEX_MAX|.
+btav_a2dp_codec_index_t A2DP_SourceCodecIndex(const uint8_t* p_codec_info);
+
+// Gets the A2DP codec name for a given |codec_index|.
+const char* A2DP_CodecIndexStr(btav_a2dp_codec_index_t codec_index);
+
+// Initializes A2DP codec-specific information into |tAVDT_CFG| configuration
+// entry pointed by |p_cfg|. The selected codec is defined by |codec_index|.
+// Returns true on success, otherwise false.
+bool A2DP_InitCodecConfig(btav_a2dp_codec_index_t codec_index,
+                          tAVDT_CFG* p_cfg);
+
+// Add enum-based flag operators to the btav_a2dp_codec_config_t fields
+#ifndef DEFINE_ENUM_FLAG_OPERATORS
+#define DEFINE_ENUM_FLAG_OPERATORS(bitmask)                                 \
+  extern "C++" {                                                            \
+  inline constexpr bitmask operator&(bitmask X, bitmask Y) {                \
+    return static_cast<bitmask>(static_cast<int>(X) & static_cast<int>(Y)); \
+  }                                                                         \
+  inline constexpr bitmask operator|(bitmask X, bitmask Y) {                \
+    return static_cast<bitmask>(static_cast<int>(X) | static_cast<int>(Y)); \
+  }                                                                         \
+  inline constexpr bitmask operator^(bitmask X, bitmask Y) {                \
+    return static_cast<bitmask>(static_cast<int>(X) ^ static_cast<int>(Y)); \
+  }                                                                         \
+  inline constexpr bitmask operator~(bitmask X) {                           \
+    return static_cast<bitmask>(~static_cast<int>(X));                      \
+  }                                                                         \
+  inline bitmask& operator&=(bitmask& X, bitmask Y) {                       \
+    X = X & Y;                                                              \
+    return X;                                                               \
+  }                                                                         \
+  inline bitmask& operator|=(bitmask& X, bitmask Y) {                       \
+    X = X | Y;                                                              \
+    return X;                                                               \
+  }                                                                         \
+  inline bitmask& operator^=(bitmask& X, bitmask Y) {                       \
+    X = X ^ Y;                                                              \
+    return X;                                                               \
+  }                                                                         \
+  }
+#endif  // DEFINE_ENUM_FLAG_OPERATORS
+DEFINE_ENUM_FLAG_OPERATORS(btav_a2dp_codec_sample_rate_t);
+DEFINE_ENUM_FLAG_OPERATORS(btav_a2dp_codec_bits_per_sample_t);
+DEFINE_ENUM_FLAG_OPERATORS(btav_a2dp_codec_channel_mode_t);
+
+#endif  // A2DP_CODEC_API_H
diff --git a/stack/include/a2dp_constants.h b/stack/include/a2dp_constants.h
new file mode 100644
index 0000000..f49a7a2
--- /dev/null
+++ b/stack/include/a2dp_constants.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2000-2012 Broadcom Corporation
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+//
+// A2DP constants defined by the Profile Specification
+//
+
+#ifndef A2DP_CONSTANTS_H
+#define A2DP_CONSTANTS_H
+
+#include <inttypes.h>
+
+/* Profile supported features */
+#define A2DP_SUPF_PLAYER 0x0001
+#define A2DP_SUPF_MIC 0x0002
+#define A2DP_SUPF_TUNER 0x0004
+#define A2DP_SUPF_MIXER 0x0008
+
+#define A2DP_SUPF_HEADPHONE 0x0001
+#define A2DP_SUPF_SPEAKER 0x0002
+#define A2DP_SUPF_RECORDER 0x0004
+#define A2DP_SUPF_AMP 0x0008
+
+/* AV Media Codec Types (Audio Codec ID) */
+/* SBC media codec type */
+#define A2DP_MEDIA_CT_SBC 0x00
+/* Non-A2DP media codec type (vendor-specific codec) */
+#define A2DP_MEDIA_CT_NON_A2DP 0xFF
+
+typedef uint8_t tA2DP_CODEC_TYPE; /* A2DP Codec type: A2DP_MEDIA_CT_* */
+
+#endif  // A2DP_CONSTANTS_H
diff --git a/stack/include/a2dp_error_codes.h b/stack/include/a2dp_error_codes.h
new file mode 100644
index 0000000..9983a03
--- /dev/null
+++ b/stack/include/a2dp_error_codes.h
@@ -0,0 +1,133 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2000-2012 Broadcom Corporation
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+//
+// A2DP Error Codes
+//
+
+#ifndef A2DP_ERROR_CODES_H
+#define A2DP_ERROR_CODES_H
+
+#include <inttypes.h>
+
+/* Success */
+#define A2DP_SUCCESS 0
+
+/* Failed */
+#define A2DP_FAIL 0x0A
+
+/* A2DP_FindService is already in progress */
+#define A2DP_BUSY 0x0B
+
+/* Bad parameters */
+#define A2DP_INVALID_PARAMS 0x0C
+
+/* Wrong codec info */
+#define A2DP_WRONG_CODEC 0x0D
+
+/* Media Codec Type is not valid */
+#define A2DP_BAD_CODEC_TYPE 0xC1
+
+/* Media Codec Type is not supported */
+#define A2DP_NS_CODEC_TYPE 0xC2
+
+/* Sampling Frequency is not valid or multiple values have been selected */
+#define A2DP_BAD_SAMP_FREQ 0xC3
+
+/* Sampling Frequency is not supported */
+#define A2DP_NS_SAMP_FREQ 0xC4
+
+/* Channel Mode is not valid or multiple values * have been selected */
+#define A2DP_BAD_CH_MODE 0xC5
+
+/* Channel Mode is not supported */
+#define A2DP_NS_CH_MODE 0xC6
+
+/* None or multiple values have been selected for Number of Subbands */
+#define A2DP_BAD_SUBBANDS 0xC7
+
+/* Number of Subbands is not supported */
+#define A2DP_NS_SUBBANDS 0xC8
+
+/* None or multiple values have been selected for Allocation Method */
+#define A2DP_BAD_ALLOC_METHOD 0xC9
+
+/* Allocation Method is not supported */
+#define A2DP_NS_ALLOC_METHOD 0xCA
+
+/* Minimum Bitpool Value is not valid */
+#define A2DP_BAD_MIN_BITPOOL 0xCB
+
+/* Minimum Bitpool Value is not supported */
+#define A2DP_NS_MIN_BITPOOL 0xCC
+
+/* Maximum Bitpool Value is not valid */
+#define A2DP_BAD_MAX_BITPOOL 0xCD
+
+/* Maximum Bitpool Value is not supported */
+#define A2DP_NS_MAX_BITPOOL 0xCE
+
+/* None or multiple values have been selected for Layer */
+#define A2DP_BAD_LAYER 0xCF
+
+/* Layer is not supported */
+#define A2DP_NS_LAYER 0xD0
+
+/* CRC is not supported */
+#define A2DP_NS_CRC 0xD1
+
+/* MPF-2 is not supported */
+#define A2DP_NS_MPF 0xD2
+
+/* VBR is not supported */
+#define A2DP_NS_VBR 0xD3
+
+/* None or multiple values have been selected for Bit Rate */
+#define A2DP_BAD_BIT_RATE 0xD4
+
+/* Bit Rate is not supported */
+#define A2DP_NS_BIT_RATE 0xD5
+
+/* Either 1) Object type is not valid (b3-b0) or 2) None or multiple values
+ * have been * selected for Object Type
+ */
+#define A2DP_BAD_OBJ_TYPE 0xD6
+
+/* Object type is not supported */
+#define A2DP_NS_OBJ_TYPE 0xD7
+
+/* None or multiple values have been selected for Channels */
+#define A2DP_BAD_CHANNEL 0xD8
+
+/* Channels is not supported */
+#define A2DP_NS_CHANNEL 0xD9
+
+/* None or multiple values have been selected for Block Length */
+#define A2DP_BAD_BLOCK_LEN 0xDD
+
+/* The requested CP Type is not supported. */
+#define A2DP_BAD_CP_TYPE 0xE0
+
+/* The format of Content Protection Service Capability/Content Protection
+ * Scheme Dependent Data is not correct.
+ */
+#define A2DP_BAD_CP_FORMAT 0xE1
+
+typedef uint8_t tA2DP_STATUS;
+
+#endif  // A2DP_ERROR_CODES_H
diff --git a/stack/include/a2dp_sbc.h b/stack/include/a2dp_sbc.h
index 1281b20..3505150 100644
--- a/stack/include/a2dp_sbc.h
+++ b/stack/include/a2dp_sbc.h
@@ -1,107 +1,61 @@
-/******************************************************************************
+/*
+ * Copyright (C) 2016 The Android Open Source Project
  *
- *  Copyright (C) 2000-2012 Broadcom Corporation
+ * 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
  *
- *  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
  *
- *  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.
- *
- ******************************************************************************/
+ * 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.
+ */
 
-/******************************************************************************
- *
- *  Interface to low complexity subband codec (SBC)
- *
- ******************************************************************************/
+//
+// A2DP Codec API for low complexity subband codec (SBC)
+//
+
 #ifndef A2DP_SBC_H
 #define A2DP_SBC_H
 
-#include "a2dp_api.h"
+#include "a2dp_codec_api.h"
+#include "a2dp_sbc_constants.h"
 #include "avdt_api.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+class A2dpCodecConfigSbc : public A2dpCodecConfig {
+ public:
+  A2dpCodecConfigSbc();
+  virtual ~A2dpCodecConfigSbc();
 
-/*****************************************************************************
- *  Constants
- ****************************************************************************/
-/* the length of the SBC Media Payload header. */
-#define A2DP_SBC_MPL_HDR_LEN 1
+  bool init() override;
+  bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability,
+                      uint8_t* p_result_codec_config) override;
 
-/* the LOSC of SBC media codec capabilitiy */
-#define A2DP_SBC_INFO_LEN 6
+ private:
+  bool updateEncoderUserConfig(
+      const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+      bool* p_restart_input, bool* p_restart_output,
+      bool* p_config_updated) override;
+};
 
-/* for Codec Specific Information Element */
-#define A2DP_SBC_IE_SAMP_FREQ_MSK 0xF0 /* b7-b4 sampling frequency */
-#define A2DP_SBC_IE_SAMP_FREQ_16 0x80  /* b7:16  kHz */
-#define A2DP_SBC_IE_SAMP_FREQ_32 0x40  /* b6:32  kHz */
-#define A2DP_SBC_IE_SAMP_FREQ_44 0x20  /* b5:44.1kHz */
-#define A2DP_SBC_IE_SAMP_FREQ_48 0x10  /* b4:48  kHz */
+class A2dpCodecConfigSbcSink : public A2dpCodecConfig {
+ public:
+  A2dpCodecConfigSbcSink();
+  virtual ~A2dpCodecConfigSbcSink();
 
-#define A2DP_SBC_IE_CH_MD_MSK 0x0F    /* b3-b0 channel mode */
-#define A2DP_SBC_IE_CH_MD_MONO 0x08   /* b3: mono */
-#define A2DP_SBC_IE_CH_MD_DUAL 0x04   /* b2: dual */
-#define A2DP_SBC_IE_CH_MD_STEREO 0x02 /* b1: stereo */
-#define A2DP_SBC_IE_CH_MD_JOINT 0x01  /* b0: joint stereo */
+  bool init() override;
+  bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability,
+                      uint8_t* p_result_codec_config) override;
 
-#define A2DP_SBC_IE_BLOCKS_MSK 0xF0 /* b7-b4 number of blocks */
-#define A2DP_SBC_IE_BLOCKS_4 0x80   /* 4 blocks */
-#define A2DP_SBC_IE_BLOCKS_8 0x40   /* 8 blocks */
-#define A2DP_SBC_IE_BLOCKS_12 0x20  /* 12blocks */
-#define A2DP_SBC_IE_BLOCKS_16 0x10  /* 16blocks */
-
-#define A2DP_SBC_IE_SUBBAND_MSK 0x0C /* b3-b2 number of subbands */
-#define A2DP_SBC_IE_SUBBAND_4 0x08   /* b3: 4 */
-#define A2DP_SBC_IE_SUBBAND_8 0x04   /* b2: 8 */
-
-#define A2DP_SBC_IE_ALLOC_MD_MSK 0x03 /* b1-b0 allocation mode */
-#define A2DP_SBC_IE_ALLOC_MD_S 0x02   /* b1: SNR */
-#define A2DP_SBC_IE_ALLOC_MD_L 0x01   /* b0: loundess */
-
-#define A2DP_SBC_IE_MIN_BITPOOL 2
-#define A2DP_SBC_IE_MAX_BITPOOL 250
-
-/* for media payload header */
-#define A2DP_SBC_HDR_F_MSK 0x80
-#define A2DP_SBC_HDR_S_MSK 0x40
-#define A2DP_SBC_HDR_L_MSK 0x20
-#define A2DP_SBC_HDR_NUM_MSK 0x0F
-
-/*****************************************************************************
- *  Type Definitions
- ****************************************************************************/
-
-/*****************************************************************************
- *  External Function Declarations
- ****************************************************************************/
-
-// Gets the A2DP SBC Source codec SEP index for a given |p_codec_info|.
-// Returns the corresponding |tA2DP_CODEC_SEP_INDEX| on success,
-// otherwise |A2DP_CODEC_SEP_INDEX_MAX|.
-tA2DP_CODEC_SEP_INDEX A2DP_SourceCodecSepIndexSbc(const uint8_t* p_codec_info);
-
-// Gets the A2DP SBC Source codec name.
-const char* A2DP_CodecSepIndexStrSbc(void);
-
-// Gets the A2DP SBC Sink codec name.
-const char* A2DP_CodecSepIndexStrSbcSink(void);
-
-// Initializes A2DP SBC Source codec information into |tAVDT_CFG| configuration
-// entry pointed by |p_cfg|.
-bool A2DP_InitCodecConfigSbc(tAVDT_CFG* p_cfg);
-
-// Initializes A2DP SBC Sink codec information into |tAVDT_CFG| configuration
-// entry pointed by |p_cfg|.
-bool A2DP_InitCodecConfigSbcSink(tAVDT_CFG* p_cfg);
+ private:
+  bool updateEncoderUserConfig(
+      const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+      bool* p_restart_input, bool* p_restart_output,
+      bool* p_config_updated) override;
+};
 
 // Checks whether the codec capabilities contain a valid A2DP SBC Source codec.
 // NOTE: only codecs that are implemented are considered valid.
@@ -147,14 +101,6 @@
 // |p_codec_info|.
 void A2DP_InitDefaultCodecSbc(uint8_t* p_codec_info);
 
-// Initializes A2DP SBC Source-to-Sink codec config from Sink codec capability.
-// |p_sink_caps| is the A2DP Sink codec capabilities to use.
-// The selected codec configuration is stored in |p_result_codec_config|.
-// Returns |A2DP_SUCCESS| on success, otherwise the corresponding A2DP error
-// status code.
-tA2DP_STATUS A2DP_InitSource2SinkCodecSbc(const uint8_t* p_sink_caps,
-                                          uint8_t* p_result_codec_config);
-
 // Builds A2DP preferred SBC Sink capability from SBC Source capability.
 // |p_src_cap| is the Source capability to use.
 // |p_pref_cfg| is the result Sink capability to store.
@@ -185,18 +131,18 @@
 // contains invalid codec information.
 int A2DP_GetTrackSampleRateSbc(const uint8_t* p_codec_info);
 
-// Gets the channel count for the A2DP SBC codec.
-// |p_codec_info| is a pointer to the SBC codec_info to decode.
-// Returns the channel count on success, or -1 if |p_codec_info|
-// contains invalid codec information.
-int A2DP_GetTrackChannelCountSbc(const uint8_t* p_codec_info);
-
 // Gets the bits per audio sample for the A2DP SBC codec.
 // |p_codec_info| is a pointer to the SBC codec_info to decode.
 // Returns the bits per audio sample on success, or -1 if |p_codec_info|
 // contains invalid codec information.
 int A2DP_GetTrackBitsPerSampleSbc(const uint8_t* p_codec_info);
 
+// Gets the channel count for the A2DP SBC codec.
+// |p_codec_info| is a pointer to the SBC codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_GetTrackChannelCountSbc(const uint8_t* p_codec_info);
+
 // Gets the number of subbands for the A2DP SBC codec.
 // |p_codec_info| is a pointer to the SBC codec_info to decode.
 // Returns the number of subbands on success, or -1 if |p_codec_info|
@@ -293,8 +239,23 @@
 // Returns true if |p_codec_info| is valid and supported, otherwise false.
 bool A2DP_AdjustCodecSbc(uint8_t* p_codec_info);
 
-#ifdef __cplusplus
-}
-#endif
+// Gets the A2DP SBC Source codec index for a given |p_codec_info|.
+// Returns the corresponding |btav_a2dp_codec_index_t| on success,
+// otherwise |BTAV_A2DP_CODEC_INDEX_MAX|.
+btav_a2dp_codec_index_t A2DP_SourceCodecIndexSbc(const uint8_t* p_codec_info);
 
-#endif /* A2DP_SBC_H */
+// Gets the A2DP SBC Source codec name.
+const char* A2DP_CodecIndexStrSbc(void);
+
+// Gets the A2DP SBC Sink codec name.
+const char* A2DP_CodecIndexStrSbcSink(void);
+
+// Initializes A2DP SBC Source codec information into |tAVDT_CFG| configuration
+// entry pointed by |p_cfg|.
+bool A2DP_InitCodecConfigSbc(tAVDT_CFG* p_cfg);
+
+// Initializes A2DP SBC Sink codec information into |tAVDT_CFG| configuration
+// entry pointed by |p_cfg|.
+bool A2DP_InitCodecConfigSbcSink(tAVDT_CFG* p_cfg);
+
+#endif  // A2DP_SBC_H
diff --git a/stack/include/a2dp_sbc_constants.h b/stack/include/a2dp_sbc_constants.h
new file mode 100644
index 0000000..cb3b801
--- /dev/null
+++ b/stack/include/a2dp_sbc_constants.h
@@ -0,0 +1,71 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2000-2012 Broadcom Corporation
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+//
+// A2DP constants for low complexity subband codec (SBC)
+//
+
+#ifndef A2DP_SBC_CONSTANTS_H
+#define A2DP_SBC_CONSTANTS_H
+
+/*****************************************************************************
+ *  Constants
+ ****************************************************************************/
+/* the length of the SBC Media Payload header. */
+#define A2DP_SBC_MPL_HDR_LEN 1
+
+/* the LOSC of SBC media codec capabilitiy */
+#define A2DP_SBC_INFO_LEN 6
+
+/* for Codec Specific Information Element */
+#define A2DP_SBC_IE_SAMP_FREQ_MSK 0xF0 /* b7-b4 sampling frequency */
+#define A2DP_SBC_IE_SAMP_FREQ_16 0x80  /* b7:16  kHz */
+#define A2DP_SBC_IE_SAMP_FREQ_32 0x40  /* b6:32  kHz */
+#define A2DP_SBC_IE_SAMP_FREQ_44 0x20  /* b5:44.1kHz */
+#define A2DP_SBC_IE_SAMP_FREQ_48 0x10  /* b4:48  kHz */
+
+#define A2DP_SBC_IE_CH_MD_MSK 0x0F    /* b3-b0 channel mode */
+#define A2DP_SBC_IE_CH_MD_MONO 0x08   /* b3: mono */
+#define A2DP_SBC_IE_CH_MD_DUAL 0x04   /* b2: dual */
+#define A2DP_SBC_IE_CH_MD_STEREO 0x02 /* b1: stereo */
+#define A2DP_SBC_IE_CH_MD_JOINT 0x01  /* b0: joint stereo */
+
+#define A2DP_SBC_IE_BLOCKS_MSK 0xF0 /* b7-b4 number of blocks */
+#define A2DP_SBC_IE_BLOCKS_4 0x80   /* 4 blocks */
+#define A2DP_SBC_IE_BLOCKS_8 0x40   /* 8 blocks */
+#define A2DP_SBC_IE_BLOCKS_12 0x20  /* 12blocks */
+#define A2DP_SBC_IE_BLOCKS_16 0x10  /* 16blocks */
+
+#define A2DP_SBC_IE_SUBBAND_MSK 0x0C /* b3-b2 number of subbands */
+#define A2DP_SBC_IE_SUBBAND_4 0x08   /* b3: 4 */
+#define A2DP_SBC_IE_SUBBAND_8 0x04   /* b2: 8 */
+
+#define A2DP_SBC_IE_ALLOC_MD_MSK 0x03 /* b1-b0 allocation mode */
+#define A2DP_SBC_IE_ALLOC_MD_S 0x02   /* b1: SNR */
+#define A2DP_SBC_IE_ALLOC_MD_L 0x01   /* b0: loundess */
+
+#define A2DP_SBC_IE_MIN_BITPOOL 2
+#define A2DP_SBC_IE_MAX_BITPOOL 250
+
+/* for media payload header */
+#define A2DP_SBC_HDR_F_MSK 0x80
+#define A2DP_SBC_HDR_S_MSK 0x40
+#define A2DP_SBC_HDR_L_MSK 0x20
+#define A2DP_SBC_HDR_NUM_MSK 0x0F
+
+#endif  // A2DP_SBC_CONSTANTS_H
diff --git a/stack/include/a2dp_sbc_encoder.h b/stack/include/a2dp_sbc_encoder.h
index 20f65ca..9206f68 100644
--- a/stack/include/a2dp_sbc_encoder.h
+++ b/stack/include/a2dp_sbc_encoder.h
@@ -24,13 +24,9 @@
 #ifndef A2DP_SBC_ENCODER_H
 #define A2DP_SBC_ENCODER_H
 
-#include "a2dp_api.h"
+#include "a2dp_codec_api.h"
 #include "osi/include/time.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 // Loads the A2DP SBC encoder.
 // Return true on success, otherwise false.
 bool A2DP_LoadEncoderSbc(void);
@@ -39,12 +35,12 @@
 void A2DP_UnloadEncoderSbc(void);
 
 // Initialize the A2DP SBC encoder.
-// If |is_peer_edr| is true, the A2DP peer device supports EDR.
-// If |peer_supports_3mbps| is true, the A2DP peer device supports 3Mbps EDR.
-// The encoder initialization parameters are in |p_init_params|.
-// |enqueue_callback} is the callback for enqueueing the encoded audio data.
-void a2dp_sbc_encoder_init(bool is_peer_edr, bool peer_supports_3mbps,
-                           const tA2DP_ENCODER_INIT_PARAMS* p_init_params,
+// |p_peer_params| contains the A2DP peer information
+// The current A2DP codec config is in |a2dp_codec_config|.
+// |read_callback| is the callback for reading the input audio data.
+// |enqueue_callback| is the callback for enqueueing the encoded audio data.
+void a2dp_sbc_encoder_init(const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+                           A2dpCodecConfig* a2dp_codec_config,
                            a2dp_source_read_callback_t read_callback,
                            a2dp_source_enqueue_callback_t enqueue_callback);
 
@@ -73,8 +69,4 @@
 // in user-friendly test format.
 void a2dp_sbc_debug_codec_dump(int fd);
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif  // A2DP_SBC_ENCODER_H
diff --git a/stack/include/a2dp_sbc_up_sample.h b/stack/include/a2dp_sbc_up_sample.h
index 78e99e8..234ce54 100644
--- a/stack/include/a2dp_sbc_up_sample.h
+++ b/stack/include/a2dp_sbc_up_sample.h
@@ -27,10 +27,6 @@
 
 #include <stdint.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /*******************************************************************************
  *
  * Function         a2dp_sbc_init_up_sample
@@ -164,8 +160,4 @@
 int a2dp_sbc_up_sample_8m(void* p_src, void* p_dst, uint32_t src_samples,
                           uint32_t dst_samples, uint32_t* p_ret);
 
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* A2DP_SBC_UP_SAMPLE_H */
+#endif  // A2DP_SBC_UP_SAMPLE_H
diff --git a/stack/include/a2dp_vendor.h b/stack/include/a2dp_vendor.h
index 0c0710a..2a0a6c8 100644
--- a/stack/include/a2dp_vendor.h
+++ b/stack/include/a2dp_vendor.h
@@ -14,19 +14,15 @@
  * limitations under the License.
  */
 
-/**
- * Vendor Specific A2DP Codecs Support
- */
+//
+// Vendor Specific A2DP Codecs Support
+//
 
 #ifndef A2DP_VENDOR_H
 #define A2DP_VENDOR_H
 
 #include <stdbool.h>
-#include "a2dp_api.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "a2dp_codec_api.h"
 
 /* Offset for A2DP vendor codec */
 #define A2DP_VENDOR_CODEC_START_IDX 3
@@ -38,22 +34,6 @@
 #define A2DP_VENDOR_CODEC_CODEC_ID_START_IDX \
   (A2DP_VENDOR_CODEC_VENDOR_ID_START_IDX + sizeof(uint32_t))
 
-// Gets the A2DP vendor Source codec SEP index for a given |p_codec_info|.
-// Returns the corresponding |tA2DP_CODEC_SEP_INDEX| on success,
-// otherwise |A2DP_CODEC_SEP_INDEX_MAX|.
-tA2DP_CODEC_SEP_INDEX A2DP_VendorSourceCodecSepIndex(
-    const uint8_t* p_codec_info);
-
-// Gets the A2DP vendor codec name for a given |codec_sep_index|.
-const char* A2DP_VendorCodecSepIndexStr(tA2DP_CODEC_SEP_INDEX codec_sep_index);
-
-// Initializes A2DP vendor codec-specific information into |tAVDT_CFG|
-// configuration entry pointed by |p_cfg|. The selected codec is defined by
-// |codec_sep_index|.
-// Returns true on success, otherwise false.
-bool A2DP_VendorInitCodecConfig(tA2DP_CODEC_SEP_INDEX codec_sep_index,
-                                tAVDT_CFG* p_cfg);
-
 // Checks whether the codec capabilities contain a valid A2DP vendor-specific
 // Source codec.
 // NOTE: only codecs that are implemented are considered valid.
@@ -96,15 +76,6 @@
 // device is supported, otherwise false.
 bool A2DP_IsVendorPeerSourceCodecSupported(const uint8_t* p_codec_info);
 
-// Initializes A2DP vendor-specific Source-to-Sink codec config from Sink
-// codec capability.
-// |p_sink_caps| is the A2DP Sink codec capability to use.
-// The selected codec configuration is stored in |p_result_codec_config|.
-// Returns |A2DP_SUCCESS| on success, otherwise the corresponding A2DP error
-// status code.
-tA2DP_STATUS A2DP_VendorInitSource2SinkCodec(const uint8_t* p_sink_caps,
-                                             uint8_t* p_result_codec_config);
-
 // Builds a vendor-specific A2DP preferred Sink capability from a vendor
 // Source capability.
 // |p_src_cap| is the Source capability to use.
@@ -155,18 +126,18 @@
 // contains invalid codec information.
 int A2DP_VendorGetTrackSampleRate(const uint8_t* p_codec_info);
 
-// Gets the channel count for the A2DP vendor-specific codec.
-// |p_codec_info| is a pointer to the vendor-specific codec_info to decode.
-// Returns the channel count on success, or -1 if |p_codec_info|
-// contains invalid codec information.
-int A2DP_VendorGetTrackChannelCount(const uint8_t* p_codec_info);
-
 // Gets the bits per audio sample for the A2DP vendor-specific codec.
 // |p_codec_info| is a pointer to the vendor-specific codec_info to decode.
 // Returns the bits per audio sample on success, or -1 if |p_codec_info|
 // contains invalid codec information.
 int A2DP_VendorGetTrackBitsPerSample(const uint8_t* p_codec_info);
 
+// Gets the channel count for the A2DP vendor-specific codec.
+// |p_codec_info| is a pointer to the vendor-specific codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackChannelCount(const uint8_t* p_codec_info);
+
 // Gets the channel type for the A2DP vendor-specific Sink codec:
 // 1 for mono, or 3 for dual/stereo/joint.
 // |p_codec_info| is a pointer to the vendor-specific codec_info to decode.
@@ -214,8 +185,20 @@
 // Returns true if |p_codec_info| is valid and supported, otherwise false.
 bool A2DP_VendorAdjustCodec(uint8_t* p_codec_info);
 
-#ifdef __cplusplus
-}
-#endif
+// Gets the A2DP vendor Source codec index for a given |p_codec_info|.
+// Returns the corresponding |btav_a2dp_codec_index_t| on success,
+// otherwise |BTAV_A2DP_CODEC_INDEX_MAX|.
+btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndex(
+    const uint8_t* p_codec_info);
 
-#endif /* A2DP_VENDOR_H */
+// Gets the A2DP vendor codec name for a given |codec_index|.
+const char* A2DP_VendorCodecIndexStr(btav_a2dp_codec_index_t codec_index);
+
+// Initializes A2DP vendor codec-specific information into |tAVDT_CFG|
+// configuration entry pointed by |p_cfg|. The selected codec is defined by
+// |codec_index|.
+// Returns true on success, otherwise false.
+bool A2DP_VendorInitCodecConfig(btav_a2dp_codec_index_t codec_index,
+                                tAVDT_CFG* p_cfg);
+
+#endif  // A2DP_VENDOR_H
diff --git a/stack/test/stack_a2dp_test.cc b/stack/test/stack_a2dp_test.cc
index 4274f5e..49a6fb7 100644
--- a/stack/test/stack_a2dp_test.cc
+++ b/stack/test/stack_a2dp_test.cc
@@ -19,6 +19,7 @@
 #include <gtest/gtest.h>
 
 #include "stack/include/a2dp_api.h"
+#include "stack/include/a2dp_codec_api.h"
 #include "stack/include/a2dp_sbc.h"
 #include "stack/include/a2dp_vendor.h"
 
@@ -43,7 +44,7 @@
     9                    // Dummy
 };
 
-const uint8_t codec_info_sbc_sink[AVDT_CODEC_SIZE] = {
+const uint8_t codec_info_sbc_sink_capability[AVDT_CODEC_SIZE] = {
     6,             // Length (A2DP_SBC_INFO_LEN)
     0,             // Media Type: AVDT_MEDIA_TYPE_AUDIO
     0,             // Media Codec Type: A2DP_MEDIA_CT_SBC
@@ -88,12 +89,23 @@
 
 }  // namespace
 
+TEST(StackA2dpTest, test_a2dp_bits_set) {
+  EXPECT_TRUE(A2DP_BitsSet(0x0) == A2DP_SET_ZERO_BIT);
+  EXPECT_TRUE(A2DP_BitsSet(0x1) == A2DP_SET_ONE_BIT);
+  EXPECT_TRUE(A2DP_BitsSet(0x2) == A2DP_SET_ONE_BIT);
+  EXPECT_TRUE(A2DP_BitsSet(0x3) == A2DP_SET_MULTL_BIT);
+  EXPECT_TRUE(A2DP_BitsSet(0x7f) == A2DP_SET_MULTL_BIT);
+  EXPECT_TRUE(A2DP_BitsSet(0x80) == A2DP_SET_ONE_BIT);
+  EXPECT_TRUE(A2DP_BitsSet(0x81) == A2DP_SET_MULTL_BIT);
+  EXPECT_TRUE(A2DP_BitsSet(0xff) == A2DP_SET_MULTL_BIT);
+}
+
 TEST(StackA2dpTest, test_a2dp_is_codec_valid) {
   EXPECT_TRUE(A2DP_IsSourceCodecValid(codec_info_sbc));
   EXPECT_TRUE(A2DP_IsPeerSourceCodecValid(codec_info_sbc));
 
-  EXPECT_TRUE(A2DP_IsSinkCodecValid(codec_info_sbc_sink));
-  EXPECT_TRUE(A2DP_IsPeerSinkCodecValid(codec_info_sbc_sink));
+  EXPECT_TRUE(A2DP_IsSinkCodecValid(codec_info_sbc_sink_capability));
+  EXPECT_TRUE(A2DP_IsPeerSinkCodecValid(codec_info_sbc_sink_capability));
 
   EXPECT_FALSE(A2DP_IsSourceCodecValid(codec_info_non_a2dp));
   EXPECT_FALSE(A2DP_IsSinkCodecValid(codec_info_non_a2dp));
@@ -133,13 +145,13 @@
 
 TEST(StackA2dpTest, test_a2dp_is_sink_codec_supported) {
   EXPECT_TRUE(A2DP_IsSinkCodecSupported(codec_info_sbc));
-  EXPECT_TRUE(A2DP_IsSinkCodecSupported(codec_info_sbc_sink));
+  EXPECT_FALSE(A2DP_IsSinkCodecSupported(codec_info_sbc_sink_capability));
   EXPECT_FALSE(A2DP_IsSinkCodecSupported(codec_info_non_a2dp));
 }
 
 TEST(StackA2dpTest, test_a2dp_is_peer_source_codec_supported) {
   EXPECT_TRUE(A2DP_IsPeerSourceCodecSupported(codec_info_sbc));
-  EXPECT_TRUE(A2DP_IsPeerSourceCodecSupported(codec_info_sbc_sink));
+  EXPECT_TRUE(A2DP_IsPeerSourceCodecSupported(codec_info_sbc_sink_capability));
   EXPECT_FALSE(A2DP_IsPeerSourceCodecSupported(codec_info_non_a2dp));
 }
 
@@ -155,25 +167,6 @@
   }
 }
 
-TEST(StackA2dpTest, test_init_source2sink_codec) {
-  uint8_t codec_info_result[AVDT_CODEC_SIZE];
-
-  memset(codec_info_result, 0, sizeof(codec_info_result));
-  EXPECT_EQ(A2DP_InitSource2SinkCodec(codec_info_sbc_sink, codec_info_result),
-            A2DP_SUCCESS);
-  // Compare the result codec with the local test codec info
-  for (size_t i = 0; i < codec_info_sbc[0] + 1; i++) {
-    EXPECT_EQ(codec_info_result[i], codec_info_sbc[i]);
-  }
-
-  // Test invalid codec info
-  uint8_t codec_info_sbc_test1[AVDT_CODEC_SIZE];
-  memset(codec_info_result, 0, sizeof(codec_info_result));
-  memset(codec_info_sbc_test1, 0, sizeof(codec_info_sbc_test1));
-  EXPECT_NE(A2DP_InitSource2SinkCodec(codec_info_sbc_test1, codec_info_result),
-            A2DP_SUCCESS);
-}
-
 TEST(StackA2dpTest, test_build_src2sink_config) {
   uint8_t codec_info_result[AVDT_CODEC_SIZE];
 
@@ -216,66 +209,6 @@
   EXPECT_TRUE(A2DP_UsesRtpHeader(false, codec_info_non_a2dp));
 }
 
-TEST(StackA2dpTest, test_a2dp_source_codec_sep_index) {
-  // Explicit tests for known codecs
-  EXPECT_EQ(A2DP_SourceCodecSepIndex(codec_info_sbc),
-            A2DP_CODEC_SEP_INDEX_SOURCE_SBC);
-  EXPECT_EQ(A2DP_SourceCodecSepIndex(codec_info_sbc_sink),
-            A2DP_CODEC_SEP_INDEX_SOURCE_SBC);
-  EXPECT_EQ(A2DP_SourceCodecSepIndex(codec_info_non_a2dp),
-            A2DP_CODEC_SEP_INDEX_MAX);
-}
-
-TEST(StackA2dpTest, test_a2dp_codec_sep_index_str) {
-  // Explicit tests for known codecs
-  EXPECT_STREQ(A2DP_CodecSepIndexStr(A2DP_CODEC_SEP_INDEX_SOURCE_SBC), "SBC");
-  EXPECT_STREQ(A2DP_CodecSepIndexStr(A2DP_CODEC_SEP_INDEX_SINK_SBC),
-               "SBC SINK");
-
-  // Test that the unknown codec string has not changed
-  EXPECT_STREQ(A2DP_CodecSepIndexStr(A2DP_CODEC_SEP_INDEX_MAX),
-               "UNKNOWN CODEC SEP INDEX");
-
-  // Test that each codec has a known string
-  for (int i = 0; i < A2DP_CODEC_SEP_INDEX_MAX; i++) {
-    tA2DP_CODEC_SEP_INDEX codec_sep_index =
-        static_cast<tA2DP_CODEC_SEP_INDEX>(i);
-    EXPECT_STRNE(A2DP_CodecSepIndexStr(codec_sep_index),
-                 "UNKNOWN CODEC SEP INDEX");
-  }
-}
-
-TEST(StackA2dpTest, test_a2dp_init_codec_config) {
-  tAVDT_CFG avdt_cfg;
-
-  //
-  // Test for SBC Source
-  //
-  memset(&avdt_cfg, 0, sizeof(avdt_cfg));
-  EXPECT_TRUE(A2DP_InitCodecConfig(A2DP_CODEC_SEP_INDEX_SOURCE_SBC, &avdt_cfg));
-  // Compare the result codec with the local test codec info
-  for (size_t i = 0; i < codec_info_sbc[0] + 1; i++) {
-    EXPECT_EQ(avdt_cfg.codec_info[i], codec_info_sbc[i]);
-  }
-// Test for content protection
-#if (BTA_AV_CO_CP_SCMS_T == TRUE)
-  EXPECT_EQ(avdt_cfg.protect_info[0], AVDT_CP_LOSC);
-  EXPECT_EQ(avdt_cfg.protect_info[1], (AVDT_CP_SCMS_T_ID & 0xFF));
-  EXPECT_EQ(avdt_cfg.protect_info[2], ((AVDT_CP_SCMS_T_ID >> 8) & 0xFF));
-  EXPECT_EQ(avdt_cfg.num_protect, 1);
-#endif
-
-  //
-  // Test for SBC Sink
-  //
-  memset(&avdt_cfg, 0, sizeof(avdt_cfg));
-  EXPECT_TRUE(A2DP_InitCodecConfig(A2DP_CODEC_SEP_INDEX_SINK_SBC, &avdt_cfg));
-  // Compare the result codec with the local test codec info
-  for (size_t i = 0; i < codec_info_sbc_sink[0] + 1; i++) {
-    EXPECT_EQ(avdt_cfg.codec_info[i], codec_info_sbc_sink[i]);
-  }
-}
-
 TEST(StackA2dpTest, test_a2dp_get_media_type) {
   uint8_t codec_info_test[AVDT_CODEC_SIZE];
 
@@ -296,7 +229,7 @@
 
   // Explicit tests for known codecs
   EXPECT_STREQ(A2DP_CodecName(codec_info_sbc), "SBC");
-  EXPECT_STREQ(A2DP_CodecName(codec_info_sbc_sink), "SBC");
+  EXPECT_STREQ(A2DP_CodecName(codec_info_sbc_sink_capability), "SBC");
   EXPECT_STREQ(A2DP_CodecName(codec_info_non_a2dp), "UNKNOWN VENDOR CODEC");
 
   // Test all unknown codecs
@@ -317,7 +250,8 @@
 }
 
 TEST(StackA2dpTest, test_a2dp_codec_type_equals) {
-  EXPECT_TRUE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_sbc_sink));
+  EXPECT_TRUE(
+      A2DP_CodecTypeEquals(codec_info_sbc, codec_info_sbc_sink_capability));
   EXPECT_TRUE(
       A2DP_CodecTypeEquals(codec_info_non_a2dp, codec_info_non_a2dp_dummy));
   EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_non_a2dp));
@@ -363,16 +297,16 @@
   EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_non_a2dp), -1);
 }
 
-TEST(StackA2dpTest, test_a2dp_get_track_channel_count) {
-  EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_sbc), 2);
-  EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_non_a2dp), -1);
-}
-
 TEST(StackA2dpTest, test_a2dp_get_track_bits_per_sample) {
   EXPECT_EQ(A2DP_GetTrackBitsPerSample(codec_info_sbc), 16);
   EXPECT_EQ(A2DP_GetTrackBitsPerSample(codec_info_non_a2dp), -1);
 }
 
+TEST(StackA2dpTest, test_a2dp_get_track_channel_count) {
+  EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_sbc), 2);
+  EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_non_a2dp), -1);
+}
+
 TEST(StackA2dpTest, test_a2dp_get_number_of_subbands_sbc) {
   EXPECT_EQ(A2DP_GetNumberOfSubbandsSbc(codec_info_sbc), 8);
   EXPECT_EQ(A2DP_GetNumberOfSubbandsSbc(codec_info_non_a2dp), -1);
@@ -400,13 +334,13 @@
 
 TEST(StackA2dpTest, test_a2dp_get_min_bitpool_sbc) {
   EXPECT_EQ(A2DP_GetMinBitpoolSbc(codec_info_sbc), 2);
-  EXPECT_EQ(A2DP_GetMinBitpoolSbc(codec_info_sbc_sink), 2);
+  EXPECT_EQ(A2DP_GetMinBitpoolSbc(codec_info_sbc_sink_capability), 2);
   EXPECT_EQ(A2DP_GetMinBitpoolSbc(codec_info_non_a2dp), -1);
 }
 
 TEST(StackA2dpTest, test_a2dp_get_max_bitpool_sbc) {
   EXPECT_EQ(A2DP_GetMaxBitpoolSbc(codec_info_sbc), 53);
-  EXPECT_EQ(A2DP_GetMaxBitpoolSbc(codec_info_sbc_sink), 53);
+  EXPECT_EQ(A2DP_GetMaxBitpoolSbc(codec_info_sbc_sink_capability), 53);
   EXPECT_EQ(A2DP_GetMaxBitpoolSbc(codec_info_non_a2dp), -1);
 }
 
@@ -495,3 +429,135 @@
          sizeof(codec_info_non_a2dp));
   EXPECT_FALSE(A2DP_AdjustCodec(codec_info_non_a2dp_test));
 }
+
+TEST(StackA2dpTest, test_a2dp_source_codec_index) {
+  // Explicit tests for known codecs
+  EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_sbc),
+            BTAV_A2DP_CODEC_INDEX_SOURCE_SBC);
+  EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_sbc_sink_capability),
+            BTAV_A2DP_CODEC_INDEX_SOURCE_SBC);
+  EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_non_a2dp),
+            BTAV_A2DP_CODEC_INDEX_MAX);
+}
+
+TEST(StackA2dpTest, test_a2dp_codec_index_str) {
+  // Explicit tests for known codecs
+  EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC), "SBC");
+  EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SINK_SBC), "SBC SINK");
+
+  // Test that the unknown codec string has not changed
+  EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_MAX),
+               "UNKNOWN CODEC INDEX");
+
+  // Test that each codec has a known string
+  for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
+    btav_a2dp_codec_index_t codec_index =
+        static_cast<btav_a2dp_codec_index_t>(i);
+    EXPECT_STRNE(A2DP_CodecIndexStr(codec_index), "UNKNOWN CODEC INDEX");
+  }
+}
+
+TEST(StackA2dpTest, test_a2dp_init_codec_config) {
+  tAVDT_CFG avdt_cfg;
+
+  //
+  // Test for SBC Source
+  //
+  memset(&avdt_cfg, 0, sizeof(avdt_cfg));
+  EXPECT_TRUE(
+      A2DP_InitCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC, &avdt_cfg));
+  // Compare the result codec with the local test codec info
+  for (size_t i = 0; i < codec_info_sbc[0] + 1; i++) {
+    EXPECT_EQ(avdt_cfg.codec_info[i], codec_info_sbc[i]);
+  }
+// Test for content protection
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+  EXPECT_EQ(avdt_cfg.protect_info[0], AVDT_CP_LOSC);
+  EXPECT_EQ(avdt_cfg.protect_info[1], (AVDT_CP_SCMS_T_ID & 0xFF));
+  EXPECT_EQ(avdt_cfg.protect_info[2], ((AVDT_CP_SCMS_T_ID >> 8) & 0xFF));
+  EXPECT_EQ(avdt_cfg.num_protect, 1);
+#endif
+
+  //
+  // Test for SBC Sink
+  //
+  memset(&avdt_cfg, 0, sizeof(avdt_cfg));
+  EXPECT_TRUE(A2DP_InitCodecConfig(BTAV_A2DP_CODEC_INDEX_SINK_SBC, &avdt_cfg));
+  // Compare the result codec with the local test codec info
+  for (size_t i = 0; i < codec_info_sbc_sink_capability[0] + 1; i++) {
+    EXPECT_EQ(avdt_cfg.codec_info[i], codec_info_sbc_sink_capability[i]);
+  }
+}
+
+TEST(A2dpCodecConfig, createCodec) {
+  for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
+    btav_a2dp_codec_index_t codec_index =
+        static_cast<btav_a2dp_codec_index_t>(i);
+    A2dpCodecConfig* codec_config = A2dpCodecConfig::createCodec(codec_index);
+    EXPECT_NE(codec_config, nullptr);
+    EXPECT_EQ(codec_config->codecIndex(), codec_index);
+    EXPECT_FALSE(codec_config->name().empty());
+    EXPECT_GT(codec_config->codecPriority(), 0U);
+    delete codec_config;
+  }
+}
+
+TEST(A2dpCodecConfig, setCodecConfig) {
+  uint8_t codec_info_result[AVDT_CODEC_SIZE];
+  btav_a2dp_codec_index_t peer_codec_index;
+  A2dpCodecs* a2dp_codecs = new A2dpCodecs();
+  A2dpCodecConfig* codec_config;
+
+  EXPECT_TRUE(a2dp_codecs->init());
+
+  // Create the codec capability
+  memset(codec_info_result, 0, sizeof(codec_info_result));
+  peer_codec_index = A2DP_SourceCodecIndex(codec_info_sbc_sink_capability);
+  EXPECT_NE(peer_codec_index, BTAV_A2DP_CODEC_INDEX_MAX);
+  codec_config =
+      a2dp_codecs->findSourceCodecConfig(codec_info_sbc_sink_capability);
+  EXPECT_NE(codec_config, nullptr);
+  EXPECT_TRUE(a2dp_codecs->setCodecConfig(codec_info_sbc_sink_capability, true,
+                                          codec_info_result));
+  EXPECT_EQ(a2dp_codecs->getCurrentCodecConfig(), codec_config);
+  // Compare the result codec with the local test codec info
+  for (size_t i = 0; i < codec_info_sbc[0] + 1; i++) {
+    EXPECT_EQ(codec_info_result[i], codec_info_sbc[i]);
+  }
+
+  // Create the codec config
+  memset(codec_info_result, 0, sizeof(codec_info_result));
+  peer_codec_index = A2DP_SourceCodecIndex(codec_info_sbc);
+  EXPECT_NE(peer_codec_index, BTAV_A2DP_CODEC_INDEX_MAX);
+  codec_config = a2dp_codecs->findSourceCodecConfig(codec_info_sbc);
+  EXPECT_NE(codec_config, nullptr);
+  EXPECT_TRUE(
+      a2dp_codecs->setCodecConfig(codec_info_sbc, false, codec_info_result));
+  EXPECT_EQ(a2dp_codecs->getCurrentCodecConfig(), codec_config);
+  // Compare the result codec with the local test codec info
+  for (size_t i = 0; i < codec_info_sbc[0] + 1; i++) {
+    EXPECT_EQ(codec_info_result[i], codec_info_sbc[i]);
+  }
+
+  // Test invalid codec info
+  uint8_t codec_info_sbc_test1[AVDT_CODEC_SIZE];
+  memset(codec_info_result, 0, sizeof(codec_info_result));
+  memset(codec_info_sbc_test1, 0, sizeof(codec_info_sbc_test1));
+  EXPECT_FALSE(a2dp_codecs->setCodecConfig(codec_info_sbc_test1, true,
+                                           codec_info_result));
+  delete a2dp_codecs;
+}
+
+TEST(A2dpCodecs, init) {
+  A2dpCodecs codecs;
+
+  EXPECT_TRUE(codecs.init());
+
+  const std::list<A2dpCodecConfig*> orderedSourceCodecs =
+      codecs.orderedSourceCodecs();
+  EXPECT_FALSE(orderedSourceCodecs.empty());
+
+  const std::list<A2dpCodecConfig*> orderedSinkCodecs =
+      codecs.orderedSinkCodecs();
+  EXPECT_FALSE(orderedSinkCodecs.empty());
+}
diff --git a/udrv/include/uipc.h b/udrv/include/uipc.h
index 9ca1505..62fadf1 100644
--- a/udrv/include/uipc.h
+++ b/udrv/include/uipc.h
@@ -97,7 +97,7 @@
  * Returns          void
  *
  ******************************************************************************/
-bool UIPC_Send(tUIPC_CH_ID ch_id, uint16_t msg_evt, uint8_t* p_buf,
+bool UIPC_Send(tUIPC_CH_ID ch_id, uint16_t msg_evt, const uint8_t* p_buf,
                uint16_t msglen);
 
 /*******************************************************************************
diff --git a/udrv/ulinux/uipc.cc b/udrv/ulinux/uipc.cc
index 2bfbd8e..c843ea7 100644
--- a/udrv/ulinux/uipc.cc
+++ b/udrv/ulinux/uipc.cc
@@ -616,8 +616,8 @@
  ** Returns          true in case of success, false in case of failure.
  **
  ******************************************************************************/
-bool UIPC_Send(tUIPC_CH_ID ch_id, UNUSED_ATTR uint16_t msg_evt, uint8_t* p_buf,
-               uint16_t msglen) {
+bool UIPC_Send(tUIPC_CH_ID ch_id, UNUSED_ATTR uint16_t msg_evt,
+               const uint8_t* p_buf, uint16_t msglen) {
   BTIF_TRACE_DEBUG("UIPC_Send : ch_id:%d %d bytes", ch_id, msglen);
 
   std::lock_guard<std::recursive_mutex> lock(uipc_main.mutex);