Add support for Multi-A2DP state machines per device

* Reimplement most of the BTIF AV state machinery in C++
 - Separate the BTIF AV Source from BTIF AV Sink state:
   btif_av_source and btif_av_sink
 - Remove the single btif_av_cb control block instance
 - Add C++ based BtifStateMachine and remove the older C-based
   btif_sm state machine
 - Introduce C++ class BtifAvPeer to keep state per peer, and use
   a single state machine instance per peer
 - Update BTA_AvStart() and BTA_AvStop() to take tBTA_AV_HNDL handle
   argument
 - Register the BTA handles when enabling the Source/Sink service.
   This needs to be done in advance during service startup, otherwise
   a connection to a remote device will fail.
 - Move local event_handler functions inside C++ ProcessEvent methods,
   and pocess the BTIF, BTA AV and BTA AVRCP events per state machine
   instance
 - Cleanup callbacks and use do_in_jni_thread() to schedule the processing
 - Add BTIF AV API that needs to be called by the AVRCP module for certain
   AVRCP events: btif_av_avrcp_event_open(), btif_av_avrcp_event_close()
   btif_av_avrcp_event_remote_play()
 - Add a mechanism to set the active device
 - Add BluetoothDevice argument to the codec-related internal Binder
   APIs: getCodecStatus(), enableOptionalCodecs(),
   disableOptionalCodecs(), setCodecConfigPreference()
 - Rename btif_av_move_idle() to btif_av_acl_disconnected() and
   refactor the processing to happen within each state machine on the
   correct thread.
 - Process cleanup_src and cleanup_sink on the JNI thread
 - Add a mechanism to track the maximum number of connected devices.
 - Add unit tests for the new BtifStateMachine class

* Redesign some of the AVDTP internals so it can handle multiple connected
  devices
 - Change some of the AVDTP struct entries to C++ classes.
   This simplifies significantly the design fixes
 - Reorganize the AvdtpScb entries: for each connection the
   corresponding SEP entries are within the corresponding AvdtpCcb entry.
 - Pass peer address as argument to the API functions inside bta_av_co.cc,
   and update the peer address inside the corresponding peer entry in
   bta_av_co.cc
 - Add various log mesages
 - Store precomputed BTA AV SCB index in the AvdtpCcb entry and use it as
   appropriate.
 - Fix the TCID computation and handling for the AvdtpAdaptationLayer
 - Fix the computed value for AVDT_NUM_RT_TBL
 - Remove video-related code inside AVDTP
 - Refactor/cleanup hard-coded callbacks that depend on the BTA_AV_NUM_STRS
   value.
 - Update various #define values to support a larger number of connected
   devices:
   - Maximum number of streams:
     BTA_AV_NUM_STRS: 2 -> 6
   - BT_RC_NUM_APP: 1 -> 12 (AVRCP-related - 2 * MaxDevices)
   - MAX_L2CAP_LINKS: 7 -> 13
   - MAX_L2CAP_CHANNELS: 16 -> 32
   - AVDT_NUM_LINKS: 2 -> 6
   - AVDT_NUM_SEPS: (6 * AVDT_NUM_LINKS) -> 6
     Now the value is used for the maximum number of SEPs per device
   - AVDT_NUM_TC_TBL: 6 -> (AVDT_NUM_SEPS + AVDT_NUM_LINKS)
   - AVCT_NUM_LINKS: 2 -> 6
   - AVCT_NUM_CONN: 3 -> 14 (2 * MaxDevices + 2)

Also:
 - Update the JNI codec-related calls to use const reference
   instead of a pointer
 - Update the implementation of bta_av_chk_2nd_start() so it is aligned
   with bta_av_chk_start() and cleaned up both functions.
 - Rename btif_dispatch_sm_event() to btif_av_dispatch_sm_event() and
   btif_report_source_codec_state() to btif_av_report_source_codec_state()
   for consistency with the rest of the BTIF AV API.
 - Add new function btif_rc_is_connected_peer() and remove
   btif_rc_get_connected_peer()
 - Add new AVRCP header file btif/include/btif_rc.h
   and move the AVRCP "extern" declarations from btif_av.cc there.
 - Rename btif_av_execute_service() to btif_av_source_execute_service()
 - Cleanup the btif_av.h API descriptions
 - Print the BTIF AV state in the "dumpsys bluetooth_manager" output
 - Print the BTA AV state in the "dumpsys bluetooth_manager" output
 - Print the AVDTP state in the "dumpsys bluetooth_manager" output
 - Refactor btif_a2dp_source_cb into a class with internal state.
 - Refactor A2DP Source worker thread handling and replace it with
   libchrome message handler
 - Refactor BtaAvCo component (bta_av_co.cc)
   - C++ classes and state
   - Rename bta_av_co_audio_src_data_path to
     bta_av_co_audio_source_data_path
   - Remove most BTA_AV_CO_CP_SCMS_T checks
   - Replace mutex_global_lock() usage with local mutex
   - Keep codec-specific state per peer
   - Keep state about the active peer - the first connected peer is the
     default active peer
   - Report source codec state only for valid peers; i.e., don't report
     source codec state for empty RawAddress
   - Keep the contect_protect_active flag per peer
   - Print the BTA AV CO state in the dumpsys bluetooth_manager" output
 - Misc cleanup in BTA AV
 - Add ToString() method to struct btav_a2dp_codec_config_t
 - Additional cleanup

Bug: 70350399
Test: Manual and unit tests
Change-Id: Icecd7fd44a222d939b63a7473a2239ae0679f08c
Merged-In: Icecd7fd44a222d939b63a7473a2239ae0679f08c
(cherry picked from commit c7242818d4180dec4eae2e75f9fb91f7f6a160aa)
diff --git a/binder/android/bluetooth/IBluetoothA2dp.aidl b/binder/android/bluetooth/IBluetoothA2dp.aidl
index 02a874c..f852d30 100644
--- a/binder/android/bluetooth/IBluetoothA2dp.aidl
+++ b/binder/android/bluetooth/IBluetoothA2dp.aidl
@@ -40,10 +40,11 @@
     oneway void adjustAvrcpAbsoluteVolume(int direction);
     oneway void setAvrcpAbsoluteVolume(int volume);
     boolean isA2dpPlaying(in BluetoothDevice device);
-    BluetoothCodecStatus getCodecStatus();
-    oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig);
-    oneway void enableOptionalCodecs();
-    oneway void disableOptionalCodecs();
+    BluetoothCodecStatus getCodecStatus(in BluetoothDevice device);
+    oneway void setCodecConfigPreference(in BluetoothDevice device,
+                in BluetoothCodecConfig codecConfig);
+    oneway void enableOptionalCodecs(in BluetoothDevice device);
+    oneway void disableOptionalCodecs(in BluetoothDevice device);
     int supportsOptionalCodecs(in BluetoothDevice device);
     int getOptionalCodecsEnabled(in BluetoothDevice device);
     oneway void setOptionalCodecsEnabled(in BluetoothDevice device, int value);
diff --git a/bta/ar/bta_ar.cc b/bta/ar/bta_ar.cc
index b178a07..bcfa89d 100644
--- a/bta/ar/bta_ar.cc
+++ b/bta/ar/bta_ar.cc
@@ -73,13 +73,14 @@
  * Returns          void
  *
  ******************************************************************************/
-static void bta_ar_avdt_cback(uint8_t handle, const RawAddress* bd_addr,
-                              uint8_t event, tAVDT_CTRL* p_data) {
+static void bta_ar_avdt_cback(uint8_t handle, const RawAddress& bd_addr,
+                              uint8_t event, tAVDT_CTRL* p_data,
+                              uint8_t scb_index) {
   /* route the AVDT registration callback to av or avk */
   if (bta_ar_cb.p_av_conn_cback)
-    (*bta_ar_cb.p_av_conn_cback)(handle, bd_addr, event, p_data);
+    (*bta_ar_cb.p_av_conn_cback)(handle, bd_addr, event, p_data, scb_index);
   if (bta_ar_cb.p_avk_conn_cback)
-    (*bta_ar_cb.p_avk_conn_cback)(handle, bd_addr, event, p_data);
+    (*bta_ar_cb.p_avk_conn_cback)(handle, bd_addr, event, p_data, scb_index);
 }
 
 /*******************************************************************************
@@ -91,7 +92,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_ar_reg_avdt(tAVDT_REG* p_reg, tAVDT_CTRL_CBACK* p_cback,
+void bta_ar_reg_avdt(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback,
                      tBTA_SYS_ID sys_id) {
   uint8_t mask = 0;
 
@@ -153,17 +154,18 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_ar_avdt_conn(tBTA_SYS_ID sys_id, const RawAddress& bd_addr) {
+void bta_ar_avdt_conn(tBTA_SYS_ID sys_id, const RawAddress& bd_addr,
+                      uint8_t scb_index) {
   uint8_t event = BTA_AR_AVDT_CONN_EVT;
   tAVDT_CTRL data;
 
   if (sys_id == BTA_ID_AV) {
     if (bta_ar_cb.p_avk_conn_cback) {
-      (*bta_ar_cb.p_avk_conn_cback)(0, &bd_addr, event, &data);
+      (*bta_ar_cb.p_avk_conn_cback)(0, bd_addr, event, &data, scb_index);
     }
   } else if (sys_id == BTA_ID_AVK) {
     if (bta_ar_cb.p_av_conn_cback) {
-      (*bta_ar_cb.p_av_conn_cback)(0, &bd_addr, event, &data);
+      (*bta_ar_cb.p_av_conn_cback)(0, bd_addr, event, &data, scb_index);
     }
   }
 }
diff --git a/bta/av/bta_av_aact.cc b/bta/av/bta_av_aact.cc
index df7660c..2429bff 100644
--- a/bta/av/bta_av_aact.cc
+++ b/bta/av/bta_av_aact.cc
@@ -24,6 +24,8 @@
  *
  ******************************************************************************/
 
+#define LOG_TAG "bt_bta_av"
+
 #include "bt_target.h"
 
 #include <base/logging.h>
@@ -38,6 +40,7 @@
 #include "device/include/interop.h"
 #include "l2c_api.h"
 #include "l2cdefs.h"
+#include "osi/include/log.h"
 #include "osi/include/osi.h"
 #include "osi/include/properties.h"
 #include "utl.h"
@@ -84,13 +87,17 @@
 };
 
 /* the call out functions for audio stream */
-const tBTA_AV_CO_FUNCTS bta_av_a2dp_cos = {
-    bta_av_co_audio_init,          bta_av_co_audio_disc_res,
-    bta_av_co_audio_getconfig,     bta_av_co_audio_setconfig,
-    bta_av_co_audio_open,          bta_av_co_audio_close,
-    bta_av_co_audio_start,         bta_av_co_audio_stop,
-    bta_av_co_audio_src_data_path, bta_av_co_audio_delay,
-    bta_av_co_audio_update_mtu};
+const tBTA_AV_CO_FUNCTS bta_av_a2dp_cos = {bta_av_co_audio_init,
+                                           bta_av_co_audio_disc_res,
+                                           bta_av_co_audio_getconfig,
+                                           bta_av_co_audio_setconfig,
+                                           bta_av_co_audio_open,
+                                           bta_av_co_audio_close,
+                                           bta_av_co_audio_start,
+                                           bta_av_co_audio_stop,
+                                           bta_av_co_audio_source_data_path,
+                                           bta_av_co_audio_delay,
+                                           bta_av_co_audio_update_mtu};
 
 /* ssm action functions for audio stream */
 const tBTA_AV_SACT bta_av_a2dp_action[] = {
@@ -197,47 +204,6 @@
     0                          /* AVDT_DELAY_REPORT_CFM_EVT */
 };
 
-static void bta_av_stream0_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data);
-static void bta_av_stream1_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data);
-#if BTA_AV_NUM_STRS > 2
-static void bta_av_stream2_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data);
-#endif
-#if BTA_AV_NUM_STRS > 3
-static void bta_av_stream3_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data);
-#endif
-#if BTA_AV_NUM_STRS > 4
-static void bta_av_stream4_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data);
-#endif
-#if BTA_AV_NUM_STRS > 5
-static void bta_av_stream5_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data);
-#endif
-/* the array of callback functions to receive events from AVDT control channel
- */
-tAVDT_CTRL_CBACK* const bta_av_dt_cback[] = {bta_av_stream0_cback,
-                                             bta_av_stream1_cback
-#if BTA_AV_NUM_STRS > 2
-                                             ,
-                                             bta_av_stream2_cback
-#endif
-#if BTA_AV_NUM_STRS > 3
-                                             ,
-                                             bta_av_stream3_cback
-#endif
-#if BTA_AV_NUM_STRS > 4
-                                             ,
-                                             bta_av_stream4_cback
-#endif
-#if BTA_AV_NUM_STRS > 5
-                                             ,
-                                             bta_av_stream5_cback
-#endif
-};
 /***********************************************
  *
  * Function         bta_get_scb_handle
@@ -366,7 +332,6 @@
  ******************************************************************************/
 static bool bta_av_next_getcap(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   int i;
-  tAVDT_GETCAP_REQ* p_req;
   bool sent_cmd = false;
   uint16_t uuid_int = p_scb->uuid_int;
   uint8_t sep_requested = 0;
@@ -384,14 +349,10 @@
       p_scb->sep_info_idx = i;
 
       /* we got a stream; get its capabilities */
-      if ((p_scb->avdt_version >= AVDT_VERSION_1_3) &&
-          (A2DP_GetAvdtpVersion() >= AVDT_VERSION_1_3)) {
-        p_req = AVDT_GetAllCapReq;
-      } else {
-        p_req = AVDT_GetCapReq;
-      }
-      (*p_req)(p_scb->peer_addr, p_scb->sep_info[i].seid, &p_scb->peer_cap,
-               bta_av_dt_cback[p_scb->hdi]);
+      bool get_all_cap = (p_scb->avdt_version >= AVDT_VERSION_1_3) &&
+                         (A2DP_GetAvdtpVersion() >= AVDT_VERSION_1_3);
+      AVDT_GetCapReq(p_scb->peer_addr, p_scb->hdi, p_scb->sep_info[i].seid,
+                     &p_scb->peer_cap, &bta_av_proc_stream_evt, get_all_cap);
       sent_cmd = true;
       break;
     }
@@ -416,11 +377,15 @@
  * Returns          void
  *
  ******************************************************************************/
-static void bta_av_proc_stream_evt(uint8_t handle, const RawAddress* bd_addr,
-                                   uint8_t event, tAVDT_CTRL* p_data,
-                                   int index) {
+void bta_av_proc_stream_evt(uint8_t handle, const RawAddress& bd_addr,
+                            uint8_t event, tAVDT_CTRL* p_data,
+                            uint8_t scb_index) {
   uint16_t sec_len = 0;
-  tBTA_AV_SCB* p_scb = bta_av_cb.p_scb[index];
+  tBTA_AV_SCB* p_scb = bta_av_cb.p_scb[scb_index];
+
+  APPL_TRACE_EVENT(
+      "%s: peer_address: %s avdt_handle: %d event=0x%x scb_index=%d p_scb=%p",
+      __func__, bd_addr.ToString().c_str(), handle, event, scb_index, p_scb);
 
   if (p_data) {
     if (event == AVDT_SECURITY_IND_EVT) {
@@ -441,64 +406,17 @@
     /* copy event data, bd addr, and handle to event message buffer */
     p_msg->hdr.offset = 0;
 
-    if (bd_addr != NULL) {
-      p_msg->bd_addr = *bd_addr;
-      VLOG(1) << __func__ << ": bd_addr:" << bd_addr;
-    }
+    p_msg->bd_addr = bd_addr;
+    p_msg->scb_index = scb_index;
+    APPL_TRACE_EVENT(LOG_TAG, "%s: stream event bd_addr: %s scb_index: %u",
+                     __func__, p_msg->bd_addr.ToString().c_str(), scb_index);
 
     if (p_data != NULL) {
       memcpy(&p_msg->msg, p_data, sizeof(tAVDT_CTRL));
       /* copy config params to event message buffer */
       switch (event) {
-        case AVDT_RECONFIG_CFM_EVT:
-          if (p_msg->msg.hdr.err_code == 0) {
-            APPL_TRACE_DEBUG(
-                "%s: reconfig cfm event codec info = 0x%06x-%06x-%06x-%02x",
-                __func__,
-                (p_msg->msg.reconfig_cfm.p_cfg->codec_info[0] << 16) +
-                    (p_msg->msg.reconfig_cfm.p_cfg->codec_info[1] << 8) +
-                    p_msg->msg.reconfig_cfm.p_cfg->codec_info[2],
-                (p_msg->msg.reconfig_cfm.p_cfg->codec_info[3] << 16) +
-                    (p_msg->msg.reconfig_cfm.p_cfg->codec_info[4] << 8) +
-                    p_msg->msg.reconfig_cfm.p_cfg->codec_info[5],
-                (p_msg->msg.reconfig_cfm.p_cfg->codec_info[6] << 16) +
-                    (p_msg->msg.reconfig_cfm.p_cfg->codec_info[7] << 8) +
-                    p_msg->msg.reconfig_cfm.p_cfg->codec_info[8],
-                p_msg->msg.reconfig_cfm.p_cfg->codec_info[9]);
-          }
-          break;
-
         case AVDT_CONFIG_IND_EVT:
-          /* We might have 2 SEP signallings(A2DP + VDP) with one peer device on
-           * one L2CAP.
-           * If we already have a signalling connection with the bd_addr and the
-           * streaming
-           * SST is at INIT state, change it to INCOMING state to handle the
-           * signalling
-           * from the 2nd SEP. */
-          if ((bta_av_find_lcb(*bd_addr, BTA_AV_LCB_FIND) != NULL) &&
-              (bta_av_is_scb_init(p_scb))) {
-            bta_av_set_scb_sst_incoming(p_scb);
-
-            /* When ACP_CONNECT_EVT was received, we put first available scb to
-             * incoming state.
-             * Later when we receive AVDT_CONFIG_IND_EVT, we use a new p_scb and
-             * set its state to
-             * incoming which we do it above.
-             * We also have to set the old p_scb state to init to be used later
-             */
-            for (int i = 0; i < BTA_AV_NUM_STRS; i++) {
-              if ((bta_av_cb.p_scb[i]) && (i != index)) {
-                if (bta_av_cb.p_scb[i]->state == BTA_AV_INCOMING_SST) {
-                  bta_av_cb.p_scb[i]->state = BTA_AV_INIT_SST;
-                  bta_av_cb.p_scb[i]->coll_mask = 0;
-                  break;
-                }
-              }
-            }
-          }
-
-          memcpy(&p_msg->cfg, p_data->config_ind.p_cfg, sizeof(tAVDT_CFG));
+          p_msg->cfg = *p_data->config_ind.p_cfg;
           break;
 
         case AVDT_SECURITY_IND_EVT:
@@ -527,8 +445,9 @@
         default:
           break;
       }
-    } else
+    } else {
       p_msg->msg.hdr.err_code = 0;
+    }
 
     /* look up application event */
     if ((p_data == NULL) || (p_data->hdr.err_code == 0)) {
@@ -540,7 +459,7 @@
     p_msg->initiator = false;
     if (event == AVDT_SUSPEND_CFM_EVT) p_msg->initiator = true;
 
-    APPL_TRACE_VERBOSE("%s: hndl:x%x", __func__, p_scb->hndl);
+    APPL_TRACE_VERBOSE("%s: hndl:0x%x", __func__, p_scb->hndl);
     p_msg->hdr.layer_specific = p_scb->hndl;
     p_msg->handle = handle;
     p_msg->avdt_event = event;
@@ -548,7 +467,7 @@
   }
 
   if (p_data) {
-    bta_av_conn_cback(handle, bd_addr, event, p_data);
+    bta_av_conn_cback(handle, bd_addr, event, p_data, scb_index);
   } else {
     APPL_TRACE_ERROR("%s: p_data is null", __func__);
   }
@@ -594,104 +513,6 @@
 
 /*******************************************************************************
  *
- * Function         bta_av_stream0_cback
- *
- * Description      This is the AVDTP callback function for stream events.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_av_stream0_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data) {
-  APPL_TRACE_VERBOSE("%s: avdt_handle: %d event=0x%x", __func__, handle, event);
-  bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 0);
-}
-
-/*******************************************************************************
- *
- * Function         bta_av_stream1_cback
- *
- * Description      This is the AVDTP callback function for stream events.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_av_stream1_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data) {
-  APPL_TRACE_EVENT("%s: avdt_handle: %d event=0x%x", __func__, handle, event);
-  bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 1);
-}
-
-#if BTA_AV_NUM_STRS > 2
-/*******************************************************************************
- *
- * Function         bta_av_stream2_cback
- *
- * Description      This is the AVDTP callback function for stream events.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_av_stream2_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data) {
-  APPL_TRACE_EVENT("%s: avdt_handle: %d event=0x%x", __func__, handle, event);
-  bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 2);
-}
-#endif
-
-#if BTA_AV_NUM_STRS > 3
-/*******************************************************************************
- *
- * Function         bta_av_stream3_cback
- *
- * Description      This is the AVDTP callback function for stream events.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_av_stream3_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data) {
-  APPL_TRACE_EVENT("%s: avdt_handle: %d event=0x%x", __func__, handle, event);
-  bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 3);
-}
-#endif
-
-/*******************************************************************************
- *
- * Function         bta_av_stream4_cback
- *
- * Description      This is the AVDTP callback function for stream events.
- *
- * Returns          void
- *
- ******************************************************************************/
-#if BTA_AV_NUM_STRS > 4
-static void bta_av_stream4_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data) {
-  APPL_TRACE_EVENT("%s: avdt_handle: %d event=0x%x", __func__, handle, event);
-  bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 4);
-}
-#endif
-
-/*******************************************************************************
- *
- * Function         bta_av_stream5_cback
- *
- * Description      This is the AVDTP callback function for stream events.
- *
- * Returns          void
- *
- ******************************************************************************/
-#if BTA_AV_NUM_STRS > 5
-static void bta_av_stream5_cback(uint8_t handle, const RawAddress* bd_addr,
-                                 uint8_t event, tAVDT_CTRL* p_data) {
-  APPL_TRACE_EVENT("%s: avdt_handle: %d event=0x%x", __func__, handle, event);
-  bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 5);
-}
-#endif
-
-/*******************************************************************************
- *
  * Function         bta_av_a2dp_sdp_cback
  *
  * Description      A2DP service discovery callback.
@@ -700,18 +521,32 @@
  *
  ******************************************************************************/
 static void bta_av_a2dp_sdp_cback(bool found, tA2DP_Service* p_service) {
-  tBTA_AV_SCB* p_scb = bta_av_hndl_to_scb(bta_av_cb.handle);
+  APPL_TRACE_DEBUG("%s: found=%s", __func__, (found) ? "true" : "false");
 
+  tBTA_AV_SCB* p_scb = bta_av_hndl_to_scb(bta_av_cb.handle);
   if (p_scb == NULL) {
     APPL_TRACE_ERROR("%s: no scb found for handle(0x%x)", __func__,
                      bta_av_cb.handle);
     return;
   }
 
+  if (!found) {
+    APPL_TRACE_ERROR("%s: peer %s A2DP service discovery failed", __func__,
+                     p_scb->peer_addr.ToString().c_str());
+  }
+  APPL_TRACE_DEBUG("%s: peer %s found=%s", __func__,
+                   p_scb->peer_addr.ToString().c_str(),
+                   (found) ? "true" : "false");
+
   tBTA_AV_SDP_RES* p_msg =
       (tBTA_AV_SDP_RES*)osi_malloc(sizeof(tBTA_AV_SDP_RES));
-  p_msg->hdr.event =
-      (found) ? BTA_AV_SDP_DISC_OK_EVT : BTA_AV_SDP_DISC_FAIL_EVT;
+  if (found) {
+    p_msg->hdr.event = BTA_AV_SDP_DISC_OK_EVT;
+  } else {
+    p_msg->hdr.event = BTA_AV_SDP_DISC_FAIL_EVT;
+    APPL_TRACE_ERROR("%s: BTA_AV_SDP_DISC_FAIL_EVT: peer_addr=%s", __func__,
+                     p_scb->peer_addr.ToString().c_str());
+  }
   if (found && (p_service != NULL))
     p_scb->avdt_version = p_service->avdt_version;
   else
@@ -761,7 +596,8 @@
   tBTA_AV_RS_RES switch_res = BTA_AV_RS_NONE;
   tBTA_AV_API_OPEN* p_buf = &p_scb->q_info.open;
 
-  APPL_TRACE_DEBUG("%s: wait:x%x", __func__, p_scb->wait);
+  APPL_TRACE_DEBUG("%s: peer %s wait:0x%x", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->wait);
   if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_RES_START)
     p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RETRY;
 
@@ -780,6 +616,8 @@
     }
   } else {
     /* report failure on OPEN */
+    APPL_TRACE_ERROR("%s: peer %s role switch failed (wait=0x%x)", __func__,
+                     p_scb->peer_addr.ToString().c_str(), p_scb->wait);
     switch_res = BTA_AV_RS_FAIL;
   }
 
@@ -807,7 +645,7 @@
 void bta_av_role_res(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   bool initiator = false;
 
-  APPL_TRACE_DEBUG("%s: q_tag:%d, wait:x%x, role:x%x", __func__, p_scb->q_tag,
+  APPL_TRACE_DEBUG("%s: q_tag:%d, wait:0x%x, role:0x%x", __func__, p_scb->q_tag,
                    p_scb->wait, p_scb->role);
   if (p_scb->role & BTA_AV_ROLE_START_INT) initiator = true;
 
@@ -863,7 +701,7 @@
     }
   }
 
-  APPL_TRACE_DEBUG("%s: wait:x%x, role:x%x", __func__, p_scb->wait,
+  APPL_TRACE_DEBUG("%s: wait:0x%x, role:0x%x", __func__, p_scb->wait,
                    p_scb->role);
 }
 
@@ -878,7 +716,11 @@
  *
  ******************************************************************************/
 void bta_av_delay_co(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
-  p_scb->p_cos->delay(p_scb->hndl, p_data->str_msg.msg.delay_rpt_cmd.delay);
+  APPL_TRACE_DEBUG("%s: peer %s handle:%d delay:%d", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl,
+                   p_data->str_msg.msg.delay_rpt_cmd.delay);
+  p_scb->p_cos->delay(p_scb->hndl, p_scb->peer_addr,
+                      p_data->str_msg.msg.delay_rpt_cmd.delay);
 }
 
 /*******************************************************************************
@@ -898,7 +740,7 @@
                           ATTR_ID_BT_PROFILE_DESC_LIST};
   uint16_t sdp_uuid = 0; /* UUID for which SDP has to be done */
 
-  APPL_TRACE_DEBUG("%s: use_rc: %d rs:%d, oc:%d", __func__,
+  APPL_TRACE_DEBUG("%s: use_rc: %d switch_res:%d, oc:%d", __func__,
                    p_data->api_open.use_rc, p_data->api_open.switch_res,
                    bta_av_cb.audio_open_cnt);
 
@@ -941,7 +783,7 @@
       break;
   }
 
-  APPL_TRACE_DEBUG("%s: ok_continue: %d wait:x%x, q_tag: %d", __func__,
+  APPL_TRACE_DEBUG("%s: ok_continue: %d wait:0x%x, q_tag: %d", __func__,
                    ok_continue, p_scb->wait, p_scb->q_tag);
   if (!ok_continue) return;
 
@@ -1053,7 +895,7 @@
   p_scb->offload_start_pending = false;
 
   p_scb->skip_sdp = false;
-  if (p_scb->deregistring) {
+  if (p_scb->deregistering) {
     /* remove stream */
     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);
@@ -1094,7 +936,7 @@
 void bta_av_config_ind(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   tBTA_AV_CI_SETCONFIG setconfig;
   tAVDT_SEP_INFO* p_info;
-  tAVDT_CFG* p_evt_cfg = &p_data->str_msg.cfg;
+  const AvdtpSepConfig* p_evt_cfg = &p_data->str_msg.cfg;
   uint8_t psc_mask = (p_evt_cfg->psc_mask | p_scb->cfg.psc_mask);
   uint8_t
       local_sep; /* sep type of local handle on which connection was received */
@@ -1103,7 +945,8 @@
   local_sep = bta_av_get_scb_sep_type(p_scb, p_msg->handle);
   p_scb->avdt_label = p_data->str_msg.msg.hdr.label;
 
-  APPL_TRACE_DEBUG("%s: local_sep = %d", __func__, local_sep);
+  APPL_TRACE_DEBUG("%s: peer %s handle:%d local_sep:%d", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl, local_sep);
   A2DP_DumpCodecInfo(p_evt_cfg->codec_info);
 
   memcpy(p_scb->cfg.codec_info, p_evt_cfg->codec_info, AVDT_CODEC_SIZE);
@@ -1148,13 +991,13 @@
     /*  in case of A2DP SINK this is the first time peer data is being sent to
      * co functions */
     if (local_sep == AVDT_TSEP_SNK) {
-      p_scb->p_cos->setcfg(p_scb->hndl, p_evt_cfg->codec_info, p_info->seid,
-                           p_scb->peer_addr, p_evt_cfg->num_protect,
+      p_scb->p_cos->setcfg(p_scb->hndl, p_scb->peer_addr, p_evt_cfg->codec_info,
+                           p_info->seid, p_evt_cfg->num_protect,
                            p_evt_cfg->protect_info, AVDT_TSEP_SNK,
                            p_msg->handle);
     } else {
-      p_scb->p_cos->setcfg(p_scb->hndl, p_evt_cfg->codec_info, p_info->seid,
-                           p_scb->peer_addr, p_evt_cfg->num_protect,
+      p_scb->p_cos->setcfg(p_scb->hndl, p_scb->peer_addr, p_evt_cfg->codec_info,
+                           p_info->seid, p_evt_cfg->num_protect,
                            p_evt_cfg->protect_info, AVDT_TSEP_SRC,
                            p_msg->handle);
     }
@@ -1183,7 +1026,7 @@
   if (bta_av_cb.conn_lcb) {
     p_rcb = bta_av_get_rcb_by_shdl((uint8_t)(p_scb->hdi + 1));
     if (p_rcb) bta_av_del_rc(p_rcb);
-    AVDT_DisconnectReq(p_scb->peer_addr, bta_av_dt_cback[p_scb->hdi]);
+    AVDT_DisconnectReq(p_scb->peer_addr, &bta_av_proc_stream_evt);
   } else {
     bta_av_ssm_execute(p_scb, BTA_AV_AVDT_DISCONNECT_EVT, NULL);
   }
@@ -1245,7 +1088,8 @@
   /* we like this codec_type. find the sep_idx */
   local_sep = bta_av_get_scb_sep_type(p_scb, avdt_handle);
   bta_av_adjust_seps_idx(p_scb, avdt_handle);
-  APPL_TRACE_DEBUG("%s: sep_idx: %d cur_psc_mask:0x%x", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s handle: sep_idx: %d cur_psc_mask:0x%x",
+                   __func__, p_scb->peer_addr.ToString().c_str(),
                    p_scb->sep_idx, p_scb->cur_psc_mask);
 
   if ((AVDT_TSEP_SNK == local_sep) &&
@@ -1267,7 +1111,7 @@
     p_scb->wait = BTA_AV_WAIT_ACP_CAPS_ON;
     if (p_data->ci_setconfig.recfg_needed)
       p_scb->role |= BTA_AV_ROLE_SUSPEND_OPT;
-    APPL_TRACE_DEBUG("%s: recfg_needed:%d role:x%x num:%d", __func__,
+    APPL_TRACE_DEBUG("%s: recfg_needed:%d role:0x%x num:%d", __func__,
                      p_data->ci_setconfig.recfg_needed, p_scb->role, num);
     /* callout module tells BTA the number of "good" SEPs and their SEIDs.
      * getcap on these SEID */
@@ -1284,7 +1128,7 @@
       /* this is called in A2DP SRC path only, In case of SINK we don't need it
        */
       if (local_sep == AVDT_TSEP_SRC)
-        p_scb->p_cos->disc_res(p_scb->hndl, num, num, 0, p_scb->peer_addr,
+        p_scb->p_cos->disc_res(p_scb->hndl, p_scb->peer_addr, num, num, 0,
                                UUID_SERVCLASS_AUDIO_SOURCE);
     } else {
       /* we do not know the peer device and it is using non-SBC codec
@@ -1326,6 +1170,9 @@
   uint8_t* p;
   uint16_t mtu;
 
+  APPL_TRACE_DEBUG("%s: peer %s handle: %d", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl);
+
   msg.hdr.layer_specific = p_scb->hndl;
   msg.is_up = true;
   msg.peer_addr = p_scb->peer_addr;
@@ -1350,7 +1197,7 @@
   memset(&p_scb->q_info, 0, sizeof(tBTA_AV_Q_INFO));
 
   p_scb->l2c_bufs = 0;
-  p_scb->p_cos->open(p_scb->hndl, mtu);
+  p_scb->p_cos->open(p_scb->hndl, p_scb->peer_addr, mtu);
 
   {
     /* TODO check if other audio channel is open.
@@ -1380,7 +1227,7 @@
       }
     }
 #if (BTA_AR_INCLUDED == TRUE)
-    bta_ar_avdt_conn(BTA_ID_AV, open.bd_addr);
+    bta_ar_avdt_conn(BTA_ID_AV, open.bd_addr, p_scb->hdi);
 #endif
     if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC) {
       open.sep = AVDT_TSEP_SNK;
@@ -1504,6 +1351,8 @@
  *
  ******************************************************************************/
 void bta_av_connect_req(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
+  APPL_TRACE_DEBUG("%s: peer %s coll_mask:0x%x", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->coll_mask);
   p_scb->sdp_discovery_started = false;
   if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) {
     /* SNK initiated L2C connection while SRC was doing SDP.    */
@@ -1515,8 +1364,8 @@
     return;
   }
 
-  AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask,
-                  bta_av_dt_cback[p_scb->hdi]);
+  AVDT_ConnectReq(p_scb->peer_addr, p_scb->hdi, p_scb->sec_mask,
+                  &bta_av_proc_stream_evt);
 }
 
 /*******************************************************************************
@@ -1556,7 +1405,9 @@
   /* our uuid in case we initiate connection */
   uint16_t uuid_int = p_scb->uuid_int;
 
-  APPL_TRACE_DEBUG("%s: initiator UUID 0x%x", __func__, uuid_int);
+  APPL_TRACE_DEBUG("%s: peer %s handle: %d initiator UUID 0x%x", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl, uuid_int);
+
   /* store number of stream endpoints returned */
   p_scb->num_seps = p_data->str_msg.msg.discover_cfm.num_seps;
 
@@ -1574,8 +1425,8 @@
     }
   }
 
-  p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, num_srcs,
-                         p_scb->peer_addr, uuid_int);
+  p_scb->p_cos->disc_res(p_scb->hndl, p_scb->peer_addr, p_scb->num_seps,
+                         num_snks, num_srcs, uuid_int);
   p_scb->num_disc_snks = num_snks;
   p_scb->num_disc_srcs = num_srcs;
 
@@ -1609,6 +1460,9 @@
 void bta_av_disc_res_as_acp(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   uint8_t num_snks = 0, i;
 
+  APPL_TRACE_DEBUG("%s: peer %s handle: %d", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl);
+
   /* store number of stream endpoints returned */
   p_scb->num_seps = p_data->str_msg.msg.discover_cfm.num_seps;
 
@@ -1620,8 +1474,8 @@
       num_snks++;
     }
   }
-  p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, 0,
-                         p_scb->peer_addr, UUID_SERVCLASS_AUDIO_SOURCE);
+  p_scb->p_cos->disc_res(p_scb->hndl, p_scb->peer_addr, p_scb->num_seps,
+                         num_snks, 0, UUID_SERVCLASS_AUDIO_SOURCE);
   p_scb->num_disc_snks = num_snks;
   p_scb->num_disc_srcs = 0;
 
@@ -1651,19 +1505,22 @@
  *
  ******************************************************************************/
 void bta_av_save_caps(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
-  tAVDT_CFG cfg;
+  AvdtpSepConfig cfg;
   tAVDT_SEP_INFO* p_info = &p_scb->sep_info[p_scb->sep_info_idx];
   uint8_t old_wait = p_scb->wait;
   bool getcap_done = false;
 
-  APPL_TRACE_DEBUG("%s: num_seps:%d sep_info_idx:%d wait:x%x", __func__,
-                   p_scb->num_seps, p_scb->sep_info_idx, p_scb->wait);
+  APPL_TRACE_DEBUG(
+      "%s: peer %s handle:%d num_seps:%d sep_info_idx:%d wait:0x%x", __func__,
+      p_scb->peer_addr.ToString().c_str(), p_scb->hndl, p_scb->num_seps,
+      p_scb->sep_info_idx, p_scb->wait);
   A2DP_DumpCodecInfo(p_scb->peer_cap.codec_info);
 
-  memcpy(&cfg, &p_scb->peer_cap, sizeof(tAVDT_CFG));
+  cfg = p_scb->peer_cap;
   /* let application know the capability of the SNK */
-  p_scb->p_cos->getcfg(p_scb->hndl, cfg.codec_info, &p_scb->sep_info_idx,
-                       p_info->seid, &cfg.num_protect, cfg.protect_info);
+  p_scb->p_cos->getcfg(p_scb->hndl, p_scb->peer_addr, cfg.codec_info,
+                       &p_scb->sep_info_idx, p_info->seid, &cfg.num_protect,
+                       cfg.protect_info);
 
   p_scb->sep_info_idx++;
   APPL_TRACE_DEBUG("%s: result: sep_info_idx:%d", __func__,
@@ -1680,7 +1537,7 @@
     getcap_done = true;
 
   if (getcap_done) {
-    APPL_TRACE_DEBUG("%s: getcap_done: num_seps:%d sep_info_idx:%d wait:x%x",
+    APPL_TRACE_DEBUG("%s: getcap_done: num_seps:%d sep_info_idx:%d wait:0x%x",
                      __func__, p_scb->num_seps, p_scb->sep_info_idx,
                      p_scb->wait);
     p_scb->wait &= ~(BTA_AV_WAIT_ACP_CAPS_ON | BTA_AV_WAIT_ACP_CAPS_STARTED);
@@ -1715,9 +1572,12 @@
 void bta_av_cco_close(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
   uint16_t mtu;
 
+  APPL_TRACE_DEBUG("%s: peer %s handle:%d", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl);
+
   mtu = bta_av_chk_mtu(p_scb, BTA_AV_MAX_A2DP_MTU);
 
-  p_scb->p_cos->close(p_scb->hndl);
+  p_scb->p_cos->close(p_scb->hndl, p_scb->peer_addr);
 }
 
 /*******************************************************************************
@@ -1778,7 +1638,7 @@
     bta_av_data.open = open;
     (*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, &bta_av_data);
   } else {
-    AVDT_DisconnectReq(p_scb->peer_addr, bta_av_dt_cback[p_scb->hdi]);
+    AVDT_DisconnectReq(p_scb->peer_addr, &bta_av_proc_stream_evt);
   }
 }
 
@@ -1794,36 +1654,37 @@
  *
  ******************************************************************************/
 void bta_av_getcap_results(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
-  tAVDT_CFG cfg;
-  uint8_t media_type;
+  AvdtpSepConfig cfg = p_scb->cfg;
+  uint8_t media_type = A2DP_GetMediaType(p_scb->peer_cap.codec_info);
   tAVDT_SEP_INFO* p_info = &p_scb->sep_info[p_scb->sep_info_idx];
-  uint16_t uuid_int; /* UUID for which connection was initiatied */
 
-  memcpy(&cfg, &p_scb->cfg, sizeof(tAVDT_CFG));
   cfg.num_codec = 1;
   cfg.num_protect = p_scb->peer_cap.num_protect;
   memcpy(cfg.codec_info, p_scb->peer_cap.codec_info, AVDT_CODEC_SIZE);
   memcpy(cfg.protect_info, p_scb->peer_cap.protect_info, AVDT_PROTECT_SIZE);
-  media_type = A2DP_GetMediaType(p_scb->peer_cap.codec_info);
 
-  APPL_TRACE_DEBUG("%s: num_codec %d", __func__, p_scb->peer_cap.num_codec);
-  APPL_TRACE_DEBUG("%s: media type x%x, x%x", __func__, media_type,
+  APPL_TRACE_DEBUG("%s: peer %s handle:%d num_codec:%d", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl,
+                   p_scb->peer_cap.num_codec);
+  APPL_TRACE_DEBUG("%s: media type 0x%x, 0x%x", __func__, media_type,
                    p_scb->media_type);
   A2DP_DumpCodecInfo(p_scb->cfg.codec_info);
 
   /* if codec present and we get a codec configuration */
   if ((p_scb->peer_cap.num_codec != 0) && (media_type == p_scb->media_type) &&
-      (p_scb->p_cos->getcfg(p_scb->hndl, cfg.codec_info, &p_scb->sep_info_idx,
-                            p_info->seid, &cfg.num_protect,
-                            cfg.protect_info) == A2DP_SUCCESS)) {
+      (p_scb->p_cos->getcfg(
+           p_scb->hndl, p_scb->peer_addr, cfg.codec_info, &p_scb->sep_info_idx,
+           p_info->seid, &cfg.num_protect, cfg.protect_info) == A2DP_SUCCESS)) {
+    /* UUID for which connection was initiatied */
+    uint16_t uuid_int = p_scb->uuid_int;
+
     /* save copy of codec configuration */
-    memcpy(&p_scb->cfg, &cfg, sizeof(tAVDT_CFG));
+    p_scb->cfg = cfg;
 
     APPL_TRACE_DEBUG("%s: result: sep_info_idx=%d", __func__,
                      p_scb->sep_info_idx);
     A2DP_DumpCodecInfo(p_scb->cfg.codec_info);
 
-    uuid_int = p_scb->uuid_int;
     APPL_TRACE_DEBUG("%s: initiator UUID = 0x%x", __func__, uuid_int);
     if (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE)
       bta_av_adjust_seps_idx(p_scb,
@@ -1852,7 +1713,7 @@
 
     /* open the stream */
     AVDT_OpenReq(p_scb->seps[p_scb->sep_idx].av_handle, p_scb->peer_addr,
-                 p_scb->sep_info[p_scb->sep_info_idx].seid, &cfg);
+                 p_scb->hdi, p_scb->sep_info[p_scb->sep_info_idx].seid, &cfg);
   } else {
     /* try the next stream, if any */
     p_scb->sep_info_idx++;
@@ -1897,8 +1758,8 @@
 void bta_av_discover_req(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
   /* send avdtp discover request */
 
-  AVDT_DiscoverReq(p_scb->peer_addr, p_scb->sep_info, BTA_AV_NUM_SEPS,
-                   bta_av_dt_cback[p_scb->hdi]);
+  AVDT_DiscoverReq(p_scb->peer_addr, p_scb->hdi, p_scb->sep_info,
+                   BTA_AV_NUM_SEPS, &bta_av_proc_stream_evt);
 }
 
 /*******************************************************************************
@@ -1931,7 +1792,7 @@
   uint8_t policy = HCI_ENABLE_SNIFF_MODE;
   uint8_t cur_role;
 
-  APPL_TRACE_DEBUG("%s: sco_occupied:%d, role:x%x, started:%d", __func__,
+  APPL_TRACE_DEBUG("%s: sco_occupied:%d, role:0x%x, started:%d", __func__,
                    bta_av_cb.sco_occupied, p_scb->role, p_scb->started);
   if (bta_av_cb.sco_occupied) {
     bta_av_start_failed(p_scb, p_data);
@@ -1963,7 +1824,7 @@
       }
     }
   }
-  APPL_TRACE_DEBUG("%s: started %d role:x%x", __func__, p_scb->started,
+  APPL_TRACE_DEBUG("%s: started %d role:0x%x", __func__, p_scb->started,
                    p_scb->role);
 }
 
@@ -1983,7 +1844,8 @@
   BT_HDR* p_buf;
   uint8_t policy = HCI_ENABLE_SNIFF_MODE;
 
-  APPL_TRACE_ERROR("%s: audio_open_cnt=%d, p_data %p", __func__,
+  APPL_TRACE_ERROR("%s: peer %s handle:%d audio_open_cnt:%d, p_data %p",
+                   __func__, p_scb->peer_addr.ToString().c_str(), p_scb->hndl,
                    bta_av_cb.audio_open_cnt, p_data);
 
   bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
@@ -2008,7 +1870,7 @@
     bta_av_stream_chg(p_scb, false);
     p_scb->co_started = false;
 
-    p_scb->p_cos->stop(p_scb->hndl);
+    p_scb->p_cos->stop(p_scb->hndl, p_scb->peer_addr);
     L2CA_SetFlushTimeout(p_scb->peer_addr, L2CAP_DEFAULT_FLUSH_TO);
   }
 
@@ -2075,7 +1937,7 @@
  *
  ******************************************************************************/
 void bta_av_reconfig(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
-  tAVDT_CFG* p_cfg;
+  AvdtpSepConfig* p_cfg;
   tBTA_AV_API_STOP stop;
   tBTA_AV_API_RCFG* p_rcfg = &p_data->api_reconfig;
 
@@ -2182,7 +2044,7 @@
   } else {
     new_buf = true;
     /* A2DP_list empty, call co_data, dup data to other channels */
-    p_buf = (BT_HDR*)p_scb->p_cos->data(p_scb->cfg.codec_info, &timestamp);
+    p_buf = p_scb->p_cos->data(p_scb->cfg.codec_info, &timestamp);
 
     if (p_buf) {
       /* use the offset area for the time stamp */
@@ -2202,7 +2064,7 @@
 
       /* opt is a bit mask, it could have several options set */
       opt = AVDT_DATA_OPT_NONE;
-      if (p_scb->no_rtp_hdr) {
+      if (p_scb->no_rtp_header) {
         opt |= AVDT_DATA_OPT_NO_RTP;
       }
 
@@ -2264,7 +2126,7 @@
           list_prepend(p_scb->a2dp_list, p_buf);
         } else {
           /* too many buffers in a2dp_list, drop it. */
-          bta_av_co_audio_drop(p_scb->hndl);
+          bta_av_co_audio_drop(p_scb->hndl, p_scb->peer_addr);
           osi_free(p_buf);
         }
       }
@@ -2290,8 +2152,9 @@
   uint8_t policy = HCI_ENABLE_SNIFF_MODE;
   uint8_t cur_role;
 
-  APPL_TRACE_DEBUG("%s: wait:x%x, role:x%x", __func__, p_scb->wait,
-                   p_scb->role);
+  APPL_TRACE_DEBUG("%s: peer %s handle:%d wait:0x%x, role:0x%x", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl,
+                   p_scb->wait, p_scb->role);
 
   p_scb->started = true;
   p_scb->current_codec = bta_av_get_a2dp_current_codec();
@@ -2311,11 +2174,16 @@
   }
   if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_FAILED) {
     /* role switch has failed */
+    APPL_TRACE_ERROR(
+        "%s: peer %s role switch failed: handle:%d wait:0x%x, role:0x%x",
+        __func__, p_scb->peer_addr.ToString().c_str(), p_scb->hndl, p_scb->wait,
+        p_scb->role);
     p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_FAILED;
     p_data = (tBTA_AV_DATA*)&hdr;
     hdr.offset = BTA_AV_RS_FAIL;
   }
-  APPL_TRACE_DEBUG("%s: wait:x%x", __func__, p_scb->wait);
+  APPL_TRACE_DEBUG("%s: peer %s wait:0x%x", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->wait);
 
   if (p_data && (p_data->hdr.offset != BTA_AV_RS_NONE)) {
     p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS;
@@ -2352,8 +2220,9 @@
   }
 
   if (p_scb->wait) {
-    APPL_TRACE_ERROR("%s: wait:x%x q_tag:%d- not started", __func__,
-                     p_scb->wait, p_scb->q_tag);
+    APPL_TRACE_ERROR("%s: peer %s wait:0x%x q_tag:%d not started", __func__,
+                     p_scb->peer_addr.ToString().c_str(), p_scb->wait,
+                     p_scb->q_tag);
     /* Clear first bit of p_scb->wait and not to return from this point else
      * HAL layer gets blocked. And if there is delay in Get Capability response
      * as
@@ -2379,7 +2248,7 @@
     p_scb->co_started = bta_av_cb.audio_open_cnt;
     flush_to = p_bta_av_cfg->p_audio_flush_to[p_scb->co_started - 1];
   } else {
-    flush_to = p_bta_av_cfg->video_flush_to;
+    flush_to = 0;
   }
   L2CA_SetFlushTimeout(p_scb->peer_addr, flush_to);
 
@@ -2417,12 +2286,14 @@
     p_scb->role &= ~BTA_AV_ROLE_AD_ACP;
     p_scb->role &= ~BTA_AV_ROLE_SUSPEND_OPT;
 
-    p_scb->no_rtp_hdr = false;
-    p_scb->p_cos->start(p_scb->hndl, p_scb->cfg.codec_info, &p_scb->no_rtp_hdr);
+    p_scb->no_rtp_header = false;
+    p_scb->p_cos->start(p_scb->hndl, p_scb->peer_addr, p_scb->cfg.codec_info,
+                        &p_scb->no_rtp_header);
     p_scb->co_started = true;
 
-    APPL_TRACE_DEBUG("%s: suspending: %d, role:x%x, init %d", __func__, suspend,
-                     p_scb->role, initiator);
+    APPL_TRACE_DEBUG("%s: peer %s suspending: %d, role:0x%x, init %d", __func__,
+                     p_scb->peer_addr.ToString().c_str(), suspend, p_scb->role,
+                     initiator);
 
     tBTA_AV_START start;
     start.suspending = suspend;
@@ -2439,7 +2310,7 @@
       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 */
-      p_scb->p_cos->stop(p_scb->hndl);
+      p_scb->p_cos->stop(p_scb->hndl, p_scb->peer_addr);
       p_scb->co_started = false;
       stop.flush = false;
       stop.suspend = true;
@@ -2485,9 +2356,9 @@
   uint8_t policy = HCI_ENABLE_SNIFF_MODE;
 
   APPL_TRACE_WARNING(
-      "%s: peer_addr=%s open_status=%d chnl=%d hndl=%d co_started=%d", __func__,
-      p_scb->peer_addr.ToString().c_str(), p_scb->open_status, p_scb->chnl,
-      p_scb->hndl, p_scb->co_started);
+      "%s: peer %s handle:%d open_status:%d chnl:%d co_started:%d", __func__,
+      p_scb->peer_addr.ToString().c_str(), p_scb->hndl, p_scb->open_status,
+      p_scb->chnl, p_scb->co_started);
 
   if ((bta_av_cb.features & BTA_AV_FEAT_MASTER) == 0 ||
       bta_av_cb.audio_open_cnt == 1)
@@ -2523,7 +2394,7 @@
     }
 
     {
-      p_scb->p_cos->close(p_scb->hndl);
+      p_scb->p_cos->close(p_scb->hndl, p_scb->peer_addr);
       data.close.chnl = p_scb->chnl;
       data.close.hndl = p_scb->hndl;
       event = BTA_AV_CLOSE_EVT;
@@ -2545,7 +2416,10 @@
  *
  ******************************************************************************/
 void bta_av_clr_cong(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
-  if (p_scb->co_started) p_scb->cong = false;
+  APPL_TRACE_DEBUG("%s", __func__);
+  if (p_scb->co_started) {
+    p_scb->cong = false;
+  }
 }
 
 /*******************************************************************************
@@ -2562,7 +2436,8 @@
   uint8_t err_code = p_data->str_msg.msg.hdr.err_code;
   uint8_t policy = HCI_ENABLE_SNIFF_MODE;
 
-  APPL_TRACE_DEBUG("%s: audio_open_cnt = %d, err_code = %d", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s handle:%d audio_open_cnt:%d err_code:%d",
+                   __func__, p_scb->peer_addr.ToString().c_str(), p_scb->hndl,
                    bta_av_cb.audio_open_cnt, err_code);
 
   if (!p_scb->started) {
@@ -2621,7 +2496,7 @@
 
     {
       p_scb->co_started = false;
-      p_scb->p_cos->stop(p_scb->hndl);
+      p_scb->p_cos->stop(p_scb->hndl, p_scb->peer_addr);
     }
     L2CA_SetFlushTimeout(p_scb->peer_addr, L2CAP_DEFAULT_FLUSH_TO);
   }
@@ -2647,7 +2522,9 @@
  ******************************************************************************/
 void bta_av_rcfg_str_ok(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   p_scb->l2c_cid = AVDT_GetL2CapChannel(p_scb->avdt_handle);
-  APPL_TRACE_DEBUG("%s: l2c_cid: %d", __func__, p_scb->l2c_cid);
+  APPL_TRACE_DEBUG("%s: peer %s handle:%d l2c_cid:%d", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl,
+                   p_scb->l2c_cid);
 
   if (p_data != NULL) {
     // p_data could be NULL if the reconfig was triggered by the local device
@@ -2657,7 +2534,7 @@
     APPL_TRACE_DEBUG("%s: l2c_cid: 0x%x stream_mtu: %d mtu: %d", __func__,
                      p_scb->l2c_cid, p_scb->stream_mtu, mtu);
     if (mtu == 0 || mtu > p_scb->stream_mtu) mtu = p_scb->stream_mtu;
-    p_scb->p_cos->update_mtu(p_scb->hndl, mtu);
+    p_scb->p_cos->update_mtu(p_scb->hndl, p_scb->peer_addr, mtu);
   }
 
   /* rc listen */
@@ -2710,7 +2587,7 @@
     /* open failed. try again */
     p_scb->num_recfg++;
     if (bta_av_cb.conn_lcb) {
-      AVDT_DisconnectReq(p_scb->peer_addr, bta_av_dt_cback[p_scb->hdi]);
+      AVDT_DisconnectReq(p_scb->peer_addr, &bta_av_proc_stream_evt);
     } else {
       bta_av_connect_req(p_scb, NULL);
     }
@@ -2727,15 +2604,18 @@
  *
  ******************************************************************************/
 void bta_av_rcfg_connect(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
+  APPL_TRACE_DEBUG("%s", __func__);
+
   p_scb->cong = false;
   p_scb->num_recfg++;
   APPL_TRACE_DEBUG("%s: num_recfg: %d", __func__, p_scb->num_recfg);
   if (p_scb->num_recfg > BTA_AV_RECONFIG_RETRY) {
     /* let bta_av_rcfg_failed report fail */
     bta_av_rcfg_failed(p_scb, NULL);
-  } else
-    AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask,
-                    bta_av_dt_cback[p_scb->hdi]);
+  } else {
+    AVDT_ConnectReq(p_scb->peer_addr, p_scb->hdi, p_scb->sec_mask,
+                    &bta_av_proc_stream_evt);
+  }
 }
 
 /*******************************************************************************
@@ -2764,9 +2644,10 @@
     (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, &bta_av_data);
     /* report close event & go to init state */
     bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, NULL);
-  } else
-    AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask,
-                    bta_av_dt_cback[p_scb->hdi]);
+  } else {
+    AVDT_ConnectReq(p_scb->peer_addr, p_scb->hdi, p_scb->sec_mask,
+                    &bta_av_proc_stream_evt);
+  }
 }
 
 /*******************************************************************************
@@ -2782,6 +2663,8 @@
 void bta_av_suspend_cont(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   uint8_t err_code = p_data->str_msg.msg.hdr.err_code;
 
+  APPL_TRACE_DEBUG("%s: err_code=%d", __func__, err_code);
+
   p_scb->started = false;
   p_scb->cong = false;
   if (err_code) {
@@ -2840,9 +2723,10 @@
       if (interop_match_name(INTEROP_DISABLE_AVDTP_RECONFIGURE, remote_name) ||
           interop_match_addr(INTEROP_DISABLE_AVDTP_RECONFIGURE,
                              (const RawAddress*)&p_scb->peer_addr)) {
-        VLOG(1) << __func__ << ": disable AVDTP RECONFIGURE: interop matched "
-                               "name "
-                << remote_name << " address " << p_scb->peer_addr;
+        LOG_INFO(LOG_TAG,
+                 "%s: disable AVDTP RECONFIGURE: interop matched "
+                 "name %s address %s",
+                 __func__, remote_name, p_scb->peer_addr.ToString().c_str());
         disable_avdtp_reconfigure = true;
       }
     }
@@ -2883,15 +2767,17 @@
  *
  ******************************************************************************/
 void bta_av_rcfg_open(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
-  APPL_TRACE_DEBUG("%s: num_disc_snks = %d", __func__, p_scb->num_disc_snks);
+  APPL_TRACE_DEBUG("%s: peer %s handle:%d num_disc_snks:%d", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->hndl,
+                   p_scb->num_disc_snks);
 
   if (p_scb->num_disc_snks == 0) {
     /* Need to update call-out module so that it will be ready for discover */
-    p_scb->p_cos->stop(p_scb->hndl);
+    p_scb->p_cos->stop(p_scb->hndl, p_scb->peer_addr);
 
     /* send avdtp discover request */
-    AVDT_DiscoverReq(p_scb->peer_addr, p_scb->sep_info, BTA_AV_NUM_SEPS,
-                     bta_av_dt_cback[p_scb->hdi]);
+    AVDT_DiscoverReq(p_scb->peer_addr, p_scb->hdi, p_scb->sep_info,
+                     BTA_AV_NUM_SEPS, &bta_av_proc_stream_evt);
   } else {
     APPL_TRACE_DEBUG("%s: calling AVDT_OpenReq()", __func__);
     A2DP_DumpCodecInfo(p_scb->cfg.codec_info);
@@ -2902,7 +2788,7 @@
 
     /* open the stream with the new config */
     p_scb->sep_info_idx = p_scb->rcfg_idx;
-    AVDT_OpenReq(p_scb->avdt_handle, p_scb->peer_addr,
+    AVDT_OpenReq(p_scb->avdt_handle, p_scb->peer_addr, p_scb->hdi,
                  p_scb->sep_info[p_scb->sep_info_idx].seid, &p_scb->cfg);
   }
 }
@@ -2934,25 +2820,23 @@
  ******************************************************************************/
 void bta_av_chk_2nd_start(tBTA_AV_SCB* p_scb,
                           UNUSED_ATTR tBTA_AV_DATA* p_data) {
-  tBTA_AV_SCB* p_scbi;
-  int i;
-  bool new_started = false;
-
-  if ((p_scb->chnl == BTA_AV_CHNL_AUDIO) && (bta_av_cb.audio_open_cnt >= 2)) {
-    /* more than one audio channel is connected */
+  if ((p_scb->chnl == BTA_AV_CHNL_AUDIO) && (bta_av_cb.audio_open_cnt >= 2) &&
+      (((p_scb->role & BTA_AV_ROLE_AD_ACP) == 0) ||  // Outgoing connection or
+       (bta_av_cb.features & BTA_AV_FEAT_ACP_START))) {  // Auto-starting option
+    // More than one audio channel is connected.
     if (!(p_scb->role & BTA_AV_ROLE_SUSPEND_OPT)) {
-      /* this channel does not need to be reconfigured.
-       * if there is other channel streaming, start the stream now */
-      for (i = 0; i < BTA_AV_NUM_STRS; i++) {
-        p_scbi = bta_av_cb.p_scb[i];
+      // This channel does not need to be reconfigured.
+      // If there is other channel streaming, start the stream now.
+      bool new_started = false;
+      for (int i = 0; i < BTA_AV_NUM_STRS; i++) {
+        tBTA_AV_SCB* p_scbi = bta_av_cb.p_scb[i];
         if (p_scbi && p_scbi->chnl == BTA_AV_CHNL_AUDIO && p_scbi->co_started) {
           if (!new_started) {
-            /* start the new stream */
+            // Start the new stream
             new_started = true;
             bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL);
           }
-          /* may need to update the flush timeout of this already started stream
-           */
+          // May need to update the flush timeout of this already started stream
           if (p_scbi->co_started != bta_av_cb.audio_open_cnt) {
             p_scbi->co_started = bta_av_cb.audio_open_cnt;
             L2CA_SetFlushTimeout(
@@ -2975,7 +2859,7 @@
  *
  ******************************************************************************/
 void bta_av_open_rc(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
-  APPL_TRACE_DEBUG("%s: use_rc: %d, wait: x%x role:x%x", __func__,
+  APPL_TRACE_DEBUG("%s: use_rc: %d, wait: 0x%x role: 0x%x", __func__,
                    p_scb->use_rc, p_scb->wait, p_scb->role);
   if ((p_scb->wait & BTA_AV_WAIT_ROLE_SW_BITS) &&
       (p_scb->q_tag == BTA_AV_Q_TAG_START)) {
@@ -3043,6 +2927,9 @@
 void bta_av_open_at_inc(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   memcpy(&(p_scb->open_api), &(p_data->api_open), sizeof(tBTA_AV_API_OPEN));
 
+  APPL_TRACE_DEBUG("%s: peer %s coll_mask:0x%x", __func__,
+                   p_scb->peer_addr.ToString().c_str(), p_scb->coll_mask);
+
   if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) {
     p_scb->coll_mask |= BTA_AV_COLL_API_CALLED;
 
diff --git a/bta/av/bta_av_act.cc b/bta/av/bta_av_act.cc
index 5dcb95b..099731e 100644
--- a/bta/av/bta_av_act.cc
+++ b/bta/av/bta_av_act.cc
@@ -109,7 +109,7 @@
         p_scb = bta_av_cb.p_scb[p_rcb->shdl - 1];
       }
       if (p_scb) {
-        APPL_TRACE_DEBUG("bta_av_del_rc shdl:%d, srch:%d rc_handle:%d",
+        APPL_TRACE_DEBUG("%s: shdl:%d, srch:%d rc_handle:%d", __func__,
                          p_rcb->shdl, p_scb->rc_handle, p_rcb->handle);
         if (p_scb->rc_handle == p_rcb->handle)
           p_scb->rc_handle = BTA_AV_RC_HANDLE_NONE;
@@ -120,10 +120,9 @@
       }
     }
 
-    APPL_TRACE_EVENT(
-        "bta_av_del_rc  handle: %d status=0x%x, rc_acp_handle:%d, idx:%d",
-        p_rcb->handle, p_rcb->status, bta_av_cb.rc_acp_handle,
-        bta_av_cb.rc_acp_idx);
+    APPL_TRACE_EVENT("%s: handle: %d status=0x%x, rc_acp_handle:%d, idx:%d",
+                     __func__, p_rcb->handle, p_rcb->status,
+                     bta_av_cb.rc_acp_handle, bta_av_cb.rc_acp_idx);
     rc_handle = p_rcb->handle;
     if (!(p_rcb->status & BTA_AV_RC_CONN_MASK) ||
         ((p_rcb->status & BTA_AV_RC_ROLE_MASK) == BTA_AV_RC_ROLE_INT)) {
@@ -137,8 +136,9 @@
     if (rc_handle == bta_av_cb.rc_acp_handle)
       bta_av_cb.rc_acp_handle = BTA_AV_RC_HANDLE_NONE;
     APPL_TRACE_EVENT(
-        "end del_rc handle: %d status=0x%x, rc_acp_handle:%d, lidx:%d",
-        p_rcb->handle, p_rcb->status, bta_av_cb.rc_acp_handle, p_rcb->lidx);
+        "%s: end del_rc handle: %d status=0x%x, rc_acp_handle:%d, lidx:%d",
+        __func__, p_rcb->handle, p_rcb->status, bta_av_cb.rc_acp_handle,
+        p_rcb->lidx);
   }
 }
 
@@ -207,7 +207,7 @@
                                  const RawAddress* peer_addr) {
   uint16_t msg_event = 0;
 
-  APPL_TRACE_EVENT("%s handle: %d event=0x%x", __func__, handle, event);
+  APPL_TRACE_EVENT("%s: handle: %d event=0x%x", __func__, handle, event);
   if (event == AVRC_OPEN_IND_EVT) {
     /* save handle of opened connection
     bta_av_cb.rc_handle = handle;*/
@@ -245,7 +245,7 @@
   uint8_t* p_data_src = NULL;
   uint16_t data_len = 0;
 
-  APPL_TRACE_DEBUG("%s handle: %u opcode=0x%x", __func__, handle, opcode);
+  APPL_TRACE_DEBUG("%s: handle: %u opcode=0x%x", __func__, handle, opcode);
 
   /* Copy avrc packet into BTA message buffer (for sending to BTA state machine)
    */
@@ -317,7 +317,7 @@
   } else {
     p_rcb = bta_av_get_rcb_by_shdl(shdl);
     if (p_rcb != NULL) {
-      APPL_TRACE_ERROR("bta_av_rc_create ACP handle exist for shdl:%d", shdl);
+      APPL_TRACE_ERROR("%s: ACP handle exist for shdl:%d", __func__, shdl);
       return p_rcb->handle;
     }
   }
@@ -338,7 +338,7 @@
   p_rcb = &p_cb->rcb[i];
 
   if (p_rcb->handle != BTA_AV_RC_HANDLE_NONE) {
-    APPL_TRACE_ERROR("bta_av_rc_create found duplicated handle:%d", rc_handle);
+    APPL_TRACE_ERROR("%s: found duplicated handle:%d", __func__, rc_handle);
   }
 
   p_rcb->handle = rc_handle;
@@ -350,12 +350,12 @@
     /* this LIDX is reserved for the AVRCP ACP connection */
     p_cb->rc_acp_handle = p_rcb->handle;
     p_cb->rc_acp_idx = (i + 1);
-    APPL_TRACE_DEBUG("rc_acp_handle:%d idx:%d", p_cb->rc_acp_handle,
-                     p_cb->rc_acp_idx);
+    APPL_TRACE_DEBUG("%s: rc_acp_handle:%d idx:%d", __func__,
+                     p_cb->rc_acp_handle, p_cb->rc_acp_idx);
   }
   APPL_TRACE_DEBUG(
-      "create %d, role: %d, shdl:%d, rc_handle:%d, lidx:%d, status:0x%x", i,
-      role, shdl, p_rcb->handle, lidx, p_rcb->status);
+      "%s: create %d, role: %d, shdl:%d, rc_handle:%d, lidx:%d, status:0x%x",
+      __func__, i, role, shdl, p_rcb->handle, lidx, p_rcb->status);
 
   return rc_handle;
 }
@@ -441,13 +441,15 @@
   uint8_t mask;
   tBTA_AV_LCB* p_lcb = NULL;
 
+  APPL_TRACE_DEBUG("%s: address: %s op:%d", __func__, addr.ToString().c_str(),
+                   op);
   for (xx = 0; xx < BTA_AV_NUM_LINKS; xx++) {
     mask = 1 << xx; /* the used mask for this lcb */
     if ((mask & p_cb->conn_lcb) && p_cb->lcb[xx].addr == addr) {
       p_lcb = &p_cb->lcb[xx];
       if (op == BTA_AV_LCB_FREE) {
         p_cb->conn_lcb &= ~mask; /* clear the connect mask */
-        APPL_TRACE_DEBUG("conn_lcb: 0x%x", p_cb->conn_lcb);
+        APPL_TRACE_DEBUG("%s: conn_lcb: 0x%x", __func__, p_cb->conn_lcb);
       }
       break;
     }
@@ -479,10 +481,10 @@
     p_scb = p_cb->p_scb[i];
     if (p_scb && p_scb->peer_addr == p_data->rc_conn_chg.peer_addr) {
       p_scb->rc_handle = p_data->rc_conn_chg.handle;
-      APPL_TRACE_DEBUG("bta_av_rc_opened shdl:%d, srch %d", i + 1,
+      APPL_TRACE_DEBUG("%s: shdl:%d, srch %d", __func__, i + 1,
                        p_scb->rc_handle);
       shdl = i + 1;
-      LOG_INFO(LOG_TAG, "%s allow incoming AVRCP connections:%d", __func__,
+      LOG_INFO(LOG_TAG, "%s: allow incoming AVRCP connections:%d", __func__,
                p_scb->use_rc);
       alarm_cancel(p_scb->avrc_ct_timer);
       disc = p_scb->hndl;
@@ -492,11 +494,11 @@
 
   i = p_data->rc_conn_chg.handle;
   if (p_cb->rcb[i].handle == BTA_AV_RC_HANDLE_NONE) {
-    APPL_TRACE_ERROR("not a valid handle:%d any more", i);
+    APPL_TRACE_ERROR("%s: not a valid handle:%d any more", __func__, i);
     return;
   }
 
-  APPL_TRACE_DEBUG("%s local features %d peer features %d", __func__,
+  APPL_TRACE_DEBUG("%s: local features %d peer features %d", __func__,
                    p_cb->features, p_cb->rcb[i].peer_features);
 
   /* listen to browsing channel when the connection is open,
@@ -515,14 +517,14 @@
       p_cb->rcb[i].lidx = tmp;
       p_cb->rc_acp_handle = p_rcb->handle;
       p_cb->rc_acp_idx = (p_rcb - p_cb->rcb) + 1;
-      APPL_TRACE_DEBUG("switching RCB rc_acp_handle:%d idx:%d",
+      APPL_TRACE_DEBUG("%s: switching RCB rc_acp_handle:%d idx:%d", __func__,
                        p_cb->rc_acp_handle, p_cb->rc_acp_idx);
     }
   }
 
   p_cb->rcb[i].shdl = shdl;
   rc_open.rc_handle = i;
-  APPL_TRACE_ERROR("bta_av_rc_opened rcb[%d] shdl:%d lidx:%d/%d", i, shdl,
+  APPL_TRACE_ERROR("%s: rcb[%d] shdl:%d lidx:%d/%d", __func__, i, shdl,
                    p_cb->rcb[i].lidx, p_cb->lcb[BTA_AV_NUM_LINKS].lidx);
   p_cb->rcb[i].status |= BTA_AV_RC_CONN_MASK;
 
@@ -531,19 +533,19 @@
      * update the index to the extra LCB */
     p_lcb = &p_cb->lcb[BTA_AV_NUM_LINKS];
     p_lcb->addr = p_data->rc_conn_chg.peer_addr;
-    VLOG(1) << "rc_only bd_addr:" << p_lcb->addr;
     p_lcb->lidx = BTA_AV_NUM_LINKS + 1;
     p_cb->rcb[i].lidx = p_lcb->lidx;
     p_lcb->conn_msk = 1;
-    APPL_TRACE_ERROR("rcb[%d].lidx=%d, lcb.conn_msk=x%x", i, p_cb->rcb[i].lidx,
-                     p_lcb->conn_msk);
+    APPL_TRACE_ERROR("%s: bd_addr: %s rcb[%d].lidx=%d, lcb.conn_msk=x%x",
+                     __func__, p_lcb->addr.ToString().c_str(), i,
+                     p_cb->rcb[i].lidx, p_lcb->conn_msk);
     disc = p_data->rc_conn_chg.handle | BTA_AV_CHNL_MSK;
   }
 
   rc_open.peer_addr = p_data->rc_conn_chg.peer_addr;
   rc_open.peer_features = p_cb->rcb[i].peer_features;
   rc_open.status = BTA_AV_SUCCESS;
-  APPL_TRACE_DEBUG("%s local features:x%x peer_features:x%x", __func__,
+  APPL_TRACE_DEBUG("%s: local features:x%x peer_features:x%x", __func__,
                    p_cb->features, rc_open.peer_features);
   if (rc_open.peer_features == 0) {
     /* we have not done SDP on peer RC capabilities.
@@ -566,7 +568,7 @@
   if ((p_cb->features & BTA_AV_FEAT_BROWSE) &&
       (rc_open.peer_features & BTA_AV_FEAT_BROWSE) &&
       ((p_cb->rcb[i].status & BTA_AV_RC_ROLE_MASK) == BTA_AV_RC_ROLE_INT)) {
-    APPL_TRACE_DEBUG("%s opening AVRC Browse channel", __func__);
+    APPL_TRACE_DEBUG("%s: opening AVRC Browse channel", __func__);
     AVRC_OpenBrowse(p_data->rc_conn_chg.handle, AVCT_INT);
   }
 }
@@ -751,21 +753,22 @@
   if ((AVRC_MIN_META_CMD_LEN + p_vendor->vendor_len) > AVRC_META_CMD_BUF_SIZE) {
     /* reject it */
     p_rc_rsp->rsp.status = AVRC_STS_BAD_PARAM;
-    APPL_TRACE_ERROR("%s Invalid meta-command length: %d", __func__,
+    APPL_TRACE_ERROR("%s: Invalid meta-command length: %d", __func__,
                      p_vendor->vendor_len);
     return 0;
   }
 
   /* Metadata messages only use PANEL sub-unit type */
   if (p_vendor->hdr.subunit_type != AVRC_SUB_PANEL) {
-    APPL_TRACE_DEBUG("SUBUNIT must be PANEL");
+    APPL_TRACE_DEBUG("%s: SUBUNIT must be PANEL", __func__);
     /* reject it */
     evt = 0;
     p_vendor->hdr.ctype = AVRC_RSP_NOT_IMPL;
     p_vendor->vendor_len = 0;
     p_rc_rsp->rsp.status = AVRC_STS_BAD_PARAM;
   } else if (!AVRC_IsValidAvcType(pdu, p_vendor->hdr.ctype)) {
-    APPL_TRACE_DEBUG("Invalid pdu/ctype: 0x%x, %d", pdu, p_vendor->hdr.ctype);
+    APPL_TRACE_DEBUG("%s: Invalid pdu/ctype: 0x%x, %d", __func__, pdu,
+                     p_vendor->hdr.ctype);
     /* reject invalid message without reporting to app */
     evt = 0;
     p_rc_rsp->rsp.status = AVRC_STS_BAD_CMD;
@@ -794,7 +797,7 @@
             memcpy(p_rc_rsp->get_caps.param.event_id,
                    p_bta_av_cfg->p_meta_evt_ids, p_bta_av_cfg->num_evt_ids);
           } else {
-            APPL_TRACE_DEBUG("Invalid capability ID: 0x%x", u8);
+            APPL_TRACE_DEBUG("%s: Invalid capability ID: 0x%x", __func__, u8);
             /* reject - unknown capability ID */
             p_rc_rsp->get_caps.status = AVRC_STS_BAD_PARAM;
           }
@@ -834,7 +837,7 @@
   rc_rsp.rsp.status = BTA_AV_STS_NO_RSP;
 
   if (NULL == p_data) {
-    APPL_TRACE_ERROR("Message from peer with no data in %s", __func__);
+    APPL_TRACE_ERROR("%s: Message from peer with no data", __func__);
     return;
   }
 
@@ -865,7 +868,7 @@
             bta_av_op_supported(p_data->rc_msg.msg.pass.op_id, is_inquiry);
       }
 
-      APPL_TRACE_DEBUG("ctype %d", p_data->rc_msg.msg.hdr.ctype)
+      APPL_TRACE_DEBUG("%s: ctype %d", __func__, p_data->rc_msg.msg.hdr.ctype)
 
       /* send response */
       if (p_data->rc_msg.msg.hdr.ctype != AVRC_RSP_INTERIM)
@@ -899,7 +902,7 @@
           (p_data->rc_msg.msg.pass.pass_len > 0)) {
         av.remote_rsp.p_data =
             (uint8_t*)osi_malloc(p_data->rc_msg.msg.pass.pass_len);
-        APPL_TRACE_DEBUG("Vendor Unique data len = %d",
+        APPL_TRACE_DEBUG("%s: Vendor Unique data len = %d", __func__,
                          p_data->rc_msg.msg.pass.pass_len);
         memcpy(av.remote_rsp.p_data, p_data->rc_msg.msg.pass.p_pass_data,
                p_data->rc_msg.msg.pass.pass_len);
@@ -1002,7 +1005,7 @@
   if (handle < BTA_AV_NUM_RCB) {
     p_rcb = &p_cb->rcb[handle];
 
-    APPL_TRACE_DEBUG("%s handle: %d, status=0x%x", __func__, p_rcb->handle,
+    APPL_TRACE_DEBUG("%s: handle: %d, status=0x%x", __func__, p_rcb->handle,
                      p_rcb->status);
     if (p_rcb->handle != BTA_AV_RC_HANDLE_NONE) {
       if (p_rcb->shdl) {
@@ -1030,7 +1033,7 @@
  *
  ******************************************************************************/
 void bta_av_rc_browse_close(tBTA_AV_CB* p_cb, tBTA_AV_DATA* p_data) {
-  APPL_TRACE_WARNING("%s empty placeholder does nothing!", __func__);
+  APPL_TRACE_WARNING("%s: empty placeholder does nothing!", __func__);
 }
 
 /*******************************************************************************
@@ -1063,54 +1066,19 @@
  *
  ******************************************************************************/
 void bta_av_stream_chg(tBTA_AV_SCB* p_scb, bool started) {
-  uint8_t started_msk;
-  int i;
-  uint8_t* p_streams;
-  bool no_streams = false;
-  tBTA_AV_SCB* p_scbi;
+  uint8_t started_msk = BTA_AV_HNDL_TO_MSK(p_scb->hdi);
 
-  started_msk = BTA_AV_HNDL_TO_MSK(p_scb->hdi);
-  APPL_TRACE_DEBUG("bta_av_stream_chg started:%d started_msk:x%x chnl:x%x",
-                   started, started_msk, p_scb->chnl);
-  if (BTA_AV_CHNL_AUDIO == p_scb->chnl)
-    p_streams = &bta_av_cb.audio_streams;
-  else
-    p_streams = &bta_av_cb.video_streams;
+  APPL_TRACE_DEBUG("%s: started:%d started_msk:x%x", __func__, started,
+                   started_msk);
 
   if (started) {
+    bta_av_cb.audio_streams |= started_msk;
     /* Let L2CAP know this channel is processed with high priority */
     L2CA_SetAclPriority(p_scb->peer_addr, L2CAP_PRIORITY_HIGH);
-    (*p_streams) |= started_msk;
   } else {
-    (*p_streams) &= ~started_msk;
-  }
-
-  if (!started) {
-    i = 0;
-    if (BTA_AV_CHNL_AUDIO == p_scb->chnl) {
-      if (bta_av_cb.video_streams == 0) no_streams = true;
-    } else {
-      no_streams = true;
-      if (bta_av_cb.audio_streams) {
-        for (; i < BTA_AV_NUM_STRS; i++) {
-          p_scbi = bta_av_cb.p_scb[i];
-          /* scb is used and started */
-          if (p_scbi && (bta_av_cb.audio_streams & BTA_AV_HNDL_TO_MSK(i)) &&
-              p_scbi->peer_addr == p_scb->peer_addr) {
-            no_streams = false;
-            break;
-          }
-        }
-      }
-    }
-
-    APPL_TRACE_DEBUG("no_streams:%d i:%d, audio_streams:x%x, video_streams:x%x",
-                     no_streams, i, bta_av_cb.audio_streams,
-                     bta_av_cb.video_streams);
-    if (no_streams) {
-      /* Let L2CAP know this channel is processed with low priority */
-      L2CA_SetAclPriority(p_scb->peer_addr, L2CAP_PRIORITY_NORMAL);
-    }
+    bta_av_cb.audio_streams &= ~started_msk;
+    /* Let L2CAP know this channel is processed with low priority */
+    L2CA_SetAclPriority(p_scb->peer_addr, L2CAP_PRIORITY_NORMAL);
   }
 }
 
@@ -1154,39 +1122,35 @@
           if (bta_av_cb.rcb[i].lidx == p_lcb->lidx) {
             bta_av_cb.rcb[i].shdl = index + 1;
             APPL_TRACE_DEBUG(
-                "conn_chg up[%d]: %d, status=0x%x, shdl:%d, lidx:%d", i,
-                bta_av_cb.rcb[i].handle, bta_av_cb.rcb[i].status,
+                "%s: conn_chg up[%d]: %d, status=0x%x, shdl:%d, lidx:%d",
+                __func__, i, bta_av_cb.rcb[i].handle, bta_av_cb.rcb[i].status,
                 bta_av_cb.rcb[i].shdl, bta_av_cb.rcb[i].lidx);
             break;
           }
         }
       }
-      if (p_scb->chnl == BTA_AV_CHNL_AUDIO) {
-        old_msk = p_cb->conn_audio;
-        p_cb->conn_audio |= mask;
-      } else {
-        old_msk = p_cb->conn_video;
-        p_cb->conn_video |= mask;
-      }
+      old_msk = p_cb->conn_audio;
+      p_cb->conn_audio |= mask;
 
       if ((old_msk & mask) == 0) {
         /* increase the audio open count, if not set yet */
         bta_av_cb.audio_open_cnt++;
       }
 
-      APPL_TRACE_DEBUG("rc_acp_handle:%d rc_acp_idx:%d", p_cb->rc_acp_handle,
-                       p_cb->rc_acp_idx);
+      APPL_TRACE_DEBUG("%s: rc_acp_handle:%d rc_acp_idx:%d", __func__,
+                       p_cb->rc_acp_handle, p_cb->rc_acp_idx);
       /* check if the AVRCP ACP channel is already connected */
       if (p_lcb && p_cb->rc_acp_handle != BTA_AV_RC_HANDLE_NONE &&
           p_cb->rc_acp_idx) {
         p_lcb_rc = &p_cb->lcb[BTA_AV_NUM_LINKS];
         APPL_TRACE_DEBUG(
-            "rc_acp is connected && conn_chg on same addr "
+            "%s: rc_acp is connected && conn_chg on same addr "
             "p_lcb_rc->conn_msk:x%x",
-            p_lcb_rc->conn_msk);
+            __func__, p_lcb_rc->conn_msk);
         /* check if the RC is connected to the scb addr */
-        VLOG(1) << "p_lcb_rc->addr: " << p_lcb_rc->addr
-                << " conn_chg.peer_addr:" << p_data->conn_chg.peer_addr;
+        LOG_INFO(LOG_TAG, "%s: p_lcb_rc->addr: %s conn_chg.peer_addr: %s",
+                 __func__, p_lcb_rc->addr.ToString().c_str(),
+                 p_data->conn_chg.peer_addr.ToString().c_str());
 
         if (p_lcb_rc->conn_msk &&
             p_lcb_rc->addr == p_data->conn_chg.peer_addr) {
@@ -1197,23 +1161,25 @@
           p_scb->rc_handle = p_cb->rc_acp_handle;
           p_rcb = &p_cb->rcb[p_cb->rc_acp_idx - 1];
           p_rcb->shdl = bta_av_get_shdl(p_scb);
-          APPL_TRACE_DEBUG("update rc_acp shdl:%d/%d srch:%d", index + 1,
-                           p_rcb->shdl, p_scb->rc_handle);
+          APPL_TRACE_DEBUG("%s: update rc_acp shdl:%d/%d srch:%d", index + 1,
+                           __func__, p_rcb->shdl, p_scb->rc_handle);
 
           p_rcb2 = bta_av_get_rcb_by_shdl(p_rcb->shdl);
           if (p_rcb2) {
             /* found the RCB that was created to associated with this SCB */
             p_cb->rc_acp_handle = p_rcb2->handle;
             p_cb->rc_acp_idx = (p_rcb2 - p_cb->rcb) + 1;
-            APPL_TRACE_DEBUG("new rc_acp_handle:%d, idx:%d",
+            APPL_TRACE_DEBUG("%s: new rc_acp_handle:%d, idx:%d", __func__,
                              p_cb->rc_acp_handle, p_cb->rc_acp_idx);
             p_rcb2->lidx = (BTA_AV_NUM_LINKS + 1);
-            APPL_TRACE_DEBUG("rc2 handle:%d lidx:%d/%d", p_rcb2->handle,
-                             p_rcb2->lidx, p_cb->lcb[p_rcb2->lidx - 1].lidx);
+            APPL_TRACE_DEBUG("%s: rc2 handle:%d lidx:%d/%d", __func__,
+                             p_rcb2->handle, p_rcb2->lidx,
+                             p_cb->lcb[p_rcb2->lidx - 1].lidx);
           }
           p_rcb->lidx = p_lcb->lidx;
-          APPL_TRACE_DEBUG("rc handle:%d lidx:%d/%d", p_rcb->handle,
-                           p_rcb->lidx, p_cb->lcb[p_rcb->lidx - 1].lidx);
+          APPL_TRACE_DEBUG("%s: rc handle:%d lidx:%d/%d", __func__,
+                           p_rcb->handle, p_rcb->lidx,
+                           p_cb->lcb[p_rcb->lidx - 1].lidx);
         }
       }
     }
@@ -1225,7 +1191,6 @@
 
     /* clear the conned mask for this channel */
     p_cb->conn_audio &= ~mask;
-    p_cb->conn_video &= ~mask;
     if (p_scb) {
       /* the stream is closed.
        * clear the peer address, so it would not mess up the AVRCP for the next
@@ -1245,11 +1210,12 @@
       }
     }
 
-    APPL_TRACE_DEBUG("bta_av_conn_chg shdl:%d", index + 1);
+    APPL_TRACE_DEBUG("%s: shdl:%d", __func__, index + 1);
     for (i = 0; i < BTA_AV_NUM_RCB; i++) {
-      APPL_TRACE_DEBUG("conn_chg dn[%d]: %d, status=0x%x, shdl:%d, lidx:%d", i,
-                       bta_av_cb.rcb[i].handle, bta_av_cb.rcb[i].status,
-                       bta_av_cb.rcb[i].shdl, bta_av_cb.rcb[i].lidx);
+      APPL_TRACE_DEBUG("%s: conn_chg dn[%d]: %d, status=0x%x, shdl:%d, lidx:%d",
+                       __func__, i, bta_av_cb.rcb[i].handle,
+                       bta_av_cb.rcb[i].status, bta_av_cb.rcb[i].shdl,
+                       bta_av_cb.rcb[i].lidx);
       if (bta_av_cb.rcb[i].shdl == index + 1) {
         bta_av_del_rc(&bta_av_cb.rcb[i]);
         /* since the connection is already down and info was removed, clean
@@ -1259,7 +1225,7 @@
       }
     }
 
-    if (p_cb->conn_audio == 0 && p_cb->conn_video == 0) {
+    if (p_cb->conn_audio == 0) {
       /* if both channels are not connected,
        * close all RC channels */
       bta_av_close_all_rc(p_cb);
@@ -1272,10 +1238,10 @@
   }
 
   APPL_TRACE_DEBUG(
-      "bta_av_conn_chg audio:%x video:%x up:%d conn_msk:0x%x chk_restore:%d "
+      "%s: audio:%x up:%d conn_msk:0x%x chk_restore:%d "
       "audio_open_cnt:%d",
-      p_cb->conn_audio, p_cb->conn_video, p_data->conn_chg.is_up, conn_msk,
-      chk_restore, p_cb->audio_open_cnt);
+      __func__, p_cb->conn_audio, p_data->conn_chg.is_up, conn_msk, chk_restore,
+      p_cb->audio_open_cnt);
 
   if (chk_restore) {
     if (p_cb->audio_open_cnt == 1) {
@@ -1368,14 +1334,14 @@
   uint8_t mask;
   tBTA_AV_LCB* p_lcb = NULL;
 
-  APPL_TRACE_DEBUG("bta_av_sig_chg event: %d", event);
+  APPL_TRACE_DEBUG("%s: event: %d", __func__, event);
   if (event == AVDT_CONNECT_IND_EVT) {
     p_lcb = bta_av_find_lcb(p_data->str_msg.bd_addr, BTA_AV_LCB_FIND);
     if (!p_lcb) {
       /* if the address does not have an LCB yet, alloc one */
       for (xx = 0; xx < BTA_AV_NUM_LINKS; xx++) {
         mask = 1 << xx;
-        APPL_TRACE_DEBUG("conn_lcb: 0x%x", p_cb->conn_lcb);
+        APPL_TRACE_DEBUG("%s: conn_lcb: 0x%x", __func__, p_cb->conn_lcb);
         /* look for a p_lcb with its p_scb registered */
         if ((!(mask & p_cb->conn_lcb)) && (p_cb->p_scb[xx] != NULL)) {
           p_lcb = &p_cb->lcb[xx];
@@ -1388,10 +1354,11 @@
           }
           /* this entry is not used yet. */
           p_cb->conn_lcb |= mask; /* mark it as used */
-          APPL_TRACE_DEBUG("start sig timer %d", p_data->hdr.offset);
+          APPL_TRACE_DEBUG("%s: start sig timer %d", __func__,
+                           p_data->hdr.offset);
           if (p_data->hdr.offset == AVDT_ACP) {
-            APPL_TRACE_DEBUG("Incoming L2CAP acquired, set state as incoming",
-                             NULL);
+            APPL_TRACE_DEBUG(
+                "%s: Incoming L2CAP acquired, set state as incoming", __func__);
             p_cb->p_scb[xx]->peer_addr = p_data->str_msg.bd_addr;
             p_cb->p_scb[xx]->use_rc =
                 true; /* allowing RC for incoming connection */
@@ -1425,7 +1392,8 @@
       if (xx == BTA_AV_NUM_LINKS) {
         /* We do not have scb for this avdt connection.     */
         /* Silently close the connection.                   */
-        APPL_TRACE_ERROR("av scb not available for avdt connection");
+        APPL_TRACE_ERROR("%s: av scb not available for avdt connection",
+                         __func__);
         AVDT_DisconnectReq(p_data->str_msg.bd_addr, NULL);
         return;
       }
@@ -1443,7 +1411,7 @@
 
     p_lcb = bta_av_find_lcb(p_data->str_msg.bd_addr, BTA_AV_LCB_FREE);
     if (p_lcb && (p_lcb->conn_msk || bta_av_cb.conn_lcb)) {
-      APPL_TRACE_DEBUG("conn_msk: 0x%x", p_lcb->conn_msk);
+      APPL_TRACE_DEBUG("%s: conn_msk: 0x%x", __func__, p_lcb->conn_msk);
       /* clean up ssm  */
       for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) {
         if (p_cb->p_scb[xx] &&
@@ -1523,7 +1491,7 @@
     p_scb = p_cb->p_scb[inx];
   }
   if (p_scb) {
-    APPL_TRACE_DEBUG("%s coll_mask = 0x%02X", __func__, p_scb->coll_mask);
+    APPL_TRACE_DEBUG("%s: coll_mask = 0x%02X", __func__, p_scb->coll_mask);
 
     if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) {
       p_scb->coll_mask &= ~BTA_AV_COLL_INC_TMR;
@@ -1580,7 +1548,7 @@
   uint16_t peer_rc_version = 0;
   uint16_t categories = 0;
 
-  APPL_TRACE_DEBUG("bta_av_check_peer_features service_uuid:x%x", service_uuid);
+  APPL_TRACE_DEBUG("%s: service_uuid:x%x", __func__, service_uuid);
   /* loop through all records we found */
   while (true) {
     /* get next record; if none found, we're done */
@@ -1606,7 +1574,7 @@
       /* get profile version (if failure, version parameter is not updated) */
       SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_AV_REMOTE_CONTROL,
                                   &peer_rc_version);
-      APPL_TRACE_DEBUG("peer_rc_version 0x%x", peer_rc_version);
+      APPL_TRACE_DEBUG("%s: peer_rc_version 0x%x", __func__, peer_rc_version);
 
       if (peer_rc_version >= AVRC_REV_1_3)
         peer_features |= (BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_METADATA);
@@ -1624,7 +1592,7 @@
       }
     }
   }
-  APPL_TRACE_DEBUG("peer_features:x%x", peer_features);
+  APPL_TRACE_DEBUG("%s: peer_features:x%x", __func__, peer_features);
   return peer_features;
 }
 
@@ -1642,13 +1610,14 @@
   tBTA_AV_FEAT peer_features = 0;
   tBTA_AV_CB* p_cb = &bta_av_cb;
 
-  APPL_TRACE_DEBUG("%s service_uuid:x%x", __func__, service_uuid);
+  APPL_TRACE_DEBUG("%s: service_uuid:x%x", __func__, service_uuid);
 
   /* loop through all records we found */
   tSDP_DISC_REC* p_rec =
       SDP_FindServiceInDb(p_cb->p_disc_db, service_uuid, NULL);
   while (p_rec) {
-    APPL_TRACE_DEBUG("%s found Service record for x%x", __func__, service_uuid);
+    APPL_TRACE_DEBUG("%s: found Service record for x%x", __func__,
+                     service_uuid);
 
     if ((SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_CLASS_ID_LIST)) !=
         NULL) {
@@ -1668,7 +1637,7 @@
       uint16_t peer_rc_version = 0;
       bool val = SDP_FindProfileVersionInRec(
           p_rec, UUID_SERVCLASS_AV_REMOTE_CONTROL, &peer_rc_version);
-      APPL_TRACE_DEBUG("%s peer_rc_version for TG 0x%x, profile_found %d",
+      APPL_TRACE_DEBUG("%s: peer_rc_version for TG 0x%x, profile_found %d",
                        __func__, peer_rc_version, val);
 
       if (peer_rc_version >= AVRC_REV_1_3)
@@ -1699,7 +1668,7 @@
     /* get next record; if none found, we're done */
     p_rec = SDP_FindServiceInDb(p_cb->p_disc_db, service_uuid, p_rec);
   }
-  APPL_TRACE_DEBUG("%s peer_features:x%x", __func__, peer_features);
+  APPL_TRACE_DEBUG("%s: peer_features:x%x", __func__, peer_features);
   return peer_features;
 }
 
@@ -1720,7 +1689,7 @@
   uint8_t rc_handle;
   tBTA_AV_FEAT peer_features = 0; /* peer features mask */
 
-  APPL_TRACE_DEBUG("%s bta_av_rc_disc_done disc:x%x", __func__, p_cb->disc);
+  APPL_TRACE_DEBUG("%s: bta_av_rc_disc_done disc:x%x", __func__, p_cb->disc);
   if (!p_cb->disc) {
     return;
   }
@@ -1741,13 +1710,13 @@
     }
   }
 
-  APPL_TRACE_DEBUG("%s rc_handle %d", __func__, rc_handle);
+  APPL_TRACE_DEBUG("%s: rc_handle %d", __func__, rc_handle);
 #if (BTA_AV_SINK_INCLUDED == TRUE)
   if (p_cb->sdp_a2dp_snk_handle) {
     /* This is Sink + CT + TG(Abs Vol) */
     peer_features =
         bta_avk_check_peer_features(UUID_SERVCLASS_AV_REM_CTRL_TARGET);
-    APPL_TRACE_DEBUG("%s populating rem ctrl target features %d", __func__,
+    APPL_TRACE_DEBUG("%s: populating rem ctrl target features %d", __func__,
                      peer_features);
     if (BTA_AV_FEAT_ADV_CTRL &
         bta_avk_check_peer_features(UUID_SERVCLASS_AV_REMOTE_CONTROL))
@@ -1778,7 +1747,7 @@
       SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_AV_REMOTE_CONTROL,
                                   &peer_rc_version);
       if (peer_rc_version <= AVRC_REV_1_3) {
-        APPL_TRACE_DEBUG("%s Using AVRCP 1.3 Capabilities with remote device",
+        APPL_TRACE_DEBUG("%s: Using AVRCP 1.3 Capabilities with remote device",
                          __func__);
         p_bta_av_cfg = &bta_av_cfg_compatibility;
       }
@@ -1788,8 +1757,8 @@
   p_cb->disc = 0;
   osi_free_and_reset((void**)&p_cb->p_disc_db);
 
-  APPL_TRACE_DEBUG("peer_features 0x%x, features 0x%x", peer_features,
-                   p_cb->features);
+  APPL_TRACE_DEBUG("%s: peer_features 0x%x, features 0x%x", __func__,
+                   peer_features, p_cb->features);
 
   /* if we have no rc connection */
   if (rc_handle == BTA_AV_RC_HANDLE_NONE) {
@@ -1805,7 +1774,7 @@
                                        (uint8_t)(p_scb->hdi + 1), p_lcb->lidx);
           p_cb->rcb[rc_handle].peer_features = peer_features;
         } else {
-          APPL_TRACE_ERROR("can not find LCB!!");
+          APPL_TRACE_ERROR("%s: can not find LCB!!", __func__);
         }
       } else if (p_scb->use_rc) {
         /* can not find AVRC on peer device. report failure */
@@ -1861,16 +1830,17 @@
 
   rc_close.rc_handle = BTA_AV_RC_HANDLE_NONE;
   p_scb = NULL;
-  APPL_TRACE_DEBUG("bta_av_rc_closed rc_handle:%d", p_msg->handle);
+  APPL_TRACE_DEBUG("%s: rc_handle:%d", __func__, p_msg->handle);
   for (i = 0; i < BTA_AV_NUM_RCB; i++) {
     p_rcb = &p_cb->rcb[i];
-    APPL_TRACE_DEBUG("bta_av_rc_closed rcb[%d] rc_handle:%d, status=0x%x", i,
+    APPL_TRACE_DEBUG("%s: rcb[%d] rc_handle:%d, status=0x%x", __func__, i,
                      p_rcb->handle, p_rcb->status);
     if (p_rcb->handle == p_msg->handle) {
       rc_close.rc_handle = i;
       p_rcb->status &= ~BTA_AV_RC_CONN_MASK;
       p_rcb->peer_features = 0;
-      APPL_TRACE_DEBUG("       shdl:%d, lidx:%d", p_rcb->shdl, p_rcb->lidx);
+      APPL_TRACE_DEBUG("%s: shdl:%d, lidx:%d", __func__, p_rcb->shdl,
+                       p_rcb->lidx);
       if (p_rcb->shdl) {
         if ((p_rcb->shdl - 1) < BTA_AV_NUM_STRS) {
           p_scb = bta_av_cb.p_scb[p_rcb->shdl - 1];
@@ -1879,14 +1849,16 @@
           rc_close.peer_addr = p_scb->peer_addr;
           if (p_scb->rc_handle == p_rcb->handle)
             p_scb->rc_handle = BTA_AV_RC_HANDLE_NONE;
-          APPL_TRACE_DEBUG("shdl:%d, srch:%d", p_rcb->shdl, p_scb->rc_handle);
+          APPL_TRACE_DEBUG("%s: shdl:%d, srch:%d", __func__, p_rcb->shdl,
+                           p_scb->rc_handle);
         }
         p_rcb->shdl = 0;
       } else if (p_rcb->lidx == (BTA_AV_NUM_LINKS + 1)) {
         /* if the RCB uses the extra LCB, use the addr for event and clean it */
         p_lcb = &p_cb->lcb[BTA_AV_NUM_LINKS];
         rc_close.peer_addr = p_msg->peer_addr;
-        VLOG(1) << "rc_only closed bd_addr:" << p_msg->peer_addr;
+        LOG_INFO(LOG_TAG, "%s: rc_only closed bd_addr: %s", __func__,
+                 p_msg->peer_addr.ToString().c_str());
         p_lcb->conn_msk = 0;
         p_lcb->lidx = 0;
       }
@@ -1940,8 +1912,8 @@
   tBTA_AV_RC_CONN_CHG* p_msg = (tBTA_AV_RC_CONN_CHG*)p_data;
   tBTA_AV_RC_BROWSE_OPEN rc_browse_open;
 
-  VLOG(1) << "bta_av_rc_browse_opened bd_addr:" << p_msg->peer_addr;
-  APPL_TRACE_DEBUG("bta_av_rc_browse_opened rc_handle:%d", p_msg->handle);
+  LOG_INFO(LOG_TAG, "%s: peer_addr: %s rc_handle:%d", __func__,
+           p_msg->peer_addr.ToString().c_str(), p_msg->handle);
 
   rc_browse_open.status = BTA_AV_SUCCESS;
   rc_browse_open.rc_handle = p_msg->handle;
@@ -1966,8 +1938,8 @@
   tBTA_AV_RC_CONN_CHG* p_msg = (tBTA_AV_RC_CONN_CHG*)p_data;
   tBTA_AV_RC_BROWSE_CLOSE rc_browse_close;
 
-  VLOG(1) << "bta_av_rc_browse_closed bd_addr:" << p_msg->peer_addr;
-  APPL_TRACE_DEBUG("bta_av_rc_browse_closed rc_handle:%d", p_msg->handle);
+  LOG_INFO(LOG_TAG, "%s: peer_addr: %s rc_handle:%d", __func__,
+           p_msg->peer_addr.ToString().c_str(), p_msg->handle);
 
   rc_browse_close.rc_handle = p_msg->handle;
   rc_browse_close.peer_addr = p_msg->peer_addr;
@@ -1997,7 +1969,8 @@
   RawAddress* p_addr = NULL;
   uint8_t rc_handle;
 
-  APPL_TRACE_DEBUG("bta_av_rc_disc 0x%x, %d", disc, bta_av_cb.disc);
+  APPL_TRACE_DEBUG("%s: disc: 0x%x, bta_av_cb.disc: 0x%x", __func__, disc,
+                   bta_av_cb.disc);
   if ((bta_av_cb.disc != 0) || (disc == 0)) return;
 
   if ((disc & BTA_AV_CHNL_MSK) == BTA_AV_CHNL_MSK) {
@@ -2011,7 +1984,7 @@
     p_scb = p_cb->p_scb[hdi];
 
     if (p_scb) {
-      APPL_TRACE_DEBUG("rc_handle %d", p_scb->rc_handle);
+      APPL_TRACE_DEBUG("%s: rc_handle %d", __func__, p_scb->rc_handle);
       p_addr = &p_scb->peer_addr;
     }
   }
@@ -2031,7 +2004,7 @@
     if (AVRC_FindService(UUID_SERVCLASS_AV_REMOTE_CONTROL, *p_addr, &db_params,
                          bta_av_avrc_sdp_cback) == AVRC_SUCCESS) {
       p_cb->disc = disc;
-      APPL_TRACE_DEBUG("disc %d", p_cb->disc);
+      APPL_TRACE_DEBUG("%s: disc 0x%x", __func__, p_cb->disc);
     }
   }
 }
@@ -2056,51 +2029,43 @@
   p_scb = bta_av_hndl_to_scb(p_data->hdr.layer_specific);
 
   if (p_scb) {
-    APPL_TRACE_DEBUG("deregistered %d(h%d)", p_scb->chnl, p_scb->hndl);
+    APPL_TRACE_DEBUG("%s: deregistered %d(h%d)", __func__, p_scb->chnl,
+                     p_scb->hndl);
     mask = BTA_AV_HNDL_TO_MSK(p_scb->hdi);
-    if (p_scb->chnl == BTA_AV_CHNL_AUDIO) {
-      p_cb->reg_audio &= ~mask;
-      if ((p_cb->conn_audio & mask) && bta_av_cb.audio_open_cnt) {
-        /* this channel is still marked as open. decrease the count */
-        bta_av_cb.audio_open_cnt--;
-      }
-      p_cb->conn_audio &= ~mask;
+    p_cb->reg_audio &= ~mask;
+    if ((p_cb->conn_audio & mask) && bta_av_cb.audio_open_cnt) {
+      /* this channel is still marked as open. decrease the count */
+      bta_av_cb.audio_open_cnt--;
+    }
+    p_cb->conn_audio &= ~mask;
 
-      if (p_scb->q_tag == BTA_AV_Q_TAG_STREAM && p_scb->a2dp_list) {
-        /* make sure no buffers are in a2dp_list */
-        while (!list_is_empty(p_scb->a2dp_list)) {
-          p_buf = (BT_HDR*)list_front(p_scb->a2dp_list);
-          list_remove(p_scb->a2dp_list, p_buf);
-          osi_free(p_buf);
-        }
+    if (p_scb->q_tag == BTA_AV_Q_TAG_STREAM && p_scb->a2dp_list) {
+      /* make sure no buffers are in a2dp_list */
+      while (!list_is_empty(p_scb->a2dp_list)) {
+        p_buf = (BT_HDR*)list_front(p_scb->a2dp_list);
+        list_remove(p_scb->a2dp_list, p_buf);
+        osi_free(p_buf);
       }
+    }
 
-      /* remove the A2DP SDP record, if no more audio stream is left */
-      if (!p_cb->reg_audio) {
+    /* remove the A2DP SDP record, if no more audio stream is left */
+    if (!p_cb->reg_audio) {
 #if (BTA_AR_INCLUDED == TRUE)
-        bta_ar_dereg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, BTA_ID_AV);
+      bta_ar_dereg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, BTA_ID_AV);
 #endif
-        if (p_cb->sdp_a2dp_handle) {
-          bta_av_del_sdp_rec(&p_cb->sdp_a2dp_handle);
-          p_cb->sdp_a2dp_handle = 0;
-          bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SOURCE);
-        }
+      if (p_cb->sdp_a2dp_handle) {
+        bta_av_del_sdp_rec(&p_cb->sdp_a2dp_handle);
+        p_cb->sdp_a2dp_handle = 0;
+        bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SOURCE);
+      }
 
 #if (BTA_AV_SINK_INCLUDED == TRUE)
-        if (p_cb->sdp_a2dp_snk_handle) {
-          bta_av_del_sdp_rec(&p_cb->sdp_a2dp_snk_handle);
-          p_cb->sdp_a2dp_snk_handle = 0;
-          bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SINK);
-        }
-#endif
+      if (p_cb->sdp_a2dp_snk_handle) {
+        bta_av_del_sdp_rec(&p_cb->sdp_a2dp_snk_handle);
+        p_cb->sdp_a2dp_snk_handle = 0;
+        bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SINK);
       }
-    } else {
-      p_cb->reg_video &= ~mask;
-      /* make sure that this channel is not connected */
-      p_cb->conn_video &= ~mask;
-      /* remove the VDP SDP record, (only one video stream at most) */
-      bta_av_del_sdp_rec(&p_cb->sdp_vdp_handle);
-      bta_sys_remove_uuid(UUID_SERVCLASS_VIDEO_SOURCE);
+#endif
     }
 
     /* make sure that the timer is not active */
@@ -2108,10 +2073,10 @@
     osi_free_and_reset((void**)&p_cb->p_scb[p_scb->hdi]);
   }
 
-  APPL_TRACE_DEBUG("audio 0x%x, video: 0x%x, disable:%d", p_cb->reg_audio,
-                   p_cb->reg_video, p_cb->disabling);
+  APPL_TRACE_DEBUG("%s: audio 0x%x, disable:%d", __func__, p_cb->reg_audio,
+                   p_cb->disabling);
   /* if no stream control block is active */
-  if ((p_cb->reg_audio + p_cb->reg_video) == 0) {
+  if (p_cb->reg_audio == 0) {
 #if (BTA_AR_INCLUDED == TRUE)
     /* deregister from AVDT */
     bta_ar_dereg_avdt(BTA_ID_AV);
diff --git a/bta/av/bta_av_api.cc b/bta/av/bta_av_api.cc
index a8e379a..73ffc8d 100644
--- a/bta/av/bta_av_api.cc
+++ b/bta/av/bta_av_api.cc
@@ -153,6 +153,10 @@
  ******************************************************************************/
 void BTA_AvOpen(const RawAddress& bd_addr, tBTA_AV_HNDL handle, bool use_rc,
                 tBTA_SEC sec_mask, uint16_t uuid) {
+  APPL_TRACE_DEBUG("%s: peer %s handle:0x%x use_rc=%s sec_mask=0x%x uuid=0x%x",
+                   __func__, bd_addr.ToString().c_str(), handle,
+                   (use_rc) ? "true" : "false", sec_mask, uuid)
+
   tBTA_AV_API_OPEN* p_buf =
       (tBTA_AV_API_OPEN*)osi_malloc(sizeof(tBTA_AV_API_OPEN));
 
@@ -177,6 +181,8 @@
  *
  ******************************************************************************/
 void BTA_AvClose(tBTA_AV_HNDL handle) {
+  APPL_TRACE_DEBUG("%s: handle:0x%x", __func__, handle);
+
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
   p_buf->event = BTA_AV_API_CLOSE_EVT;
@@ -213,10 +219,11 @@
  * Returns          void
  *
  ******************************************************************************/
-void BTA_AvStart(void) {
+void BTA_AvStart(tBTA_AV_HNDL handle) {
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
   p_buf->event = BTA_AV_API_START_EVT;
+  p_buf->layer_specific = handle;
 
   bta_sys_sendmsg(p_buf);
 }
@@ -270,11 +277,12 @@
  * Returns          void
  *
  ******************************************************************************/
-void BTA_AvStop(bool suspend) {
+void BTA_AvStop(tBTA_AV_HNDL handle, bool suspend) {
   tBTA_AV_API_STOP* p_buf =
       (tBTA_AV_API_STOP*)osi_malloc(sizeof(tBTA_AV_API_STOP));
 
   p_buf->hdr.event = BTA_AV_API_STOP_EVT;
+  p_buf->hdr.layer_specific = handle;
   p_buf->flush = true;
   p_buf->suspend = suspend;
   p_buf->reconfig_stop = false;
diff --git a/bta/av/bta_av_cfg.cc b/bta/av/bta_av_cfg.cc
index ee4ccb7..76c9454 100644
--- a/bta/av/bta_av_cfg.cc
+++ b/bta/av/bta_av_cfg.cc
@@ -128,19 +128,16 @@
 
 /* This configuration to be used when we are Src + TG + CT( only for abs vol) */
 const tBTA_AV_CFG bta_av_cfg = {
-    BTA_AV_RC_COMP_ID, /* AVRCP Company ID */
-    512,                  /* AVRCP MTU at L2CAP for control channel */
-    BTA_AV_MAX_RC_BR_MTU, /* AVRCP MTU at L2CAP for browsing channel */
+    BTA_AV_RC_COMP_ID,     /* AVRCP Company ID */
+    512,                   /* AVRCP MTU at L2CAP for control channel */
+    BTA_AV_MAX_RC_BR_MTU,  /* AVRCP MTU at L2CAP for browsing channel */
     BTA_AV_RC_SUPF_CT,     /* AVRCP controller categories */
     BTA_AV_RC_SUPF_TG,     /* AVRCP target categories */
     672,                   /* AVDTP signaling channel MTU at L2CAP */
-    BTA_AV_MAX_A2DP_MTU,   /* AVDTP audio transport channel MTU at L2CAP
-                              */
+    BTA_AV_MAX_A2DP_MTU,   /* AVDTP audio transport channel MTU at L2CAP */
     bta_av_audio_flush_to, /* AVDTP audio transport channel flush
                               timeout */
     6,                     /* AVDTP audio channel max data queue size */
-    BTA_AV_MAX_VDP_MTU,    /* AVDTP video transport channel MTU at L2CAP */
-    600,                   /* AVDTP video transport channel flush timeout */
     false, /* true, to accept AVRC 1.3 group nevigation command */
     2,     /* company id count in p_meta_co_ids */
     BTA_AV_NUM_RC_EVT_IDS,    /* event id count in p_meta_evt_ids */
@@ -150,8 +147,7 @@
                                  for company id */
     bta_av_meta_caps_evt_ids, /* the the metadata Get Capabilities
                                  response for event id */
-    NULL,                     /* the action function table for VDP stream */
-    NULL,                     /* action function to register VDP */
+    NULL,                     /* the action function table for audio stream */
     BTA_AV_RC_CT_NAME,        /* Default AVRCP controller name */
     BTA_AV_RC_TG_NAME         /* Default AVRCP target name */
 };
@@ -159,19 +155,15 @@
 /* This configuration to be used when we are Sink + CT + TG( only for abs vol)
  */
 const tBTA_AV_CFG bta_avk_cfg = {
-    AVRC_CO_METADATA, /* AVRCP Company ID */
-    512, /* AVRCP MTU at L2CAP for control channel */
+    AVRC_CO_METADATA,      /* AVRCP Company ID */
+    512,                   /* AVRCP MTU at L2CAP for control channel */
     BTA_AV_MAX_RC_BR_MTU,  /* AVRCP MTU at L2CAP for browsing channel */
     BTA_AVK_RC_SUPF_CT,    /* AVRCP controller categories */
     BTA_AVK_RC_SUPF_TG,    /* AVRCP target categories */
     672,                   /* AVDTP signaling channel MTU at L2CAP */
-    BTA_AV_MAX_A2DP_MTU,   /* AVDTP audio transport channel MTU at L2CAP
-                              */
-    bta_av_audio_flush_to, /* AVDTP audio transport channel flush
-                              timeout */
+    BTA_AV_MAX_A2DP_MTU,   /* AVDTP audio transport channel MTU at L2CAP */
+    bta_av_audio_flush_to, /* AVDTP audio transport channel flush timeout */
     6,                     /* AVDTP audio channel max data queue size */
-    BTA_AV_MAX_VDP_MTU,    /* AVDTP video transport channel MTU at L2CAP */
-    600,                   /* AVDTP video transport channel flush timeout */
     false, /* true, to accept AVRC 1.3 group nevigation command */
     2,     /* company id count in p_meta_co_ids */
     BTA_AVK_NUM_RC_EVT_IDS,    /* event id count in p_meta_evt_ids */
@@ -181,39 +173,33 @@
                                   for company id */
     bta_avk_meta_caps_evt_ids, /* the the metadata Get Capabilities
                                   response for event id */
-    NULL,                      /* the action function table for VDP stream */
-    NULL,                      /* action function to register VDP */
+    NULL,                      /* the action function table for audio stream */
     {0},                       /* Default AVRCP controller name */
     {0},                       /* Default AVRCP target name */
 };
 
 /* This configuration to be used when we are using AVRCP1.3 */
 const tBTA_AV_CFG bta_av_cfg_compatibility = {
-    BTA_AV_RC_COMP_ID, /* AVRCP Company ID */
-    512,                  /* AVRCP MTU at L2CAP for control channel */
-    BTA_AV_MAX_RC_BR_MTU, /* AVRCP MTU at L2CAP for browsing channel */
-    BTA_AV_RC_SUPF_CT, /* AVRCP controller categories */
-    AVRC_SUPF_TG_CAT1, /* Only support CAT1 for AVRCP1.3 */
-    672,               /* AVDTP signaling channel MTU at L2CAP */
-    BTA_AV_MAX_A2DP_MTU, /* AVDTP audio transport channel MTU at L2CAP
-                          */
-    bta_av_audio_flush_to, /* AVDTP audio transport channel flush
-                              timeout */
-    6,                  /* AVDTP audio channel max data queue size */
-    BTA_AV_MAX_VDP_MTU, /* AVDTP video transport channel MTU at L2CAP */
-    600,                /* AVDTP video transport channel flush timeout */
-    false,              /* true, to accept AVRC 1.3 group nevigation command */
-    2,                  /* company id count in p_meta_co_ids */
-    BTA_AV_NUM_RC_EVT_IDS_AVRCP13,    /* event id count for AVRCP1.3*/
+    BTA_AV_RC_COMP_ID,     /* AVRCP Company ID */
+    512,                   /* AVRCP MTU at L2CAP for control channel */
+    BTA_AV_MAX_RC_BR_MTU,  /* AVRCP MTU at L2CAP for browsing channel */
+    BTA_AV_RC_SUPF_CT,     /* AVRCP controller categories */
+    AVRC_SUPF_TG_CAT1,     /* Only support CAT1 for AVRCP1.3 */
+    672,                   /* AVDTP signaling channel MTU at L2CAP */
+    BTA_AV_MAX_A2DP_MTU,   /* AVDTP audio transport channel MTU at L2CAP */
+    bta_av_audio_flush_to, /* AVDTP audio transport channel flush timeout */
+    6,                     /* AVDTP audio channel max data queue size */
+    false, /* true, to accept AVRC 1.3 group nevigation command */
+    2,     /* company id count in p_meta_co_ids */
+    BTA_AV_NUM_RC_EVT_IDS_AVRCP13,    /* event id count for AVRCP1.3 */
     BTA_AV_RC_PASS_RSP_CODE,          /* the default response code for pass
                                          through commands */
     bta_av_meta_caps_co_ids,          /* the metadata Get Capabilities response
                                          for company id */
     bta_av_meta_caps_evt_ids_avrcp13, /* the the metadata Get Capabilities
                                          response for event id, compatible
-                                         with AVRCP1.3*/
-    NULL,              /* the action function table for VDP stream */
-    NULL,              /* action function to register VDP */
+                                         with AVRCP1.3 */
+    NULL,              /* the action function table for audio stream */
     BTA_AV_RC_CT_NAME, /* Default AVRCP controller name */
     BTA_AV_RC_TG_NAME  /* Default AVRCP target name */
 };
diff --git a/bta/av/bta_av_ci.cc b/bta/av/bta_av_ci.cc
index 64d24d7..a3d552d 100644
--- a/bta/av/bta_av_ci.cc
+++ b/bta/av/bta_av_ci.cc
@@ -23,6 +23,10 @@
  *
  ******************************************************************************/
 
+#define LOG_TAG "bt_bta_av"
+
+#include "osi/include/log.h"
+
 #include "bta_av_ci.h"
 #include "bta_api.h"
 #include "bta_av_int.h"
@@ -36,7 +40,7 @@
  *
  * Description      This function sends an event to the AV indicating that
  *                  the phone has audio stream data ready to send and AV
- *                  should call bta_av_co_audio_src_data_path().
+ *                  should call bta_av_co_audio_source_data_path().
  *
  * Returns          void
  *
@@ -63,13 +67,19 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_av_ci_setconfig(tBTA_AV_HNDL hndl, uint8_t err_code, uint8_t category,
-                         uint8_t num_seid, uint8_t* p_seid, bool recfg_needed,
-                         uint8_t avdt_handle) {
+void bta_av_ci_setconfig(tBTA_AV_HNDL bta_av_handle, uint8_t err_code,
+                         uint8_t category, uint8_t num_seid, uint8_t* p_seid,
+                         bool recfg_needed, uint8_t avdt_handle) {
+  LOG_DEBUG(LOG_TAG,
+            "%s: bta_av_handle=%d err_code=%d category=%d "
+            "num_seid=%d recfg_needed=%s avdt_handle=%d",
+            __func__, bta_av_handle, err_code, category, num_seid,
+            recfg_needed ? "true" : "false", avdt_handle);
+
   tBTA_AV_CI_SETCONFIG* p_buf =
       (tBTA_AV_CI_SETCONFIG*)osi_malloc(sizeof(tBTA_AV_CI_SETCONFIG));
 
-  p_buf->hdr.layer_specific = hndl;
+  p_buf->hdr.layer_specific = bta_av_handle;
   p_buf->hdr.event = (err_code == A2DP_SUCCESS) ? BTA_AV_CI_SETCONFIG_OK_EVT
                                                 : BTA_AV_CI_SETCONFIG_FAIL_EVT;
   p_buf->err_code = err_code;
diff --git a/bta/av/bta_av_int.h b/bta/av/bta_av_int.h
index e99c4bd..e97321d 100644
--- a/bta/av/bta_av_int.h
+++ b/bta/av/bta_av_int.h
@@ -156,30 +156,40 @@
 
 /* function types for call-out functions */
 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,
-                                    const RawAddress& addr,
-                                    uint16_t uuid_local);
-typedef tA2DP_STATUS (*tBTA_AV_CO_GETCFG)(tBTA_AV_HNDL hndl,
+                                AvdtpSepConfig* p_cfg);
+typedef void (*tBTA_AV_CO_DISC_RES)(tBTA_AV_HNDL bta_av_handle,
+                                    const RawAddress& peer_addr,
+                                    uint8_t num_seps, uint8_t num_snk,
+                                    uint8_t num_src, uint16_t uuid_local);
+typedef tA2DP_STATUS (*tBTA_AV_CO_GETCFG)(tBTA_AV_HNDL bta_av_handle,
+                                          const RawAddress& peer_addr,
                                           uint8_t* p_codec_info,
                                           uint8_t* p_sep_info_idx, uint8_t seid,
                                           uint8_t* p_num_protect,
                                           uint8_t* p_protect_info);
-typedef void (*tBTA_AV_CO_SETCFG)(tBTA_AV_HNDL hndl,
+typedef void (*tBTA_AV_CO_SETCFG)(tBTA_AV_HNDL bta_av_handle,
+                                  const RawAddress& peer_addr,
                                   const uint8_t* p_codec_info, uint8_t seid,
-                                  const RawAddress& addr, uint8_t num_protect,
+                                  uint8_t num_protect,
                                   const uint8_t* p_protect_info,
                                   uint8_t t_local_sep, uint8_t avdt_handle);
-typedef void (*tBTA_AV_CO_OPEN)(tBTA_AV_HNDL hndl, uint16_t mtu);
-typedef void (*tBTA_AV_CO_CLOSE)(tBTA_AV_HNDL hndl);
-typedef void (*tBTA_AV_CO_START)(tBTA_AV_HNDL hndl, uint8_t* p_codec_info,
-                                 bool* p_no_rtp_hdr);
-typedef void (*tBTA_AV_CO_STOP)(tBTA_AV_HNDL hndl);
-typedef void* (*tBTA_AV_CO_DATAPATH)(const uint8_t* p_codec_info,
-                                     uint32_t* p_timestamp);
-typedef void (*tBTA_AV_CO_DELAY)(tBTA_AV_HNDL hndl, uint16_t delay);
-typedef void (*tBTA_AV_CO_UPDATE_MTU)(tBTA_AV_HNDL hndl, uint16_t mtu);
+typedef void (*tBTA_AV_CO_OPEN)(tBTA_AV_HNDL bta_av_handle,
+                                const RawAddress& peer_addr, uint16_t mtu);
+typedef void (*tBTA_AV_CO_CLOSE)(tBTA_AV_HNDL bta_av_handle,
+                                 const RawAddress& peer_addr);
+typedef void (*tBTA_AV_CO_START)(tBTA_AV_HNDL bta_av_handle,
+                                 const RawAddress& peer_addr,
+                                 const uint8_t* p_codec_info,
+                                 bool* p_no_rtp_header);
+typedef void (*tBTA_AV_CO_STOP)(tBTA_AV_HNDL bta_av_handle,
+                                const RawAddress& peer_addr);
+typedef BT_HDR* (*tBTA_AV_CO_DATAPATH)(const uint8_t* p_codec_info,
+                                       uint32_t* p_timestamp);
+typedef void (*tBTA_AV_CO_DELAY)(tBTA_AV_HNDL bta_av_handle,
+                                 const RawAddress& peer_addr, uint16_t delay);
+typedef void (*tBTA_AV_CO_UPDATE_MTU)(tBTA_AV_HNDL bta_av_handle,
+                                      const RawAddress& peer_addr,
+                                      uint16_t mtu);
 
 /* the call-out functions for one stream */
 typedef struct {
@@ -313,9 +323,10 @@
 /* data type for all stream events from AVDTP */
 typedef struct {
   BT_HDR hdr;
-  tAVDT_CFG cfg;   /* configuration/capabilities parameters */
+  AvdtpSepConfig cfg; /* configuration/capabilities parameters */
   tAVDT_CTRL msg;  /* AVDTP callback message parameters */
   RawAddress bd_addr; /* bd address */
+  uint8_t scb_index;
   uint8_t handle;
   uint8_t avdt_event;
   bool initiator; /* true, if local device initiates the SUSPEND */
@@ -446,11 +457,11 @@
   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[BTAV_A2DP_CODEC_INDEX_MAX];
-  tAVDT_CFG peer_cap; /* buffer used for get capabilities */
+  AvdtpSepConfig peer_cap; /* buffer used for get capabilities */
   list_t* a2dp_list; /* used for audio channels only */
   tBTA_AV_Q_INFO q_info;
   tAVDT_SEP_INFO sep_info[BTA_AV_NUM_SEPS]; /* stream discovery results */
-  tAVDT_CFG cfg;                            /* local SEP configuration */
+  AvdtpSepConfig cfg;                       /* local SEP configuration */
   alarm_t* avrc_ct_timer;                   /* delay timer for AVRC CT */
   RawAddress peer_addr;                     /* peer BD address */
   uint16_t l2c_cid;                         /* L2CAP channel ID */
@@ -488,13 +499,13 @@
                         successfull, else False if command fails */
   bool suspend_sup;  /* true if Suspend stream is supported, else false if
                         suspend command fails */
-  bool deregistring; /* true if deregistering */
+  bool deregistering; /* true if deregistering */
   bool sco_suspend;  /* true if SUSPEND is issued automatically for SCO */
   uint8_t coll_mask; /* Mask to check incoming and outgoing collision */
   tBTA_AV_API_OPEN open_api; /* Saved OPEN api message */
   uint8_t wait;  /* set 0x1, when getting Caps as ACP, set 0x2, when started */
   uint8_t q_tag; /* identify the associated q_info union member */
-  bool no_rtp_hdr;   /* true if add no RTP header*/
+  bool no_rtp_header; /* true if add no RTP header */
   uint16_t uuid_int; /*intended UUID of Initiator to connect to */
   bool offload_start_pending;
   bool skip_sdp; /* Decides if sdp to be done prior to profile connection */
@@ -541,7 +552,6 @@
       accept_signalling_timer;  /* timer to monitor signalling when accepting */
   uint32_t sdp_a2dp_handle;     /* SDP record handle for audio src */
   uint32_t sdp_a2dp_snk_handle; /* SDP record handle for audio snk */
-  uint32_t sdp_vdp_handle;      /* SDP record handle for video src */
   tBTA_AV_FEAT features;        /* features mask */
   tBTA_SEC sec_mask;            /* security mask */
   tBTA_AV_HNDL handle;          /* the handle for SDP activity */
@@ -549,19 +559,15 @@
   uint8_t
       disc; /* (hdi+1) or (rc_handle|BTA_AV_CHNL_MSK) if p_disc_db is in use */
   uint8_t state;          /* state machine state */
-  uint8_t conn_rc;        /* handle mask of connected RCP channels */
   uint8_t conn_audio;     /* handle mask of connected audio channels */
-  uint8_t conn_video;     /* handle mask of connected video channels */
   uint8_t conn_lcb;       /* index mask of used LCBs */
   uint8_t audio_open_cnt; /* number of connected audio channels */
   uint8_t reg_audio;      /* handle mask of registered audio channels */
-  uint8_t reg_video;      /* handle mask of registered video channels */
   uint8_t rc_acp_handle;
   uint8_t rc_acp_idx; /* (index + 1) to RCB */
   uint8_t rs_idx;    /* (index + 1) to SCB for the one waiting for RS on open */
   bool sco_occupied; /* true if SCO is being used or call is in progress */
   uint8_t audio_streams; /* handle mask of streaming audio channels */
-  uint8_t video_streams; /* handle mask of streaming video channels */
 } tBTA_AV_CB;
 
 /*****************************************************************************
@@ -583,7 +589,6 @@
 
 extern const tBTA_AV_SACT bta_av_a2dp_action[];
 extern const tBTA_AV_CO_FUNCTS bta_av_a2dp_cos;
-extern tAVDT_CTRL_CBACK* const bta_av_dt_cback[];
 extern void bta_av_sink_data_cback(uint8_t handle, BT_HDR* p_pkt,
                                    uint32_t time_stamp, uint8_t m_pt);
 
@@ -595,8 +600,9 @@
 extern bool bta_av_chk_start(tBTA_AV_SCB* p_scb);
 extern void bta_av_restore_switch(void);
 extern uint16_t bta_av_chk_mtu(tBTA_AV_SCB* p_scb, uint16_t mtu);
-extern void bta_av_conn_cback(uint8_t handle, const RawAddress* bd_addr,
-                              uint8_t event, tAVDT_CTRL* p_data);
+extern void bta_av_conn_cback(uint8_t handle, const RawAddress& bd_addr,
+                              uint8_t event, tAVDT_CTRL* p_data,
+                              uint8_t scb_index);
 extern uint8_t bta_av_rc_create(tBTA_AV_CB* p_cb, uint8_t role, uint8_t shdl,
                                 uint8_t lidx);
 extern void bta_av_stream_chg(tBTA_AV_SCB* p_scb, bool started);
@@ -606,6 +612,7 @@
 extern bool bta_av_is_scb_init(tBTA_AV_SCB* p_scb);
 extern void bta_av_set_scb_sst_incoming(tBTA_AV_SCB* p_scb);
 extern tBTA_AV_LCB* bta_av_find_lcb(const RawAddress& addr, uint8_t op);
+extern const char* bta_av_sst_code(uint8_t state);
 
 /* main functions */
 extern void bta_av_api_deregister(tBTA_AV_DATA* p_data);
@@ -646,6 +653,10 @@
 extern tBTA_AV_RCB* bta_av_get_rcb_by_shdl(uint8_t shdl);
 extern void bta_av_del_rc(tBTA_AV_RCB* p_rcb);
 
+extern void bta_av_proc_stream_evt(uint8_t handle, const RawAddress& bd_addr,
+                                   uint8_t event, tAVDT_CTRL* p_data,
+                                   uint8_t scb_index);
+
 /* ssm action functions */
 extern void bta_av_do_disc_a2dp(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data);
 extern void bta_av_cleanup(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data);
diff --git a/bta/av/bta_av_main.cc b/bta/av/bta_av_main.cc
index f941865..e35e349 100644
--- a/bta/av/bta_av_main.cc
+++ b/bta/av/bta_av_main.cc
@@ -304,49 +304,32 @@
  *
  ******************************************************************************/
 static tBTA_AV_SCB* bta_av_alloc_scb(tBTA_AV_CHNL chnl) {
-  tBTA_AV_SCB* p_ret = NULL;
-  int xx;
-  tBTA_AV_STATUS sts = BTA_AV_SUCCESS;
-
-  if (chnl == BTA_AV_CHNL_VIDEO) {
-    if (p_bta_av_cfg->p_act_tbl == NULL || p_bta_av_cfg->p_reg == NULL) {
-      APPL_TRACE_ERROR("Video streaming not supported");
-      sts = BTA_AV_FAIL;
-    } else {
-      /* allow only one Video channel */
-      if (bta_av_cb.reg_video) {
-        APPL_TRACE_ERROR("Already registered");
-        sts = BTA_AV_FAIL;
-      }
-    }
-  } else if (chnl != BTA_AV_CHNL_AUDIO) {
-    APPL_TRACE_ERROR("bad channel: %d", chnl);
-    sts = BTA_AV_FAIL;
+  if (chnl != BTA_AV_CHNL_AUDIO) {
+    APPL_TRACE_ERROR("%s: bad channel: %d", __func__, chnl);
+    return nullptr;
   }
 
-  if (sts == BTA_AV_SUCCESS) {
-    for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) {
-      if (bta_av_cb.p_scb[xx] == NULL) {
-        /* found an empty spot */
-        p_ret = (tBTA_AV_SCB*)osi_calloc(sizeof(tBTA_AV_SCB));
-        p_ret->rc_handle = BTA_AV_RC_HANDLE_NONE;
-        p_ret->chnl = chnl;
-        p_ret->hndl = (tBTA_AV_HNDL)((xx + 1) | chnl);
-        p_ret->hdi = xx;
-        p_ret->a2dp_list = list_new(NULL);
-        p_ret->avrc_ct_timer = alarm_new("bta_av.avrc_ct_timer");
-        bta_av_cb.p_scb[xx] = p_ret;
-        break;
-      }
-    }
+  for (int xx = 0; xx < BTA_AV_NUM_STRS; xx++) {
+    if (bta_av_cb.p_scb[xx] != nullptr) continue;
+    // Found an empty spot
+    tBTA_AV_SCB* p_ret = (tBTA_AV_SCB*)osi_calloc(sizeof(tBTA_AV_SCB));
+    p_ret->rc_handle = BTA_AV_RC_HANDLE_NONE;
+    p_ret->chnl = chnl;
+    p_ret->hndl = (tBTA_AV_HNDL)((xx + 1) | chnl);
+    p_ret->hdi = xx;
+    p_ret->a2dp_list = list_new(nullptr);
+    p_ret->avrc_ct_timer = alarm_new("bta_av.avrc_ct_timer");
+    bta_av_cb.p_scb[xx] = p_ret;
+    return p_ret;
   }
-  return p_ret;
+
+  return nullptr;
 }
 
 /*******************************************************************************
  ******************************************************************************/
-void bta_av_conn_cback(UNUSED_ATTR uint8_t handle, const RawAddress* bd_addr,
-                       uint8_t event, tAVDT_CTRL* p_data) {
+void bta_av_conn_cback(UNUSED_ATTR uint8_t handle, const RawAddress& bd_addr,
+                       uint8_t event, tAVDT_CTRL* p_data, uint8_t scb_index) {
   uint16_t evt = 0;
   tBTA_AV_SCB* p_scb = NULL;
 
@@ -359,7 +342,7 @@
   {
     evt = BTA_AV_SIG_CHG_EVT;
     if (event == AVDT_DISCONNECT_IND_EVT) {
-      p_scb = bta_av_addr_to_scb(*bd_addr);
+      p_scb = bta_av_addr_to_scb(bd_addr);
     } else if (event == AVDT_CONNECT_IND_EVT) {
       APPL_TRACE_DEBUG("%s: CONN_IND is ACP:%d", __func__,
                        p_data->hdr.err_param);
@@ -370,11 +353,14 @@
     p_msg->hdr.event = evt;
     p_msg->hdr.layer_specific = event;
     p_msg->hdr.offset = p_data->hdr.err_param;
-    p_msg->bd_addr = *bd_addr;
+    p_msg->bd_addr = bd_addr;
+    p_msg->scb_index = scb_index;
     if (p_scb) {
-      APPL_TRACE_DEBUG("scb hndl x%x, role x%x", p_scb->hndl, p_scb->role);
+      APPL_TRACE_DEBUG("%s: scb hndl x%x, role x%x", __func__, p_scb->hndl,
+                       p_scb->role);
     }
-    VLOG(1) << "conn_cback bd_addr:" << bd_addr;
+    LOG_INFO(LOG_TAG, "%s: conn_cback bd_addr: %s", __func__,
+             bd_addr.ToString().c_str());
     bta_sys_sendmsg(p_msg);
   }
 }
@@ -409,12 +395,12 @@
 static void bta_av_api_register(tBTA_AV_DATA* p_data) {
   tBTA_AV_REGISTER registr;
   tBTA_AV_SCB* p_scb; /* stream control block */
-  tAVDT_REG reg;
-  tAVDT_CS cs;
+  AvdtpRcb reg;
+  AvdtpStreamConfig avdtp_stream_config;
   char* p_service_name;
   tBTA_UTL_COD cod;
 
-  memset(&cs, 0, sizeof(tAVDT_CS));
+  avdtp_stream_config.Reset();
 
   registr.status = BTA_AV_FAIL_RESOURCES;
   registr.app_id = p_data->api_reg.app_id;
@@ -422,7 +408,8 @@
 
   char avrcp_version[PROPERTY_VALUE_MAX] = {0};
   osi_property_get(AVRCP_VERSION_PROPERTY, avrcp_version, AVRCP_1_4_STRING);
-  LOG_INFO(LOG_TAG, "AVRCP version used for sdp: \"%s\"", avrcp_version);
+  LOG_INFO(LOG_TAG, "%s: AVRCP version used for sdp: \"%s\"", __func__,
+           avrcp_version);
 
   uint16_t profile_initialized = p_data->api_reg.service_uuid;
   if (profile_initialized == UUID_SERVCLASS_AUDIO_SINK) {
@@ -431,21 +418,21 @@
     p_bta_av_cfg = &bta_av_cfg;
 
     if (!strncmp(AVRCP_1_3_STRING, avrcp_version, sizeof(AVRCP_1_3_STRING))) {
-      LOG_INFO(LOG_TAG, "AVRCP 1.3 capabilites used");
+      LOG_INFO(LOG_TAG, "%s: AVRCP 1.3 capabilites used", __func__);
       p_bta_av_cfg = &bta_av_cfg_compatibility;
     }
   }
 
   APPL_TRACE_DEBUG("%s: profile: 0x%x", __func__, profile_initialized);
   if (p_bta_av_cfg == NULL) {
-    APPL_TRACE_ERROR("AV configuration is null!");
+    APPL_TRACE_ERROR("%s: AV configuration is null!", __func__);
     return;
   }
 
   do {
     p_scb = bta_av_alloc_scb(registr.chnl);
     if (p_scb == NULL) {
-      APPL_TRACE_ERROR("failed to alloc SCB");
+      APPL_TRACE_ERROR("%s: failed to alloc SCB", __func__);
       break;
     }
 
@@ -455,13 +442,14 @@
     /* initialize the stream control block */
     registr.status = BTA_AV_SUCCESS;
 
-    if ((bta_av_cb.reg_audio + bta_av_cb.reg_video) == 0) {
+    if (bta_av_cb.reg_audio == 0) {
       /* the first channel registered. register to AVDTP */
       reg.ctrl_mtu = p_bta_av_cfg->sig_mtu;
       reg.ret_tout = BTA_AV_RET_TOUT;
       reg.sig_tout = BTA_AV_SIG_TOUT;
       reg.idle_tout = BTA_AV_IDLE_TOUT;
       reg.sec_mask = bta_av_cb.sec_mask;
+      reg.scb_index = p_scb->hdi;
 #if (BTA_AR_INCLUDED == TRUE)
       bta_ar_reg_avdt(&reg, bta_av_conn_cback, BTA_ID_AV);
 #endif
@@ -519,11 +507,13 @@
     } /* if 1st channel */
 
     /* get stream configuration and create stream */
-    cs.cfg.num_codec = 1;
-    cs.nsc_mask =
-        AVDT_NSC_RECONFIG |
-        ((bta_av_cb.features & BTA_AV_FEAT_PROTECT) ? 0 : AVDT_NSC_SECURITY);
-    APPL_TRACE_DEBUG("nsc_mask: 0x%x", cs.nsc_mask);
+    avdtp_stream_config.cfg.num_codec = 1;
+    avdtp_stream_config.nsc_mask = AvdtpStreamConfig::AVDT_NSC_RECONFIG;
+    if (!(bta_av_cb.features & BTA_AV_FEAT_PROTECT)) {
+      avdtp_stream_config.nsc_mask |= AvdtpStreamConfig::AVDT_NSC_SECURITY;
+    }
+    APPL_TRACE_DEBUG("%s: nsc_mask: 0x%x", __func__,
+                     avdtp_stream_config.nsc_mask);
 
     if (p_data->api_reg.p_service_name[0] == 0) {
       p_service_name = NULL;
@@ -535,140 +525,131 @@
     p_scb->recfg_sup = true;
     p_scb->skip_sdp = false;
 
-    cs.p_ctrl_cback = bta_av_dt_cback[p_scb->hdi];
-    if (registr.chnl == BTA_AV_CHNL_AUDIO) {
-      /* set up the audio stream control block */
-      p_scb->p_act_tbl = (const tBTA_AV_ACT*)bta_av_a2dp_action;
-      p_scb->p_cos = &bta_av_a2dp_cos;
-      p_scb->media_type = AVDT_MEDIA_TYPE_AUDIO;
-      cs.cfg.psc_mask = AVDT_PSC_TRANS;
-      cs.media_type = AVDT_MEDIA_TYPE_AUDIO;
-      cs.mtu = p_bta_av_cfg->audio_mtu;
-      cs.flush_to = L2CAP_DEFAULT_FLUSH_TO;
-      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;
+    avdtp_stream_config.scb_index = p_scb->hdi;
+    avdtp_stream_config.p_avdt_ctrl_cback = &bta_av_proc_stream_evt;
 
-      if (bta_av_cb.features & BTA_AV_FEAT_REPORT) {
-        cs.cfg.psc_mask |= AVDT_PSC_REPORT;
-        cs.p_report_cback = bta_av_a2dp_report_cback;
+    /* set up the audio stream control block */
+    p_scb->p_act_tbl = (const tBTA_AV_ACT*)bta_av_a2dp_action;
+    p_scb->p_cos = &bta_av_a2dp_cos;
+    p_scb->media_type = AVDT_MEDIA_TYPE_AUDIO;
+    avdtp_stream_config.cfg.psc_mask = AVDT_PSC_TRANS;
+    avdtp_stream_config.media_type = AVDT_MEDIA_TYPE_AUDIO;
+    avdtp_stream_config.mtu = p_bta_av_cfg->audio_mtu;
+    avdtp_stream_config.flush_to = L2CAP_DEFAULT_FLUSH_TO;
+    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 (bta_av_cb.features & BTA_AV_FEAT_REPORT) {
+      avdtp_stream_config.cfg.psc_mask |= AVDT_PSC_REPORT;
+      avdtp_stream_config.p_report_cback = bta_av_a2dp_report_cback;
+    }
+    if (bta_av_cb.features & BTA_AV_FEAT_DELAY_RPT)
+      avdtp_stream_config.cfg.psc_mask |= AVDT_PSC_DELAY_RPT;
+
+    if (profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) {
+      avdtp_stream_config.tsep = AVDT_TSEP_SRC;
+      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) {
+      avdtp_stream_config.tsep = AVDT_TSEP_SNK;
+      avdtp_stream_config.p_sink_data_cback = bta_av_sink_data_cback;
+      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 < BTAV_A2DP_CODEC_INDEX_MAX; xx++) {
+      p_scb->seps[xx].av_handle = 0;
+    }
+
+    /* keep the configuration in the stream control block */
+    p_scb->cfg = avdtp_stream_config.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, &avdtp_stream_config.cfg)) {
+        continue;
       }
-      if (bta_av_cb.features & BTA_AV_FEAT_DELAY_RPT)
-        cs.cfg.psc_mask |= AVDT_PSC_DELAY_RPT;
-
-      if (profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) {
-        cs.tsep = AVDT_TSEP_SRC;
-        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_index_min = BTAV_A2DP_CODEC_INDEX_SINK_MIN;
-        codec_index_max = BTAV_A2DP_CODEC_INDEX_SINK_MAX;
+      if (AVDT_CreateStream(p_scb->app_id, &p_scb->seps[codec_index].av_handle,
+                            avdtp_stream_config) != AVDT_SUCCESS) {
+        continue;
       }
-
-      /* Initialize handles to zero */
-      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_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_index].av_handle, &cs) !=
-            AVDT_SUCCESS) {
-          continue;
-        }
-        /* Save a copy of the codec */
-        memcpy(p_scb->seps[codec_index].codec_info, cs.cfg.codec_info,
-               AVDT_CODEC_SIZE);
-        p_scb->seps[codec_index].tsep = cs.tsep;
-        if (cs.tsep == AVDT_TSEP_SNK) {
-          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_index].p_app_sink_data_cback = NULL;
-        }
-      }
-
-      if (!bta_av_cb.reg_audio) {
-        bta_av_cb.sdp_a2dp_handle = 0;
-        bta_av_cb.sdp_a2dp_snk_handle = 0;
-        if (profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) {
-          /* create the SDP records on the 1st audio channel */
-          bta_av_cb.sdp_a2dp_handle = SDP_CreateRecord();
-          A2DP_AddRecord(UUID_SERVCLASS_AUDIO_SOURCE, p_service_name, NULL,
-                         A2DP_SUPF_PLAYER, bta_av_cb.sdp_a2dp_handle);
-          bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SOURCE);
-        } else if (profile_initialized == UUID_SERVCLASS_AUDIO_SINK) {
-#if (BTA_AV_SINK_INCLUDED == TRUE)
-          bta_av_cb.sdp_a2dp_snk_handle = SDP_CreateRecord();
-          A2DP_AddRecord(UUID_SERVCLASS_AUDIO_SINK, p_service_name, NULL,
-                         A2DP_SUPF_PLAYER, bta_av_cb.sdp_a2dp_snk_handle);
-          bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SINK);
-#endif
-        }
-        /* start listening when A2DP is registered */
-        if (bta_av_cb.features & BTA_AV_FEAT_RCTG)
-          bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1);
-
-        /* if the AV and AVK are both supported, it cannot support the CT role
+      /* Save a copy of the codec */
+      memcpy(p_scb->seps[codec_index].codec_info,
+             avdtp_stream_config.cfg.codec_info, AVDT_CODEC_SIZE);
+      p_scb->seps[codec_index].tsep = avdtp_stream_config.tsep;
+      if (avdtp_stream_config.tsep == AVDT_TSEP_SNK) {
+        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.
          */
-        if (bta_av_cb.features & (BTA_AV_FEAT_RCCT)) {
-          /* if TG is not supported, we need to register to AVCT now */
-          if ((bta_av_cb.features & (BTA_AV_FEAT_RCTG)) == 0) {
+        p_scb->seps[codec_index].p_app_sink_data_cback = NULL;
+      }
+    }
+
+    if (!bta_av_cb.reg_audio) {
+      bta_av_cb.sdp_a2dp_handle = 0;
+      bta_av_cb.sdp_a2dp_snk_handle = 0;
+      if (profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) {
+        /* create the SDP records on the 1st audio channel */
+        bta_av_cb.sdp_a2dp_handle = SDP_CreateRecord();
+        A2DP_AddRecord(UUID_SERVCLASS_AUDIO_SOURCE, p_service_name, NULL,
+                       A2DP_SUPF_PLAYER, bta_av_cb.sdp_a2dp_handle);
+        bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SOURCE);
+      } else if (profile_initialized == UUID_SERVCLASS_AUDIO_SINK) {
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+        bta_av_cb.sdp_a2dp_snk_handle = SDP_CreateRecord();
+        A2DP_AddRecord(UUID_SERVCLASS_AUDIO_SINK, p_service_name, NULL,
+                       A2DP_SUPF_PLAYER, bta_av_cb.sdp_a2dp_snk_handle);
+        bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SINK);
+#endif
+      }
+      /* start listening when A2DP is registered */
+      if (bta_av_cb.features & BTA_AV_FEAT_RCTG)
+        bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1);
+
+      /* if the AV and AVK are both supported, it cannot support the CT role
+       */
+      if (bta_av_cb.features & (BTA_AV_FEAT_RCCT)) {
+        /* if TG is not supported, we need to register to AVCT now */
+        if ((bta_av_cb.features & (BTA_AV_FEAT_RCTG)) == 0) {
 #if (BTA_AR_INCLUDED == TRUE)
 #if (BTA_AV_WITH_AVCTP_AUTHORIZATION == TRUE)
-            bta_ar_reg_avct(p_bta_av_cfg->avrc_mtu, p_bta_av_cfg->avrc_br_mtu,
-                            bta_av_cb.sec_mask, BTA_ID_AV);
+          bta_ar_reg_avct(p_bta_av_cfg->avrc_mtu, p_bta_av_cfg->avrc_br_mtu,
+                          bta_av_cb.sec_mask, BTA_ID_AV);
 #else
-            bta_ar_reg_avct(
-                p_bta_av_cfg->avrc_mtu, p_bta_av_cfg->avrc_br_mtu,
-                (uint8_t)(bta_av_cb.sec_mask & (~BTA_SEC_AUTHORIZE)),
-                BTA_ID_AV);
+          bta_ar_reg_avct(p_bta_av_cfg->avrc_mtu, p_bta_av_cfg->avrc_br_mtu,
+                          (uint8_t)(bta_av_cb.sec_mask & (~BTA_SEC_AUTHORIZE)),
+                          BTA_ID_AV);
 #endif
 #endif
-            bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1);
-          }
-#if (BTA_AR_INCLUDED == TRUE)
-          /* create an SDP record as AVRC CT. We create 1.3 for SOURCE
-           * because we rely on feature bits being scanned by external
-           * devices more than the profile version itself.
-           *
-           * We create 1.4 for SINK since we support browsing.
-           */
-          if (profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) {
-            bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL, NULL,
-                            p_bta_av_cfg->avrc_ct_cat, BTA_ID_AV,
-                            (bta_av_cb.features & BTA_AV_FEAT_BROWSE),
-                            AVRC_REV_1_3);
-          } else if (profile_initialized == UUID_SERVCLASS_AUDIO_SINK) {
-            bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL, NULL,
-                            p_bta_av_cfg->avrc_ct_cat, BTA_ID_AV,
-                            (bta_av_cb.features & BTA_AV_FEAT_BROWSE),
-                            AVRC_REV_1_6);
-          }
-#endif
+          bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1);
         }
+#if (BTA_AR_INCLUDED == TRUE)
+        /* create an SDP record as AVRC CT. We create 1.3 for SOURCE
+         * because we rely on feature bits being scanned by external
+         * devices more than the profile version itself.
+         *
+         * We create 1.4 for SINK since we support browsing.
+         */
+        if (profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) {
+          bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL, NULL,
+                          p_bta_av_cfg->avrc_ct_cat, BTA_ID_AV,
+                          (bta_av_cb.features & BTA_AV_FEAT_BROWSE),
+                          AVRC_REV_1_3);
+        } else if (profile_initialized == UUID_SERVCLASS_AUDIO_SINK) {
+          bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL, NULL,
+                          p_bta_av_cfg->avrc_ct_cat, BTA_ID_AV,
+                          (bta_av_cb.features & BTA_AV_FEAT_BROWSE),
+                          AVRC_REV_1_6);
+        }
+#endif
       }
-      bta_av_cb.reg_audio |= BTA_AV_HNDL_TO_MSK(p_scb->hdi);
-      APPL_TRACE_DEBUG("reg_audio: 0x%x", bta_av_cb.reg_audio);
-    } else {
-      bta_av_cb.reg_video = BTA_AV_HNDL_TO_MSK(p_scb->hdi);
-      bta_av_cb.sdp_vdp_handle = SDP_CreateRecord();
-      /* register the video channel */
-      /* no need to verify the function pointer here. it's verified prior */
-      (*p_bta_av_cfg->p_reg)(&cs, p_service_name, p_scb);
     }
+    bta_av_cb.reg_audio |= BTA_AV_HNDL_TO_MSK(p_scb->hdi);
+    APPL_TRACE_DEBUG("%s: reg_audio: 0x%x", __func__, bta_av_cb.reg_audio);
   } while (0);
 
   /* call callback with register event */
@@ -691,7 +672,7 @@
   tBTA_AV_SCB* p_scb = bta_av_hndl_to_scb(p_data->hdr.layer_specific);
 
   if (p_scb) {
-    p_scb->deregistring = true;
+    p_scb->deregistering = true;
     bta_av_ssm_execute(p_scb, BTA_AV_API_CLOSE_EVT, p_data);
   } else {
     bta_av_dereg_comp(p_data);
@@ -745,12 +726,13 @@
  *
  ******************************************************************************/
 static void bta_av_api_to_ssm(tBTA_AV_DATA* p_data) {
-  int xx;
   uint16_t event =
       p_data->hdr.event - BTA_AV_FIRST_A2S_API_EVT + BTA_AV_FIRST_A2S_SSM_EVT;
+  tBTA_AV_HNDL handle = p_data->hdr.layer_specific;
+  tBTA_AV_SCB* p_scb = bta_av_hndl_to_scb(handle);
 
-  for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) {
-    bta_av_ssm_execute(bta_av_cb.p_scb[xx], event, p_data);
+  if (p_scb != nullptr) {
+    bta_av_ssm_execute(p_scb, event, p_data);
   }
 }
 
@@ -760,37 +742,31 @@
  *
  * Description      if this is audio channel, check if more than one audio
  *                  channel is connected & already started.
+ *                  This function needs to be kept very similar to
+ *                  bta_av_chk_2nd_start
  *
  * Returns          true, if need api_start
  *
  ******************************************************************************/
 bool bta_av_chk_start(tBTA_AV_SCB* p_scb) {
   bool start = false;
-  tBTA_AV_SCB* p_scbi;
-  int i;
 
-  if (p_scb->chnl == BTA_AV_CHNL_AUDIO) {
-    if ((bta_av_cb.audio_open_cnt >= 2) &&
-        ((0 ==
-          (p_scb->role & BTA_AV_ROLE_AD_ACP)) || /* Outgoing connection or   */
-         (bta_av_cb.features &
-          BTA_AV_FEAT_ACP_START))) /* auto-starting option     */
-    {
-      /* more than one audio channel is connected */
-      /* if this is the 2nd stream as ACP, give INT a chance to issue the START
-       * command */
-      for (i = 0; i < BTA_AV_NUM_STRS; i++) {
-        p_scbi = bta_av_cb.p_scb[i];
-        if (p_scbi && p_scbi->chnl == BTA_AV_CHNL_AUDIO && p_scbi->co_started) {
-          start = true;
-          /* may need to update the flush timeout of this already started stream
-           */
-          if (p_scbi->co_started != bta_av_cb.audio_open_cnt) {
-            p_scbi->co_started = bta_av_cb.audio_open_cnt;
-            L2CA_SetFlushTimeout(
-                p_scbi->peer_addr,
-                p_bta_av_cfg->p_audio_flush_to[p_scbi->co_started - 1]);
-          }
+  if ((p_scb->chnl == BTA_AV_CHNL_AUDIO) && (bta_av_cb.audio_open_cnt >= 2) &&
+      (((p_scb->role & BTA_AV_ROLE_AD_ACP) == 0) ||  // Outgoing connection or
+       (bta_av_cb.features & BTA_AV_FEAT_ACP_START))) {  // Auto-starting option
+    // More than one audio channel is connected.
+    // If this is the 2nd stream as ACP, give INT a chance to issue the START
+    // command.
+    for (int i = 0; i < BTA_AV_NUM_STRS; i++) {
+      tBTA_AV_SCB* p_scbi = bta_av_cb.p_scb[i];
+      if (p_scbi && p_scbi->chnl == BTA_AV_CHNL_AUDIO && p_scbi->co_started) {
+        start = true;
+        // May need to update the flush timeout of this already started stream
+        if (p_scbi->co_started != bta_av_cb.audio_open_cnt) {
+          p_scbi->co_started = bta_av_cb.audio_open_cnt;
+          L2CA_SetFlushTimeout(
+              p_scbi->peer_addr,
+              p_bta_av_cfg->p_audio_flush_to[p_scbi->co_started - 1]);
         }
       }
     }
@@ -813,7 +789,7 @@
   int i;
   uint8_t mask;
 
-  APPL_TRACE_DEBUG("reg_audio: 0x%x", bta_av_cb.reg_audio);
+  APPL_TRACE_DEBUG("%s: reg_audio: 0x%x", __func__, bta_av_cb.reg_audio);
   for (i = 0; i < BTA_AV_NUM_STRS; i++) {
     mask = BTA_AV_HNDL_TO_MSK(i);
     if (p_cb->conn_audio == mask) {
@@ -843,7 +819,10 @@
   uint8_t cur_role;
   uint8_t peer_idx = 0;
 
-  APPL_TRACE_DEBUG("bta_av_sys_rs_cback: %d", bta_av_cb.rs_idx);
+  APPL_TRACE_DEBUG(
+      "%s: peer %s new_role:%d hci_status:0x%x bta_av_cb.rs_idx:%d", __func__,
+      peer_addr.ToString().c_str(), id, app_id, bta_av_cb.rs_idx);
+
   for (i = 0; i < BTA_AV_NUM_STRS; i++) {
     /* loop through all the SCBs to find matching peer addresses and report the
      * role change event */
@@ -852,8 +831,9 @@
     if (p_scb && p_scb->peer_addr == peer_addr) {
       tBTA_AV_ROLE_RES* p_buf =
           (tBTA_AV_ROLE_RES*)osi_malloc(sizeof(tBTA_AV_ROLE_RES));
-      APPL_TRACE_DEBUG("new_role:%d, hci_status:x%x hndl: x%x", id, app_id,
-                       p_scb->hndl);
+      APPL_TRACE_DEBUG(
+          "%s: peer %s found: new_role:%d, hci_status:0x%x hndl:0x%x", __func__,
+          peer_addr.ToString().c_str(), id, app_id, p_scb->hndl);
       /*
       if ((id != BTM_ROLE_MASTER) && (app_id != HCI_SUCCESS))
       {
@@ -886,13 +866,20 @@
       p_scb = bta_av_cb.p_scb[bta_av_cb.rs_idx - 1];
     }
     if (p_scb && p_scb->q_tag == BTA_AV_Q_TAG_OPEN) {
-      APPL_TRACE_DEBUG("bta_av_sys_rs_cback: rs_idx(%d), hndl:x%x q_tag: %d",
-                       bta_av_cb.rs_idx, p_scb->hndl, p_scb->q_tag);
+      APPL_TRACE_DEBUG("%s: peer %s rs_idx:%d, hndl:0x%x q_tag:%d", __func__,
+                       p_scb->peer_addr.ToString().c_str(), bta_av_cb.rs_idx,
+                       p_scb->hndl, p_scb->q_tag);
 
-      if (HCI_SUCCESS == app_id || HCI_ERR_NO_CONNECTION == app_id)
+      if (HCI_SUCCESS == app_id || HCI_ERR_NO_CONNECTION == app_id) {
         p_scb->q_info.open.switch_res = BTA_AV_RS_OK;
-      else
+      } else {
+        APPL_TRACE_ERROR(
+            "%s: peer %s (p_scb peer %s) role switch failed: new_role:%d "
+            "hci_status:0x%x",
+            __func__, peer_addr.ToString().c_str(),
+            p_scb->peer_addr.ToString().c_str(), id, app_id);
         p_scb->q_info.open.switch_res = BTA_AV_RS_FAIL;
+      }
 
       /* Continue av open process */
       bta_av_do_disc_a2dp(p_scb, (tBTA_AV_DATA*)&(p_scb->q_info.open));
@@ -920,7 +907,7 @@
   int i;
   tBTA_AV_API_STOP stop;
 
-  APPL_TRACE_DEBUG("bta_av_sco_chg_cback:%d status:%d", id, status);
+  APPL_TRACE_DEBUG("%s: id:%d status:%d", __func__, id, status);
   if (id) {
     bta_av_cb.sco_occupied = true;
 
@@ -929,7 +916,7 @@
       p_scb = bta_av_cb.p_scb[i];
 
       if (p_scb && p_scb->co_started && (!p_scb->sco_suspend)) {
-        APPL_TRACE_DEBUG("suspending scb:%d", i);
+        APPL_TRACE_DEBUG("%s: suspending scb:%d", __func__, i);
         /* scb is used and started, not suspended automatically */
         p_scb->sco_suspend = true;
         stop.flush = false;
@@ -946,7 +933,7 @@
 
       if (p_scb && p_scb->sco_suspend) /* scb is used and suspended for SCO */
       {
-        APPL_TRACE_DEBUG("starting scb:%d", i);
+        APPL_TRACE_DEBUG("%s: starting scb:%d", __func__, i);
         bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL);
       }
     }
@@ -974,9 +961,8 @@
   for (i = 0; i < BTA_AV_NUM_STRS; i++) {
     mask = BTA_AV_HNDL_TO_MSK(i);
     p_scbi = bta_av_cb.p_scb[i];
-    if (p_scbi && (p_scb->hdi != i) &&    /* not the original channel */
-        ((bta_av_cb.conn_audio & mask) || /* connected audio */
-         (bta_av_cb.conn_video & mask)))  /* connected video */
+    if (p_scbi && (p_scb->hdi != i) &&   /* not the original channel */
+        ((bta_av_cb.conn_audio & mask))) /* connected audio */
     {
       BTM_GetRole(p_scbi->peer_addr, &role);
       /* this channel is open - clear the role switch link policy for this link
@@ -1018,7 +1004,8 @@
   bool is_ok = true;
 
   if (BTM_GetRole(p_scb->peer_addr, &role) == BTM_SUCCESS) {
-    LOG_INFO(LOG_TAG, "%s hndl:x%x role:%d conn_audio:x%x bits:%d features:x%x",
+    LOG_INFO(LOG_TAG,
+             "%s: hndl:x%x role:%d conn_audio:x%x bits:%d features:x%x",
              __func__, p_scb->hndl, role, bta_av_cb.conn_audio, bits,
              bta_av_cb.features);
     if (BTM_ROLE_MASTER != role &&
@@ -1066,15 +1053,15 @@
         if ((p_scb != p_scbi) && p_scbi &&
             (p_scbi->chnl == BTA_AV_CHNL_AUDIO)) {
           mask = BTA_AV_HNDL_TO_MSK(i);
-          APPL_TRACE_DEBUG("[%d] mtu: %d, mask:0x%x", i, p_scbi->stream_mtu,
-                           mask);
+          APPL_TRACE_DEBUG("%s: [%d] mtu: %d, mask:0x%x", __func__, i,
+                           p_scbi->stream_mtu, mask);
           if (bta_av_cb.conn_audio & mask) {
             if (ret_mtu > p_scbi->stream_mtu) ret_mtu = p_scbi->stream_mtu;
           }
         }
       }
     }
-    APPL_TRACE_DEBUG("bta_av_chk_mtu audio count:%d, conn_audio:0x%x, ret:%d",
+    APPL_TRACE_DEBUG("%s: count:%d, conn_audio:0x%x, ret:%d", __func__,
                      bta_av_cb.audio_open_cnt, bta_av_cb.conn_audio, ret_mtu);
   }
   return ret_mtu;
@@ -1111,7 +1098,7 @@
 
     if (list_length(p_scbi->a2dp_list) > p_bta_av_cfg->audio_mqs) {
       // Drop the oldest packet
-      bta_av_co_audio_drop(p_scbi->hndl);
+      bta_av_co_audio_drop(p_scbi->hndl, p_scbi->peer_addr);
       BT_HDR* p_buf_drop = static_cast<BT_HDR*>(list_front(p_scbi->a2dp_list));
       list_remove(p_scbi->a2dp_list, p_buf_drop);
       osi_free(p_buf_drop);
@@ -1144,12 +1131,13 @@
 
   /* set next state */
   p_cb->state = state_table[event][BTA_AV_NEXT_STATE];
-  APPL_TRACE_EVENT("next state=%d event offset:%d", p_cb->state, event);
+  APPL_TRACE_EVENT("%s: next state=%d event offset:%d", __func__, p_cb->state,
+                   event);
 
   /* execute action functions */
   action = state_table[event][BTA_AV_ACTION_COL];
   if (action != BTA_AV_IGNORE) {
-    APPL_TRACE_EVENT("%s action executed %d", __func__, action);
+    APPL_TRACE_EVENT("%s: action executed %d", __func__, action);
     (*bta_av_action[action])(p_cb, p_data);
   }
 }
@@ -1181,7 +1169,7 @@
     /* state machine events */
     bta_av_sm_execute(&bta_av_cb, p_msg->event, (tBTA_AV_DATA*)p_msg);
   } else {
-    APPL_TRACE_VERBOSE("handle=0x%x", p_msg->layer_specific);
+    APPL_TRACE_VERBOSE("%s: handle=0x%x", __func__, p_msg->layer_specific);
     /* stream state machine events */
     bta_av_ssm_execute(bta_av_hndl_to_scb(p_msg->layer_specific), p_msg->event,
                        (tBTA_AV_DATA*)p_msg);
@@ -1346,3 +1334,110 @@
       return "unknown";
   }
 }
+
+void bta_debug_av_dump(int fd) {
+  dprintf(fd, "\nBTA AV State:\n");
+  dprintf(fd, "  State Machine State: %s\n", bta_av_st_code(bta_av_cb.state));
+  dprintf(fd, "  Link signalling timer: %s\n",
+          alarm_is_scheduled(bta_av_cb.link_signalling_timer)
+              ? "Scheduled"
+              : "Not scheduled");
+  dprintf(fd, "  Accept signalling timer: %s\n",
+          alarm_is_scheduled(bta_av_cb.accept_signalling_timer)
+              ? "Scheduled"
+              : "Not scheduled");
+  dprintf(fd, "  SDP A2DP source handle: %d\n", bta_av_cb.sdp_a2dp_handle);
+  dprintf(fd, "  SDP A2DP sink handle: %d\n", bta_av_cb.sdp_a2dp_snk_handle);
+  dprintf(fd, "  Features: 0x%x\n", bta_av_cb.features);
+  dprintf(fd, "  Security mask: 0x%x\n", bta_av_cb.sec_mask);
+  dprintf(fd, "  SDP handle: %d\n", bta_av_cb.handle);
+  dprintf(fd, "  Disabling: %s\n", bta_av_cb.disabling ? "true" : "false");
+  dprintf(fd, "  SCO occupied: %s\n",
+          bta_av_cb.sco_occupied ? "true" : "false");
+  dprintf(fd, "  Connected audio channels: %d\n", bta_av_cb.audio_open_cnt);
+  dprintf(fd, "  Connected audio channels mask: 0x%x\n", bta_av_cb.conn_audio);
+  dprintf(fd, "  Streaming audio channels mask: 0x%x\n",
+          bta_av_cb.audio_streams);
+  dprintf(fd, "  Registered audio channels mask: 0x%x\n", bta_av_cb.reg_audio);
+  dprintf(fd, "  Connected LCBs mask: 0x%x\n", bta_av_cb.conn_lcb);
+
+  for (size_t i = 0; i < sizeof(bta_av_cb.lcb) / sizeof(bta_av_cb.lcb[0]);
+       i++) {
+    const tBTA_AV_LCB& lcb = bta_av_cb.lcb[i];
+    dprintf(fd, "\n  Link control block: %zu peer: %s\n", i,
+            lcb.addr.ToString().c_str());
+    dprintf(fd, "    Connected stream handle mask: 0x%x\n", lcb.conn_msk);
+    dprintf(fd, "    Index(+1) to LCB: %d\n", lcb.lidx);
+  }
+  for (size_t i = 0; i < BTA_AV_NUM_STRS; i++) {
+    const tBTA_AV_SCB* p_scb = bta_av_cb.p_scb[i];
+    if (p_scb == nullptr) {
+      continue;
+    }
+    dprintf(fd, "\n  BTA ID: %zu peer: %s\n", i,
+            p_scb->peer_addr.ToString().c_str());
+    dprintf(fd, "    SDP discovery started: %s\n",
+            p_scb->sdp_discovery_started ? "true" : "false");
+    for (size_t j = 0; j < BTAV_A2DP_CODEC_INDEX_MAX; j++) {
+      const tBTA_AV_SEP& sep = p_scb->seps[j];
+      dprintf(fd, "    SEP ID: %zu\n", j);
+      dprintf(fd, "      SEP AVDTP handle: %d\n", sep.av_handle);
+      dprintf(fd, "      Local SEP type: %d\n", sep.tsep);
+      dprintf(fd, "      Codec: %s\n", A2DP_CodecName(sep.codec_info));
+    }
+    dprintf(fd, "    BTA info tag: %d\n", p_scb->q_tag);
+    dprintf(fd, "    API Open peer: %s\n",
+            p_scb->q_info.open.bd_addr.ToString().c_str());
+    dprintf(fd, "      Use AVRCP: %s\n",
+            p_scb->q_info.open.use_rc ? "true" : "false");
+    dprintf(fd, "      Security mask: 0x%x\n", p_scb->q_info.open.sec_mask);
+    dprintf(fd, "      Switch result: %d\n", p_scb->q_info.open.switch_res);
+    dprintf(fd, "      Initiator UUID: 0x%x\n", p_scb->q_info.open.uuid);
+    dprintf(fd, "    Saved API Open peer: %s\n",
+            p_scb->open_api.bd_addr.ToString().c_str());
+    dprintf(fd, "      Use AVRCP: %s\n",
+            p_scb->open_api.use_rc ? "true" : "false");
+    dprintf(fd, "      Security mask: 0x%x\n", p_scb->open_api.sec_mask);
+    dprintf(fd, "      Switch result: %d\n", p_scb->open_api.switch_res);
+    dprintf(fd, "      Initiator UUID: 0x%x\n", p_scb->open_api.uuid);
+    // TODO: Print p_scb->sep_info[], cfg, avrc_ct_timer, current_codec ?
+    dprintf(fd, "    L2CAP Channel ID: %d\n", p_scb->l2c_cid);
+    dprintf(fd, "    Stream MTU: %d\n", p_scb->stream_mtu);
+    dprintf(fd, "    AVDTP version: 0x%x\n", p_scb->avdt_version);
+    dprintf(fd, "    Security mask: 0x%x\n", p_scb->sec_mask);
+    dprintf(fd, "    Media type: %d\n", p_scb->media_type);
+    dprintf(fd, "    Congested: %s\n", p_scb->cong ? "true" : "false");
+    dprintf(fd, "    Open status: %d\n", p_scb->open_status);
+    dprintf(fd, "    Channel: %d\n", p_scb->chnl);
+    dprintf(fd, "    BTA handle: %d\n", p_scb->hndl);
+    dprintf(fd, "    Protocol service capabilities mask: 0x%x\n",
+            p_scb->cur_psc_mask);
+    dprintf(fd, "    AVDTP handle: %d\n", p_scb->avdt_handle);
+    dprintf(fd, "    Stream control block index: %d\n", p_scb->hdi);
+    dprintf(fd, "    State machine state: %s(%d)\n",
+            bta_av_sst_code(p_scb->state), p_scb->state);
+    dprintf(fd, "    AVDTP label: 0x%x\n", p_scb->avdt_label);
+    dprintf(fd, "    Application ID: %d\n", p_scb->app_id);
+    dprintf(fd, "    Role: 0x%x\n", p_scb->role);
+    dprintf(fd, "    Queued L2CAP buffers: %d\n", p_scb->l2c_bufs);
+    dprintf(fd, "    AVRCP allowed: %s\n", p_scb->use_rc ? "true" : "false");
+    dprintf(fd, "    Stream started: %s\n", p_scb->started ? "true" : "false");
+    dprintf(fd, "    Stream call-out started: %d\n", p_scb->co_started);
+    dprintf(fd, "    AVDTP Reconfig supported: %s\n",
+            p_scb->recfg_sup ? "true" : "false");
+    dprintf(fd, "    AVDTP Suspend supported: %s\n",
+            p_scb->suspend_sup ? "true" : "false");
+    dprintf(fd, "    Deregistering: %s\n",
+            p_scb->deregistering ? "true" : "false");
+    dprintf(fd, "    SCO automatic Suspend: %s\n",
+            p_scb->sco_suspend ? "true" : "false");
+    dprintf(fd, "    Incoming/outgoing connection collusion mask: 0x%x\n",
+            p_scb->coll_mask);
+    dprintf(fd, "    Wait mask: 0x%x\n", p_scb->wait);
+    dprintf(fd, "    Don't use RTP header: %s\n",
+            p_scb->no_rtp_header ? "true" : "false");
+    dprintf(fd, "    Intended UUID of Initiator to connect to: 0x%x\n",
+            p_scb->uuid_int);
+    dprintf(fd, "    Skip SDP: %s\n", p_scb->skip_sdp ? "true" : "false");
+  }
+}
diff --git a/bta/av/bta_av_ssm.cc b/bta/av/bta_av_ssm.cc
index 79b37b3..afa78cc 100644
--- a/bta/av/bta_av_ssm.cc
+++ b/bta/av/bta_av_ssm.cc
@@ -107,8 +107,8 @@
 /* state table for init state */
 static const uint8_t bta_av_sst_init[][BTA_AV_NUM_COLS] = {
     /* Event                     Action 1               Action 2 Next state */
-    /* AP_OPEN_EVT */ {BTA_AV_DO_DISC, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
-    /* AP_CLOSE_EVT */ {BTA_AV_CLEANUP, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
+    /* API_OPEN_EVT */ {BTA_AV_DO_DISC, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* API_CLOSE_EVT */ {BTA_AV_CLEANUP, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* API_RECONFIG_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
@@ -117,8 +117,8 @@
     /* API_RC_OPEN_EVT  */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
-    /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                 BTA_AV_INIT_SST},
+    /* CI_SETCONFIG_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* SDP_DISC_OK_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* STR_DISC_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
@@ -130,179 +130,180 @@
     /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* STR_CLOSE_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
-    /* STR_CONFIG_IND_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE,
-                              BTA_AV_INIT_SST},
-    /* STR_SECURITY_IND_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_INIT_SST},
-    /* STR_SECURITY_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_INIT_SST},
+    /* STR_CONFIG_IND_EVT */
+    {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
+    /* STR_SECURITY_IND_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
+    /* STR_SECURITY_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
-    /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_INIT_SST},
+    /* STR_RECONFIG_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* AVRC_TIMER_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* AVDT_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* AVDT_DISCONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* ROLE_CHANGE_EVT*/ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* AVDT_DELAY_RPT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
-    /* API_OFFLOAD_START_EVT */ {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE,
-                                 BTA_AV_INIT_SST},
-    /* API_OFFLOAD_START_RSP_EVT */ {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE,
-                                     BTA_AV_INIT_SST}};
+    /* API_OFFLOAD_START_EVT */
+    {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
+    /* API_OFFLOAD_START_RSP_EVT */
+    {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE, BTA_AV_INIT_SST}};
 
 /* state table for incoming state */
 static const uint8_t bta_av_sst_incoming[][BTA_AV_NUM_COLS] = {
     /* Event                     Action 1               Action 2 Next state */
-    /* AP_OPEN_EVT */ {BTA_AV_OPEN_AT_INC, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
-    /* AP_CLOSE_EVT */ {BTA_AV_CCO_CLOSE, BTA_AV_DISCONNECT_REQ,
-                        BTA_AV_CLOSING_SST},
+    /* API_OPEN_EVT */ {BTA_AV_OPEN_AT_INC, BTA_AV_SIGNORE,
+                        BTA_AV_INCOMING_SST},
+    /* API_CLOSE_EVT */
+    {BTA_AV_CCO_CLOSE, BTA_AV_DISCONNECT_REQ, BTA_AV_CLOSING_SST},
     /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
     /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
-    /* API_RECONFIG_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                            BTA_AV_INCOMING_SST},
-    /* API_PROTECT_REQ_EVT */ {BTA_AV_SECURITY_REQ, BTA_AV_SIGNORE,
-                               BTA_AV_INCOMING_SST},
-    /* API_PROTECT_RSP_EVT */ {BTA_AV_SECURITY_RSP, BTA_AV_SIGNORE,
-                               BTA_AV_INCOMING_SST},
-    /* API_RC_OPEN_EVT  */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                            BTA_AV_INCOMING_SST},
-    /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                              BTA_AV_INCOMING_SST},
-    /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SETCONFIG_RSP, BTA_AV_ST_RC_TIMER,
-                               BTA_AV_INCOMING_SST},
-    /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_CLEANUP,
-                                 BTA_AV_INIT_SST},
-    /* SDP_DISC_OK_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE,
-                           BTA_AV_INCOMING_SST},
-    /* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE,
-                             BTA_AV_INCOMING_SST},
-    /* STR_DISC_OK_EVT */ {BTA_AV_DISC_RES_AS_ACP, BTA_AV_SIGNORE,
-                           BTA_AV_INCOMING_SST},
-    /* STR_DISC_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                             BTA_AV_INCOMING_SST},
-    /* STR_GETCAP_OK_EVT */ {BTA_AV_SAVE_CAPS, BTA_AV_SIGNORE,
-                             BTA_AV_INCOMING_SST},
-    /* STR_GETCAP_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                               BTA_AV_INCOMING_SST},
+    /* API_RECONFIG_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* API_PROTECT_REQ_EVT */
+    {BTA_AV_SECURITY_REQ, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* API_PROTECT_RSP_EVT */
+    {BTA_AV_SECURITY_RSP, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* API_RC_OPEN_EVT  */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* SRC_DATA_READY_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* CI_SETCONFIG_OK_EVT */
+    {BTA_AV_SETCONFIG_RSP, BTA_AV_ST_RC_TIMER, BTA_AV_INCOMING_SST},
+    /* CI_SETCONFIG_FAIL_EVT */
+    {BTA_AV_SETCONFIG_REJ, BTA_AV_CLEANUP, BTA_AV_INIT_SST},
+    /* SDP_DISC_OK_EVT */
+    {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* SDP_DISC_FAIL_EVT */
+    {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_DISC_OK_EVT */
+    {BTA_AV_DISC_RES_AS_ACP, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_DISC_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_GETCAP_OK_EVT */
+    {BTA_AV_SAVE_CAPS, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_GETCAP_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
     /* STR_OPEN_OK_EVT */ {BTA_AV_STR_OPENED, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
-    /* STR_OPEN_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                             BTA_AV_INCOMING_SST},
-    /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                            BTA_AV_INCOMING_SST},
-    /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                              BTA_AV_INCOMING_SST},
+    /* STR_OPEN_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_START_OK_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_START_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
     /* STR_CLOSE_EVT */ {BTA_AV_CCO_CLOSE, BTA_AV_CLEANUP, BTA_AV_INIT_SST},
-    /* STR_CONFIG_IND_EVT */ {BTA_AV_CONFIG_IND, BTA_AV_SIGNORE,
-                              BTA_AV_INCOMING_SST},
-    /* STR_SECURITY_IND_EVT */ {BTA_AV_SECURITY_IND, BTA_AV_SIGNORE,
-                                BTA_AV_INCOMING_SST},
-    /* STR_SECURITY_CFM_EVT */ {BTA_AV_SECURITY_CFM, BTA_AV_SIGNORE,
-                                BTA_AV_INCOMING_SST},
-    /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                             BTA_AV_INCOMING_SST},
-    /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                               BTA_AV_INCOMING_SST},
-    /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_INCOMING_SST},
+    /* STR_CONFIG_IND_EVT */
+    {BTA_AV_CONFIG_IND, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_SECURITY_IND_EVT */
+    {BTA_AV_SECURITY_IND, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_SECURITY_CFM_EVT */
+    {BTA_AV_SECURITY_CFM, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_WRITE_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_SUSPEND_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_RECONFIG_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
     /* AVRC_TIMER_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
-    /* AVDT_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                            BTA_AV_INCOMING_SST},
-    /* AVDT_DISCONNECT_EVT */ {BTA_AV_CCO_CLOSE, BTA_AV_DISCONNECT_REQ,
-                               BTA_AV_CLOSING_SST},
+    /* AVDT_CONNECT_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* AVDT_DISCONNECT_EVT */
+    {BTA_AV_CCO_CLOSE, BTA_AV_DISCONNECT_REQ, BTA_AV_CLOSING_SST},
     /* ROLE_CHANGE_EVT*/ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
-    /* AVDT_DELAY_RPT_EVT */ {BTA_AV_DELAY_CO, BTA_AV_SIGNORE,
-                              BTA_AV_INCOMING_SST},
+    /* AVDT_DELAY_RPT_EVT */
+    {BTA_AV_DELAY_CO, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
     /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
-    /* API_OFFLOAD_START_EVT */ {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE,
-                                 BTA_AV_INCOMING_SST},
-    /* API_OFFLOAD_START_RSP_EVT */ {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE,
-                                     BTA_AV_INCOMING_SST}};
+    /* API_OFFLOAD_START_EVT */
+    {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* API_OFFLOAD_START_RSP_EVT */
+    {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST}};
 
 /* state table for opening state */
 static const uint8_t bta_av_sst_opening[][BTA_AV_NUM_COLS] = {
     /* Event                     Action 1               Action 2 Next state */
-    /* AP_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
-    /* AP_CLOSE_EVT */ {BTA_AV_DO_CLOSE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* API_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* API_CLOSE_EVT */ {BTA_AV_DO_CLOSE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
     /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
     /* API_RECONFIG_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
-    /* API_PROTECT_REQ_EVT */ {BTA_AV_SECURITY_REQ, BTA_AV_SIGNORE,
-                               BTA_AV_OPENING_SST},
-    /* API_PROTECT_RSP_EVT */ {BTA_AV_SECURITY_RSP, BTA_AV_SIGNORE,
-                               BTA_AV_OPENING_SST},
+    /* API_PROTECT_REQ_EVT */
+    {BTA_AV_SECURITY_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* API_PROTECT_RSP_EVT */
+    {BTA_AV_SECURITY_RSP, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
     /* API_RC_OPEN_EVT  */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
-    /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                              BTA_AV_OPENING_SST},
-    /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                               BTA_AV_OPENING_SST},
-    /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                 BTA_AV_OPENING_SST},
-    /* SDP_DISC_OK_EVT */ {BTA_AV_CONNECT_REQ, BTA_AV_SIGNORE,
-                           BTA_AV_OPENING_SST},
-    /* SDP_DISC_FAIL_EVT */ {BTA_AV_CONNECT_REQ, BTA_AV_SIGNORE,
-                             BTA_AV_OPENING_SST},
-    /* STR_DISC_OK_EVT */ {BTA_AV_DISC_RESULTS, BTA_AV_SIGNORE,
-                           BTA_AV_OPENING_SST},
-    /* STR_DISC_FAIL_EVT */ {BTA_AV_OPEN_FAILED, BTA_AV_SIGNORE,
-                             BTA_AV_CLOSING_SST},
-    /* STR_GETCAP_OK_EVT */ {BTA_AV_GETCAP_RESULTS, BTA_AV_SIGNORE,
-                             BTA_AV_OPENING_SST},
-    /* STR_GETCAP_FAIL_EVT */ {BTA_AV_OPEN_FAILED, BTA_AV_SIGNORE,
-                               BTA_AV_CLOSING_SST},
-    /* STR_OPEN_OK_EVT */ {BTA_AV_ST_RC_TIMER, BTA_AV_STR_OPENED,
-                           BTA_AV_OPEN_SST},
-    /* STR_OPEN_FAIL_EVT */ {BTA_AV_OPEN_FAILED, BTA_AV_SIGNORE,
-                             BTA_AV_CLOSING_SST},
+    /* SRC_DATA_READY_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* CI_SETCONFIG_OK_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* CI_SETCONFIG_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* SDP_DISC_OK_EVT */
+    {BTA_AV_CONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* SDP_DISC_FAIL_EVT */
+    {BTA_AV_CONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* STR_DISC_OK_EVT */
+    {BTA_AV_DISC_RESULTS, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* STR_DISC_FAIL_EVT */
+    {BTA_AV_OPEN_FAILED, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_GETCAP_OK_EVT */
+    {BTA_AV_GETCAP_RESULTS, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* STR_GETCAP_FAIL_EVT */
+    {BTA_AV_OPEN_FAILED, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_OPEN_OK_EVT */
+    {BTA_AV_ST_RC_TIMER, BTA_AV_STR_OPENED, BTA_AV_OPEN_SST},
+    /* STR_OPEN_FAIL_EVT */
+    {BTA_AV_OPEN_FAILED, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
-    /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                              BTA_AV_OPENING_SST},
+    /* STR_START_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
     /* STR_CLOSE_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
-    /* STR_CONFIG_IND_EVT */ {BTA_AV_CONFIG_IND, BTA_AV_SIGNORE,
-                              BTA_AV_INCOMING_SST},
-    /* STR_SECURITY_IND_EVT */ {BTA_AV_SECURITY_IND, BTA_AV_SIGNORE,
-                                BTA_AV_OPENING_SST},
-    /* STR_SECURITY_CFM_EVT */ {BTA_AV_SECURITY_CFM, BTA_AV_SIGNORE,
-                                BTA_AV_OPENING_SST},
-    /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                             BTA_AV_OPENING_SST},
-    /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                               BTA_AV_OPENING_SST},
-    /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_OPENING_SST},
-    /* AVRC_TIMER_EVT */ {BTA_AV_SWITCH_ROLE, BTA_AV_SIGNORE,
-                          BTA_AV_OPENING_SST},
-    /* AVDT_CONNECT_EVT */ {BTA_AV_DISCOVER_REQ, BTA_AV_SIGNORE,
-                            BTA_AV_OPENING_SST},
-    /* AVDT_DISCONNECT_EVT */ {BTA_AV_CONN_FAILED, BTA_AV_SIGNORE,
-                               BTA_AV_INIT_SST},
+    /* STR_CONFIG_IND_EVT */
+    {BTA_AV_CONFIG_IND, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST},
+    /* STR_SECURITY_IND_EVT */
+    {BTA_AV_SECURITY_IND, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* STR_SECURITY_CFM_EVT */
+    {BTA_AV_SECURITY_CFM, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* STR_WRITE_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* STR_SUSPEND_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* STR_RECONFIG_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* AVRC_TIMER_EVT */
+    {BTA_AV_SWITCH_ROLE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* AVDT_CONNECT_EVT */
+    {BTA_AV_DISCOVER_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* AVDT_DISCONNECT_EVT */
+    {BTA_AV_CONN_FAILED, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* ROLE_CHANGE_EVT*/ {BTA_AV_ROLE_RES, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
-    /* AVDT_DELAY_RPT_EVT */ {BTA_AV_DELAY_CO, BTA_AV_SIGNORE,
-                              BTA_AV_OPENING_SST},
+    /* AVDT_DELAY_RPT_EVT */
+    {BTA_AV_DELAY_CO, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
     /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
-    /* API_OFFLOAD_START_EVT */ {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE,
-                                 BTA_AV_OPENING_SST},
-    /* API_OFFLOAD_START_RSP_EVT */ {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE,
-                                     BTA_AV_OPENING_SST}};
+    /* API_OFFLOAD_START_EVT */
+    {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST},
+    /* API_OFFLOAD_START_RSP_EVT */
+    {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE, BTA_AV_OPENING_SST}};
 
 /* state table for open state */
 static const uint8_t bta_av_sst_open[][BTA_AV_NUM_COLS] = {
     /* Event                     Action 1               Action 2 Next state */
-    /* AP_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
-    /* AP_CLOSE_EVT */ {BTA_AV_DO_CLOSE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* API_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
+    /* API_CLOSE_EVT */ {BTA_AV_DO_CLOSE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* AP_START_EVT */ {BTA_AV_DO_START, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* AP_STOP_EVT */ {BTA_AV_STR_STOPPED, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* API_RECONFIG_EVT */ {BTA_AV_RECONFIG, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
-    /* API_PROTECT_REQ_EVT */ {BTA_AV_SECURITY_REQ, BTA_AV_SIGNORE,
-                               BTA_AV_OPEN_SST},
-    /* API_PROTECT_RSP_EVT */ {BTA_AV_SECURITY_RSP, BTA_AV_SIGNORE,
-                               BTA_AV_OPEN_SST},
+    /* API_PROTECT_REQ_EVT */
+    {BTA_AV_SECURITY_REQ, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
+    /* API_PROTECT_RSP_EVT */
+    {BTA_AV_SECURITY_RSP, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* API_RC_OPEN_EVT  */ {BTA_AV_SET_USE_RC, BTA_AV_OPEN_RC, BTA_AV_OPEN_SST},
-    /* SRC_DATA_READY_EVT */ {BTA_AV_DATA_PATH, BTA_AV_SIGNORE,
-                              BTA_AV_OPEN_SST},
+    /* SRC_DATA_READY_EVT */
+    {BTA_AV_DATA_PATH, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
-    /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                 BTA_AV_OPEN_SST},
+    /* CI_SETCONFIG_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* SDP_DISC_OK_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* STR_DISC_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
@@ -312,40 +313,40 @@
     /* STR_OPEN_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* STR_OPEN_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* STR_START_OK_EVT */ {BTA_AV_START_OK, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
-    /* STR_START_FAIL_EVT */ {BTA_AV_START_FAILED, BTA_AV_SIGNORE,
-                              BTA_AV_OPEN_SST},
+    /* STR_START_FAIL_EVT */
+    {BTA_AV_START_FAILED, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* STR_CLOSE_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
-    /* STR_CONFIG_IND_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE,
-                              BTA_AV_OPEN_SST},
-    /* STR_SECURITY_IND_EVT */ {BTA_AV_SECURITY_IND, BTA_AV_SIGNORE,
-                                BTA_AV_OPEN_SST},
-    /* STR_SECURITY_CFM_EVT */ {BTA_AV_SECURITY_CFM, BTA_AV_SIGNORE,
-                                BTA_AV_OPEN_SST},
-    /* STR_WRITE_CFM_EVT */ {BTA_AV_CLR_CONG, BTA_AV_DATA_PATH,
-                             BTA_AV_OPEN_SST},
-    /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SUSPEND_CFM, BTA_AV_SIGNORE,
-                               BTA_AV_OPEN_SST},
-    /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_OPEN_SST},
-    /* AVRC_TIMER_EVT */ {BTA_AV_OPEN_RC, BTA_AV_CHK_2ND_START,
-                          BTA_AV_OPEN_SST},
+    /* STR_CONFIG_IND_EVT */
+    {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
+    /* STR_SECURITY_IND_EVT */
+    {BTA_AV_SECURITY_IND, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
+    /* STR_SECURITY_CFM_EVT */
+    {BTA_AV_SECURITY_CFM, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
+    /* STR_WRITE_CFM_EVT */
+    {BTA_AV_CLR_CONG, BTA_AV_DATA_PATH, BTA_AV_OPEN_SST},
+    /* STR_SUSPEND_CFM_EVT */
+    {BTA_AV_SUSPEND_CFM, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
+    /* STR_RECONFIG_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
+    /* AVRC_TIMER_EVT */
+    {BTA_AV_OPEN_RC, BTA_AV_CHK_2ND_START, BTA_AV_OPEN_SST},
     /* AVDT_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
-    /* AVDT_DISCONNECT_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE,
-                               BTA_AV_INIT_SST},
+    /* AVDT_DISCONNECT_EVT */
+    {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* ROLE_CHANGE_EVT*/ {BTA_AV_ROLE_RES, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* AVDT_DELAY_RPT_EVT */ {BTA_AV_DELAY_CO, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
     /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
-    /* API_OFFLOAD_START_EVT */ {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE,
-                                 BTA_AV_OPEN_SST},
-    /* API_OFFLOAD_START_RSP_EVT */ {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE,
-                                     BTA_AV_OPEN_SST}};
+    /* API_OFFLOAD_START_EVT */
+    {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
+    /* API_OFFLOAD_START_RSP_EVT */
+    {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE, BTA_AV_OPEN_SST}};
 
 /* state table for reconfig state */
 static const uint8_t bta_av_sst_rcfg[][BTA_AV_NUM_COLS] = {
     /* Event                     Action 1               Action 2 Next state */
-    /* AP_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
-    /* AP_CLOSE_EVT */ {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE,
-                        BTA_AV_CLOSING_SST},
+    /* API_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
+    /* API_CLOSE_EVT */
+    {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* API_RECONFIG_EVT */ {BTA_AV_RECONFIG, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
@@ -354,109 +355,109 @@
     /* API_RC_OPEN_EVT  */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
-    /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                 BTA_AV_RCFG_SST},
+    /* CI_SETCONFIG_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* SDP_DISC_OK_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
-    /* STR_DISC_OK_EVT */ {BTA_AV_DISC_RESULTS, BTA_AV_SIGNORE,
-                           BTA_AV_RCFG_SST},
-    /* STR_DISC_FAIL_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE,
-                             BTA_AV_INIT_SST},
-    /* STR_GETCAP_OK_EVT */ {BTA_AV_GETCAP_RESULTS, BTA_AV_SIGNORE,
-                             BTA_AV_RCFG_SST},
-    /* STR_GETCAP_FAIL_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE,
-                               BTA_AV_INIT_SST},
+    /* STR_DISC_OK_EVT */
+    {BTA_AV_DISC_RESULTS, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
+    /* STR_DISC_FAIL_EVT */
+    {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
+    /* STR_GETCAP_OK_EVT */
+    {BTA_AV_GETCAP_RESULTS, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
+    /* STR_GETCAP_FAIL_EVT */
+    {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* STR_OPEN_OK_EVT */ {BTA_AV_RCFG_STR_OK, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
-    /* STR_OPEN_FAIL_EVT */ {BTA_AV_RCFG_FAILED, BTA_AV_SIGNORE,
-                             BTA_AV_RCFG_SST},
+    /* STR_OPEN_FAIL_EVT */
+    {BTA_AV_RCFG_FAILED, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* STR_CLOSE_EVT */ {BTA_AV_RCFG_CONNECT, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
-    /* STR_CONFIG_IND_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE,
-                              BTA_AV_RCFG_SST},
-    /* STR_SECURITY_IND_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_RCFG_SST},
-    /* STR_SECURITY_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_RCFG_SST},
+    /* STR_CONFIG_IND_EVT */
+    {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
+    /* STR_SECURITY_IND_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
+    /* STR_SECURITY_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
-    /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SUSPEND_CFM, BTA_AV_SUSPEND_CONT,
-                               BTA_AV_RCFG_SST},
-    /* STR_RECONFIG_CFM_EVT */ {BTA_AV_RCFG_CFM, BTA_AV_SIGNORE,
-                                BTA_AV_RCFG_SST},
+    /* STR_SUSPEND_CFM_EVT */
+    {BTA_AV_SUSPEND_CFM, BTA_AV_SUSPEND_CONT, BTA_AV_RCFG_SST},
+    /* STR_RECONFIG_CFM_EVT */
+    {BTA_AV_RCFG_CFM, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* AVRC_TIMER_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* AVDT_CONNECT_EVT */ {BTA_AV_RCFG_OPEN, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
-    /* AVDT_DISCONNECT_EVT */ {BTA_AV_RCFG_DISCNTD, BTA_AV_SIGNORE,
-                               BTA_AV_RCFG_SST},
+    /* AVDT_DISCONNECT_EVT */
+    {BTA_AV_RCFG_DISCNTD, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* ROLE_CHANGE_EVT*/ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* AVDT_DELAY_RPT_EVT */ {BTA_AV_DELAY_CO, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
     /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
-    /* API_OFFLOAD_START_EVT */ {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE,
-                                 BTA_AV_RCFG_SST},
-    /* API_OFFLOAD_START_RSP_EVT */ {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE,
-                                     BTA_AV_RCFG_SST}};
+    /* API_OFFLOAD_START_EVT */
+    {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE, BTA_AV_RCFG_SST},
+    /* API_OFFLOAD_START_RSP_EVT */
+    {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE, BTA_AV_RCFG_SST}};
 
 /* state table for closing state */
 static const uint8_t bta_av_sst_closing[][BTA_AV_NUM_COLS] = {
     /* Event                     Action 1               Action 2 Next state */
-    /* AP_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
-    /* AP_CLOSE_EVT */ {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE,
-                        BTA_AV_CLOSING_SST},
+    /* API_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* API_CLOSE_EVT */
+    {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* AP_START_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* AP_STOP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* API_RECONFIG_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
-    /* API_PROTECT_REQ_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                               BTA_AV_CLOSING_SST},
-    /* API_PROTECT_RSP_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                               BTA_AV_CLOSING_SST},
+    /* API_PROTECT_REQ_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* API_PROTECT_RSP_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* API_RC_OPEN_EVT  */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
-    /* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                              BTA_AV_CLOSING_SST},
-    /* CI_SETCONFIG_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                               BTA_AV_CLOSING_SST},
-    /* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                 BTA_AV_CLOSING_SST},
+    /* SRC_DATA_READY_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* CI_SETCONFIG_OK_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* CI_SETCONFIG_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* SDP_DISC_OK_EVT */ {BTA_AV_SDP_FAILED, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
-    /* SDP_DISC_FAIL_EVT */ {BTA_AV_SDP_FAILED, BTA_AV_SIGNORE,
-                             BTA_AV_INIT_SST},
+    /* SDP_DISC_FAIL_EVT */
+    {BTA_AV_SDP_FAILED, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* STR_DISC_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
-    /* STR_DISC_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                             BTA_AV_CLOSING_SST},
-    /* STR_GETCAP_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                             BTA_AV_CLOSING_SST},
-    /* STR_GETCAP_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                               BTA_AV_CLOSING_SST},
+    /* STR_DISC_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_GETCAP_OK_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_GETCAP_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* STR_OPEN_OK_EVT */ {BTA_AV_DO_CLOSE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
-    /* STR_OPEN_FAIL_EVT */ {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE,
-                             BTA_AV_CLOSING_SST},
+    /* STR_OPEN_FAIL_EVT */
+    {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* STR_START_OK_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
-    /* STR_START_FAIL_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                              BTA_AV_CLOSING_SST},
-    /* STR_CLOSE_EVT */ {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE,
-                         BTA_AV_CLOSING_SST},
-    /* STR_CONFIG_IND_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE,
-                              BTA_AV_CLOSING_SST},
-    /* STR_SECURITY_IND_EVT */ {BTA_AV_SECURITY_REJ, BTA_AV_SIGNORE,
-                                BTA_AV_CLOSING_SST},
-    /* STR_SECURITY_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_CLOSING_SST},
-    /* STR_WRITE_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                             BTA_AV_CLOSING_SST},
-    /* STR_SUSPEND_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                               BTA_AV_CLOSING_SST},
-    /* STR_RECONFIG_CFM_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                                BTA_AV_CLOSING_SST},
+    /* STR_START_FAIL_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_CLOSE_EVT */
+    {BTA_AV_DISCONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_CONFIG_IND_EVT */
+    {BTA_AV_SETCONFIG_REJ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_SECURITY_IND_EVT */
+    {BTA_AV_SECURITY_REJ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_SECURITY_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_WRITE_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_SUSPEND_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* STR_RECONFIG_CFM_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* AVRC_TIMER_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* AVDT_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
-    /* AVDT_DISCONNECT_EVT */ {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE,
-                               BTA_AV_INIT_SST},
+    /* AVDT_DISCONNECT_EVT */
+    {BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
     /* ROLE_CHANGE_EVT*/ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
-    /* AVDT_DELAY_RPT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE,
-                              BTA_AV_CLOSING_SST},
+    /* AVDT_DELAY_RPT_EVT */
+    {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
     /* ACP_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
-    /* API_OFFLOAD_START_EVT */ {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE,
-                                 BTA_AV_CLOSING_SST},
-    /* API_OFFLOAD_START_RSP_EVT */ {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE,
-                                     BTA_AV_CLOSING_SST}};
+    /* API_OFFLOAD_START_EVT */
+    {BTA_AV_OFFLOAD_REQ, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST},
+    /* API_OFFLOAD_START_RSP_EVT */
+    {BTA_AV_OFFLOAD_RSP, BTA_AV_SIGNORE, BTA_AV_CLOSING_SST}};
 
 /* type for state table */
 typedef const uint8_t (*tBTA_AV_SST_TBL)[BTA_AV_NUM_COLS];
@@ -466,7 +467,6 @@
     bta_av_sst_init, bta_av_sst_incoming, bta_av_sst_opening,
     bta_av_sst_open, bta_av_sst_rcfg,     bta_av_sst_closing};
 
-static const char* bta_av_sst_code(uint8_t state);
 
 /*******************************************************************************
  *
@@ -480,49 +480,32 @@
  ******************************************************************************/
 void bta_av_ssm_execute(tBTA_AV_SCB* p_scb, uint16_t event,
                         tBTA_AV_DATA* p_data) {
-  tBTA_AV_SST_TBL state_table;
-  uint8_t action;
-  int i, xx;
-
   if (p_scb == NULL) {
     /* this stream is not registered */
-    APPL_TRACE_EVENT("AV channel not registered");
+    APPL_TRACE_EVENT("%s: AV channel not registered", __func__);
     return;
   }
 
-  /* In case incoming connection is for VDP, we need to swap scb.        */
-  /* When ACP_CONNECT_EVT was received, we put first available scb to    */
-  /* to Incoming state. Later, when STR_CONFIG_IND_EVT is coming, we     */
-  /* know if it is A2DP or VDP.                                          */
-  if ((p_scb->state == BTA_AV_INIT_SST) &&
-      (event == BTA_AV_STR_CONFIG_IND_EVT)) {
-    for (xx = 0; xx < BTA_AV_NUM_STRS; xx++) {
-      if (bta_av_cb.p_scb[xx]) {
-        if (bta_av_cb.p_scb[xx]->state == BTA_AV_INCOMING_SST) {
-          bta_av_cb.p_scb[xx]->state = BTA_AV_INIT_SST;
-          bta_av_cb.p_scb[xx]->coll_mask = 0;
-          p_scb->state = BTA_AV_INCOMING_SST;
-          break;
-        }
-      }
-    }
-  }
-
-  APPL_TRACE_VERBOSE("%s: AV Sevent(0x%x)=0x%x(%s) state=%d(%s)", __func__,
-                     p_scb->hndl, event, bta_av_evt_code(event), p_scb->state,
-                     bta_av_sst_code(p_scb->state));
+  APPL_TRACE_VERBOSE(
+      "%s: peer %s AV event(0x%x)=0x%x(%s) state=%d(%s) p_scb=%p", __func__,
+      p_scb->peer_addr.ToString().c_str(), p_scb->hndl, event,
+      bta_av_evt_code(event), p_scb->state, bta_av_sst_code(p_scb->state),
+      p_scb);
 
   /* look up the state table for the current state */
-  state_table = bta_av_sst_tbl[p_scb->state];
-
-  event -= BTA_AV_FIRST_SSM_EVT;
+  tBTA_AV_SST_TBL state_table = bta_av_sst_tbl[p_scb->state];
 
   /* set next state */
+  event -= BTA_AV_FIRST_SSM_EVT;
   p_scb->state = state_table[event][BTA_AV_SNEXT_STATE];
 
+  APPL_TRACE_VERBOSE("%s: peer %s AV next state=%d(%s) p_scb=%p", __func__,
+                     p_scb->peer_addr.ToString().c_str(), p_scb->state,
+                     bta_av_sst_code(p_scb->state), p_scb);
+
   /* execute action functions */
-  for (i = 0; i < BTA_AV_SACTIONS; i++) {
-    action = state_table[event][i];
+  for (int i = 0; i < BTA_AV_SACTIONS; i++) {
+    uint8_t action = state_table[event][i];
     if (action != BTA_AV_SIGNORE) {
       (*p_scb->p_act_tbl[action])(p_scb, p_data);
     } else
@@ -581,9 +564,19 @@
  *
  ******************************************************************************/
 void bta_av_set_scb_sst_init(tBTA_AV_SCB* p_scb) {
-  if (p_scb) {
-    p_scb->state = BTA_AV_INIT_SST;
+  if (p_scb == nullptr) {
+    return;
   }
+
+  uint8_t next_state = BTA_AV_INIT_SST;
+
+  APPL_TRACE_VERBOSE(
+      "%s: peer %s AV (hndl=0x%x) state=%d(%s) next state=%d(%s) p_scb=%p",
+      __func__, p_scb->peer_addr.ToString().c_str(), p_scb->hndl, p_scb->state,
+      bta_av_sst_code(p_scb->state), next_state, bta_av_sst_code(next_state),
+      p_scb);
+
+  p_scb->state = next_state;
 }
 
 /*******************************************************************************
@@ -634,7 +627,7 @@
  * Returns          char *
  *
  ******************************************************************************/
-static const char* bta_av_sst_code(uint8_t state) {
+const char* bta_av_sst_code(uint8_t state) {
   switch (state) {
     case BTA_AV_INIT_SST:
       return "INIT";
diff --git a/bta/include/bta_ar_api.h b/bta/include/bta_ar_api.h
index 18fe59e..b2a8753 100644
--- a/bta/include/bta_ar_api.h
+++ b/bta/include/bta_ar_api.h
@@ -60,7 +60,7 @@
  * Returns          void
  *
  ******************************************************************************/
-extern void bta_ar_reg_avdt(tAVDT_REG* p_reg, tAVDT_CTRL_CBACK* p_cback,
+extern void bta_ar_reg_avdt(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback,
                             tBTA_SYS_ID sys_id);
 
 /*******************************************************************************
@@ -86,7 +86,8 @@
  * Returns          void
  *
  ******************************************************************************/
-extern void bta_ar_avdt_conn(tBTA_SYS_ID sys_id, const RawAddress& bd_addr);
+extern void bta_ar_avdt_conn(tBTA_SYS_ID sys_id, const RawAddress& bd_addr,
+                             uint8_t scb_index);
 
 /*******************************************************************************
  *
diff --git a/bta/include/bta_av_api.h b/bta/include/bta_av_api.h
index 83ec437..07105ef 100644
--- a/bta/include/bta_av_api.h
+++ b/bta/include/bta_av_api.h
@@ -91,9 +91,9 @@
 /* handle index to mask */
 #define BTA_AV_HNDL_TO_MSK(h) ((uint8_t)(1 << (h)))
 
-/* maximum number of streams created: 1 for audio, 1 for video */
+/* maximum number of streams created */
 #ifndef BTA_AV_NUM_STRS
-#define BTA_AV_NUM_STRS 2
+#define BTA_AV_NUM_STRS 6
 #endif
 
 #ifndef BTA_AV_MAX_A2DP_MTU
@@ -101,10 +101,6 @@
 #define BTA_AV_MAX_A2DP_MTU 1008
 #endif
 
-#ifndef BTA_AV_MAX_VDP_MTU
-#define BTA_AV_MAX_VDP_MTU 1008
-#endif
-
 /* operation id list for BTA_AvRemoteCmd */
 typedef uint8_t tBTA_AV_RC;
 
@@ -199,7 +195,7 @@
   bool suspending;
 } tBTA_AV_START;
 
-/* data associated with BTA_AV_SUSPEND_EVT */
+/* data associated with BTA_AV_SUSPEND_EVT, BTA_AV_STOP_EVT */
 typedef struct {
   tBTA_AV_CHNL chnl;
   tBTA_AV_HNDL hndl;
@@ -367,9 +363,6 @@
 union tBTA_AV_DATA;
 typedef void (*tBTA_AV_ACT)(tBTA_AV_SCB* p_cb, tBTA_AV_DATA* p_data);
 
-/* type for registering VDP */
-typedef void(tBTA_AV_REG)(tAVDT_CS* p_cs, char* p_service_name, void* p_data);
-
 /* AV configuration structure */
 typedef struct {
   uint32_t company_id;  /* AVRCP Company ID */
@@ -382,8 +375,6 @@
   const uint16_t*
       p_audio_flush_to;    /* AVDTP audio transport channel flush timeout */
   uint16_t audio_mqs;      /* AVDTP audio channel max data queue size */
-  uint16_t video_mtu;      /* AVDTP video transport channel MTU at L2CAP */
-  uint16_t video_flush_to; /* AVDTP video transport channel flush timeout */
   bool avrc_group;     /* true, to accept AVRC 1.3 group nevigation command */
   uint8_t num_co_ids;  /* company id count in p_meta_co_ids */
   uint8_t num_evt_ids; /* event id count in p_meta_evt_ids */
@@ -393,8 +384,7 @@
       p_meta_co_ids; /* the metadata Get Capabilities response for company id */
   const uint8_t* p_meta_evt_ids; /* the the metadata Get Capabilities response
                                     for event id */
-  const tBTA_AV_ACT* p_act_tbl;  /* the action function table for VDP stream */
-  tBTA_AV_REG* p_reg;            /* action function to register VDP */
+  const tBTA_AV_ACT* p_act_tbl;  /* action function table for audio stream */
   char avrc_controller_name[BTA_SERVICE_NAME_LEN]; /* Default AVRCP controller
                                                       name */
   char avrc_target_name[BTA_SERVICE_NAME_LEN]; /* Default AVRCP target name*/
@@ -505,7 +495,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void BTA_AvStart(void);
+void BTA_AvStart(tBTA_AV_HNDL handle);
 
 /*******************************************************************************
  *
@@ -518,7 +508,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void BTA_AvStop(bool suspend);
+void BTA_AvStop(tBTA_AV_HNDL handle, bool suspend);
 
 /*******************************************************************************
  *
@@ -704,4 +694,12 @@
  ******************************************************************************/
 void BTA_AvOffloadStartRsp(tBTA_AV_HNDL hndl, tBTA_AV_STATUS status);
 
+/**
+ * Dump debug-related information for the BTA AV module.
+ *
+ * @param fd the file descriptor to use for writing the ASCII formatted
+ * information
+ */
+void bta_debug_av_dump(int fd);
+
 #endif /* BTA_AV_API_H */
diff --git a/bta/include/bta_av_ci.h b/bta/include/bta_av_ci.h
index 94b94f6..07b59e6 100644
--- a/bta/include/bta_av_ci.h
+++ b/bta/include/bta_av_ci.h
@@ -35,7 +35,7 @@
  *
  * Description      This function sends an event to the AV indicating that
  *                  the phone has audio stream data ready to send and AV
- *                  should call bta_av_co_audio_src_data_path().
+ *                  should call bta_av_co_audio_source_data_path().
  *
  * Returns          void
  *
@@ -55,7 +55,7 @@
  * Returns          void
  *
  ******************************************************************************/
-extern void bta_av_ci_setconfig(tBTA_AV_HNDL hndl, uint8_t err_code,
+extern void bta_av_ci_setconfig(tBTA_AV_HNDL bta_av_handle, uint8_t err_code,
                                 uint8_t category, uint8_t num_seid,
                                 uint8_t* p_seid, bool recfg_needed,
                                 uint8_t avdt_handle);
diff --git a/bta/include/bta_av_co.h b/bta/include/bta_av_co.h
index d5c979c..39bb4ff 100644
--- a/bta/include/bta_av_co.h
+++ b/bta/include/bta_av_co.h
@@ -45,7 +45,7 @@
  *
  ******************************************************************************/
 bool bta_av_co_audio_init(btav_a2dp_codec_index_t codec_index,
-                          tAVDT_CFG* p_cfg);
+                          AvdtpSepConfig* p_cfg);
 
 /*******************************************************************************
  *
@@ -59,9 +59,10 @@
  * Returns          void.
  *
  ******************************************************************************/
-void bta_av_co_audio_disc_res(tBTA_AV_HNDL hndl, uint8_t num_seps,
-                              uint8_t num_snk, uint8_t num_src,
-                              const RawAddress& addr, uint16_t uuid_local);
+void bta_av_co_audio_disc_res(tBTA_AV_HNDL bta_av_handle,
+                              const RawAddress& peer_address, uint8_t num_seps,
+                              uint8_t num_sinks, uint8_t num_sources,
+                              uint16_t uuid_local);
 
 /*******************************************************************************
  *
@@ -75,7 +76,9 @@
  * Returns          Stream codec and content protection configuration info.
  *
  ******************************************************************************/
-tA2DP_STATUS bta_av_co_audio_getconfig(tBTA_AV_HNDL hndl, uint8_t* p_codec_info,
+tA2DP_STATUS bta_av_co_audio_getconfig(tBTA_AV_HNDL bta_av_handle,
+                                       const RawAddress& peer_address,
+                                       uint8_t* p_codec_info,
                                        uint8_t* p_sep_info_idx, uint8_t seid,
                                        uint8_t* p_num_protect,
                                        uint8_t* p_protect_info);
@@ -92,8 +95,9 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, const uint8_t* p_codec_info,
-                               uint8_t seid, const RawAddress& addr,
+void bta_av_co_audio_setconfig(tBTA_AV_HNDL bta_av_handle,
+                               const RawAddress& peer_address,
+                               const uint8_t* p_codec_info, uint8_t seid,
                                uint8_t num_protect,
                                const uint8_t* p_protect_info,
                                uint8_t t_local_sep, uint8_t avdt_handle);
@@ -111,7 +115,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_av_co_audio_open(tBTA_AV_HNDL hndl, uint16_t mtu);
+void bta_av_co_audio_open(tBTA_AV_HNDL bta_av_handle,
+                          const RawAddress& peer_address, uint16_t mtu);
 
 /*******************************************************************************
  *
@@ -127,7 +132,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_av_co_audio_close(tBTA_AV_HNDL hndl);
+void bta_av_co_audio_close(tBTA_AV_HNDL bta_av_handle,
+                           const RawAddress& peer_address);
 
 /*******************************************************************************
  *
@@ -140,8 +146,9 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_av_co_audio_start(tBTA_AV_HNDL hndl, uint8_t* p_codec_info,
-                           bool* p_no_rtp_hdr);
+void bta_av_co_audio_start(tBTA_AV_HNDL bta_av_handle,
+                           const RawAddress& peer_address,
+                           const uint8_t* p_codec_info, bool* p_no_rtp_header);
 
 /*******************************************************************************
  *
@@ -154,11 +161,12 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_av_co_audio_stop(tBTA_AV_HNDL hndl);
+void bta_av_co_audio_stop(tBTA_AV_HNDL bta_av_handle,
+                          const RawAddress& peer_address);
 
 /*******************************************************************************
  *
- * Function         bta_av_co_audio_src_data_path
+ * Function         bta_av_co_audio_source_data_path
  *
  * Description      This function is called to get the next data buffer from
  *                  the audio codec
@@ -167,14 +175,14 @@
  *                  Otherwise, a buffer (BT_HDR*) containing the audio data.
  *
  ******************************************************************************/
-void* bta_av_co_audio_src_data_path(const uint8_t* p_codec_info,
-                                    uint32_t* p_timestamp);
+BT_HDR* bta_av_co_audio_source_data_path(const uint8_t* p_codec_info,
+                                         uint32_t* p_timestamp);
 
 /*******************************************************************************
  *
  * Function         bta_av_co_audio_drop
  *
- * Description      An Audio packet is dropped. .
+ * Description      An Audio packet is dropped.
  *                  It's very likely that the connected headset with this handle
  *                  is moved far away. The implementation may want to reduce
  *                  the encoder bit rate setting to reduce the packet size.
@@ -182,7 +190,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_av_co_audio_drop(tBTA_AV_HNDL hndl);
+void bta_av_co_audio_drop(tBTA_AV_HNDL bta_av_handle,
+                          const RawAddress& peer_address);
 
 /*******************************************************************************
  *
@@ -196,7 +205,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_av_co_audio_delay(tBTA_AV_HNDL hndl, uint16_t delay);
+void bta_av_co_audio_delay(tBTA_AV_HNDL bta_av_handle,
+                           const RawAddress& peer_address, uint16_t delay);
 
 /*******************************************************************************
  *
@@ -211,6 +221,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_av_co_audio_update_mtu(tBTA_AV_HNDL hndl, uint16_t mtu);
+void bta_av_co_audio_update_mtu(tBTA_AV_HNDL bta_av_handle,
+                                const RawAddress& peer_address, uint16_t mtu);
 
 #endif /* BTA_AV_CO_H */
diff --git a/bta/sys/bta_sys_conn.cc b/bta/sys/bta_sys_conn.cc
index 01cd055..3c3bc63 100644
--- a/bta/sys/bta_sys_conn.cc
+++ b/bta/sys/bta_sys_conn.cc
@@ -99,6 +99,8 @@
  ******************************************************************************/
 void bta_sys_notify_role_chg(const RawAddress& p_bda, uint8_t new_role,
                              uint8_t hci_status) {
+  APPL_TRACE_DEBUG("%s: peer %s new_role:%d hci_status:0x%x", __func__,
+                   p_bda.ToString().c_str(), new_role, hci_status);
   if (bta_sys_cb.p_role_cb) {
     bta_sys_cb.p_role_cb(BTA_SYS_ROLE_CHANGE, new_role, hci_status, p_bda);
   }
diff --git a/btif/Android.bp b/btif/Android.bp
index b6ab774..88b3cfa 100644
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -65,7 +65,6 @@
         "src/btif_rc.cc",
         "src/btif_sdp.cc",
         "src/btif_sdp_server.cc",
-        "src/btif_sm.cc",
         "src/btif_sock.cc",
         "src/btif_sock_rfc.cc",
         "src/btif_sock_l2cap.cc",
@@ -108,6 +107,7 @@
     srcs: ["test/btif_storage_test.cc"],
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
+        "libaudioclient",
         "libhidlbase",
         "liblog",
         "libprotobuf-cpp-lite",
@@ -115,17 +115,20 @@
         "libutils",
     ],
     static_libs: [
-        "libbtif",
         "libbt-bta",
         "libbtcore",
         "libbt-stack",
+        "libbt-sbc-encoder",
+        "libFraunhoferAAC",
         "libbtdevice",
         "libbt-hci",
+        "libudrv-uipc",
         "libbluetooth-types",
         "libosi",
         "libbt-protos",
     ],
     whole_static_libs: [
+        "libbtif",
         "libbluetooth-for-tests",
     ],
     cflags: ["-DBUILDCFG"],
@@ -152,3 +155,25 @@
     ],
     cflags: ["-DBUILDCFG"],
 }
+
+// btif state machine unit tests for target
+// ========================================================
+cc_test {
+    name: "net_test_btif_state_machine",
+    defaults: ["fluoride_defaults"],
+    include_dirs: btifCommonIncludes,
+    host_supported: true,
+    srcs: [
+      "test/btif_state_machine_test.cc"
+    ],
+    header_libs: ["libbluetooth_headers"],
+    shared_libs: [
+        "liblog",
+        "libcutils",
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libosi",
+    ],
+    cflags: ["-DBUILDCFG"],
+}
diff --git a/btif/BUILD.gn b/btif/BUILD.gn
index 598a512..f0c80a7 100644
--- a/btif/BUILD.gn
+++ b/btif/BUILD.gn
@@ -50,7 +50,6 @@
     "src/btif_rc.cc",
     "src/btif_sdp.cc",
     "src/btif_sdp_server.cc",
-    "src/btif_sm.cc",
     "src/btif_sock.cc",
     "src/btif_sock_l2cap.cc",
     "src/btif_sock_rfc.cc",
diff --git a/btif/co/bta_av_co.cc b/btif/co/bta_av_co.cc
index ce76c19..c149ced 100644
--- a/btif/co/bta_av_co.cc
+++ b/btif/co/bta_av_co.cc
@@ -23,407 +23,808 @@
  *
  ******************************************************************************/
 
-#include "bta_av_co.h"
+#include <mutex>
+
+#include <base/bind.h>
 #include <base/logging.h>
 #include <string.h>
+
+#include "bt_target.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_av_co.h"
 #include "bta_sys.h"
 
 #include "btif_av.h"
 #include "btif_av_co.h"
 #include "btif_util.h"
-#include "osi/include/mutex.h"
 #include "osi/include/osi.h"
 
-/*****************************************************************************
- **  Constants
- *****************************************************************************/
-
-/* Macro to retrieve the number of elements in a statically allocated array */
+// Macro to retrieve the number of elements in a statically allocated array
 #define BTA_AV_CO_NUM_ELEMENTS(__a) (sizeof(__a) / sizeof((__a)[0]))
 
-/* Macro to convert audio handle to index and vice versa */
-#define BTA_AV_CO_AUDIO_HNDL_TO_INDX(hndl) (((hndl) & (~BTA_AV_CHNL_MSK)) - 1)
-#define BTA_AV_CO_AUDIO_INDX_TO_HNDL(indx) (((indx) + 1) | BTA_AV_CHNL_AUDIO)
+// Macro to convert BTA AV audio handle to index and vice versa
+#define BTA_AV_CO_AUDIO_HANDLE_TO_INDEX(bta_av_handle) \
+  (((bta_av_handle) & (~BTA_AV_CHNL_MSK)) - 1)
+#define BTA_AV_CO_AUDIO_INDEX_TO_HANDLE(index) \
+  (((index) + 1) | BTA_AV_CHNL_AUDIO)
 
-/* SCMS-T protect info */
-const uint8_t bta_av_co_cp_scmst[AVDT_CP_INFO_LEN] = {0x02, 0x02, 0x00};
-
-/*****************************************************************************
- *  Local data
- ****************************************************************************/
-typedef struct {
-  uint8_t sep_info_idx;                   /* local SEP index (in BTA tables) */
-  uint8_t seid;                           /* peer SEP index (in peer tables) */
-  uint8_t codec_caps[AVDT_CODEC_SIZE];    /* peer SEP codec capabilities */
-  uint8_t num_protect;                    /* peer SEP number of CP elements */
-  uint8_t protect_info[AVDT_CP_INFO_LEN]; /* peer SEP content protection info */
-} tBTA_AV_CO_SINK;
-
-typedef struct {
-  RawAddress addr; /* address of audio/video peer */
-  tBTA_AV_CO_SINK
-      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 */
-  uint8_t num_rx_sinks;  /* number of received sinks */
-  uint8_t num_rx_srcs;   /* number of received srcs */
-  uint8_t num_sup_sinks; /* number of supported sinks in the sinks array */
-  uint8_t num_sup_srcs;  /* number of supported srcs in the srcs array */
-  const tBTA_AV_CO_SINK* p_sink;         /* currently selected sink */
-  const tBTA_AV_CO_SINK* p_src;          /* currently selected src */
-  uint8_t codec_config[AVDT_CODEC_SIZE]; /* current codec configuration */
-  bool cp_active;                        /* current CP configuration */
-  bool acp;                              /* acceptor */
-  bool reconfig_needed;                  /* reconfiguration is needed */
-  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 {
-  bool active;
-  uint8_t flag;
-} tBTA_AV_CO_CP;
-
-class BtaAvCoCb {
+class BtaAvCoSep {
  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;
-
-  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);
-    }
+  BtaAvCoSep()
+      : sep_info_idx(0), seid(0), codec_caps{}, num_protect(0), protect_info{} {
+    Reset();
   }
+
+  /**
+   * Reset the state.
+   */
+  void Reset() {
+    sep_info_idx = 0;
+    seid = 0;
+    memset(codec_caps, 0, sizeof(codec_caps));
+    num_protect = 0;
+    memset(protect_info, 0, sizeof(protect_info));
+  }
+
+  uint8_t sep_info_idx;                    // Local SEP index (in BTA tables)
+  uint8_t seid;                            // Peer SEP index (in peer tables)
+  uint8_t codec_caps[AVDT_CODEC_SIZE];     // Peer SEP codec capabilities
+  uint8_t num_protect;                     // Peer SEP number of CP elements
+  uint8_t protect_info[AVDT_CP_INFO_LEN];  // Peer SEP content protection info
 };
 
-/* Control block instance */
-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,
-                                              const uint8_t* p_protect_info);
-static const tBTA_AV_CO_SINK* bta_av_co_find_peer_src_supports_codec(
-    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(
-    A2dpCodecConfig& codec_config, tBTA_AV_CO_PEER* p_peer);
-static bool bta_av_co_audio_update_selectable_codec(
-    A2dpCodecConfig& codec_config, const 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);
-
-/*******************************************************************************
- **
- ** Function         bta_av_co_cp_get_flag
- **
- ** Description      Get content protection flag
- **                  AVDT_CP_SCMS_COPY_NEVER
- **                  AVDT_CP_SCMS_COPY_ONCE
- **                  AVDT_CP_SCMS_COPY_FREE
- **
- ** Returns          The current flag value
- **
- ******************************************************************************/
-static uint8_t bta_av_co_cp_get_flag(void) { return bta_av_co_cb.cp.flag; }
-
-/*******************************************************************************
- **
- ** Function         bta_av_co_cp_set_flag
- **
- ** Description      Set content protection flag
- **                  AVDT_CP_SCMS_COPY_NEVER
- **                  AVDT_CP_SCMS_COPY_ONCE
- **                  AVDT_CP_SCMS_COPY_FREE
- **
- ** Returns          true if setting the SCMS flag is supported else false
- **
- ******************************************************************************/
-static bool bta_av_co_cp_set_flag(uint8_t cp_flag) {
-  APPL_TRACE_DEBUG("%s: cp_flag = %d", __func__, cp_flag);
-
-#if (BTA_AV_CO_CP_SCMS_T == TRUE)
-#else
-  if (cp_flag != AVDT_CP_SCMS_COPY_FREE) {
-    return false;
+class BtaAvCoPeer {
+ public:
+  BtaAvCoPeer()
+      : addr(RawAddress::kEmpty),
+        num_sinks(0),
+        num_sources(0),
+        num_seps(0),
+        num_rx_sinks(0),
+        num_rx_sources(0),
+        num_sup_sinks(0),
+        num_sup_sources(0),
+        p_sink(nullptr),
+        p_source(nullptr),
+        codec_config{},
+        acceptor(false),
+        reconfig_needed(false),
+        opened(false),
+        mtu(0),
+        uuid_to_connect(0),
+        bta_av_handle_(0),
+        codecs_(nullptr),
+        content_protect_active_(false) {
+    Reset(0);
   }
+
+  /**
+   * Initialize the state.
+   *
+   * @param codec_priorities the codec priorities to use for the initialization
+   */
+  void Init(const std::vector<btav_a2dp_codec_config_t>& codec_priorities);
+
+  /**
+   * Reset the state.
+   *
+   * @param bta_av_handle the BTA AV handle to use
+   */
+  void Reset(tBTA_AV_HNDL bta_av_handle);
+
+  /**
+   * Get the BTA AV handle.
+   *
+   * @return the BTA AV handle
+   */
+  tBTA_AV_HNDL BtaAvHandle() const { return bta_av_handle_; }
+
+  /**
+   * Get the A2DP codecs.
+   *
+   * @return the A2DP codecs
+   */
+  A2dpCodecs* GetCodecs() { return codecs_; }
+
+  bool ContentProtectActive() const { return content_protect_active_; }
+  void SetContentProtectActive(bool cp_active) {
+    content_protect_active_ = cp_active;
+  }
+
+  RawAddress addr;                                // Peer address
+  BtaAvCoSep sinks[BTAV_A2DP_CODEC_INDEX_MAX];    // Supported sinks
+  BtaAvCoSep sources[BTAV_A2DP_CODEC_INDEX_MAX];  // Supported sources
+  uint8_t num_sinks;                      // Total number of sinks at peer
+  uint8_t num_sources;                    // Total number of sources at peer
+  uint8_t num_seps;                       // Total number of SEPs at peer
+  uint8_t num_rx_sinks;                   // Number of received sinks
+  uint8_t num_rx_sources;                 // Number of received sources
+  uint8_t num_sup_sinks;                  // Number of supported sinks
+  uint8_t num_sup_sources;                // Number of supported sources
+  const BtaAvCoSep* p_sink;               // Currently selected sink
+  const BtaAvCoSep* p_source;             // Currently selected source
+  uint8_t codec_config[AVDT_CODEC_SIZE];  // Current codec configuration
+  bool acceptor;                          // True if acceptor
+  bool reconfig_needed;                   // True if reconfiguration is needed
+  bool opened;                            // True if opened
+  uint16_t mtu;                           // Maximum Transmit Unit size
+  uint16_t uuid_to_connect;               // UUID of peer device
+
+ private:
+  tBTA_AV_HNDL bta_av_handle_;   // BTA AV handle to use
+  A2dpCodecs* codecs_;           // Locally supported codecs
+  bool content_protect_active_;  // True if Content Protect is active
+};
+
+class BtaAvCo {
+ public:
+  BtaAvCo(bool content_protect_enabled)
+      : active_peer_(nullptr),
+        codec_config_{},
+        content_protect_enabled_(content_protect_enabled),
+        content_protect_flag_(0) {
+    Reset();
+  }
+
+  /**
+   * Initialize the state.
+   *
+   * @param codec_priorities the codec priorities to use for the initialization
+   */
+  void Init(const std::vector<btav_a2dp_codec_config_t>& codec_priorities);
+
+  /**
+   * Get the current codec configuration for the active peer.
+   *
+   * @return the current codec configuration if found, otherwise nullptr
+   */
+  A2dpCodecConfig* GetActivePeerCurrentCodec();
+
+  /**
+   * Find the peer UUID for a given BTA AV handle.
+   *
+   * @param bta_av_handle the BTA AV handle to use
+   * @return the peer UUID if found, otherwise 0
+   */
+  uint16_t FindPeerUuid(tBTA_AV_HNDL bta_av_handle);
+
+  /**
+   * Process the AVDTP discovery result: number of Stream End Points (SEP)
+   * found during the AVDTP stream discovery process.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   * @param num_seps the number of discovered SEPs
+   * @param num_sinks number of discovered Sink SEPs
+   * @param num_sources number of discovered Source SEPs
+   * @param uuid_local local UUID
+   */
+  void ProcessDiscoveryResult(tBTA_AV_HNDL bta_av_handle,
+                              const RawAddress& peer_address, uint8_t num_seps,
+                              uint8_t num_sinks, uint8_t num_sources,
+                              uint16_t uuid_local);
+
+  /**
+   * Process retrieved codec configuration and content protection from
+   * Peer Source SEP.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   * @param p_codec_info the peer sink capability filled-in by the caller.
+   * On success, it will contain the current codec configuration for the peer.
+   * @param p_sep_info_idx the peer SEP index for the corresponding peer
+   * sink capability filled-in by the caller. On success, it will contain
+   * the SEP index for the current codec configuration for the peer.
+   * @param seid the peer SEP index in peer tables
+   * @param p_num_protect the peer SEP number of content protection elements
+   * filled-in by the caller. On success, it will contain the SEP number of
+   * content protection elements for the current codec configuration for the
+   * peer.
+   * @param p_protect_info the peer SEP content protection info filled-in by
+   * the caller. On success, it will contain the SEP content protection info
+   * for the current codec configuration for the peer.
+   * @return A2DP_SUCCESS on success, otherwise A2DP_FAIL
+   */
+  tA2DP_STATUS ProcessSourceGetConfig(tBTA_AV_HNDL bta_av_handle,
+                                      const RawAddress& peer_address,
+                                      uint8_t* p_codec_info,
+                                      uint8_t* p_sep_info_idx, uint8_t seid,
+                                      uint8_t* p_num_protect,
+                                      uint8_t* p_protect_info);
+
+  /**
+   * Process retrieved codec configuration and content protection from
+   * Peer Source SEP.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   * @param p_codec_info the peer source capability filled-in by the caller.
+   * On success, it will contain the current codec configuration for the peer.
+   * @param p_sep_info_idx the peer SEP index for the corresponding peer
+   * source capability filled-in by the caller. On success, it will contain
+   * the SEP index for the current codec configuration for the peer.
+   * @param seid the peer SEP index in peer tables
+   * @param p_num_protect the peer SEP number of content protection elements
+   * filled-in by the caller. On success, it will contain the SEP number of
+   * content protection elements for the current codec configuration for the
+   * peer.
+   * @param p_protect_info the peer SEP content protection info filled-in by
+   * the caller. On success, it will contain the SEP content protection info
+   * for the current codec configuration for the peer.
+   * @return A2DP_SUCCESS on success, otherwise A2DP_FAIL
+   */
+  tA2DP_STATUS ProcessSinkGetConfig(tBTA_AV_HNDL bta_av_handle,
+                                    const RawAddress& peer_address,
+                                    uint8_t* p_codec_info,
+                                    uint8_t* p_sep_info_idx, uint8_t seid,
+                                    uint8_t* p_num_protect,
+                                    uint8_t* p_protect_info);
+
+  /**
+   * Process AVDTP Set Config to set the codec and content protection
+   * configuration of the audio stream.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   * @param p_codec_info the codec configuration to set
+   * @param seid stream endpoint ID of stream initiating the operation
+   * @param peer_address the peer address
+   * @param num_protect the peer SEP number of content protection elements
+   * @param p_protect_info the peer SEP conntent protection info
+   * @param t_local_sep the local SEP: AVDT_TSEP_SRC or AVDT_TSEP_SNK
+   * @param avdt_handle the AVDTP handle
+   */
+  void ProcessSetConfig(tBTA_AV_HNDL bta_av_handle,
+                        const RawAddress& peer_address,
+                        const uint8_t* p_codec_info, uint8_t seid,
+                        uint8_t num_protect, const uint8_t* p_protect_info,
+                        uint8_t t_local_sep, uint8_t avdt_handle);
+
+  /**
+   * Process AVDTP Open when the stream connection is opened.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   * @param mtu the MTU of the connection
+   */
+  void ProcessOpen(tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address,
+                   uint16_t mtu);
+
+  /**
+   * Process AVDTP Close when the stream connection is closed.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   */
+  void ProcessClose(tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address);
+
+  /**
+   * Process AVDTP Start when the audio data streaming is started.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   * @param p_codec_info the codec configuration
+   * @param p_no_rtp_header on return, set to true if the audio data packets
+   * should not contain RTP header
+   */
+  void ProcessStart(tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address,
+                    const uint8_t* p_codec_info, bool* p_no_rtp_header);
+
+  /**
+   * Process AVDTP Stop when the audio data streaming is stopped.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   */
+  void ProcessStop(tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address);
+
+  /**
+   * Get the next encoded audio data packet to send.
+   *
+   * @param p_codec_info the codec configuration
+   * @param p_timestamp on return, set to the timestamp of the data packet
+   * @return the next encoded data packet or nullptr if no encoded data to send
+   */
+  BT_HDR* GetNextSourceDataPacket(const uint8_t* p_codec_info,
+                                  uint32_t* p_timestamp);
+
+  /**
+   * An audio packet has been dropped.
+   * This signal can be used by the encoder to reduce the encoder bit rate
+   * setting.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   */
+  void DataPacketWasDropped(tBTA_AV_HNDL bta_av_handle,
+                            const RawAddress& peer_address);
+
+  /**
+   * Process AVDTP Audio Delay when the initial delay report is received by
+   * the Source.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   * @param delay the reported delay in 1/10th of a millisecond
+   */
+  void ProcessAudioDelay(tBTA_AV_HNDL bta_av_handle,
+                         const RawAddress& peer_address, uint16_t delay);
+
+  /**
+   * Update the MTU of the audio data connection.
+   *
+   * @param bta_av_handle the BTA AV handle to identify the peer
+   * @param peer_address the peer address
+   * @param mtu the new MTU of the audio data connection
+   */
+  void UpdateMtu(tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address,
+                 uint16_t mtu);
+
+  /**
+   * Set the active peer.
+   *
+   * @param peer_address the peer address
+   * @return true on success, otherwise false
+   */
+  bool SetActivePeer(const RawAddress& peer_address);
+
+  /**
+   * Get the encoder parameters for a peer.
+   *
+   * @param peer_address the peer address
+   * @param p_peer_params on return, set to the peer's encoder parameters
+   */
+  void GetPeerEncoderParameters(const RawAddress& peer_address,
+                                tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params);
+
+  /**
+   * Get the Source encoder interface for the current codec.
+   *
+   * @return the Source encoder interface for the current codec
+   */
+  const tA2DP_ENCODER_INTERFACE* GetSourceEncoderInterface();
+
+  /**
+   * Get the Sink decoder interface for the current codec.
+   *
+   * @return the Sink decoder interface for the current codec
+   */
+  const tA2DP_DECODER_INTERFACE* GetSinkDecoderInterface();
+
+  /**
+   * Set the codec user configuration.
+   *
+   * @param peer_address the peer address
+   * @param codec_user_config the codec user configuration to set
+   * @return true on success, otherwise false
+   */
+  bool SetCodecUserConfig(const RawAddress& peer_address,
+                          const btav_a2dp_codec_config_t& codec_user_config);
+
+  /**
+   * Set the codec audio configuration.
+   *
+   * @param codec_audio_config the codec audio configuration to set
+   * @return true on success, otherwise false
+   */
+  bool SetCodecAudioConfig(const btav_a2dp_codec_config_t& codec_audio_config);
+
+  /**
+   * Report the source codec state for a peer
+   *
+   * @param p_peer the peer to report
+   * @return true on success, otherwise false
+   */
+  bool ReportSourceCodecState(BtaAvCoPeer* p_peer);
+
+  /**
+   * Get the content protection flag.
+   *
+   * @return the content protection flag. It should be one of the following:
+   * AVDT_CP_SCMS_COPY_NEVER, AVDT_CP_SCMS_COPY_ONCE, AVDT_CP_SCMS_COPY_FREE
+   */
+  uint8_t ContentProtectFlag() const { return content_protect_flag_; }
+
+  /**
+   * Set the content protection flag.
+   *
+   * @param cp_flag the content protection flag. It should be one of the
+   * following:
+   * AVDT_CP_SCMS_COPY_NEVER, AVDT_CP_SCMS_COPY_ONCE, AVDT_CP_SCMS_COPY_FREE
+   * NOTE: If Content Protection is not enabled on the system, then
+   * the only acceptable vailue is AVDT_CP_SCMS_COPY_FREE.
+   */
+  void SetContentProtectFlag(uint8_t cp_flag) {
+    if (!ContentProtectEnabled() && (cp_flag != AVDT_CP_SCMS_COPY_FREE)) {
+      return;
+    }
+    content_protect_flag_ = cp_flag;
+  }
+
+  /**
+   * Dump debug-related information.
+   *
+   * @param fd the file descritor to use for writing the ASCII formatted
+   * information
+   */
+  void DebugDump(int fd);
+
+ private:
+  /**
+   * Reset the state.
+   */
+  void Reset();
+
+  /**
+   * Find the peer entry for a given peer address.
+   *
+   * @param peer_address the peer address to use
+   * @return the peer entry if found, otherwise nullptr
+   */
+  BtaAvCoPeer* FindPeer(const RawAddress& peer_address);
+
+  /**
+   * Find the peer entry for a given BTA AV handle.
+   *
+   * @param bta_av_handle the BTA AV handle to use
+   * @return the peer entry if found, otherwise nullptr
+   */
+  BtaAvCoPeer* FindPeer(tBTA_AV_HNDL bta_av_handle);
+
+  /**
+   * Find the peer entry for a given BTA AV handle and update it with the
+   * peer address.
+   *
+   * @param bta_av_handle the BTA AV handle to use
+   * @param peer_address the peer address
+   * @return the peer entry if found, otherwise nullptr
+   */
+  BtaAvCoPeer* FindPeerAndUpdate(tBTA_AV_HNDL bta_av_handle,
+                                 const RawAddress& peer_address);
+
+  /**
+   * Find the Source Peer SEP that supports the current codec config.
+   *
+   * @param p_peer the peer to use
+   * @return the peer SEP that supports the current codec config, otherwise
+   * nullptr
+   */
+  const BtaAvCoSep* FindPeerSourceSepForCurrentCodec(const BtaAvCoPeer* p_peer);
+
+  /**
+   * Select the Source codec configuration based on peer codec support.
+   *
+   * Furthermore, the local state for the remaining non-selected codecs is
+   * updated to reflect whether the codec is selectable.
+   *
+   * @param p_peer the peer to use
+   * @return a pointer to the corresponding SEP Sink entry on success,
+   * otherwise nullptr
+   */
+  BtaAvCoSep* SelectSourceCodec(BtaAvCoPeer* p_peer);
+
+  /**
+   * Save new codec configuration.
+   *
+   * @param p_peer the peer to use
+   * @param new_codec_config the new codec configuration to use
+   * @param num_protect the number of content protection elements
+   * @param p_protect_info the content protection info to use
+   */
+  void SaveNewCodecConfig(BtaAvCoPeer* p_peer, const uint8_t* new_codec_config,
+                          uint8_t num_protect, const uint8_t* p_protect_info);
+
+  /**
+   * Set 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.
+   *
+   * @param p_peer is the peer device that sent the OTA codec configuration
+   * @param 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.
+   * @param num_protect is the number of content protection methods to use
+   * @param p_protect_info contains the content protection information to use.
+   * @param p_restart_output if there is a change in the encoder configuration
+   * that requires restarting of the A2DP connection, flag |p_restart_output|
+   * is set to true.
+   * @return true on success, otherwise false
+   */
+  bool SetCodecOtaConfig(BtaAvCoPeer* p_peer, const uint8_t* p_ota_codec_config,
+                         uint8_t num_protect, const uint8_t* p_protect_info,
+                         bool* p_restart_output);
+
+  /**
+   * Update a selectable codec with the corresponding codec information from
+   * a peer device.
+   *
+   * @param codec_config the codec config info to identify the codec to update
+   * @param p_peer the peer to use
+   * @return true if the codec is updated, otherwise false
+   */
+  bool UpdateSelectableCodec(const A2dpCodecConfig& codec_config,
+                             BtaAvCoPeer* p_peer);
+
+  /**
+   * Attempt to select codec configuration for a peer.
+   *
+   * @param codec_config the codec configuration to use
+   * @param p_peer the peer to use
+   * @return a pointer to the corresponding SEP Sink entry on success,
+   * otnerwise nullptr
+   */
+  BtaAvCoSep* AttemptCodecSelection(const A2dpCodecConfig& codec_config,
+                                    BtaAvCoPeer* p_peer);
+
+  /**
+   * Check if a Sink SEP has content protection enabled.
+   *
+   * @param p_sink the peer Sink SEP to check
+   * @return true if the peer Sink SEP has content protection enabled,
+   * otherwise false
+   */
+  bool AudioSinkHasContentProtection(const BtaAvCoSep* p_sink);
+
+  /**
+   * Check if a content protection service is SCMS-T.
+   *
+   * @param p_orotect_info the content protection info to check
+   * @return true if the Contention Protection in @param p_protect_info
+   * is SCMS-T, otherwise false
+   */
+  static bool ContentProtectIsScmst(const uint8_t* p_protect_info);
+
+  /**
+   * Check if audio protect info contains SCMS-T Content Protection.
+   *
+   * @param num_protect number of protect schemes
+   * @param p_protect_info the protect info to check
+   * @return true if @param p_protect_info contains SCMS-T, otherwise false
+   */
+  static bool AudioProtectHasScmst(uint8_t num_protect,
+                                   const uint8_t* p_protect_info);
+
+  bool ContentProtectEnabled() const { return content_protect_enabled_; }
+
+  std::recursive_mutex codec_lock_;  // Protect access to the codec state
+  std::vector<btav_a2dp_codec_config_t> codec_priorities_;  // Configured
+  BtaAvCoPeer peers_[BTA_AV_NUM_STRS];     // Connected peer information
+  BtaAvCoPeer* active_peer_;               // The current active peer
+  uint8_t codec_config_[AVDT_CODEC_SIZE];  // Current codec configuration
+  const bool content_protect_enabled_;     // True if Content Protect is enabled
+  uint8_t content_protect_flag_;           // Content Protect flag
+};
+
+// SCMS-T protect info
+const uint8_t bta_av_co_cp_scmst[AVDT_CP_INFO_LEN] = {0x02, 0x02, 0x00};
+
+// Control block instance
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+static const bool kContentProtectEnabled = true;
+#else
+static const bool kContentProtectEnabled = false;
 #endif
-  bta_av_co_cb.cp.flag = cp_flag;
-  return true;
+static BtaAvCo bta_av_co_cb(kContentProtectEnabled);
+
+void BtaAvCoPeer::Init(
+    const std::vector<btav_a2dp_codec_config_t>& codec_priorities) {
+  Reset(bta_av_handle_);
+
+  // Reset the current config
+  codecs_ = new A2dpCodecs(codec_priorities);
+  codecs_->init();
+  A2DP_InitDefaultCodec(codec_config);
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_get_peer
- **
- ** Description      find the peer entry for a given handle
- **
- ** Returns          the control block
- **
- ******************************************************************************/
-static tBTA_AV_CO_PEER* bta_av_co_get_peer(tBTA_AV_HNDL hndl) {
+void BtaAvCoPeer::Reset(tBTA_AV_HNDL bta_av_handle) {
+  addr = RawAddress::kEmpty;
+  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(sinks); i++) {
+    BtaAvCoSep& sink = sinks[i];
+    sink.Reset();
+  }
+  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(sources); i++) {
+    BtaAvCoSep& source = sources[i];
+    source.Reset();
+  }
+  num_sinks = 0;
+  num_sources = 0;
+  num_seps = 0;
+  num_rx_sinks = 0;
+  num_rx_sources = 0;
+  num_sup_sinks = 0;
+  num_sup_sources = 0;
+  p_sink = nullptr;
+  p_source = nullptr;
+  memset(codec_config, 0, sizeof(codec_config));
+  acceptor = false;
+  reconfig_needed = false;
+  opened = false;
+  mtu = 0;
+  uuid_to_connect = 0;
+
+  bta_av_handle_ = bta_av_handle;
+  delete codecs_;
+  codecs_ = nullptr;
+  content_protect_active_ = false;
+}
+
+void BtaAvCo::Init(
+    const std::vector<btav_a2dp_codec_config_t>& codec_priorities) {
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  std::lock_guard<std::recursive_mutex> lock(codec_lock_);
+
+  // Reset the control block
+  Reset();
+  codec_priorities_ = codec_priorities;
+
+  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
+    BtaAvCoPeer* p_peer = &peers_[i];
+    p_peer->Init(codec_priorities);
+  }
+}
+
+void BtaAvCo::Reset() {
+  codec_priorities_.clear();
+  active_peer_ = nullptr;
+  content_protect_flag_ = 0;
+  memset(codec_config_, 0, sizeof(codec_config_));
+
+  if (ContentProtectEnabled()) {
+    SetContentProtectFlag(AVDT_CP_SCMS_COPY_NEVER);
+  } else {
+    SetContentProtectFlag(AVDT_CP_SCMS_COPY_FREE);
+  }
+
+  // Reset the peers and initialize the handles
+  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
+    BtaAvCoPeer* p_peer = &peers_[i];
+    p_peer->Reset(BTA_AV_CO_AUDIO_INDEX_TO_HANDLE(i));
+  }
+}
+
+A2dpCodecConfig* BtaAvCo::GetActivePeerCurrentCodec() {
+  std::lock_guard<std::recursive_mutex> lock(codec_lock_);
+
+  if (active_peer_ == nullptr || active_peer_->GetCodecs() == nullptr) {
+    return nullptr;
+  }
+  return active_peer_->GetCodecs()->getCurrentCodecConfig();
+}
+
+BtaAvCoPeer* BtaAvCo::FindPeer(const RawAddress& peer_address) {
+  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
+    BtaAvCoPeer* p_peer = &peers_[i];
+    if (p_peer->addr == peer_address) {
+      return p_peer;
+    }
+  }
+  return nullptr;
+}
+
+BtaAvCoPeer* BtaAvCo::FindPeer(tBTA_AV_HNDL bta_av_handle) {
   uint8_t index;
 
-  index = BTA_AV_CO_AUDIO_HNDL_TO_INDX(hndl);
+  index = BTA_AV_CO_AUDIO_HANDLE_TO_INDEX(bta_av_handle);
 
-  APPL_TRACE_DEBUG("%s: handle = %d index = %d", __func__, hndl, index);
+  APPL_TRACE_DEBUG("%s: bta_av_handle = 0x%x index = %d", __func__,
+                   bta_av_handle, index);
 
-  /* Sanity check */
-  if (index >= BTA_AV_CO_NUM_ELEMENTS(bta_av_co_cb.peers)) {
-    APPL_TRACE_ERROR("%s: peer index out of bounds: %d", __func__, index);
-    return NULL;
+  // Sanity check
+  if (index >= BTA_AV_CO_NUM_ELEMENTS(peers_)) {
+    APPL_TRACE_ERROR(
+        "%s: peer index %d for BTA AV handle 0x%x is out of bounds", __func__,
+        index, bta_av_handle);
+    return nullptr;
   }
 
-  return &bta_av_co_cb.peers[index];
+  return &peers_[index];
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_init
- **
- ** Description      This callout function is executed by AV when it is
- **                  started by calling BTA_AvRegister().  This function can be
- **                  used by the phone to initialize audio paths or for other
- **                  initialization purposes.
- **
- **
- ** Returns          Stream codec and content protection capabilities info.
- **
- ******************************************************************************/
-bool bta_av_co_audio_init(btav_a2dp_codec_index_t codec_index,
-                          tAVDT_CFG* p_cfg) {
-  return A2DP_InitCodecConfig(codec_index, p_cfg);
+BtaAvCoPeer* BtaAvCo::FindPeerAndUpdate(tBTA_AV_HNDL bta_av_handle,
+                                        const RawAddress& peer_address) {
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle = 0x%x", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle);
+
+  BtaAvCoPeer* p_peer = FindPeer(bta_av_handle);
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR("%s: peer for BTA AV handle 0x%x not found", __func__,
+                     bta_av_handle);
+    return nullptr;
+  }
+
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle = 0x%x previous address %s",
+                   __func__, peer_address.ToString().c_str(), bta_av_handle,
+                   p_peer->addr.ToString().c_str());
+  p_peer->addr = peer_address;
+  return p_peer;
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_disc_res
- **
- ** Description      This callout function is executed by AV to report the
- **                  number of stream end points (SEP) were found during the
- **                  AVDT stream discovery process.
- **
- **
- ** Returns          void.
- **
- ******************************************************************************/
-void bta_av_co_audio_disc_res(tBTA_AV_HNDL hndl, uint8_t num_seps,
-                              uint8_t num_sink, uint8_t num_src,
-                              const RawAddress& addr, uint16_t uuid_local) {
-  tBTA_AV_CO_PEER* p_peer;
+uint16_t BtaAvCo::FindPeerUuid(tBTA_AV_HNDL bta_av_handle) {
+  BtaAvCoPeer* p_peer = FindPeer(bta_av_handle);
+  if (p_peer == nullptr) {
+    return 0;
+  }
+  return p_peer->uuid_to_connect;
+}
 
-  APPL_TRACE_DEBUG("%s: h:x%x num_seps:%d num_sink:%d num_src:%d", __func__,
-                   hndl, num_seps, num_sink, num_src);
+void BtaAvCo::ProcessDiscoveryResult(tBTA_AV_HNDL bta_av_handle,
+                                     const RawAddress& peer_address,
+                                     uint8_t num_seps, uint8_t num_sinks,
+                                     uint8_t num_sources, uint16_t uuid_local) {
+  APPL_TRACE_DEBUG(
+      "%s: peer %s bta_av_handle:0x%x num_seps:%d num_sinks:%d num_sources:%d",
+      __func__, peer_address.ToString().c_str(), bta_av_handle, num_seps,
+      num_sinks, num_sources);
 
-  /* Find the peer info */
-  p_peer = bta_av_co_get_peer(hndl);
-  if (p_peer == NULL) {
-    APPL_TRACE_ERROR("%s: could not find peer entry", __func__);
+  // Find the peer
+  BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR(
+        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
+        __func__, bta_av_handle, peer_address.ToString().c_str());
     return;
   }
 
   /* Sanity check : this should never happen */
   if (p_peer->opened) {
-    APPL_TRACE_ERROR("%s: peer already opened", __func__);
+    APPL_TRACE_ERROR("%s: peer %s already opened", __func__,
+                     peer_address.ToString().c_str());
   }
 
   /* Copy the discovery results */
-  p_peer->addr = addr;
-  p_peer->num_sinks = num_sink;
-  p_peer->num_srcs = num_src;
+  p_peer->addr = peer_address;
+  p_peer->num_sinks = num_sinks;
+  p_peer->num_sources = num_sources;
   p_peer->num_seps = num_seps;
   p_peer->num_rx_sinks = 0;
-  p_peer->num_rx_srcs = 0;
+  p_peer->num_rx_sources = 0;
   p_peer->num_sup_sinks = 0;
-  if (uuid_local == UUID_SERVCLASS_AUDIO_SINK)
+  if (uuid_local == UUID_SERVCLASS_AUDIO_SINK) {
     p_peer->uuid_to_connect = UUID_SERVCLASS_AUDIO_SOURCE;
-  else if (uuid_local == UUID_SERVCLASS_AUDIO_SOURCE)
+  } else if (uuid_local == UUID_SERVCLASS_AUDIO_SOURCE) {
     p_peer->uuid_to_connect = UUID_SERVCLASS_AUDIO_SINK;
+  }
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_audio_sink_getconfig
- **
- ** Description      This callout function is executed by AV to retrieve the
- **                  desired codec and content protection configuration for the
- **                  A2DP Sink audio stream in Initiator.
- **
- **
- ** Returns          Pass or Fail for current getconfig.
- **
- ******************************************************************************/
-static tA2DP_STATUS bta_av_audio_sink_getconfig(
-    tBTA_AV_HNDL hndl, uint8_t* p_codec_info, uint8_t* p_sep_info_idx,
-    uint8_t seid, uint8_t* p_num_protect, uint8_t* p_protect_info) {
-  tA2DP_STATUS result = A2DP_FAIL;
-  tBTA_AV_CO_PEER* p_peer;
-
-  APPL_TRACE_DEBUG("%s: handle:0x%x codec:%s seid:%d", __func__, hndl,
+tA2DP_STATUS BtaAvCo::ProcessSourceGetConfig(
+    tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address,
+    uint8_t* p_codec_info, uint8_t* p_sep_info_idx, uint8_t seid,
+    uint8_t* p_num_protect, uint8_t* p_protect_info) {
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle:0x%x codec:%s seid:%d", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle,
                    A2DP_CodecName(p_codec_info), seid);
   APPL_TRACE_DEBUG("%s: num_protect:0x%02x protect_info:0x%02x%02x%02x",
                    __func__, *p_num_protect, p_protect_info[0],
                    p_protect_info[1], p_protect_info[2]);
-
-  /* Retrieve the peer info */
-  p_peer = bta_av_co_get_peer(hndl);
-  if (p_peer == NULL) {
-    APPL_TRACE_ERROR("%s: could not find peer entry", __func__);
-    return A2DP_FAIL;
-  }
-
-  APPL_TRACE_DEBUG("%s: peer(o=%d,n_sinks=%d,n_rx_sinks=%d,n_sup_sinks=%d)",
-                   __func__, p_peer->opened, p_peer->num_srcs,
-                   p_peer->num_rx_srcs, p_peer->num_sup_srcs);
-
-  p_peer->num_rx_srcs++;
-
-  /* Check the peer's SOURCE codec */
-  if (A2DP_IsPeerSourceCodecValid(p_codec_info)) {
-    /* If there is room for a new one */
-    if (p_peer->num_sup_srcs < BTA_AV_CO_NUM_ELEMENTS(p_peer->srcs)) {
-      tBTA_AV_CO_SINK* p_src = &p_peer->srcs[p_peer->num_sup_srcs++];
-
-      APPL_TRACE_DEBUG("%s: saved caps[%x:%x:%x:%x:%x:%x]", __func__,
-                       p_codec_info[1], p_codec_info[2], p_codec_info[3],
-                       p_codec_info[4], p_codec_info[5], p_codec_info[6]);
-
-      memcpy(p_src->codec_caps, p_codec_info, AVDT_CODEC_SIZE);
-      p_src->sep_info_idx = *p_sep_info_idx;
-      p_src->seid = seid;
-      p_src->num_protect = *p_num_protect;
-      memcpy(p_src->protect_info, p_protect_info, AVDT_CP_INFO_LEN);
-    } else {
-      APPL_TRACE_ERROR("%s: no more room for SRC info", __func__);
-    }
-  }
-
-  /* If last SINK get capabilities or all supported codec caps retrieved */
-  if ((p_peer->num_rx_srcs == p_peer->num_srcs) ||
-      (p_peer->num_sup_srcs == BTA_AV_CO_NUM_ELEMENTS(p_peer->srcs))) {
-    APPL_TRACE_DEBUG("%s: last SRC reached", __func__);
-
-    /* Protect access to bta_av_co_cb.codec_config */
-    mutex_global_lock();
-
-    /* Find a src that matches the codec config */
-    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);
-
-      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;
-#endif
-
-      *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();
-  }
-  return result;
-}
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_getconfig
- **
- ** Description      This callout function is executed by AV to retrieve the
- **                  desired codec and content protection configuration for the
- **                  audio stream.
- **
- **
- ** Returns          Stream codec and content protection configuration info.
- **
- ******************************************************************************/
-tA2DP_STATUS bta_av_co_audio_getconfig(tBTA_AV_HNDL hndl, uint8_t* p_codec_info,
-                                       uint8_t* p_sep_info_idx, uint8_t seid,
-                                       uint8_t* p_num_protect,
-                                       uint8_t* p_protect_info) {
-  tBTA_AV_CO_PEER* p_peer;
-
-  APPL_TRACE_DEBUG("%s", __func__);
   A2DP_DumpCodecInfo(p_codec_info);
 
-  /* Retrieve the peer info */
-  p_peer = bta_av_co_get_peer(hndl);
-  if (p_peer == NULL) {
-    APPL_TRACE_ERROR("%s: could not find peer entry", __func__);
+  // Find the peer
+  BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR(
+        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
+        __func__, bta_av_handle, peer_address.ToString().c_str());
     return A2DP_FAIL;
   }
-
-  if (p_peer->uuid_to_connect == UUID_SERVCLASS_AUDIO_SOURCE) {
-    return bta_av_audio_sink_getconfig(hndl, p_codec_info, p_sep_info_idx, seid,
-                                       p_num_protect, p_protect_info);
-  }
-  APPL_TRACE_DEBUG("%s: handle:0x%x codec:%s seid:%d", __func__, hndl,
-                   A2DP_CodecName(p_codec_info), seid);
-  APPL_TRACE_DEBUG("%s: num_protect:0x%02x protect_info:0x%02x%02x%02x",
-                   __func__, *p_num_protect, p_protect_info[0],
-                   p_protect_info[1], p_protect_info[2]);
   APPL_TRACE_DEBUG("%s: peer(o=%d, n_sinks=%d, n_rx_sinks=%d, n_sup_sinks=%d)",
                    __func__, p_peer->opened, p_peer->num_sinks,
                    p_peer->num_rx_sinks, p_peer->num_sup_sinks);
 
   p_peer->num_rx_sinks++;
 
-  /* Check the peer's SINK codec */
+  // Check the peer's Sink codec
   if (A2DP_IsPeerSinkCodecValid(p_codec_info)) {
-    /* If there is room for a new one */
+    // If there is room for a new one
     if (p_peer->num_sup_sinks < BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks)) {
-      tBTA_AV_CO_SINK* p_sink = &p_peer->sinks[p_peer->num_sup_sinks++];
+      BtaAvCoSep* p_sink = &p_peer->sinks[p_peer->num_sup_sinks++];
 
       APPL_TRACE_DEBUG("%s: saved caps[%x:%x:%x:%x:%x:%x]", __func__,
                        p_codec_info[1], p_codec_info[2], p_codec_info[3],
@@ -435,42 +836,42 @@
       p_sink->num_protect = *p_num_protect;
       memcpy(p_sink->protect_info, p_protect_info, AVDT_CP_INFO_LEN);
     } else {
-      APPL_TRACE_ERROR("%s: no more room for SINK info", __func__);
+      APPL_TRACE_ERROR("%s: no more room for Sink info", __func__);
     }
   }
 
-  // Check if this is the last SINK get capabilities or all supported codec
+  // Check if this is the last Sink get capabilities or all supported codec
   // capabilities are retrieved.
   if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
       (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
     return A2DP_FAIL;
   }
-  APPL_TRACE_DEBUG("%s: last sink reached", __func__);
+  APPL_TRACE_DEBUG("%s: last Sink reached", __func__);
 
-  const tBTA_AV_CO_SINK* p_sink = bta_av_co_audio_set_codec(p_peer);
-  if (p_sink == NULL) {
-    APPL_TRACE_ERROR("%s: cannot set up codec for the peer SINK", __func__);
+  // Select the Source codec
+  const BtaAvCoSep* p_sink = SelectSourceCodec(p_peer);
+  if (p_sink == nullptr) {
+    APPL_TRACE_ERROR("%s: cannot set up codec for the peer Sink", __func__);
     return A2DP_FAIL;
   }
 
   // By default, no content protection
   *p_num_protect = 0;
-#if (BTA_AV_CO_CP_SCMS_T == TRUE)
-  if (p_peer->cp_active) {
+  if (ContentProtectEnabled() && p_peer->ContentProtectActive()) {
     *p_num_protect = AVDT_CP_INFO_LEN;
     memcpy(p_protect_info, bta_av_co_cp_scmst, AVDT_CP_INFO_LEN);
   }
-#endif
 
-  // If acceptor -> reconfig otherwise reply for configuration.
-  if (p_peer->acp) {
-    // Stop fetching caps once we retrieved a supported codec.
+  // If acceptor -> reconfig otherwise reply for configuration
+  if (p_peer->acceptor) {
+    // Stop fetching caps once we retrieved a supported codec
     APPL_TRACE_EVENT("%s: no need to fetch more SEPs", __func__);
     *p_sep_info_idx = p_peer->num_seps;
     if (p_peer->reconfig_needed) {
-      APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(x%x)", __func__, hndl);
-      BTA_AvReconfig(hndl, true, p_sink->sep_info_idx, p_peer->codec_config,
-                     *p_num_protect, bta_av_co_cp_scmst);
+      APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x)", __func__,
+                       bta_av_handle);
+      BTA_AvReconfig(bta_av_handle, true, p_sink->sep_info_idx,
+                     p_peer->codec_config, *p_num_protect, bta_av_co_cp_scmst);
     }
   } else {
     *p_sep_info_idx = p_sink->sep_info_idx;
@@ -480,103 +881,201 @@
   return A2DP_SUCCESS;
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_setconfig
- **
- ** Description      This callout function is executed by AV to set the codec
- **                  and content protection configuration of the audio stream.
- **
- **
- ** Returns          void
- **
- ******************************************************************************/
-void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, const uint8_t* p_codec_info,
-                               UNUSED_ATTR uint8_t seid,
-                               UNUSED_ATTR const RawAddress& addr,
-                               uint8_t num_protect,
+tA2DP_STATUS BtaAvCo::ProcessSinkGetConfig(tBTA_AV_HNDL bta_av_handle,
+                                           const RawAddress& peer_address,
+                                           uint8_t* p_codec_info,
+                                           uint8_t* p_sep_info_idx,
+                                           uint8_t seid, uint8_t* p_num_protect,
+                                           uint8_t* p_protect_info) {
+  std::lock_guard<std::recursive_mutex> lock(codec_lock_);
+
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle:0x%x codec:%s seid:%d", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle,
+                   A2DP_CodecName(p_codec_info), seid);
+  APPL_TRACE_DEBUG("%s: num_protect:0x%02x protect_info:0x%02x%02x%02x",
+                   __func__, *p_num_protect, p_protect_info[0],
+                   p_protect_info[1], p_protect_info[2]);
+  A2DP_DumpCodecInfo(p_codec_info);
+
+  // Find the peer
+  BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR(
+        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
+        __func__, bta_av_handle, peer_address.ToString().c_str());
+    return A2DP_FAIL;
+  }
+  APPL_TRACE_DEBUG(
+      "%s: peer %s found (o=%d, n_sources=%d, n_rx_sources=%d, "
+      "n_sup_sources=%d)",
+      __func__, p_peer->addr.ToString().c_str(), p_peer->opened,
+      p_peer->num_sources, p_peer->num_rx_sources, p_peer->num_sup_sources);
+
+  p_peer->num_rx_sources++;
+
+  // Check the peer's Source codec
+  if (A2DP_IsPeerSourceCodecValid(p_codec_info)) {
+    // If there is room for a new one
+    if (p_peer->num_sup_sources < BTA_AV_CO_NUM_ELEMENTS(p_peer->sources)) {
+      BtaAvCoSep* p_source = &p_peer->sources[p_peer->num_sup_sources++];
+
+      APPL_TRACE_DEBUG("%s: saved caps[%x:%x:%x:%x:%x:%x]", __func__,
+                       p_codec_info[1], p_codec_info[2], p_codec_info[3],
+                       p_codec_info[4], p_codec_info[5], p_codec_info[6]);
+
+      memcpy(p_source->codec_caps, p_codec_info, AVDT_CODEC_SIZE);
+      p_source->sep_info_idx = *p_sep_info_idx;
+      p_source->seid = seid;
+      p_source->num_protect = *p_num_protect;
+      memcpy(p_source->protect_info, p_protect_info, AVDT_CP_INFO_LEN);
+    } else {
+      APPL_TRACE_ERROR("%s: no more room for Source info", __func__);
+    }
+  }
+
+  // Check if this is the last Source get capabilities or all supported codec
+  // capabilities are retrieved.
+  if ((p_peer->num_rx_sources != p_peer->num_sources) &&
+      (p_peer->num_sup_sources != BTA_AV_CO_NUM_ELEMENTS(p_peer->sources))) {
+    return A2DP_FAIL;
+  }
+  APPL_TRACE_DEBUG("%s: last Source reached", __func__);
+
+  // Find a Peer SEP that matches the codec config
+  const BtaAvCoSep* p_source = FindPeerSourceSepForCurrentCodec(p_peer);
+  if (p_source == nullptr) {
+    APPL_TRACE_ERROR("%s: cannot set up codec for the peer Source", __func__);
+    return A2DP_FAIL;
+  }
+
+  uint8_t pref_config[AVDT_CODEC_SIZE];
+  APPL_TRACE_DEBUG("%s: codec supported", __func__);
+
+  // Build the codec configuration for this Sink and save the new config
+  p_peer->p_source = p_source;
+  // Get preferred config from Source capabilities
+  if (A2DP_BuildSrc2SinkConfig(p_source->codec_caps, pref_config) !=
+      A2DP_SUCCESS) {
+    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;
+
+  if (ContentProtectEnabled()) {
+    p_peer->SetContentProtectActive(false);
+  }
+
+  *p_sep_info_idx = p_source->sep_info_idx;
+  memcpy(p_codec_info, p_peer->codec_config, AVDT_CODEC_SIZE);
+
+  return A2DP_SUCCESS;
+}
+
+void BtaAvCo::ProcessSetConfig(tBTA_AV_HNDL bta_av_handle,
+                               UNUSED_ATTR const RawAddress& peer_address,
+                               const uint8_t* p_codec_info,
+                               UNUSED_ATTR uint8_t seid, uint8_t num_protect,
                                const uint8_t* p_protect_info,
                                uint8_t t_local_sep, uint8_t avdt_handle) {
-  tBTA_AV_CO_PEER* p_peer;
   tA2DP_STATUS status = A2DP_SUCCESS;
   uint8_t category = A2DP_SUCCESS;
   bool reconfig_needed = false;
 
+  APPL_TRACE_DEBUG(
+      "%s: bta_av_handle=0x%x peer_address=%s seid=%d "
+      "num_protect=%d t_local_sep=%d avdt_handle=%d",
+      __func__, bta_av_handle, peer_address.ToString().c_str(), seid,
+      num_protect, t_local_sep, avdt_handle);
   APPL_TRACE_DEBUG("%s: p_codec_info[%x:%x:%x:%x:%x:%x]", __func__,
                    p_codec_info[1], p_codec_info[2], p_codec_info[3],
                    p_codec_info[4], p_codec_info[5], p_codec_info[6]);
-  APPL_TRACE_DEBUG("num_protect:0x%02x protect_info:0x%02x%02x%02x",
-                   num_protect, p_protect_info[0], p_protect_info[1],
+  APPL_TRACE_DEBUG("%s: num_protect:0x%02x protect_info:0x%02x%02x%02x",
+                   __func__, num_protect, p_protect_info[0], p_protect_info[1],
                    p_protect_info[2]);
   A2DP_DumpCodecInfo(p_codec_info);
 
-  /* Retrieve the peer info */
-  p_peer = bta_av_co_get_peer(hndl);
-  if (p_peer == NULL) {
-    APPL_TRACE_ERROR("%s: could not find peer entry", __func__);
-    /* Call call-in rejecting the configuration */
-    bta_av_ci_setconfig(hndl, A2DP_BUSY, AVDT_ASC_CODEC, 0, NULL, false,
-                        avdt_handle);
+  // Find the peer
+  BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR(
+        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
+        __func__, bta_av_handle, peer_address.ToString().c_str());
+    // Call call-in rejecting the configuration
+    bta_av_ci_setconfig(bta_av_handle, A2DP_BUSY, AVDT_ASC_CODEC, 0, nullptr,
+                        false, avdt_handle);
     return;
   }
 
-  APPL_TRACE_DEBUG("%s: peer(o=%d, n_sinks=%d, n_rx_sinks=%d, n_sup_sinks=%d)",
-                   __func__, p_peer->opened, p_peer->num_sinks,
-                   p_peer->num_rx_sinks, p_peer->num_sup_sinks);
+  APPL_TRACE_DEBUG(
+      "%s: peer %s found (o=%d, n_sinks=%d, n_rx_sinks=%d, "
+      "n_sup_sinks=%d)",
+      __func__, p_peer->addr.ToString().c_str(), p_peer->opened,
+      p_peer->num_sinks, p_peer->num_rx_sinks, p_peer->num_sup_sinks);
 
-  /* Sanity check: should not be opened at this point */
+  // Sanity check: should not be opened at this point
   if (p_peer->opened) {
-    APPL_TRACE_ERROR("%s: peer already in use", __func__);
+    APPL_TRACE_ERROR("%s: peer %s already in use", __func__,
+                     p_peer->addr.ToString().c_str());
   }
 
   if (num_protect != 0) {
-#if (BTA_AV_CO_CP_SCMS_T == TRUE)
-    /* If CP is supported */
-    if ((num_protect != 1) || (!bta_av_co_cp_is_scmst(p_protect_info))) {
-      APPL_TRACE_ERROR("%s: wrong CP configuration", __func__);
+    if (ContentProtectEnabled()) {
+      if ((num_protect != 1) ||
+          !BtaAvCo::ContentProtectIsScmst(p_protect_info)) {
+        APPL_TRACE_ERROR("%s: wrong CP configuration for peer %s", __func__,
+                         p_peer->addr.ToString().c_str());
+        status = A2DP_BAD_CP_TYPE;
+        category = AVDT_ASC_PROTECT;
+      }
+    } else {
+      // Do not support content protection for the time being
+      APPL_TRACE_ERROR("%s: wrong CP configuration for peer %s", __func__,
+                       p_peer->addr.ToString().c_str());
       status = A2DP_BAD_CP_TYPE;
       category = AVDT_ASC_PROTECT;
     }
-#else
-    /* Do not support content protection for the time being */
-    APPL_TRACE_ERROR("%s: wrong CP configuration", __func__);
-    status = A2DP_BAD_CP_TYPE;
-    category = AVDT_ASC_PROTECT;
-#endif
   }
 
   if (status == A2DP_SUCCESS) {
     bool codec_config_supported = false;
 
     if (t_local_sep == AVDT_TSEP_SNK) {
-      APPL_TRACE_DEBUG("%s: peer is A2DP SRC", __func__);
+      APPL_TRACE_DEBUG("%s: peer %s is A2DP Source", __func__,
+                       p_peer->addr.ToString().c_str());
       codec_config_supported = A2DP_IsSinkCodecSupported(p_codec_info);
       if (codec_config_supported) {
-        // If Peer is SRC, and our config subset matches with what is
+        // If Peer is Source, and our config subset matches with what is
         // requested by peer, then just accept what peer wants.
-        bta_av_co_save_new_codec_config(p_peer, p_codec_info, num_protect,
-                                        p_protect_info);
+        SaveNewCodecConfig(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__);
+      APPL_TRACE_DEBUG("%s: peer %s is A2DP SINK", __func__,
+                       p_peer->addr.ToString().c_str());
       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));
+      if ((p_peer->GetCodecs() == nullptr) ||
+          !SetCodecOtaConfig(p_peer, p_codec_info, num_protect, p_protect_info,
+                             &restart_output)) {
+        APPL_TRACE_ERROR("%s: cannot set source codec %s for peer %s", __func__,
+                         A2DP_CodecName(p_codec_info),
+                         p_peer->addr.ToString().c_str());
       } else {
         codec_config_supported = true;
         // Check if reconfiguration is needed
         if (restart_output ||
-            ((num_protect == 1) && (!bta_av_co_cb.cp.active))) {
+            ((num_protect == 1) && !p_peer->ContentProtectActive())) {
           reconfig_needed = true;
         }
       }
     }
 
-    /* Check if codec configuration is supported */
+    // Check if codec configuration is supported
     if (!codec_config_supported) {
       category = AVDT_ASC_CODEC;
       status = A2DP_WRONG_CODEC;
@@ -584,124 +1083,105 @@
   }
 
   if (status != A2DP_SUCCESS) {
-    APPL_TRACE_DEBUG("%s: reject s=%d c=%d", __func__, status, category);
-    /* Call call-in rejecting the configuration */
-    bta_av_ci_setconfig(hndl, status, category, 0, NULL, false, avdt_handle);
+    APPL_TRACE_DEBUG("%s: peer %s reject s=%d c=%d", __func__,
+                     p_peer->addr.ToString().c_str(), status, category);
+    // Call call-in rejecting the configuration
+    bta_av_ci_setconfig(bta_av_handle, status, category, 0, nullptr, false,
+                        avdt_handle);
     return;
   }
 
-  /* Mark that this is an acceptor peer */
-  p_peer->acp = true;
+  // Mark that this is an acceptor peer
+  p_peer->acceptor = true;
   p_peer->reconfig_needed = reconfig_needed;
-  APPL_TRACE_DEBUG("%s: accept reconf=%d", __func__, reconfig_needed);
-  /* Call call-in accepting the configuration */
-  bta_av_ci_setconfig(hndl, A2DP_SUCCESS, A2DP_SUCCESS, 0, NULL,
+  APPL_TRACE_DEBUG("%s: peer %s accept reconf=%d", __func__,
+                   p_peer->addr.ToString().c_str(), reconfig_needed);
+  // Call call-in accepting the configuration
+  bta_av_ci_setconfig(bta_av_handle, A2DP_SUCCESS, A2DP_SUCCESS, 0, nullptr,
                       reconfig_needed, avdt_handle);
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_open
- **
- ** Description      This function is called by AV when the audio stream
- **                  connection is opened.
- **
- **
- ** Returns          void
- **
- ******************************************************************************/
-void bta_av_co_audio_open(tBTA_AV_HNDL hndl, uint16_t mtu) {
-  tBTA_AV_CO_PEER* p_peer;
+void BtaAvCo::ProcessOpen(tBTA_AV_HNDL bta_av_handle,
+                          const RawAddress& peer_address, uint16_t mtu) {
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x mtu:%d", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle, mtu);
 
-  APPL_TRACE_DEBUG("%s: handle: %d mtu:%d", __func__, hndl, mtu);
+  // Find the peer
+  BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR(
+        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
+        __func__, bta_av_handle, peer_address.ToString().c_str());
+    return;
+  }
+  p_peer->opened = true;
+  p_peer->mtu = mtu;
 
-  /* Retrieve the peer info */
-  p_peer = bta_av_co_get_peer(hndl);
-  if (p_peer == NULL) {
-    APPL_TRACE_ERROR("%s: could not find peer entry", __func__);
-  } else {
-    p_peer->opened = true;
-    p_peer->mtu = mtu;
+  // The first connected peer becomes the active peer
+  if (active_peer_ == nullptr) {
+    active_peer_ = p_peer;
   }
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_close
- **
- ** Description      This function is called by AV when the audio stream
- **                  connection is closed.
- **
- **
- ** Returns          void
- **
- ******************************************************************************/
-void bta_av_co_audio_close(tBTA_AV_HNDL hndl) {
-  tBTA_AV_CO_PEER* p_peer;
+void BtaAvCo::ProcessClose(tBTA_AV_HNDL bta_av_handle,
+                           const RawAddress& peer_address) {
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle);
 
-  APPL_TRACE_DEBUG("%s", __func__);
-
-  /* Retrieve the peer info */
-  p_peer = bta_av_co_get_peer(hndl);
-  if (p_peer) {
-    /* Mark the peer closed and clean the peer info */
-    memset(p_peer, 0, sizeof(*p_peer));
-  } else {
-    APPL_TRACE_ERROR("%s: could not find peer entry", __func__);
+  // Find the peer
+  BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR(
+        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
+        __func__, bta_av_handle, peer_address.ToString().c_str());
+    return;
   }
+  // Reset the active peer
+  if (active_peer_ == p_peer) {
+    active_peer_ = nullptr;
+  }
+  // Mark the peer closed and clean the peer info
+  p_peer->Init(codec_priorities_);
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_start
- **
- ** Description      This function is called by AV when the audio streaming data
- **                  transfer is started.
- **
- **
- ** Returns          void
- **
- ******************************************************************************/
-void bta_av_co_audio_start(UNUSED_ATTR tBTA_AV_HNDL hndl,
-                           UNUSED_ATTR uint8_t* p_codec_info,
-                           UNUSED_ATTR bool* p_no_rtp_hdr) {
-  APPL_TRACE_DEBUG("%s", __func__);
+void BtaAvCo::ProcessStart(tBTA_AV_HNDL bta_av_handle,
+                           const RawAddress& peer_address,
+                           const uint8_t* p_codec_info, bool* p_no_rtp_header) {
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle);
+
+  // Find the peer
+  BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR(
+        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
+        __func__, bta_av_handle, peer_address.ToString().c_str());
+    return;
+  }
+
+  bool add_rtp_header =
+      A2DP_UsesRtpHeader(p_peer->ContentProtectActive(), p_codec_info);
+
+  APPL_TRACE_DEBUG("%s: bta_av_handle: 0x%x add_rtp_header: %s", __func__,
+                   bta_av_handle, add_rtp_header ? "true" : "false");
+  *p_no_rtp_header = !add_rtp_header;
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_stop
- **
- ** Description      This function is called by AV when the audio streaming data
- **                  transfer is stopped.
- **
- **
- ** Returns          void
- **
- ******************************************************************************/
-void bta_av_co_audio_stop(UNUSED_ATTR tBTA_AV_HNDL hndl) {
-  APPL_TRACE_DEBUG("%s", __func__);
+void BtaAvCo::ProcessStop(tBTA_AV_HNDL bta_av_handle,
+                          const RawAddress& peer_address) {
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle);
+  // Nothing to do
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_src_data_path
- **
- ** Description      This function is called to manage data transfer from
- **                  the audio codec to AVDTP.
- **
- ** Returns          Pointer to the GKI buffer to send, NULL if no buffer to
- **                  send
- **
- ******************************************************************************/
-void* bta_av_co_audio_src_data_path(const uint8_t* p_codec_info,
-                                    uint32_t* p_timestamp) {
+BT_HDR* BtaAvCo::GetNextSourceDataPacket(const uint8_t* p_codec_info,
+                                         uint32_t* p_timestamp) {
   BT_HDR* p_buf;
 
   APPL_TRACE_DEBUG("%s: codec: %s", __func__, A2DP_CodecName(p_codec_info));
 
   p_buf = btif_a2dp_source_audio_readbuf();
-  if (p_buf == NULL) return NULL;
+  if (p_buf == nullptr) return nullptr;
 
   /*
    * Retrieve the timestamp information from the media packet,
@@ -718,373 +1198,126 @@
                      A2DP_GetCodecType(p_codec_info));
   }
 
-#if (BTA_AV_CO_CP_SCMS_T == TRUE)
-  if (bta_av_co_cb.cp.active) {
+  if (ContentProtectEnabled() && (active_peer_ != nullptr) &&
+      active_peer_->ContentProtectActive()) {
     p_buf->len++;
     p_buf->offset--;
     uint8_t* p = (uint8_t*)(p_buf + 1) + p_buf->offset;
-    *p = bta_av_co_cp_get_flag();
+    *p = ContentProtectFlag();
   }
-#endif
 
   return p_buf;
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_drop
- **
- ** Description      An Audio packet is dropped. .
- **                  It's very likely that the connected headset with this
- **                  handle is moved far away. The implementation may want to
- **                  reduce the encoder bit rate setting to reduce the packet
- **                  size.
- **
- ** Returns          void
- **
- ******************************************************************************/
-void bta_av_co_audio_drop(tBTA_AV_HNDL hndl) {
-  APPL_TRACE_ERROR("%s: dropped audio packet on handle 0x%x", __func__, hndl);
+void BtaAvCo::DataPacketWasDropped(tBTA_AV_HNDL bta_av_handle,
+                                   const RawAddress& peer_address) {
+  APPL_TRACE_ERROR("%s: peer %s dropped audio packet on handle 0x%x", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle);
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_delay
- **
- ** Description      This function is called by AV when the audio stream
- **                  connection needs to send the initial delay report to the
- **                  connected SRC.
- **
- **
- ** Returns          void
- **
- ******************************************************************************/
-void bta_av_co_audio_delay(tBTA_AV_HNDL hndl, uint16_t delay) {
-  APPL_TRACE_ERROR("%s: handle: x%x, delay:0x%x", __func__, hndl, delay);
+void BtaAvCo::ProcessAudioDelay(tBTA_AV_HNDL bta_av_handle,
+                                const RawAddress& peer_address,
+                                uint16_t delay) {
+  APPL_TRACE_ERROR("%s: peer %s bta_av_handle: 0x%x delay:0x%x", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle, delay);
+  // Nothing to do
 }
 
-void bta_av_co_audio_update_mtu(tBTA_AV_HNDL hndl, uint16_t mtu) {
-  tBTA_AV_CO_PEER* p_peer;
+void BtaAvCo::UpdateMtu(tBTA_AV_HNDL bta_av_handle,
+                        const RawAddress& peer_address, uint16_t mtu) {
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x mtu: %d", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle, mtu);
 
-  APPL_TRACE_DEBUG("%s: handle: %d mtu: %d", __func__, hndl, mtu);
-
-  /* Retrieve the peer info */
-  p_peer = bta_av_co_get_peer(hndl);
-  if (p_peer == NULL) {
-    APPL_TRACE_ERROR("%s: could not find peer entry", __func__);
+  // Find the peer
+  BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR(
+        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
+        __func__, bta_av_handle, peer_address.ToString().c_str());
     return;
   }
   p_peer->mtu = mtu;
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_cp_is_scmst
- **
- ** Description      Check if a content protection service is SCMS-T
- **
- ** Returns          true if this CP is SCMS-T, false otherwise
- **
- ******************************************************************************/
-static bool bta_av_co_cp_is_scmst(const uint8_t* p_protect_info) {
-  APPL_TRACE_DEBUG("%s", __func__);
+bool BtaAvCo::SetActivePeer(const RawAddress& peer_address) {
+  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
+                   peer_address.ToString().c_str());
 
-  if (*p_protect_info >= AVDT_CP_LOSC) {
-    uint16_t cp_id;
+  std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
-    p_protect_info++;
-    STREAM_TO_UINT16(cp_id, p_protect_info);
-    if (cp_id == AVDT_CP_SCMS_T_ID) {
-      APPL_TRACE_DEBUG("%s: SCMS-T found", __func__);
-      return true;
-    }
+  if (peer_address.IsEmpty()) {
+    // Reset the active peer;
+    active_peer_ = nullptr;
+    memset(codec_config_, 0, sizeof(codec_config_));
+    return true;
   }
 
-  return false;
-}
-
-// Check if audio protect info contains SCMS-T Copy Protection
-// Returns true if |p_protect_info| contains SCMS-T, otherwise false.
-static bool bta_av_co_audio_protect_has_scmst(uint8_t num_protect,
-                                              const uint8_t* p_protect_info) {
-  APPL_TRACE_DEBUG("%s", __func__);
-
-  while (num_protect--) {
-    if (bta_av_co_cp_is_scmst(p_protect_info)) return true;
-    /* Move to the next SC */
-    p_protect_info += *p_protect_info + 1;
-  }
-  APPL_TRACE_DEBUG("%s: SCMS-T not found", __func__);
-  return false;
-}
-
-/*******************************************************************************
- **
- ** Function         bta_av_co_audio_sink_supports_cp
- **
- ** Description      Check if a sink supports the current content protection
- **
- ** Returns          true if the sink supports this CP, false otherwise
- **
- ******************************************************************************/
-static bool bta_av_co_audio_sink_supports_cp(const tBTA_AV_CO_SINK* p_sink) {
-  APPL_TRACE_DEBUG("%s", __func__);
-
-  /* Check if content protection is enabled for this stream */
-  if (bta_av_co_cp_get_flag() != AVDT_CP_SCMS_COPY_FREE) {
-    return bta_av_co_audio_protect_has_scmst(p_sink->num_protect,
-                                             p_sink->protect_info);
+  // Find the peer
+  BtaAvCoPeer* p_peer = FindPeer(peer_address);
+  if (p_peer == nullptr) {
+    return false;
   }
 
-  APPL_TRACE_DEBUG("%s: not required", __func__);
+  active_peer_ = p_peer;
+  memcpy(codec_config_, active_peer_->codec_config, AVDT_CODEC_SIZE);
+  ReportSourceCodecState(active_peer_);
   return true;
 }
 
-/*******************************************************************************
- **
- ** Function         bta_av_co_find_peer_src_supports_codec
- **
- ** Description      Find a peer acting as src that supports codec config
- **
- ** Returns          The peer source that supports the codec, otherwise NULL.
- **
- ******************************************************************************/
-static const tBTA_AV_CO_SINK* bta_av_co_find_peer_src_supports_codec(
-    const tBTA_AV_CO_PEER* p_peer) {
-  APPL_TRACE_DEBUG("%s: peer num_sup_srcs = %d", __func__,
-                   p_peer->num_sup_srcs);
-
-  for (size_t index = 0; index < p_peer->num_sup_srcs; index++) {
-    const uint8_t* p_codec_caps = p_peer->srcs[index].codec_caps;
-    if (A2DP_CodecTypeEquals(bta_av_co_cb.codec_config, p_codec_caps) &&
-        A2DP_IsPeerSourceCodecSupported(p_codec_caps)) {
-      return &p_peer->srcs[index];
-    }
-  }
-  return NULL;
-}
-
-//
-// Select the current codec configuration based on peer codec support.
-// Furthermore, the local state for the remaining non-selected codecs is
-// updated to reflect whether the codec is selectable.
-// Return a pointer to the corresponding |tBTA_AV_CO_SINK| sink entry
-// on success, otherwise NULL.
-//
-static tBTA_AV_CO_SINK* bta_av_co_audio_set_codec(tBTA_AV_CO_PEER* p_peer) {
-  tBTA_AV_CO_SINK* p_sink = NULL;
-
-  // Update all selectable codecs.
-  // This is needed to update the selectable parameters for each codec.
-  // NOTE: The selectable codec info is used only for informational purpose.
-  for (const auto& iter : bta_av_co_cb.codecs->orderedSourceCodecs()) {
-    APPL_TRACE_DEBUG("%s: updating selectable codec %s", __func__,
-                     iter->name().c_str());
-    bta_av_co_audio_update_selectable_codec(*iter, p_peer);
-  }
-
-  // Select the codec
-  for (const auto& iter : bta_av_co_cb.codecs->orderedSourceCodecs()) {
-    APPL_TRACE_DEBUG("%s: trying codec %s", __func__, iter->name().c_str());
-    p_sink = bta_av_co_audio_codec_selected(*iter, p_peer);
-    if (p_sink != NULL) {
-      APPL_TRACE_DEBUG("%s: selected codec %s", __func__, iter->name().c_str());
-      break;
-    }
-    APPL_TRACE_DEBUG("%s: cannot use codec %s", __func__, iter->name().c_str());
-  }
-
-  // NOTE: Unconditionally dispatch the event to make sure a callback with
-  // the most recent codec info is generated.
-  btif_dispatch_sm_event(BTIF_AV_SOURCE_CONFIG_UPDATED_EVT, &p_peer->addr,
-                         sizeof(RawAddress));
-
-  return p_sink;
-}
-
-// 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(
-    A2dpCodecConfig& codec_config, tBTA_AV_CO_PEER* p_peer) {
-  uint8_t new_codec_config[AVDT_CODEC_SIZE];
-
-  APPL_TRACE_DEBUG("%s", __func__);
-
-  // 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++) {
-    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__, codec_config.name().c_str());
-      continue;
-    }
-    p_sink = &p_peer->sinks[index];
-    break;
-  }
-  if (p_sink == NULL) {
-    APPL_TRACE_DEBUG("%s: peer sink for codec %s not found", __func__,
-                     codec_config.name().c_str());
-    return NULL;
-  }
-  if (!bta_av_co_cb.codecs->setCodecConfig(
-          p_sink->codec_caps, true /* is_capability */, new_codec_config,
-          true /* select_current_codec */)) {
-    APPL_TRACE_DEBUG("%s: cannot set source codec %s", __func__,
-                     codec_config.name().c_str());
-    return NULL;
-  }
-  p_peer->p_sink = p_sink;
-
-  bta_av_co_save_new_codec_config(p_peer, new_codec_config, p_sink->num_protect,
-                                  p_sink->protect_info);
-  // NOTE: Event BTIF_AV_SOURCE_CONFIG_UPDATED_EVT is dispatched by the caller
-
-  return p_sink;
-}
-
-// Update a selectable codec |codec_config| with the corresponding codec
-// information from a peer device |p_peer|.
-// Returns true if the codec is updated, otherwise false.
-static bool bta_av_co_audio_update_selectable_codec(
-    A2dpCodecConfig& codec_config, const tBTA_AV_CO_PEER* p_peer) {
-  uint8_t new_codec_config[AVDT_CODEC_SIZE];
-
-  APPL_TRACE_DEBUG("%s", __func__);
-
-  // Find the peer sink for the codec
-  const tBTA_AV_CO_SINK* p_sink = NULL;
-  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_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__, codec_config.name().c_str());
-      continue;
-    }
-    p_sink = &p_peer->sinks[index];
-    break;
-  }
-  if (p_sink == NULL) {
-    // The peer sink device does not support this codec
-    return false;
-  }
-  if (!bta_av_co_cb.codecs->setCodecConfig(
-          p_sink->codec_caps, true /* is_capability */, new_codec_config,
-          false /* select_current_codec */)) {
-    APPL_TRACE_DEBUG("%s: cannot update source codec %s", __func__,
-                     codec_config.name().c_str());
-    return false;
-  }
-  return true;
-}
-
-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) {
-  APPL_TRACE_DEBUG("%s", __func__);
-  A2DP_DumpCodecInfo(new_codec_config);
-
-  // Protect access to bta_av_co_cb.codec_config
-  mutex_global_lock();
-
-  memcpy(bta_av_co_cb.codec_config, new_codec_config,
-         sizeof(bta_av_co_cb.codec_config));
-  memcpy(p_peer->codec_config, new_codec_config, AVDT_CODEC_SIZE);
-
-#if (BTA_AV_CO_CP_SCMS_T == TRUE)
-  /* Check if this sink supports SCMS */
-  bool cp_active =
-      bta_av_co_audio_protect_has_scmst(num_protect, p_protect_info);
-  bta_av_co_cb.cp.active = cp_active;
-  p_peer->cp_active = cp_active;
-#endif
-
-  // Protect access to bta_av_co_cb.codec_config
-  mutex_global_unlock();
-}
-
-void bta_av_co_get_peer_params(tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params) {
+void BtaAvCo::GetPeerEncoderParameters(
+    const RawAddress& peer_address,
+    tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params) {
   uint16_t min_mtu = 0xFFFF;
 
-  APPL_TRACE_DEBUG("%s", __func__);
+  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
+                   peer_address.ToString().c_str());
   CHECK(p_peer_params != nullptr);
 
-  /* Protect access to bta_av_co_cb.codec_config */
-  mutex_global_lock();
+  std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
-  /* Compute the MTU */
-  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(bta_av_co_cb.peers); i++) {
-    const tBTA_AV_CO_PEER* p_peer = &bta_av_co_cb.peers[i];
+  // Compute the MTU
+  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
+    const BtaAvCoPeer* p_peer = &peers_[i];
     if (!p_peer->opened) continue;
+    if (p_peer->addr != peer_address) continue;
     if (p_peer->mtu < min_mtu) min_mtu = p_peer->mtu;
   }
   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();
+  p_peer_params->is_peer_edr = btif_av_is_peer_edr(peer_address);
+  p_peer_params->peer_supports_3mbps =
+      btif_av_peer_supports_3mbps(peer_address);
 }
 
-const tA2DP_ENCODER_INTERFACE* bta_av_co_get_encoder_interface(void) {
-  /* Protect access to bta_av_co_cb.codec_config */
-  mutex_global_lock();
+const tA2DP_ENCODER_INTERFACE* BtaAvCo::GetSourceEncoderInterface() {
+  std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
-  const tA2DP_ENCODER_INTERFACE* encoder_interface =
-      A2DP_GetEncoderInterface(bta_av_co_cb.codec_config);
-
-  /* Protect access to bta_av_co_cb.codec_config */
-  mutex_global_unlock();
-
-  return encoder_interface;
+  return A2DP_GetEncoderInterface(codec_config_);
 }
 
-const tA2DP_DECODER_INTERFACE* bta_av_co_get_decoder_interface(void) {
-  /* Protect access to bta_av_co_cb.codec_config */
-  mutex_global_lock();
+const tA2DP_DECODER_INTERFACE* BtaAvCo::GetSinkDecoderInterface() {
+  std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
-  const tA2DP_DECODER_INTERFACE* decoder_interface =
-      A2DP_GetDecoderInterface(bta_av_co_cb.codec_config);
-
-  /* Protect access to bta_av_co_cb.codec_config */
-  mutex_global_unlock();
-
-  return decoder_interface;
+  return A2DP_GetDecoderInterface(codec_config_);
 }
 
-bool bta_av_co_set_codec_user_config(
+bool BtaAvCo::SetCodecUserConfig(
+    const RawAddress& peer_address,
     const btav_a2dp_codec_config_t& codec_user_config) {
   uint8_t result_codec_config[AVDT_CODEC_SIZE];
-  const tBTA_AV_CO_SINK* p_sink = nullptr;
+  const BtaAvCoSep* p_sink = nullptr;
   bool restart_input = false;
   bool restart_output = false;
   bool config_updated = false;
   bool success = true;
 
-  // 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;
-    }
-  }
+  APPL_TRACE_DEBUG("%s: peer_address=%s codec_user_config=%s", __func__,
+                   peer_address.ToString().c_str(),
+                   codec_user_config.ToString().c_str());
+
+  BtaAvCoPeer* p_peer = FindPeer(peer_address);
   if (p_peer == nullptr) {
-    APPL_TRACE_ERROR("%s: no open peer to configure", __func__);
+    APPL_TRACE_ERROR("%s: cannot find peer %s to configure", __func__,
+                     peer_address.ToString().c_str());
     success = false;
     goto done;
   }
@@ -1095,7 +1328,9 @@
       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;
+      if (!AudioSinkHasContentProtection(&p_peer->sinks[index])) {
+        continue;
+      }
       p_sink = &p_peer->sinks[index];
       break;
     }
@@ -1111,8 +1346,8 @@
   }
 
   tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
-  bta_av_co_get_peer_params(&peer_params);
-  if (!bta_av_co_cb.codecs->setCodecUserConfig(
+  GetPeerEncoderParameters(p_peer->addr, &peer_params);
+  if (!p_peer->GetCodecs()->setCodecUserConfig(
           codec_user_config, &peer_params, p_sink->codec_caps,
           result_codec_config, &restart_input, &restart_output,
           &config_updated)) {
@@ -1122,12 +1357,12 @@
 
   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
+    if (ContentProtectEnabled() && p_peer->ContentProtectActive()) {
+      num_protect = AVDT_CP_INFO_LEN;
+    }
 
-    p_sink = bta_av_co_audio_set_codec(p_peer);
-    if (p_sink == NULL) {
+    p_sink = SelectSourceCodec(p_peer);
+    if (p_sink == nullptr) {
       APPL_TRACE_ERROR("%s: cannot set up codec for the peer SINK", __func__);
       success = false;
       goto done;
@@ -1141,8 +1376,9 @@
       goto done;
     }
 
-    APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(x%x)", __func__, p_peer->handle);
-    BTA_AvReconfig(p_peer->handle, true, p_sink->sep_info_idx,
+    APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x)", __func__,
+                     p_peer->BtaAvHandle());
+    BTA_AvReconfig(p_peer->BtaAvHandle(), true, p_sink->sep_info_idx,
                    p_peer->codec_config, num_protect, bta_av_co_cp_scmst);
   }
 
@@ -1152,44 +1388,334 @@
   // request succeeded or failed.
   // NOTE: Currently, the input is restarted by sending an upcall
   // and informing the Media Framework about the change.
-  RawAddress empty_raw_address = RawAddress::kEmpty;
-  RawAddress* p_addr;
   if (p_peer != nullptr) {
-    p_addr = &p_peer->addr;
-  } else {
-    p_addr = &empty_raw_address;
+    return ReportSourceCodecState(p_peer);
   }
-  btif_dispatch_sm_event(BTIF_AV_SOURCE_CONFIG_UPDATED_EVT, p_addr,
-                         sizeof(RawAddress));
 
   return success;
 }
 
-// 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) {
+bool BtaAvCo::SetCodecAudioConfig(
+    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;
+
+  APPL_TRACE_DEBUG("%s: codec_audio_config: %s", __func__,
+                   codec_audio_config.ToString().c_str());
+
+  // Find the peer that is currently open
+  BtaAvCoPeer* p_peer = active_peer_;
+  if (p_peer == nullptr) {
+    APPL_TRACE_ERROR("%s: no active peer to configure", __func__);
+    return false;
+  }
+
+  // Use the current sink codec
+  const BtaAvCoSep* 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;
+  GetPeerEncoderParameters(p_peer->addr, &peer_params);
+  if (!p_peer->GetCodecs()->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 (ContentProtectEnabled() && p_peer->ContentProtectActive()) {
+      num_protect = AVDT_CP_INFO_LEN;
+    }
+
+    SaveNewCodecConfig(p_peer, result_codec_config, p_sink->num_protect,
+                       p_sink->protect_info);
+
+    // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
+    if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
+        (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
+      APPL_TRACE_WARNING("%s: not all peer's capabilities have been retrieved",
+                         __func__);
+    } else {
+      APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x)", __func__,
+                       p_peer->BtaAvHandle());
+      BTA_AvReconfig(p_peer->BtaAvHandle(), 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.
+    return ReportSourceCodecState(p_peer);
+  }
+
+  return true;
+}
+
+bool BtaAvCo::ReportSourceCodecState(BtaAvCoPeer* p_peer) {
+  btav_a2dp_codec_config_t codec_config;
+  std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities;
+  std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities;
+
+  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
+                   p_peer->addr.ToString().c_str());
+  if (!p_peer->GetCodecs()->getCodecConfigAndCapabilities(
+          &codec_config, &codecs_local_capabilities,
+          &codecs_selectable_capabilities)) {
+    APPL_TRACE_WARNING(
+        "%s: Peer %s : error reporting audio source codec state: "
+        "cannot get codec config and capabilities",
+        __func__, p_peer->addr.ToString().c_str());
+    return false;
+  }
+  do_in_jni_thread(
+      FROM_HERE,
+      base::Bind(&btif_av_report_source_codec_state, p_peer->addr, codec_config,
+                 codecs_local_capabilities, codecs_selectable_capabilities));
+  return true;
+}
+
+void BtaAvCo::DebugDump(int fd) {
+  std::lock_guard<std::recursive_mutex> lock(codec_lock_);
+
+  dprintf(fd, "\nA2DP Codecs and Peers State:\n");
+  dprintf(fd, "  Active peer: %s\n",
+          (active_peer_ != nullptr) ? active_peer_->addr.ToString().c_str()
+                                    : "null");
+
+  for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
+    const BtaAvCoPeer& peer = peers_[i];
+    dprintf(fd, "  Peer: %s\n", peer.addr.ToString().c_str());
+    dprintf(fd, "    Number of sinks: %u\n", peer.num_sinks);
+    dprintf(fd, "    Number of sources: %u\n", peer.num_sources);
+    dprintf(fd, "    Number of SEPs: %u\n", peer.num_seps);
+    dprintf(fd, "    Number of received sinks: %u\n", peer.num_rx_sinks);
+    dprintf(fd, "    Number of received sources: %u\n", peer.num_rx_sources);
+    dprintf(fd, "    Number of supported sinks: %u\n", peer.num_sup_sinks);
+    dprintf(fd, "    Number of supported sources: %u\n", peer.num_sup_sources);
+    dprintf(fd, "    Acceptor: %s\n", (peer.acceptor) ? "true" : "false");
+    dprintf(fd, "    Reconfig needed: %s\n",
+            (peer.reconfig_needed) ? "true" : "false");
+    dprintf(fd, "    Opened: %s\n", (peer.opened) ? "true" : "false");
+    dprintf(fd, "    MTU: %u\n", peer.mtu);
+    dprintf(fd, "    UUID to connect: 0x%x\n", peer.uuid_to_connect);
+    dprintf(fd, "    BTA AV handle: %u\n", peer.BtaAvHandle());
+  }
+
+  //
+  // Active peer codec-specific stats
+  //
+  if (active_peer_ != nullptr) {
+    A2dpCodecs* a2dp_codecs = active_peer_->GetCodecs();
+    if (a2dp_codecs != nullptr) {
+      a2dp_codecs->debug_codec_dump(fd);
+    }
+  }
+}
+
+bool BtaAvCo::ContentProtectIsScmst(const uint8_t* p_protect_info) {
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  if (*p_protect_info >= AVDT_CP_LOSC) {
+    uint16_t cp_id;
+    p_protect_info++;
+    STREAM_TO_UINT16(cp_id, p_protect_info);
+    if (cp_id == AVDT_CP_SCMS_T_ID) {
+      APPL_TRACE_DEBUG("%s: SCMS-T found", __func__);
+      return true;
+    }
+  }
+  return false;
+}
+
+bool BtaAvCo::AudioProtectHasScmst(uint8_t num_protect,
+                                   const uint8_t* p_protect_info) {
+  APPL_TRACE_DEBUG("%s", __func__);
+  while (num_protect--) {
+    if (BtaAvCo::ContentProtectIsScmst(p_protect_info)) return true;
+    // Move to the next Content Protect schema
+    p_protect_info += *p_protect_info + 1;
+  }
+  APPL_TRACE_DEBUG("%s: SCMS-T not found", __func__);
+  return false;
+}
+
+bool BtaAvCo::AudioSinkHasContentProtection(const BtaAvCoSep* p_sink) {
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  // Check if content protection is enabled for this stream
+  if (ContentProtectFlag() != AVDT_CP_SCMS_COPY_FREE) {
+    return BtaAvCo::AudioProtectHasScmst(p_sink->num_protect,
+                                         p_sink->protect_info);
+  }
+
+  APPL_TRACE_DEBUG("%s: not required", __func__);
+  return true;
+}
+
+const BtaAvCoSep* BtaAvCo::FindPeerSourceSepForCurrentCodec(
+    const BtaAvCoPeer* p_peer) {
+  APPL_TRACE_DEBUG("%s: peer num_sup_sources = %d", __func__,
+                   p_peer->num_sup_sources);
+
+  for (size_t index = 0; index < p_peer->num_sup_sources; index++) {
+    const uint8_t* p_codec_caps = p_peer->sources[index].codec_caps;
+    if (A2DP_CodecTypeEquals(codec_config_, p_codec_caps) &&
+        A2DP_IsPeerSourceCodecSupported(p_codec_caps)) {
+      return &p_peer->sources[index];
+    }
+  }
+  return nullptr;
+}
+
+BtaAvCoSep* BtaAvCo::SelectSourceCodec(BtaAvCoPeer* p_peer) {
+  BtaAvCoSep* p_sink = nullptr;
+
+  // Update all selectable codecs.
+  // This is needed to update the selectable parameters for each codec.
+  // NOTE: The selectable codec info is used only for informational purpose.
+  for (const auto& iter : p_peer->GetCodecs()->orderedSourceCodecs()) {
+    APPL_TRACE_DEBUG("%s: updating selectable codec %s", __func__,
+                     iter->name().c_str());
+    UpdateSelectableCodec(*iter, p_peer);
+  }
+
+  // Select the codec
+  for (const auto& iter : p_peer->GetCodecs()->orderedSourceCodecs()) {
+    APPL_TRACE_DEBUG("%s: trying codec %s", __func__, iter->name().c_str());
+    p_sink = AttemptCodecSelection(*iter, p_peer);
+    if (p_sink != nullptr) {
+      APPL_TRACE_DEBUG("%s: selected codec %s", __func__, iter->name().c_str());
+      break;
+    }
+    APPL_TRACE_DEBUG("%s: cannot use codec %s", __func__, iter->name().c_str());
+  }
+
+  // NOTE: Unconditionally dispatch the event to make sure a callback with
+  // the most recent codec info is generated.
+  ReportSourceCodecState(p_peer);
+
+  return p_sink;
+}
+
+BtaAvCoSep* BtaAvCo::AttemptCodecSelection(const A2dpCodecConfig& codec_config,
+                                           BtaAvCoPeer* p_peer) {
+  uint8_t new_codec_config[AVDT_CODEC_SIZE];
+
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  // Find the peer sink for the codec
+  BtaAvCoSep* 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 != codec_config.codecIndex()) {
+      continue;
+    }
+    if (!AudioSinkHasContentProtection(&p_peer->sinks[index])) {
+      APPL_TRACE_DEBUG(
+          "%s: peer sink for codec %s does not support "
+          "Content Protection",
+          __func__, codec_config.name().c_str());
+      continue;
+    }
+    p_sink = &p_peer->sinks[index];
+    break;
+  }
+  if (p_sink == nullptr) {
+    APPL_TRACE_DEBUG("%s: peer sink for codec %s not found", __func__,
+                     codec_config.name().c_str());
+    return nullptr;
+  }
+  if (!p_peer->GetCodecs()->setCodecConfig(
+          p_sink->codec_caps, true /* is_capability */, new_codec_config,
+          true /* select_current_codec */)) {
+    APPL_TRACE_DEBUG("%s: cannot set source codec %s", __func__,
+                     codec_config.name().c_str());
+    return nullptr;
+  }
+  p_peer->p_sink = p_sink;
+
+  SaveNewCodecConfig(p_peer, new_codec_config, p_sink->num_protect,
+                     p_sink->protect_info);
+
+  return p_sink;
+}
+
+bool BtaAvCo::UpdateSelectableCodec(const A2dpCodecConfig& codec_config,
+                                    BtaAvCoPeer* p_peer) {
+  uint8_t new_codec_config[AVDT_CODEC_SIZE];
+
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  // Find the peer sink for the codec
+  const BtaAvCoSep* 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 != codec_config.codecIndex()) {
+      continue;
+    }
+    if (!AudioSinkHasContentProtection(&p_peer->sinks[index])) {
+      APPL_TRACE_DEBUG(
+          "%s: peer sink for codec %s does not support "
+          "Content Protection",
+          __func__, codec_config.name().c_str());
+      continue;
+    }
+    p_sink = &p_peer->sinks[index];
+    break;
+  }
+  if (p_sink == nullptr) {
+    // The peer sink device does not support this codec
+    return false;
+  }
+  if (!p_peer->GetCodecs()->setCodecConfig(
+          p_sink->codec_caps, true /* is_capability */, new_codec_config,
+          false /* select_current_codec */)) {
+    APPL_TRACE_DEBUG("%s: cannot update source codec %s", __func__,
+                     codec_config.name().c_str());
+    return false;
+  }
+  return true;
+}
+
+void BtaAvCo::SaveNewCodecConfig(BtaAvCoPeer* p_peer,
+                                 const uint8_t* new_codec_config,
+                                 uint8_t num_protect,
+                                 const uint8_t* p_protect_info) {
+  APPL_TRACE_DEBUG("%s: peer %s", __func__, p_peer->addr.ToString().c_str());
+  A2DP_DumpCodecInfo(new_codec_config);
+
+  std::lock_guard<std::recursive_mutex> lock(codec_lock_);
+
+  memcpy(codec_config_, new_codec_config, sizeof(codec_config_));
+  memcpy(p_peer->codec_config, new_codec_config, AVDT_CODEC_SIZE);
+
+  if (ContentProtectEnabled()) {
+    // Check if this Sink supports SCMS
+    bool cp_active = BtaAvCo::AudioProtectHasScmst(num_protect, p_protect_info);
+    p_peer->SetContentProtectActive(cp_active);
+  }
+}
+
+bool BtaAvCo::SetCodecOtaConfig(BtaAvCoPeer* 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;
 
-  APPL_TRACE_DEBUG("%s", __func__);
+  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
+                   p_peer->addr.ToString().c_str());
   A2DP_DumpCodecInfo(p_ota_codec_config);
 
   *p_restart_output = false;
@@ -1201,12 +1727,14 @@
     APPL_TRACE_WARNING("%s: invalid peer codec config", __func__);
     return false;
   }
-  const tBTA_AV_CO_SINK* p_sink = nullptr;
+  const BtaAvCoSep* 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;
+    if (!AudioSinkHasContentProtection(&p_peer->sinks[index])) {
+      continue;
+    }
     p_sink = &p_peer->sinks[index];
     break;
   }
@@ -1219,8 +1747,8 @@
   }
 
   tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
-  bta_av_co_get_peer_params(&peer_params);
-  if (!bta_av_co_cb.codecs->setCodecOtaConfig(
+  GetPeerEncoderParameters(p_peer->addr, &peer_params);
+  if (!p_peer->GetCodecs()->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__);
@@ -1233,127 +1761,148 @@
 
     *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);
+    SaveNewCodecConfig(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, &p_peer->addr,
-                           sizeof(RawAddress));
+    ReportSourceCodecState(p_peer);
   }
 
   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);
-
-    // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
-    if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
-        (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
-      APPL_TRACE_WARNING("%s: not all peer's capabilities have been retrieved",
-                         __func__);
-    } else {
-      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, &p_peer->addr,
-                           sizeof(RawAddress));
-  }
-
-  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(
     const std::vector<btav_a2dp_codec_config_t>& codec_priorities) {
-  APPL_TRACE_DEBUG("%s", __func__);
-
-  /* Reset the control block */
-  bta_av_co_cb.reset();
-
-#if (BTA_AV_CO_CP_SCMS_T == TRUE)
-  bta_av_co_cp_set_flag(AVDT_CP_SCMS_COPY_NEVER);
-#else
-  bta_av_co_cp_set_flag(AVDT_CP_SCMS_COPY_FREE);
-#endif
-
-  /* Reset the current config */
-  /* Protect access to bta_av_co_cb.codec_config */
-  mutex_global_lock();
-  bta_av_co_cb.codecs = new A2dpCodecs(codec_priorities);
-  bta_av_co_cb.codecs->init();
-  A2DP_InitDefaultCodec(bta_av_co_cb.codec_config);
-  mutex_global_unlock();
-
-  // NOTE: Unconditionally dispatch the event to make sure a callback with
-  // the most recent codec info is generated.
-  RawAddress empty_raw_address = RawAddress::kEmpty;
-  btif_dispatch_sm_event(BTIF_AV_SOURCE_CONFIG_UPDATED_EVT, &empty_raw_address,
-                         sizeof(RawAddress));
+  bta_av_co_cb.Init(codec_priorities);
 }
+
+A2dpCodecConfig* bta_av_get_a2dp_current_codec(void) {
+  return bta_av_co_cb.GetActivePeerCurrentCodec();
+}
+
+bool bta_av_co_audio_init(btav_a2dp_codec_index_t codec_index,
+                          AvdtpSepConfig* p_cfg) {
+  return A2DP_InitCodecConfig(codec_index, p_cfg);
+}
+
+void bta_av_co_audio_disc_res(tBTA_AV_HNDL bta_av_handle,
+                              const RawAddress& peer_address, uint8_t num_seps,
+                              uint8_t num_sinks, uint8_t num_sources,
+                              uint16_t uuid_local) {
+  bta_av_co_cb.ProcessDiscoveryResult(bta_av_handle, peer_address, num_seps,
+                                      num_sinks, num_sources, uuid_local);
+}
+
+tA2DP_STATUS bta_av_co_audio_getconfig(tBTA_AV_HNDL bta_av_handle,
+                                       const RawAddress& peer_address,
+                                       uint8_t* p_codec_info,
+                                       uint8_t* p_sep_info_idx, uint8_t seid,
+                                       uint8_t* p_num_protect,
+                                       uint8_t* p_protect_info) {
+  uint16_t peer_uuid = bta_av_co_cb.FindPeerUuid(bta_av_handle);
+
+  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle=0x%x peer_uuid=0x%x", __func__,
+                   peer_address.ToString().c_str(), bta_av_handle, peer_uuid);
+
+  switch (peer_uuid) {
+    case UUID_SERVCLASS_AUDIO_SOURCE:
+      return bta_av_co_cb.ProcessSinkGetConfig(
+          bta_av_handle, peer_address, p_codec_info, p_sep_info_idx, seid,
+          p_num_protect, p_protect_info);
+    case UUID_SERVCLASS_AUDIO_SINK:
+      return bta_av_co_cb.ProcessSourceGetConfig(
+          bta_av_handle, peer_address, p_codec_info, p_sep_info_idx, seid,
+          p_num_protect, p_protect_info);
+    default:
+      break;
+  }
+  APPL_TRACE_ERROR("%s: Invalid peer UUID: 0x%x for bta_av_handle 0x%x",
+                   peer_uuid, bta_av_handle);
+  return A2DP_FAIL;
+}
+
+void bta_av_co_audio_setconfig(tBTA_AV_HNDL bta_av_handle,
+                               const RawAddress& peer_address,
+                               const uint8_t* p_codec_info, uint8_t seid,
+                               uint8_t num_protect,
+                               const uint8_t* p_protect_info,
+                               uint8_t t_local_sep, uint8_t avdt_handle) {
+  bta_av_co_cb.ProcessSetConfig(bta_av_handle, peer_address, p_codec_info, seid,
+                                num_protect, p_protect_info, t_local_sep,
+                                avdt_handle);
+}
+
+void bta_av_co_audio_open(tBTA_AV_HNDL bta_av_handle,
+                          const RawAddress& peer_address, uint16_t mtu) {
+  bta_av_co_cb.ProcessOpen(bta_av_handle, peer_address, mtu);
+}
+
+void bta_av_co_audio_close(tBTA_AV_HNDL bta_av_handle,
+                           const RawAddress& peer_address) {
+  bta_av_co_cb.ProcessClose(bta_av_handle, peer_address);
+}
+
+void bta_av_co_audio_start(tBTA_AV_HNDL bta_av_handle,
+                           const RawAddress& peer_address,
+                           const uint8_t* p_codec_info, bool* p_no_rtp_header) {
+  bta_av_co_cb.ProcessStart(bta_av_handle, peer_address, p_codec_info,
+                            p_no_rtp_header);
+}
+
+void bta_av_co_audio_stop(tBTA_AV_HNDL bta_av_handle,
+                          const RawAddress& peer_address) {
+  bta_av_co_cb.ProcessStop(bta_av_handle, peer_address);
+}
+
+BT_HDR* bta_av_co_audio_source_data_path(const uint8_t* p_codec_info,
+                                         uint32_t* p_timestamp) {
+  return bta_av_co_cb.GetNextSourceDataPacket(p_codec_info, p_timestamp);
+}
+
+void bta_av_co_audio_drop(tBTA_AV_HNDL bta_av_handle,
+                          const RawAddress& peer_address) {
+  bta_av_co_cb.DataPacketWasDropped(bta_av_handle, peer_address);
+}
+
+void bta_av_co_audio_delay(tBTA_AV_HNDL bta_av_handle,
+                           const RawAddress& peer_address, uint16_t delay) {
+  bta_av_co_cb.ProcessAudioDelay(bta_av_handle, peer_address, delay);
+}
+
+void bta_av_co_audio_update_mtu(tBTA_AV_HNDL bta_av_handle,
+                                const RawAddress& peer_address, uint16_t mtu) {
+  bta_av_co_cb.UpdateMtu(bta_av_handle, peer_address, mtu);
+}
+
+bool bta_av_co_set_active_peer(const RawAddress& peer_address) {
+  return bta_av_co_cb.SetActivePeer(peer_address);
+}
+
+void bta_av_co_get_peer_params(const RawAddress& peer_address,
+                               tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params) {
+  bta_av_co_cb.GetPeerEncoderParameters(peer_address, p_peer_params);
+}
+
+const tA2DP_ENCODER_INTERFACE* bta_av_co_get_encoder_interface(void) {
+  return bta_av_co_cb.GetSourceEncoderInterface();
+}
+
+const tA2DP_DECODER_INTERFACE* bta_av_co_get_decoder_interface(void) {
+  return bta_av_co_cb.GetSinkDecoderInterface();
+}
+
+bool bta_av_co_set_codec_user_config(
+    const RawAddress& peer_address,
+    const btav_a2dp_codec_config_t& codec_user_config) {
+  return bta_av_co_cb.SetCodecUserConfig(peer_address, codec_user_config);
+}
+
+bool bta_av_co_set_codec_audio_config(
+    const btav_a2dp_codec_config_t& codec_audio_config) {
+  return bta_av_co_cb.SetCodecAudioConfig(codec_audio_config);
+}
+
+void btif_a2dp_codec_debug_dump(int fd) { bta_av_co_cb.DebugDump(fd); }
diff --git a/btif/include/btif_a2dp.h b/btif/include/btif_a2dp.h
index bbc04df..11fd079 100644
--- a/btif/include/btif_a2dp.h
+++ b/btif/include/btif_a2dp.h
@@ -29,11 +29,13 @@
 
 // Process 'start' request from the BTIF state machine to prepare for A2DP
 // streaming.
+// |peer_addr| is the peer address.
 // |p_av_start| is the data associated with the request - see |tBTA_AV_START|.
 // |pending_start| should be set to true if the BTIF state machine is in
 // 'pending start' state.
 // Returns true if an ACK for the local command was sent, otherwise false.
-bool btif_a2dp_on_started(tBTA_AV_START* p_av_start, bool pending_start);
+bool btif_a2dp_on_started(const RawAddress& peer_addr,
+                          tBTA_AV_START* p_av_start, bool pending_start);
 
 // Process 'stop' request from the BTIF state machine to stop A2DP streaming.
 // |p_av_suspend| is the data associated with the request - see
diff --git a/btif/include/btif_a2dp_source.h b/btif/include/btif_a2dp_source.h
index 112e1ef..024b5e5 100644
--- a/btif/include/btif_a2dp_source.h
+++ b/btif/include/btif_a2dp_source.h
@@ -46,8 +46,9 @@
 bool btif_a2dp_source_is_streaming(void);
 
 // Setup the A2DP Source codec, and prepare the encoder.
+// The peer address is |peer_addr|.
 // This function should be called prior to starting A2DP streaming.
-void btif_a2dp_source_setup_codec(void);
+void btif_a2dp_source_setup_codec(const RawAddress& peer_addr);
 
 // Process a request to start the A2DP audio encoding task.
 void btif_a2dp_source_start_audio_req(void);
@@ -57,8 +58,10 @@
 
 // Process a request to update the A2DP audio encoder with user preferred
 // codec configuration.
+// The peer address is |peer_addr|.
 // |codec_user_config| contains the preferred codec user configuration.
 void btif_a2dp_source_encoder_user_config_update_req(
+    const RawAddress& peer_addr,
     const btav_a2dp_codec_config_t& codec_user_config);
 
 // Process a request to update the A2DP audio encoding with new audio
diff --git a/btif/include/btif_av.h b/btif/include/btif_av.h
index b3dcfca..1f07cd6 100644
--- a/btif/include/btif_av.h
+++ b/btif/include/btif_av.h
@@ -16,177 +16,167 @@
  *
  ******************************************************************************/
 
-/*******************************************************************************
- *
- *  Filename:      btif_av.h
- *
- *  Description:   Main API header file for all BTIF AV functions accessed
- *                 from internal stack.
- *
- ******************************************************************************/
+/**
+ * BTIF AV API functions accessed internally.
+ */
 
 #ifndef BTIF_AV_H
 #define BTIF_AV_H
 
 #include "bta_av_api.h"
 #include "btif_common.h"
-#include "btif_sm.h"
 
-/*******************************************************************************
- *  Type definitions for callback functions
- ******************************************************************************/
+/**
+ * When the local device is A2DP source, get the address of the active peer.
+ */
+RawAddress btif_av_source_active_peer(void);
 
-typedef enum {
-  /* Reuse BTA_AV_XXX_EVT - No need to redefine them here */
-  BTIF_AV_CONNECT_REQ_EVT = BTA_AV_MAX_EVT,
-  BTIF_AV_DISCONNECT_REQ_EVT,
-  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,
-} btif_av_sm_event_t;
+/**
+ * When the local device is A2DP sink, get the address of the active peer.
+ */
+RawAddress btif_av_sink_active_peer(void);
 
-/*******************************************************************************
- *  BTIF AV API
- ******************************************************************************/
-
-/*******************************************************************************
- *
- * Function         btif_av_get_addr
- *
- * Description      Fetches current AV BD address
- *
- * Returns          BD address
- *
- ******************************************************************************/
-
-RawAddress btif_av_get_addr(void);
-
-/*******************************************************************************
- * Function         btif_av_is_sink_enabled
- *
- * Description      Checks if A2DP Sink is enabled or not
- *
- * Returns          true if A2DP Sink is enabled, false otherwise
- *
- ******************************************************************************/
-
+/**
+ * Check whether A2DP Sink is enabled.
+ */
 bool btif_av_is_sink_enabled(void);
 
-/*******************************************************************************
- *
- * Function         btif_av_stream_ready
- *
- * Description      Checks whether AV is ready for starting a stream
- *
- * Returns          None
- *
- ******************************************************************************/
+/**
+ * Start streaming.
+ */
+void btif_av_stream_start(void);
 
+/**
+ * Stop streaming.
+ */
+void btif_av_stream_stop(void);
+
+/**
+ * Suspend streaming.
+ */
+void btif_av_stream_suspend(void);
+
+/**
+ * Start offload streaming.
+ */
+void btif_av_stream_start_offload(void);
+
+/**
+ * Check whether ready to start the A2DP stream.
+ */
 bool btif_av_stream_ready(void);
 
-/*******************************************************************************
- *
- * Function         btif_av_stream_started_ready
- *
- * Description      Checks whether AV ready for media start in streaming state
- *
- * Returns          None
- *
- ******************************************************************************/
-
+/**
+ * Check whether the A2DP stream is in started state and ready
+ * for media start.
+ */
 bool btif_av_stream_started_ready(void);
 
-/*******************************************************************************
- *
- * Function         btif_dispatch_sm_event
- *
- * Description      Send event to AV statemachine
- *
- * Returns          None
- *
- ******************************************************************************/
-
-/* used to pass events to AV statemachine from other tasks */
-void btif_dispatch_sm_event(btif_av_sm_event_t event, void* p_data, int len);
-
-/*******************************************************************************
- *
- * Function         btif_av_init
- *
- * Description      Initializes btif AV if not already done
- *
- * Returns          bt_status_t
- *
- ******************************************************************************/
-
-bt_status_t btif_av_init(int service_id);
-
-/*******************************************************************************
- *
- * Function         btif_av_is_connected
- *
- * Description      Checks if av has a connected sink
- *
- * Returns          bool
- *
- ******************************************************************************/
-
+/**
+ * Check whether there is a connected peer (either Source or Sink)
+ */
 bool btif_av_is_connected(void);
 
-/*******************************************************************************
+/**
+ * Get the Stream Endpoint Type of the Active peer.
  *
- * Function         btif_av_get_peer_sep
- *
- * Description      Get the stream endpoint type.
- *
- * Returns          The stream endpoint type: either AVDT_TSEP_SRC or
- *                  AVDT_TSEP_SNK.
- *
- ******************************************************************************/
-
+ * @return the stream endpoint type: either AVDT_TSEP_SRC or AVDT_TSEP_SNK
+ */
 uint8_t btif_av_get_peer_sep(void);
 
-/*******************************************************************************
- *
- * Function         btif_av_is_peer_edr
- *
- * Description      Check if the connected a2dp device supports
- *                  EDR or not. Only when connected this function
- *                  will accurately provide a true capability of
- *                  remote peer. If not connected it will always be false.
- *
- * Returns          true if remote device is capable of EDR
- *
- ******************************************************************************/
-
-bool btif_av_is_peer_edr(void);
-
-/******************************************************************************
- *
- * Function         btif_av_clear_remote_suspend_flag
- *
- * Description      Clears remote suspended flag
- *
- * Returns          Void
- ******************************************************************************/
+/**
+ * Clear the remote suspended flag for the active peer.
+ */
 void btif_av_clear_remote_suspend_flag(void);
 
-/*******************************************************************************
+/**
+ * Process AVRCP Open event.
  *
- * Function         btif_av_peer_supports_3mbps
+ * @param peer_address the peer address
+ */
+void btif_av_avrcp_event_open(const RawAddress& peer_address);
+
+/**
+ * Process AVRCP Close event.
  *
- * Description      Check if the connected A2DP device supports
- *                  3 Mbps EDR. This function will only work while connected.
- *                  If not connected it will always return false.
+ * @param peer_address the peer address
+ */
+void btif_av_avrcp_event_close(const RawAddress& peer_address);
+
+/**
+ * Process AVRCP Remote Play event.
  *
- * Returns          true if remote device is EDR and supports 3 Mbps
+ * @param peer_address the peer address
+ */
+void btif_av_avrcp_event_remote_play(const RawAddress& peer_address);
+
+/**
+ * Check whether the connected A2DP peer supports EDR.
  *
- ******************************************************************************/
-bool btif_av_peer_supports_3mbps(void);
+ * The value can be provided only if the remote peer is connected.
+ * Otherwise, the answer will be always false.
+ *
+ * @param peer_address the peer address
+ * @return true if the remote peer is capable of EDR
+ */
+bool btif_av_is_peer_edr(const RawAddress& peer_address);
+
+/**
+ * Check whether the connected A2DP peer supports 3 Mbps EDR.
+ *
+ * The value can be provided only if the remote peer is connected.
+ * Otherwise, the answer will be always false.
+ *
+ * @param peer_address the peer address
+ * @return true if the remote peer is capable of EDR and supports 3 Mbps
+ */
+bool btif_av_peer_supports_3mbps(const RawAddress& peer_address);
+
+/**
+ * Report A2DP Source Codec State for a peer.
+ *
+ * @param peer_address the address of the peer to report
+ * @param codec_config the codec config to report
+ * @param codecs_local_capabilities the codecs local capabilities to report
+ * @param codecs_selectable_capabilities the codecs selectable capabilities
+ * to report
+ */
+void btif_av_report_source_codec_state(
+    const RawAddress& peer_address,
+    const btav_a2dp_codec_config_t& codec_config,
+    const std::vector<btav_a2dp_codec_config_t>& codecs_local_capabilities,
+    const std::vector<btav_a2dp_codec_config_t>&
+        codecs_selectable_capabilities);
+
+/**
+ * Initialize / shut down the A2DP Source service.
+ *
+ * @param enable true to enable the A2DP Source service, false to disable it
+ * @return BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+ */
+bt_status_t btif_av_source_execute_service(bool enable);
+
+/**
+ * Initialize / shut down the A2DP Sink service.
+ *
+ * @param enable true to enable the A2DP Sink service, false to disable it
+ * @return BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+ */
+bt_status_t btif_av_sink_execute_service(bool enable);
+
+/**
+ * Peer ACL disconnected.
+ *
+ * @param peer_address the disconnected peer address
+ */
+void btif_av_acl_disconnected(const RawAddress& peer_address);
+
+/**
+ * Dump debug-related information for the BTIF AV module.
+ *
+ * @param fd the file descriptor to use for writing the ASCII formatted
+ * information
+ */
+void btif_debug_av_dump(int fd);
 
 #endif /* BTIF_AV_H */
diff --git a/btif/include/btif_av_co.h b/btif/include/btif_av_co.h
index 00fd5b9..0576c27 100644
--- a/btif/include/btif_av_co.h
+++ b/btif/include/btif_av_co.h
@@ -22,14 +22,16 @@
 #include "btif/include/btif_a2dp_source.h"
 #include "stack/include/a2dp_codec_api.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+// Sets the active peer to |peer_addr|.
+// Returns true on success, otherwise false.
+bool bta_av_co_set_active_peer(const RawAddress& peer_addr);
 
 // Gets the A2DP peer parameters that are used to initialize the encoder.
+// The peer address is |peer_addr|.
 // 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);
+void bta_av_co_get_peer_params(const RawAddress& peer_addr,
+                               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|.
@@ -44,9 +46,11 @@
 const tA2DP_DECODER_INTERFACE* bta_av_co_get_decoder_interface(void);
 
 // Sets the user preferred codec configuration.
+// The peer address is |peer_addr|.
 // |codec_user_config| contains the preferred codec configuration.
 // Returns true on success, otherwise false.
 bool bta_av_co_set_codec_user_config(
+    const RawAddress& peer_addr,
     const btav_a2dp_codec_config_t& codec_user_config);
 
 // Sets the Audio HAL selected audio feeding parameters.
@@ -61,17 +65,13 @@
 void bta_av_co_init(
     const std::vector<btav_a2dp_codec_config_t>& codec_priorities);
 
-// 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.
+// Gets the current A2DP codec for the active peer.
 // Returns a pointer to the current |A2dpCodec| if valid, otherwise nullptr.
 A2dpCodecConfig* bta_av_get_a2dp_current_codec(void);
 
-#ifdef __cplusplus
-}
-#endif
+// Dump A2DP codec debug-related information for the A2DP module.
+// |fd| is the file descriptor to use for writing the ASCII formatted
+// information.
+void btif_a2dp_codec_debug_dump(int fd);
 
 #endif  // BTIF_AV_CO_H
diff --git a/btif/include/btif_rc.h b/btif/include/btif_rc.h
new file mode 100644
index 0000000..cd2d0cc
--- /dev/null
+++ b/btif/include/btif_rc.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BTIF_RC_H
+#define BTIF_RC_H
+
+#include "bta_av_api.h"
+
+class RawAddress;
+
+void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV* p_data);
+uint8_t btif_rc_get_connected_peer_handle(const RawAddress& peer_addr);
+void btif_rc_check_handle_pending_play(const RawAddress& peer_addr,
+                                       bool bSendToApp);
+bool btif_rc_is_connected_peer(const RawAddress& peer_addr);
+
+#endif  // BTIF_RC_H
diff --git a/btif/include/btif_sm.h b/btif/include/btif_sm.h
deleted file mode 100644
index 166745d..0000000
--- a/btif/include/btif_sm.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2009-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.
- *
- ******************************************************************************/
-
-/*****************************************************************************
- *
- *  Filename:      btif_sm.h
- *
- *  Description:   Generic BTIF state machine API
- *
- *****************************************************************************/
-
-#ifndef BTIF_SM_H
-#define BTIF_SM_H
-
-#include <hardware/bluetooth.h>
-
-#include "stack/include/bt_types.h"
-
-/*****************************************************************************
- *  Constants & Macros
- *****************************************************************************/
-
-/* Generic Enter/Exit state machine events */
-#define BTIF_SM_ENTER_EVT 0xFFFF
-#define BTIF_SM_EXIT_EVT 0xFFFE
-
-/*****************************************************************************
- *  Type definitions and return values
- *****************************************************************************/
-typedef uint32_t btif_sm_state_t;
-typedef uint32_t btif_sm_event_t;
-typedef void* btif_sm_handle_t;
-typedef bool (*btif_sm_handler_t)(btif_sm_event_t event, void* data);
-
-/*****************************************************************************
- *  Functions
- *
- *  NOTE: THESE APIs SHOULD BE INVOKED ONLY IN THE BTIF CONTEXT
- *
- *****************************************************************************/
-
-/*****************************************************************************
- *
- * Function     btif_sm_init
- *
- * Description  Initializes the state machine with the state handlers
- *              The caller should ensure that the table and the corresponding
- *              states match. The location that 'p_handlers' points to shall
- *              be available until the btif_sm_shutdown API is invoked.
- *
- * Returns      Returns a pointer to the initialized state machine handle.
- *
- *****************************************************************************/
-btif_sm_handle_t btif_sm_init(const btif_sm_handler_t* p_handlers,
-                              btif_sm_state_t initial_state);
-
-/*****************************************************************************
- *
- * Function     btif_sm_shutdown
- *
- * Description  Tears down the state machine
- *
- * Returns      None
- *
- *****************************************************************************/
-void btif_sm_shutdown(btif_sm_handle_t handle);
-
-/*****************************************************************************
- *
- * Function     btif_sm_get_state
- *
- * Description  Fetches the current state of the state machine
- *
- * Returns      Current state
- *
- *****************************************************************************/
-btif_sm_state_t btif_sm_get_state(btif_sm_handle_t handle);
-
-/*****************************************************************************
- *
- * Function     btif_sm_dispatch
- *
- * Description  Dispatches the 'event' along with 'data' to the current state
- *              handler
- *
- * Returns      Returns BT_STATUS_OK on success, BT_STATUS_FAIL otherwise
- *
- *****************************************************************************/
-bt_status_t btif_sm_dispatch(btif_sm_handle_t handle, btif_sm_event_t event,
-                             void* data);
-
-/*****************************************************************************
- *
- * Function     btif_sm_change_state
- *
- * Description  Make a transition to the new 'state'. The 'BTIF_SM_EXIT_EVT'
- *              shall be invoked before exiting the current state. The
- *              'BTIF_SM_ENTER_EVT' shall be invoked before entering the new
- *              state
- *
- * Returns      Returns BT_STATUS_OK on success, BT_STATUS_FAIL otherwise
- *
- *****************************************************************************/
-bt_status_t btif_sm_change_state(btif_sm_handle_t handle,
-                                 btif_sm_state_t state);
-
-#endif /* BTIF_SM_H */
diff --git a/btif/include/btif_state_machine.h b/btif/include/btif_state_machine.h
new file mode 100644
index 0000000..49b1583
--- /dev/null
+++ b/btif/include/btif_state_machine.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BTIF_STATE_MACHINE_H
+#define BTIF_STATE_MACHINE_H
+
+#include <map>
+#include <utility>
+
+#include <base/logging.h>
+
+/**
+ * State machine used by BTIF components.
+ */
+class BtifStateMachine {
+ public:
+  enum { kStateInvalid = -1 };
+
+  /**
+   * A class to represent the state in the State Machine.
+   */
+  class State {
+    friend class BtifStateMachine;
+
+   public:
+    /**
+     * Constructor.
+     *
+     * @param sm the State Machine to use
+     * @param state_id the unique State ID. It should be a non-negative number.
+     */
+    State(BtifStateMachine& sm, int state_id) : sm_(sm), state_id_(state_id) {}
+
+    virtual ~State() = default;
+
+    /**
+     * Process an event.
+     * TODO: The arguments are wrong - used for backward compatibility.
+     * Will be replaced later.
+     *
+     * @param event the event type
+     * @param p_data the event data
+     * @return true if the processing was completed, otherwise false
+     */
+    virtual bool ProcessEvent(uint32_t event, void* p_data) = 0;
+
+    /**
+     * Get the State ID.
+     *
+     * @return the State ID
+     */
+    int StateId() const { return state_id_; }
+
+   protected:
+    /**
+     * Called when a state is entered.
+     */
+    virtual void OnEnter() {}
+
+    /**
+     * Called when a state is exited.
+     */
+    virtual void OnExit() {}
+
+    /**
+     * Transition the State Machine to a new state.
+     *
+     * @param dest_state_id the state ID to transition to. It must be one
+     * of the unique state IDs when the corresponding state was created.
+     */
+    void TransitionTo(int dest_state_id) { sm_.TransitionTo(dest_state_id); }
+
+    /**
+     * Transition the State Machine to a new state.
+     *
+     * @param dest_state the state to transition to. It cannot be nullptr.
+     */
+    void TransitionTo(BtifStateMachine::State* dest_state) {
+      sm_.TransitionTo(dest_state);
+    }
+
+   private:
+    BtifStateMachine& sm_;
+    int state_id_;
+  };
+
+  BtifStateMachine()
+      : initial_state_(nullptr),
+        previous_state_(nullptr),
+        current_state_(nullptr) {}
+  ~BtifStateMachine() {
+    for (auto& kv : states_) delete kv.second;
+  }
+
+  /**
+   * Start the State Machine operation.
+   */
+  void Start() { TransitionTo(initial_state_); }
+
+  /**
+   * Quit the State Machine operation.
+   */
+  void Quit() { previous_state_ = current_state_ = nullptr; }
+
+  /**
+   * Get the current State ID.
+   *
+   * @return the current State ID
+   */
+  int StateId() const {
+    if (current_state_ != nullptr) {
+      return current_state_->StateId();
+    }
+    return kStateInvalid;
+  }
+
+  /**
+   * Get the previous current State ID.
+   *
+   * @return the previous State ID
+   */
+  int PreviousStateId() const {
+    if (previous_state_ != nullptr) {
+      return previous_state_->StateId();
+    }
+    return kStateInvalid;
+  }
+
+  /**
+   * Process an event.
+   * TODO: The arguments are wrong - used for backward compatibility.
+   * Will be replaced later.
+   *
+   * @param event the event type
+   * @param p_data the event data
+   * @return true if the processing was completed, otherwise false
+   */
+  bool ProcessEvent(uint32_t event, void* p_data) {
+    if (current_state_ == nullptr) return false;
+    return current_state_->ProcessEvent(event, p_data);
+  }
+
+  /**
+   * Transition the State Machine to a new state.
+   *
+   * @param dest_state_id the state ID to transition to. It must be one
+   * of the unique state IDs when the corresponding state was created.
+   */
+  void TransitionTo(int dest_state_id) {
+    auto it = states_.find(dest_state_id);
+
+    CHECK(it != states_.end()) << "Unknown State ID: " << dest_state_id;
+    State* dest_state = it->second;
+    TransitionTo(dest_state);
+  }
+
+  /**
+   * Transition the State Machine to a new state.
+   *
+   * @param dest_state the state to transition to. It cannot be nullptr.
+   */
+  void TransitionTo(BtifStateMachine::State* dest_state) {
+    if (current_state_ != nullptr) {
+      current_state_->OnExit();
+    }
+    previous_state_ = current_state_;
+    current_state_ = dest_state;
+    current_state_->OnEnter();
+  }
+
+  /**
+   * Add a state to the State Machine.
+   * The state machine takes ownership on the state - i.e., the state will
+   * be deleted by the State Machine itself.
+   *
+   * @param state the state to add
+   */
+  void AddState(State* state) {
+    states_.insert(std::make_pair(state->StateId(), state));
+  }
+
+  /**
+   * Set the initial state of the State Machine.
+   *
+   * @param initial_state the initial state
+   */
+  void SetInitialState(State* initial_state) { initial_state_ = initial_state; }
+
+ private:
+  State* initial_state_;
+  State* previous_state_;
+  State* current_state_;
+  std::map<int, State*> states_;
+};
+
+#endif  // BTIF_STATE_MACHINE_H
diff --git a/btif/src/bluetooth.cc b/btif/src/bluetooth.cc
index 89e6802..5d2ffcc 100644
--- a/btif/src/bluetooth.cc
+++ b/btif/src/bluetooth.cc
@@ -50,6 +50,7 @@
 #include "bta/include/bta_hf_client_api.h"
 #include "btif_a2dp.h"
 #include "btif_api.h"
+#include "btif_av.h"
 #include "btif_config.h"
 #include "btif_debug.h"
 #include "btif_debug_btsnoop.h"
@@ -312,6 +313,9 @@
   btif_debug_conn_dump(fd);
   btif_debug_bond_event_dump(fd);
   btif_debug_a2dp_dump(fd);
+  btif_debug_av_dump(fd);
+  bta_debug_av_dump(fd);
+  stack_debug_avdtp_api_dump(fd);
   btif_debug_config_dump(fd);
   BTA_HfClientDumpStatistics(fd);
   wakelock_debug_dump(fd);
diff --git a/btif/src/btif_a2dp.cc b/btif/src/btif_a2dp.cc
index 1888166..e71d895 100644
--- a/btif/src/btif_a2dp.cc
+++ b/btif/src/btif_a2dp.cc
@@ -29,6 +29,7 @@
 #include "btif_a2dp_sink.h"
 #include "btif_a2dp_source.h"
 #include "btif_av.h"
+#include "btif_av_co.h"
 #include "btif_util.h"
 #include "osi/include/log.h"
 
@@ -42,7 +43,8 @@
   }
 }
 
-bool btif_a2dp_on_started(tBTA_AV_START* p_av_start, bool pending_start) {
+bool btif_a2dp_on_started(const RawAddress& peer_addr,
+                          tBTA_AV_START* p_av_start, bool pending_start) {
   bool ack = false;
 
   APPL_TRACE_WARNING("## ON A2DP STARTED ##");
@@ -69,7 +71,7 @@
         /* We were remotely started, make sure codec
          * is setup before datapath is started.
          */
-        btif_a2dp_source_setup_codec();
+        btif_a2dp_source_setup_codec(peer_addr);
       }
 
       /* media task is autostarted upon a2dp audiopath connection */
@@ -126,4 +128,5 @@
 void btif_debug_a2dp_dump(int fd) {
   btif_a2dp_source_debug_dump(fd);
   btif_a2dp_sink_debug_dump(fd);
+  btif_a2dp_codec_debug_dump(fd);
 }
diff --git a/btif/src/btif_a2dp_control.cc b/btif/src/btif_a2dp_control.cc
index b9429ca..51a89e9 100644
--- a/btif/src/btif_a2dp_control.cc
+++ b/btif/src/btif_a2dp_control.cc
@@ -118,7 +118,7 @@
          * If we are the source, the ACK will be sent after the start
          * procedure is completed, othewise send it now.
          */
-        btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, 0);
+        btif_av_stream_start();
         if (btif_av_get_peer_sep() == AVDT_TSEP_SRC)
           btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
         break;
@@ -145,15 +145,14 @@
         btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
         break;
       }
-
-      btif_dispatch_sm_event(BTIF_AV_STOP_STREAM_REQ_EVT, NULL, 0);
+      btif_av_stream_stop();
       btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
       break;
 
     case A2DP_CTRL_CMD_SUSPEND:
       /* Local suspend */
       if (btif_av_stream_started_ready()) {
-        btif_dispatch_sm_event(BTIF_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0);
+        btif_av_stream_suspend();
         break;
       }
       /* If we are not in started state, just ack back ok and let
@@ -259,7 +258,7 @@
     }
 
     case A2DP_CTRL_CMD_OFFLOAD_START:
-      btif_dispatch_sm_event(BTIF_AV_OFFLOAD_START_REQ_EVT, NULL, 0);
+      btif_av_stream_start_offload();
       break;
 
     default:
@@ -330,7 +329,7 @@
        */
       if (btif_a2dp_source_is_streaming()) {
         /* Post stop event and wait for audio path to stop */
-        btif_dispatch_sm_event(BTIF_AV_STOP_STREAM_REQ_EVT, NULL, 0);
+        btif_av_stream_stop();
       }
       break;
 
diff --git a/btif/src/btif_a2dp_source.cc b/btif/src/btif_a2dp_source.cc
index ec0612b..5098ea9 100644
--- a/btif/src/btif_a2dp_source.cc
+++ b/btif/src/btif_a2dp_source.cc
@@ -21,6 +21,7 @@
 #define ATRACE_TAG ATRACE_TAG_AUDIO
 
 #include <base/logging.h>
+#include <base/run_loop.h>
 #ifndef OS_GENERIC
 #include <cutils/trace.h>
 #endif
@@ -40,7 +41,6 @@
 #include "osi/include/fixed_queue.h"
 #include "osi/include/log.h"
 #include "osi/include/metrics.h"
-#include "osi/include/mutex.h"
 #include "osi/include/osi.h"
 #include "osi/include/thread.h"
 #include "osi/include/time.h"
@@ -56,42 +56,22 @@
  */
 #define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ (MAX_PCM_FRAME_NUM_PER_TICK * 2)
 
-enum {
-  BTIF_A2DP_SOURCE_STATE_OFF,
-  BTIF_A2DP_SOURCE_STATE_STARTING_UP,
-  BTIF_A2DP_SOURCE_STATE_RUNNING,
-  BTIF_A2DP_SOURCE_STATE_SHUTTING_DOWN
-};
+class SchedulingStats {
+ public:
+  SchedulingStats() { Reset(); }
+  void Reset() {
+    total_updates = 0;
+    last_update_us = 0;
+    overdue_scheduling_count = 0;
+    total_overdue_scheduling_delta_us = 0;
+    max_overdue_scheduling_delta_us = 0;
+    premature_scheduling_count = 0;
+    total_premature_scheduling_delta_us = 0;
+    max_premature_scheduling_delta_us = 0;
+    exact_scheduling_count = 0;
+    total_scheduling_time_us = 0;
+  }
 
-/* BTIF Media Source event definition */
-enum {
-  BTIF_MEDIA_AUDIO_TX_START = 1,
-  BTIF_MEDIA_AUDIO_TX_STOP,
-  BTIF_MEDIA_AUDIO_TX_FLUSH,
-  BTIF_MEDIA_SOURCE_ENCODER_INIT,
-  BTIF_MEDIA_SOURCE_ENCODER_USER_CONFIG_UPDATE,
-  BTIF_MEDIA_AUDIO_FEEDING_UPDATE
-};
-
-/* tBTIF_A2DP_SOURCE_ENCODER_INIT msg structure */
-typedef struct {
-  BT_HDR hdr;
-  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;
 
@@ -121,14 +101,38 @@
 
   // Accumulated and counted scheduling time (in us)
   uint64_t total_scheduling_time_us;
-} scheduling_stats_t;
+};
 
-typedef struct {
+class BtifMediaStats {
+ public:
+  BtifMediaStats() { Reset(); }
+  void Reset() {
+    session_start_us = 0;
+    session_end_us = 0;
+    tx_queue_enqueue_stats.Reset();
+    tx_queue_dequeue_stats.Reset();
+    tx_queue_total_frames = 0;
+    tx_queue_max_frames_per_packet = 0;
+    tx_queue_total_queueing_time_us = 0;
+    tx_queue_max_queueing_time_us = 0;
+    tx_queue_total_readbuf_calls = 0;
+    tx_queue_last_readbuf_us = 0;
+    tx_queue_total_flushed_messages = 0;
+    tx_queue_last_flushed_us = 0;
+    tx_queue_total_dropped_messages = 0;
+    tx_queue_max_dropped_messages = 0;
+    tx_queue_dropouts = 0;
+    tx_queue_last_dropouts_us = 0;
+    media_read_total_underflow_bytes = 0;
+    media_read_total_underflow_count = 0;
+    media_read_last_underflow_us = 0;
+  }
+
   uint64_t session_start_us;
   uint64_t session_end_us;
 
-  scheduling_stats_t tx_queue_enqueue_stats;
-  scheduling_stats_t tx_queue_dequeue_stats;
+  SchedulingStats tx_queue_enqueue_stats;
+  SchedulingStats tx_queue_dequeue_stats;
 
   size_t tx_queue_total_frames;
   size_t tx_queue_max_frames_per_packet;
@@ -150,64 +154,171 @@
   size_t media_read_total_underflow_bytes;
   size_t media_read_total_underflow_count;
   uint64_t media_read_last_underflow_us;
-} btif_media_stats_t;
+};
 
-typedef struct {
-  thread_t* worker_thread;
-  fixed_queue_t* cmd_msg_queue;
+class BtWorkerThread {
+ public:
+  BtWorkerThread(const std::string& thread_name)
+      : thread_name_(thread_name),
+        message_loop_(nullptr),
+        run_loop_(nullptr),
+        message_loop_thread_(nullptr) {}
+
+  void StartUp() {
+    if (message_loop_thread_ != nullptr) {
+      return;  // Already started up
+    }
+    message_loop_ = new base::MessageLoop();
+    run_loop_ = new base::RunLoop();
+    message_loop_->task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&BtWorkerThread::NotifyStarted, base::Unretained(this)));
+
+    message_loop_thread_ = thread_new_sized(thread_name_.c_str(), SIZE_MAX);
+    CHECK(message_loop_thread_ != nullptr);
+    // TODO: Not needed here - done by raise_priority_a2dp()
+    // thread_set_rt_priority(message_loop_thread_, THREAD_RT_PRIORITY);
+    thread_post(message_loop_thread_, &BtWorkerThread::RunThread, this);
+  }
+
+  bt_status_t DoInThread(const tracked_objects::Location& from_here,
+                         const base::Closure& task) {
+    if ((message_loop_ == nullptr) || !message_loop_->task_runner().get()) {
+      LOG_ERROR(
+          LOG_TAG,
+          "%s: Dropping message for thread %s: message loop is not initialized",
+          __func__, thread_name_.c_str());
+      return BT_STATUS_FAIL;
+    }
+    if (!message_loop_->task_runner()->PostTask(from_here, task)) {
+      LOG_ERROR(LOG_TAG,
+                "%s: Posting task to message loop for thread %s failed",
+                __func__, thread_name_.c_str());
+      return BT_STATUS_FAIL;
+    }
+    return BT_STATUS_SUCCESS;
+  }
+
+  void ShutDown() {
+    if ((run_loop_ != nullptr) && (message_loop_ != nullptr)) {
+      message_loop_->task_runner()->PostTask(FROM_HERE,
+                                             run_loop_->QuitClosure());
+    }
+    thread_free(message_loop_thread_);
+    message_loop_thread_ = nullptr;
+  }
+
+  void NotifyStarted() {
+    LOG_INFO(LOG_TAG, "%s: message loop for thread %s started", __func__,
+             thread_name_.c_str());
+  }
+
+  void NotifyFinished() {
+    LOG_INFO(LOG_TAG, "%s: message loop for thread %s finished", __func__,
+             thread_name_.c_str());
+  }
+
+ private:
+  static void RunThread(void* context) {
+    BtWorkerThread* wt = static_cast<BtWorkerThread*>(context);
+    wt->Run();
+  }
+
+  void Run() {
+    CHECK(message_loop_ != nullptr);
+    CHECK(run_loop_ != nullptr);
+    run_loop_->Run();
+
+    delete message_loop_;
+    message_loop_ = nullptr;
+    delete run_loop_;
+    run_loop_ = nullptr;
+
+    NotifyFinished();
+  }
+
+  std::string thread_name_;
+  base::MessageLoop* message_loop_;
+  base::RunLoop* run_loop_;
+  thread_t* message_loop_thread_;
+};
+
+class BtifA2dpSource {
+ public:
+  enum RunState {
+    kStateOff,
+    kStateStartingUp,
+    kStateRunning,
+    kStateShuttingDown
+  };
+
+  BtifA2dpSource()
+      : tx_audio_queue(nullptr),
+        tx_flush(false),
+        media_alarm(nullptr),
+        encoder_interface(nullptr),
+        encoder_interval_ms(0),
+        state_(kStateOff) {}
+
+  void Reset() {
+    fixed_queue_free(tx_audio_queue, nullptr);
+    tx_audio_queue = nullptr;
+    tx_flush = false;
+    alarm_free(media_alarm);
+    media_alarm = nullptr;
+    encoder_interface = nullptr;
+    encoder_interval_ms = 0;
+    stats.Reset();
+    accumulated_stats.Reset();
+    state_ = kStateOff;
+  }
+
+  BtifA2dpSource::RunState State() const { return state_; }
+  void SetState(BtifA2dpSource::RunState state) { state_ = state; }
+
   fixed_queue_t* tx_audio_queue;
   bool tx_flush; /* Discards any outgoing data when true */
   alarm_t* media_alarm;
   const tA2DP_ENCODER_INTERFACE* encoder_interface;
   period_ms_t encoder_interval_ms; /* Local copy of the encoder interval */
-  btif_media_stats_t stats;
-  btif_media_stats_t accumulated_stats;
-} tBTIF_A2DP_SOURCE_CB;
+  BtifMediaStats stats;
+  BtifMediaStats accumulated_stats;
 
-static tBTIF_A2DP_SOURCE_CB btif_a2dp_source_cb;
-static int btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_OFF;
+ private:
+  BtifA2dpSource::RunState state_;
+};
 
-static void btif_a2dp_source_command_ready(fixed_queue_t* queue, void* context);
-static void btif_a2dp_source_startup_delayed(void* context);
-static void btif_a2dp_source_shutdown_delayed(void* context);
+static BtWorkerThread btif_a2dp_source_thread("btif_a2dp_source_thread");
+static BtifA2dpSource btif_a2dp_source_cb;
+
+static void btif_a2dp_source_startup_delayed(void);
+static void btif_a2dp_source_shutdown_delayed(void);
 static void btif_a2dp_source_audio_tx_start_event(void);
 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_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);
-static void btif_a2dp_source_encoder_init_req(
-    tBTIF_A2DP_SOURCE_ENCODER_INIT* p_msg);
+static void btif_a2dp_source_audio_tx_flush_event(void);
+static void btif_a2dp_source_encoder_init_event(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS& peer_params,
+    const RawAddress& peer_address);
+static void btif_a2dp_source_encoder_user_config_update_event(
+    const RawAddress& peer_address,
+    const btav_a2dp_codec_config_t& codec_user_config);
+static void btif_a2dp_source_audio_feeding_update_event(
+    const btav_a2dp_codec_config_t& codec_audio_config);
 static bool btif_a2dp_source_audio_tx_flush_req(void);
 static void btif_a2dp_source_alarm_cb(void* context);
-static void btif_a2dp_source_audio_handle_timer(void* context);
+static void btif_a2dp_source_audio_handle_timer(void);
 static uint32_t btif_a2dp_source_read_callback(uint8_t* p_buf, uint32_t len);
 static bool btif_a2dp_source_enqueue_callback(BT_HDR* p_buf, size_t frames_n);
 static void log_tstamps_us(const char* comment, uint64_t timestamp_us);
-static void update_scheduling_stats(scheduling_stats_t* stats, uint64_t now_us,
+static void update_scheduling_stats(SchedulingStats* stats, uint64_t now_us,
                                     uint64_t expected_delta);
 static void btm_read_rssi_cb(void* data);
 static void btm_read_failed_contact_counter_cb(void* data);
 static void btm_read_automatic_flush_timeout_cb(void* data);
 static void btm_read_tx_power_cb(void* data);
 
-UNUSED_ATTR static const char* dump_media_event(uint16_t event) {
-  switch (event) {
-    CASE_RETURN_STR(BTIF_MEDIA_AUDIO_TX_START)
-    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_SOURCE_ENCODER_USER_CONFIG_UPDATE)
-    CASE_RETURN_STR(BTIF_MEDIA_AUDIO_FEEDING_UPDATE)
-    default:
-      break;
-  }
-  return "UNKNOWN A2DP SOURCE EVENT";
-}
-
-void btif_a2dp_source_accumulate_scheduling_stats(scheduling_stats_t* src,
-                                                  scheduling_stats_t* dst) {
+void btif_a2dp_source_accumulate_scheduling_stats(SchedulingStats* src,
+                                                  SchedulingStats* dst) {
   dst->total_updates += src->total_updates;
   dst->last_update_us = src->last_update_us;
   dst->overdue_scheduling_count += src->overdue_scheduling_count;
@@ -226,8 +337,8 @@
   dst->total_scheduling_time_us += src->total_scheduling_time_us;
 }
 
-void btif_a2dp_source_accumulate_stats(btif_media_stats_t* src,
-                                       btif_media_stats_t* dst) {
+void btif_a2dp_source_accumulate_stats(BtifMediaStats* src,
+                                       BtifMediaStats* dst) {
   dst->tx_queue_total_frames += src->tx_queue_total_frames;
   dst->tx_queue_max_frames_per_packet = std::max(
       dst->tx_queue_max_frames_per_packet, src->tx_queue_max_frames_per_packet);
@@ -252,152 +363,103 @@
                                                &dst->tx_queue_enqueue_stats);
   btif_a2dp_source_accumulate_scheduling_stats(&src->tx_queue_dequeue_stats,
                                                &dst->tx_queue_dequeue_stats);
-  memset(src, 0, sizeof(btif_media_stats_t));
+  src->Reset();
 }
 
 bool btif_a2dp_source_startup(void) {
-  if (btif_a2dp_source_state != BTIF_A2DP_SOURCE_STATE_OFF) {
+  if (btif_a2dp_source_cb.State() != BtifA2dpSource::kStateOff) {
     APPL_TRACE_ERROR("%s: A2DP Source media task already running", __func__);
     return false;
   }
 
-  memset(&btif_a2dp_source_cb, 0, sizeof(btif_a2dp_source_cb));
-  btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_STARTING_UP;
+  btif_a2dp_source_cb.Reset();
+  btif_a2dp_source_cb.SetState(BtifA2dpSource::kStateStartingUp);
+  btif_a2dp_source_cb.tx_audio_queue = fixed_queue_new(SIZE_MAX);
 
   APPL_TRACE_EVENT("## A2DP SOURCE START MEDIA THREAD ##");
 
   /* Start A2DP Source media task */
-  btif_a2dp_source_cb.worker_thread =
-      thread_new_sized("btif_a2dp_source_worker_thread", SIZE_MAX);
-  if (btif_a2dp_source_cb.worker_thread == NULL) {
-    APPL_TRACE_ERROR("%s: unable to start up media thread", __func__);
-    btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_OFF;
-    return false;
-  }
-
-  btif_a2dp_source_cb.tx_audio_queue = fixed_queue_new(SIZE_MAX);
-
-  btif_a2dp_source_cb.cmd_msg_queue = fixed_queue_new(SIZE_MAX);
-  fixed_queue_register_dequeue(
-      btif_a2dp_source_cb.cmd_msg_queue,
-      thread_get_reactor(btif_a2dp_source_cb.worker_thread),
-      btif_a2dp_source_command_ready, NULL);
+  btif_a2dp_source_thread.StartUp();
 
   APPL_TRACE_EVENT("## A2DP SOURCE MEDIA THREAD STARTED ##");
 
   /* Schedule the rest of the startup operations */
-  thread_post(btif_a2dp_source_cb.worker_thread,
-              btif_a2dp_source_startup_delayed, NULL);
+  btif_a2dp_source_thread.DoInThread(
+      FROM_HERE, base::Bind(&btif_a2dp_source_startup_delayed));
 
   return true;
 }
 
-static void btif_a2dp_source_startup_delayed(UNUSED_ATTR void* context) {
+static void btif_a2dp_source_startup_delayed(void) {
   raise_priority_a2dp(TASK_HIGH_MEDIA);
   btif_a2dp_control_init();
-  btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_RUNNING;
+  btif_a2dp_source_cb.SetState(BtifA2dpSource::kStateRunning);
   BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
       system_bt_osi::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
 }
 
 void btif_a2dp_source_shutdown(void) {
-  if ((btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_OFF) ||
-      (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_SHUTTING_DOWN)) {
+  if ((btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) ||
+      (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateShuttingDown)) {
     return;
   }
 
   /* Make sure no channels are restarted while shutting down */
-  btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_SHUTTING_DOWN;
+  btif_a2dp_source_cb.SetState(BtifA2dpSource::kStateShuttingDown);
 
   APPL_TRACE_EVENT("## A2DP SOURCE STOP MEDIA THREAD ##");
 
   // Stop the timer
   alarm_free(btif_a2dp_source_cb.media_alarm);
-  btif_a2dp_source_cb.media_alarm = NULL;
+  btif_a2dp_source_cb.media_alarm = nullptr;
 
   // Exit the thread
-  fixed_queue_free(btif_a2dp_source_cb.cmd_msg_queue, NULL);
-  btif_a2dp_source_cb.cmd_msg_queue = NULL;
-  thread_post(btif_a2dp_source_cb.worker_thread,
-              btif_a2dp_source_shutdown_delayed, NULL);
-  thread_free(btif_a2dp_source_cb.worker_thread);
-  btif_a2dp_source_cb.worker_thread = NULL;
+  btif_a2dp_source_thread.DoInThread(
+      FROM_HERE, base::Bind(&btif_a2dp_source_shutdown_delayed));
+  btif_a2dp_source_thread.ShutDown();
 }
 
-static void btif_a2dp_source_shutdown_delayed(UNUSED_ATTR void* context) {
+static void btif_a2dp_source_shutdown_delayed(void) {
   btif_a2dp_control_cleanup();
-  fixed_queue_free(btif_a2dp_source_cb.tx_audio_queue, NULL);
-  btif_a2dp_source_cb.tx_audio_queue = NULL;
+  fixed_queue_free(btif_a2dp_source_cb.tx_audio_queue, nullptr);
+  btif_a2dp_source_cb.tx_audio_queue = nullptr;
 
-  btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_OFF;
+  btif_a2dp_source_cb.SetState(BtifA2dpSource::kStateOff);
   BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
       system_bt_osi::DISCONNECT_REASON_UNKNOWN, 0);
 }
 
 bool btif_a2dp_source_media_task_is_running(void) {
-  return (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_RUNNING);
+  return (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateRunning);
 }
 
 bool btif_a2dp_source_media_task_is_shutting_down(void) {
-  return (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_SHUTTING_DOWN);
+  return (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateShuttingDown);
 }
 
 bool btif_a2dp_source_is_streaming(void) {
   return alarm_is_scheduled(btif_a2dp_source_cb.media_alarm);
 }
 
-static void btif_a2dp_source_command_ready(fixed_queue_t* queue,
-                                           UNUSED_ATTR void* context) {
-  BT_HDR* p_msg = (BT_HDR*)fixed_queue_dequeue(queue);
+void btif_a2dp_source_setup_codec(const RawAddress& peer_address) {
+  APPL_TRACE_EVENT("## A2DP SOURCE SETUP CODEC (peer %s) ##",
+                   peer_address.ToString().c_str());
 
-  LOG_VERBOSE(LOG_TAG, "%s: event %d %s", __func__, p_msg->event,
-              dump_media_event(p_msg->event));
+  // Check to make sure the platform has 8 bits/byte since
+  // we're using that in frame size calculations now.
+  CHECK(CHAR_BIT == 8);
 
-  switch (p_msg->event) {
-    case BTIF_MEDIA_AUDIO_TX_START:
-      btif_a2dp_source_audio_tx_start_event();
-      break;
-    case BTIF_MEDIA_AUDIO_TX_STOP:
-      btif_a2dp_source_audio_tx_stop_event();
-      break;
-    case BTIF_MEDIA_AUDIO_TX_FLUSH:
-      btif_a2dp_source_audio_tx_flush_event(p_msg);
-      break;
-    case BTIF_MEDIA_SOURCE_ENCODER_INIT:
-      btif_a2dp_source_encoder_init_event(p_msg);
-      break;
-    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);
-      break;
-  }
-
-  osi_free(p_msg);
-  LOG_VERBOSE(LOG_TAG, "%s: %s DONE", __func__, dump_media_event(p_msg->event));
-}
-
-void btif_a2dp_source_setup_codec(void) {
-  APPL_TRACE_EVENT("## A2DP SOURCE SETUP CODEC ##");
-
-  mutex_global_lock();
-
-  /* Init the encoding task */
-  btif_a2dp_source_encoder_init();
-
-  mutex_global_unlock();
+  tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
+  bta_av_co_get_peer_params(peer_address, &peer_params);
+  btif_a2dp_source_thread.DoInThread(
+      FROM_HERE, base::Bind(&btif_a2dp_source_encoder_init_event, peer_params,
+                            peer_address));
 }
 
 void btif_a2dp_source_start_audio_req(void) {
-  BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
-
-  p_buf->event = BTIF_MEDIA_AUDIO_TX_START;
-  fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
-  memset(&btif_a2dp_source_cb.stats, 0, sizeof(btif_media_stats_t));
+  btif_a2dp_source_thread.DoInThread(
+      FROM_HERE, base::Bind(&btif_a2dp_source_audio_tx_start_event));
+  btif_a2dp_source_cb.stats.Reset();
   // Assign session_start_us to 1 when time_get_os_boottime_us() is 0 to
   // indicate btif_a2dp_source_start_audio_req() has been called
   btif_a2dp_source_cb.stats.session_start_us = time_get_os_boottime_us();
@@ -408,61 +470,28 @@
 }
 
 void btif_a2dp_source_stop_audio_req(void) {
-  BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
+  btif_a2dp_source_thread.DoInThread(
+      FROM_HERE, base::Bind(&btif_a2dp_source_audio_tx_stop_event));
 
-  p_buf->event = BTIF_MEDIA_AUDIO_TX_STOP;
-
-  /*
-   * Explicitly check whether btif_a2dp_source_cb.cmd_msg_queue is not NULL
-   * to avoid a race condition during shutdown of the Bluetooth stack.
-   * This race condition is triggered when A2DP audio is streaming on
-   * shutdown:
-   * "btif_a2dp_source_on_stopped() -> btif_a2dp_source_stop_audio_req()"
-   * is called to stop the particular audio stream, and this happens right
-   * after the "BTIF_AV_CLEANUP_REQ_EVT -> btif_a2dp_source_shutdown()"
-   * processing during the shutdown of the Bluetooth stack.
-   */
-  if (btif_a2dp_source_cb.cmd_msg_queue != NULL) {
-    fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
-  }
   btif_a2dp_source_cb.stats.session_end_us = time_get_os_boottime_us();
   btif_a2dp_source_update_metrics();
   btif_a2dp_source_accumulate_stats(&btif_a2dp_source_cb.stats,
                                     &btif_a2dp_source_cb.accumulated_stats);
 }
 
-static void btif_a2dp_source_encoder_init(void) {
-  tBTIF_A2DP_SOURCE_ENCODER_INIT msg;
+static void btif_a2dp_source_encoder_init_event(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS& peer_params,
+    const RawAddress& peer_address) {
+  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
+                   peer_address.ToString().c_str());
 
-  // Check to make sure the platform has 8 bits/byte since
-  // we're using that in frame size calculations now.
-  CHECK(CHAR_BIT == 8);
-
-  APPL_TRACE_DEBUG("%s", __func__);
-
-  bta_av_co_get_peer_params(&msg.peer_params);
-  btif_a2dp_source_encoder_init_req(&msg);
-}
-
-static void btif_a2dp_source_encoder_init_req(
-    tBTIF_A2DP_SOURCE_ENCODER_INIT* p_msg) {
-  tBTIF_A2DP_SOURCE_ENCODER_INIT* p_buf =
-      (tBTIF_A2DP_SOURCE_ENCODER_INIT*)osi_malloc(
-          sizeof(tBTIF_A2DP_SOURCE_ENCODER_INIT));
-
-  memcpy(p_buf, p_msg, sizeof(tBTIF_A2DP_SOURCE_ENCODER_INIT));
-  p_buf->hdr.event = BTIF_MEDIA_SOURCE_ENCODER_INIT;
-  fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
-}
-
-static void btif_a2dp_source_encoder_init_event(BT_HDR* p_msg) {
-  tBTIF_A2DP_SOURCE_ENCODER_INIT* p_encoder_init =
-      (tBTIF_A2DP_SOURCE_ENCODER_INIT*)p_msg;
-
-  APPL_TRACE_DEBUG("%s", __func__);
-
+  if (!bta_av_co_set_active_peer(peer_address)) {
+    APPL_TRACE_ERROR("%s: Cannot stream audio: cannot set active peer to %s",
+                     __func__, peer_address.ToString().c_str());
+    return;
+  }
   btif_a2dp_source_cb.encoder_interface = bta_av_co_get_encoder_interface();
-  if (btif_a2dp_source_cb.encoder_interface == NULL) {
+  if (btif_a2dp_source_cb.encoder_interface == nullptr) {
     APPL_TRACE_ERROR("%s: Cannot stream audio: no source encoder interface",
                      __func__);
     return;
@@ -476,8 +505,8 @@
   }
 
   btif_a2dp_source_cb.encoder_interface->encoder_init(
-      &p_encoder_init->peer_params, a2dp_codec_config,
-      btif_a2dp_source_read_callback, btif_a2dp_source_enqueue_callback);
+      &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 =
@@ -485,50 +514,41 @@
 }
 
 void btif_a2dp_source_encoder_user_config_update_req(
+    const RawAddress& peer_address,
     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));
-
-  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);
+  btif_a2dp_source_thread.DoInThread(
+      FROM_HERE, base::Bind(&btif_a2dp_source_encoder_user_config_update_event,
+                            peer_address, codec_user_config));
 }
 
-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)) {
+static void btif_a2dp_source_encoder_user_config_update_event(
+    const RawAddress& peer_address,
+    const btav_a2dp_codec_config_t& codec_user_config) {
+  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
+                   peer_address.ToString().c_str());
+  if (!bta_av_co_set_codec_user_config(peer_address, codec_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);
+  btif_a2dp_source_thread.DoInThread(
+      FROM_HERE, base::Bind(&btif_a2dp_source_audio_feeding_update_event,
+                            codec_audio_config));
 }
 
-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;
-
+static void btif_a2dp_source_audio_feeding_update_event(
+    const btav_a2dp_codec_config_t& codec_audio_config) {
   APPL_TRACE_DEBUG("%s", __func__);
-  if (!bta_av_co_set_codec_audio_config(p_feeding->feeding_params)) {
+  if (!bta_av_co_set_codec_audio_config(codec_audio_config)) {
     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;
+  if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
   /* Make sure media task is stopped */
   btif_a2dp_source_stop_audio_req();
@@ -537,10 +557,10 @@
 void btif_a2dp_source_on_stopped(tBTA_AV_SUSPEND* p_av_suspend) {
   APPL_TRACE_EVENT("## ON A2DP SOURCE STOPPED ##");
 
-  if (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_OFF) return;
+  if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
   /* allow using this api for other than suspend */
-  if (p_av_suspend != NULL) {
+  if (p_av_suspend != nullptr) {
     if (p_av_suspend->status != BTA_AV_SUCCESS) {
       APPL_TRACE_EVENT("AV STOP FAILED (%d)", p_av_suspend->status);
       if (p_av_suspend->initiator) {
@@ -565,7 +585,7 @@
 void btif_a2dp_source_on_suspended(tBTA_AV_SUSPEND* p_av_suspend) {
   APPL_TRACE_EVENT("## ON A2DP SOURCE SUSPENDED ##");
 
-  if (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_OFF) return;
+  if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
   /* check for status failures */
   if (p_av_suspend->status != BTA_AV_SUCCESS) {
@@ -598,7 +618,7 @@
       btif_a2dp_source_is_streaming() ? "true" : "false");
 
   /* Reset the media feeding state */
-  CHECK(btif_a2dp_source_cb.encoder_interface != NULL);
+  CHECK(btif_a2dp_source_cb.encoder_interface != nullptr);
   btif_a2dp_source_cb.encoder_interface->feeding_reset();
 
   APPL_TRACE_EVENT(
@@ -608,14 +628,14 @@
   alarm_free(btif_a2dp_source_cb.media_alarm);
   btif_a2dp_source_cb.media_alarm =
       alarm_new_periodic("btif.a2dp_source_media_alarm");
-  if (btif_a2dp_source_cb.media_alarm == NULL) {
+  if (btif_a2dp_source_cb.media_alarm == nullptr) {
     LOG_ERROR(LOG_TAG, "%s unable to allocate media alarm", __func__);
     return;
   }
 
   alarm_set(btif_a2dp_source_cb.media_alarm,
             btif_a2dp_source_cb.encoder_interface->get_encoder_interval_ms(),
-            btif_a2dp_source_alarm_cb, NULL);
+            btif_a2dp_source_alarm_cb, nullptr);
 }
 
 static void btif_a2dp_source_audio_tx_stop_event(void) {
@@ -628,7 +648,7 @@
 
   /* Stop the timer first */
   alarm_free(btif_a2dp_source_cb.media_alarm);
-  btif_a2dp_source_cb.media_alarm = NULL;
+  btif_a2dp_source_cb.media_alarm = nullptr;
 
   UIPC_Close(UIPC_CH_ID_AV_AUDIO);
 
@@ -651,28 +671,28 @@
   btif_a2dp_source_cb.tx_flush = false;
 
   /* Reset the media feeding state */
-  if (btif_a2dp_source_cb.encoder_interface != NULL)
+  if (btif_a2dp_source_cb.encoder_interface != nullptr)
     btif_a2dp_source_cb.encoder_interface->feeding_reset();
 }
 
 static void btif_a2dp_source_alarm_cb(UNUSED_ATTR void* context) {
-  thread_post(btif_a2dp_source_cb.worker_thread,
-              btif_a2dp_source_audio_handle_timer, NULL);
+  btif_a2dp_source_thread.DoInThread(
+      FROM_HERE, base::Bind(&btif_a2dp_source_audio_handle_timer));
 }
 
-static void btif_a2dp_source_audio_handle_timer(UNUSED_ATTR void* context) {
+static void btif_a2dp_source_audio_handle_timer(void) {
   uint64_t timestamp_us = time_get_os_boottime_us();
   log_tstamps_us("A2DP Source tx timer", timestamp_us);
 
   if (alarm_is_scheduled(btif_a2dp_source_cb.media_alarm)) {
-    CHECK(btif_a2dp_source_cb.encoder_interface != NULL);
+    CHECK(btif_a2dp_source_cb.encoder_interface != nullptr);
     size_t transmit_queue_length =
         fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue);
 #ifndef OS_GENERIC
     ATRACE_INT("btif TX queue", transmit_queue_length);
 #endif
     if (btif_a2dp_source_cb.encoder_interface->set_transmit_queue_length !=
-        NULL) {
+        nullptr) {
       btif_a2dp_source_cb.encoder_interface->set_transmit_queue_length(
           transmit_queue_length);
     }
@@ -747,7 +767,7 @@
     }
 
     // Request additional debug info if we had to flush buffers
-    RawAddress peer_bda = btif_av_get_addr();
+    RawAddress peer_bda = btif_av_source_active_peer();
     tBTM_STATUS status = BTM_ReadRSSI(peer_bda, btm_read_rssi_cb);
     if (status != BTM_CMD_STARTED) {
       LOG_WARN(LOG_TAG, "%s: Cannot read RSSI: status %d", __func__, status);
@@ -776,18 +796,18 @@
   btif_a2dp_source_cb.stats.tx_queue_total_frames += frames_n;
   btif_a2dp_source_cb.stats.tx_queue_max_frames_per_packet = std::max(
       frames_n, btif_a2dp_source_cb.stats.tx_queue_max_frames_per_packet);
-  CHECK(btif_a2dp_source_cb.encoder_interface != NULL);
+  CHECK(btif_a2dp_source_cb.encoder_interface != nullptr);
 
   fixed_queue_enqueue(btif_a2dp_source_cb.tx_audio_queue, p_buf);
 
   return true;
 }
 
-static void btif_a2dp_source_audio_tx_flush_event(UNUSED_ATTR BT_HDR* p_msg) {
+static void btif_a2dp_source_audio_tx_flush_event(void) {
   /* Flush all enqueued audio buffers (encoded) */
   APPL_TRACE_DEBUG("%s", __func__);
 
-  if (btif_a2dp_source_cb.encoder_interface != NULL)
+  if (btif_a2dp_source_cb.encoder_interface != nullptr)
     btif_a2dp_source_cb.encoder_interface->feeding_flush();
 
   btif_a2dp_source_cb.stats.tx_queue_total_flushed_messages +=
@@ -796,27 +816,12 @@
       time_get_os_boottime_us();
   fixed_queue_flush(btif_a2dp_source_cb.tx_audio_queue, osi_free);
 
-  UIPC_Ioctl(UIPC_CH_ID_AV_AUDIO, UIPC_REQ_RX_FLUSH, NULL);
+  UIPC_Ioctl(UIPC_CH_ID_AV_AUDIO, UIPC_REQ_RX_FLUSH, nullptr);
 }
 
 static bool btif_a2dp_source_audio_tx_flush_req(void) {
-  BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
-
-  p_buf->event = BTIF_MEDIA_AUDIO_TX_FLUSH;
-
-  /*
-   * Explicitly check whether the btif_a2dp_source_cb.cmd_msg_queue is not
-   * NULL to avoid a race condition during shutdown of the Bluetooth stack.
-   * This race condition is triggered when A2DP audio is streaming on
-   * shutdown:
-   * "btif_a2dp_source_on_stopped() -> btif_a2dp_source_audio_tx_flush_req()"
-   * is called to stop the particular audio stream, and this happens right
-   * after the "BTIF_AV_CLEANUP_REQ_EVT -> btif_a2dp_source_shutdown()"
-   * processing during the shutdown of the Bluetooth stack.
-   */
-  if (btif_a2dp_source_cb.cmd_msg_queue != NULL)
-    fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
-
+  btif_a2dp_source_thread.DoInThread(
+      FROM_HERE, base::Bind(&btif_a2dp_source_audio_tx_flush_event));
   return true;
 }
 
@@ -827,7 +832,7 @@
 
   btif_a2dp_source_cb.stats.tx_queue_total_readbuf_calls++;
   btif_a2dp_source_cb.stats.tx_queue_last_readbuf_us = now_us;
-  if (p_buf != NULL) {
+  if (p_buf != nullptr) {
     // Update the statistics
     update_scheduling_stats(&btif_a2dp_source_cb.stats.tx_queue_dequeue_stats,
                             now_us,
@@ -845,7 +850,7 @@
   prev_us = timestamp_us;
 }
 
-static void update_scheduling_stats(scheduling_stats_t* stats, uint64_t now_us,
+static void update_scheduling_stats(SchedulingStats* stats, uint64_t now_us,
                                     uint64_t expected_delta) {
   uint64_t last_us = stats->last_update_us;
 
@@ -888,12 +893,9 @@
   btif_a2dp_source_accumulate_stats(&btif_a2dp_source_cb.stats,
                                     &btif_a2dp_source_cb.accumulated_stats);
   uint64_t now_us = time_get_os_boottime_us();
-  btif_media_stats_t* accumulated_stats =
-      &btif_a2dp_source_cb.accumulated_stats;
-  scheduling_stats_t* enqueue_stats =
-      &accumulated_stats->tx_queue_enqueue_stats;
-  scheduling_stats_t* dequeue_stats =
-      &accumulated_stats->tx_queue_dequeue_stats;
+  BtifMediaStats* accumulated_stats = &btif_a2dp_source_cb.accumulated_stats;
+  SchedulingStats* enqueue_stats = &accumulated_stats->tx_queue_enqueue_stats;
+  SchedulingStats* dequeue_stats = &accumulated_stats->tx_queue_dequeue_stats;
   size_t ave_size;
   uint64_t ave_time_us;
 
@@ -1050,53 +1052,45 @@
       (unsigned long long)dequeue_stats->max_premature_scheduling_delta_us /
           1000,
       (unsigned long long)ave_time_us / 1000);
-
-  //
-  // Codec-specific stats
-  //
-  A2dpCodecs* a2dp_codecs = bta_av_get_a2dp_codecs();
-  if (a2dp_codecs != nullptr) {
-    a2dp_codecs->debug_codec_dump(fd);
-  }
 }
 
 void btif_a2dp_source_update_metrics(void) {
-  btif_media_stats_t* stats = &btif_a2dp_source_cb.stats;
-  scheduling_stats_t* enqueue_stats = &stats->tx_queue_enqueue_stats;
+  const BtifMediaStats& stats = btif_a2dp_source_cb.stats;
+  const SchedulingStats& enqueue_stats = stats.tx_queue_enqueue_stats;
   A2dpSessionMetrics metrics;
   // session_start_us is 0 when btif_a2dp_source_start_audio_req() is not called
   // mark the metric duration as invalid (-1) in this case
-  if (stats->session_start_us != 0) {
-    int64_t session_end_us = stats->session_end_us == 0
+  if (stats.session_start_us != 0) {
+    int64_t session_end_us = stats.session_end_us == 0
                                  ? time_get_os_boottime_us()
-                                 : stats->session_end_us;
+                                 : stats.session_end_us;
     metrics.audio_duration_ms =
-        (session_end_us - stats->session_start_us) / 1000;
+        (session_end_us - stats.session_start_us) / 1000;
   }
 
-  if (enqueue_stats->total_updates > 1) {
+  if (enqueue_stats.total_updates > 1) {
     metrics.media_timer_min_ms =
         btif_a2dp_source_cb.encoder_interval_ms -
-        (enqueue_stats->max_premature_scheduling_delta_us / 1000);
+        (enqueue_stats.max_premature_scheduling_delta_us / 1000);
     metrics.media_timer_max_ms =
         btif_a2dp_source_cb.encoder_interval_ms +
-        (enqueue_stats->max_overdue_scheduling_delta_us / 1000);
+        (enqueue_stats.max_overdue_scheduling_delta_us / 1000);
 
-    metrics.total_scheduling_count = enqueue_stats->overdue_scheduling_count +
-                                     enqueue_stats->premature_scheduling_count +
-                                     enqueue_stats->exact_scheduling_count;
+    metrics.total_scheduling_count = enqueue_stats.overdue_scheduling_count +
+                                     enqueue_stats.premature_scheduling_count +
+                                     enqueue_stats.exact_scheduling_count;
     if (metrics.total_scheduling_count > 0) {
-      metrics.media_timer_avg_ms = enqueue_stats->total_scheduling_time_us /
+      metrics.media_timer_avg_ms = enqueue_stats.total_scheduling_time_us /
                                    (1000 * metrics.total_scheduling_count);
     }
 
-    metrics.buffer_overruns_max_count = stats->tx_queue_max_dropped_messages;
-    metrics.buffer_overruns_total = stats->tx_queue_total_dropped_messages;
-    metrics.buffer_underruns_count = stats->media_read_total_underflow_count;
+    metrics.buffer_overruns_max_count = stats.tx_queue_max_dropped_messages;
+    metrics.buffer_overruns_total = stats.tx_queue_total_dropped_messages;
+    metrics.buffer_underruns_count = stats.media_read_total_underflow_count;
     metrics.buffer_underruns_average = 0;
     if (metrics.buffer_underruns_count > 0) {
       metrics.buffer_underruns_average =
-          stats->media_read_total_underflow_bytes /
+          stats.media_read_total_underflow_bytes /
           metrics.buffer_underruns_count;
     }
   }
diff --git a/btif/src/btif_av.cc b/btif/src/btif_av.cc
index 7acc23d..03424b5 100644
--- a/btif/src/btif_av.cc
+++ b/btif/src/btif_av.cc
@@ -20,8 +20,11 @@
 
 #include "btif_av.h"
 
+#include <base/bind.h>
 #include <base/logging.h>
+#include <base/strings/stringprintf.h>
 #include <string.h>
+#include <map>
 
 #include <hardware/bluetooth.h>
 #include <hardware/bt_av.h>
@@ -36,81 +39,432 @@
 #include "btif_a2dp_sink.h"
 #include "btif_av_co.h"
 #include "btif_profile_queue.h"
+#include "btif_rc.h"
+#include "btif_state_machine.h"
 #include "btif_util.h"
 #include "btu.h"
 #include "osi/include/allocator.h"
 #include "osi/include/osi.h"
+#include "osi/include/properties.h"
 
 /*****************************************************************************
  *  Constants & Macros
  *****************************************************************************/
-#define BTIF_AV_SERVICE_NAME "Advanced Audio"
-#define BTIF_AVK_SERVICE_NAME "Advanced Audio Sink"
-
-#define BTIF_TIMEOUT_AV_OPEN_ON_RC_MS (2 * 1000)
-
-typedef enum {
-  BTIF_AV_STATE_IDLE = 0x0,
-  BTIF_AV_STATE_OPENING,
-  BTIF_AV_STATE_OPENED,
-  BTIF_AV_STATE_STARTED,
-  BTIF_AV_STATE_CLOSING
-} btif_av_state_t;
-
-/* Should not need dedicated suspend state as actual actions are no
-   different than open state. Suspend flags are needed however to prevent
-   media task from trying to restart stream during remote suspend or while
-   we are in the process of a local suspend */
-
-#define BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING 0x1
-#define BTIF_AV_FLAG_REMOTE_SUSPEND 0x2
-#define BTIF_AV_FLAG_PENDING_START 0x4
-#define BTIF_AV_FLAG_PENDING_STOP 0x8
+static const std::string kBtifAvSourceServiceName = "Advanced Audio Source";
+static const std::string kBtifAvSinkServiceName = "Advanced Audio Sink";
+static const std::string kBtifAvMaxConnectedAudioDevices =
+    "persist.bluetooth.maxconnectedaudiodevices";
+static constexpr int kDefaultMaxConnectedAudioDevices = 1;
+static constexpr tBTA_AV_HNDL kBtaHandleUnknown = 0;
 
 /*****************************************************************************
  *  Local type definitions
  *****************************************************************************/
 
 typedef struct {
-  tBTA_AV_HNDL bta_handle;
-  RawAddress peer_bda;
-  bool self_initiated_connection;
-  btif_sm_handle_t sm_handle;
-  uint8_t flags;
-  tBTA_AV_EDR edr;
-  uint8_t peer_sep; /* sep type of peer device */
-  std::vector<btav_a2dp_codec_config_t> codec_priorities;
-} btif_av_cb_t;
-
-typedef struct {
-  RawAddress* target_bda;
-  uint16_t uuid;
-} btif_av_connect_req_t;
-
-typedef struct {
   int sample_rate;
   int channel_count;
   RawAddress peer_bd;
 } btif_av_sink_config_req_t;
 
+/**
+ * BTIF AV events
+ */
+typedef enum {
+  /* Reuse BTA_AV_XXX_EVT - No need to redefine them here */
+  BTIF_AV_CONNECT_REQ_EVT = BTA_AV_MAX_EVT,
+  BTIF_AV_DISCONNECT_REQ_EVT,
+  BTIF_AV_START_STREAM_REQ_EVT,
+  BTIF_AV_STOP_STREAM_REQ_EVT,
+  BTIF_AV_SUSPEND_STREAM_REQ_EVT,
+  BTIF_AV_SINK_CONFIG_REQ_EVT,
+  BTIF_AV_ACL_DISCONNECTED,
+  BTIF_AV_OFFLOAD_START_REQ_EVT,
+  BTIF_AV_AVRCP_OPEN_EVT,
+  BTIF_AV_AVRCP_CLOSE_EVT,
+  BTIF_AV_AVRCP_REMOTE_PLAY_EVT,
+} btif_av_sm_event_t;
+
+class BtifAvEvent {
+ public:
+  BtifAvEvent(uint32_t event, const void* p_data, size_t data_length);
+  BtifAvEvent(const BtifAvEvent& other);
+  BtifAvEvent() = delete;
+  ~BtifAvEvent();
+  BtifAvEvent& operator=(const BtifAvEvent& other);
+
+  uint32_t Event() const { return event_; }
+  void* Data() const { return data_; }
+  size_t DataLength() const { return data_length_; }
+  std::string ToString() const;
+  static std::string EventName(uint32_t event);
+
+ private:
+  void DeepCopy(uint32_t event, const void* p_data, size_t data_length);
+  void DeepFree();
+
+  uint32_t event_;
+  void* data_;
+  size_t data_length_;
+};
+
+class BtifAvPeer;
+
+// Should not need dedicated Suspend state as actual actions are no
+// different than Open state. Suspend flags are needed however to prevent
+// media task from trying to restart stream during remote Suspend or while
+// we are in the process of a local Suspend.
+class BtifAvStateMachine : public BtifStateMachine {
+ public:
+  enum {
+    kStateIdle,     // AVDTP disconnected
+    kStateOpening,  // Opening AVDTP connection
+    kStateOpened,   // AVDTP is in OPEN state
+    kStateStarted,  // A2DP stream started
+    kStateClosing,  // Closing AVDTP connection
+  };
+
+  class StateIdle : public State {
+   public:
+    StateIdle(BtifAvStateMachine& sm)
+        : State(sm, kStateIdle), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAvPeer& peer_;
+  };
+
+  class StateOpening : public State {
+   public:
+    StateOpening(BtifAvStateMachine& sm)
+        : State(sm, kStateOpening), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAvPeer& peer_;
+  };
+
+  class StateOpened : public State {
+   public:
+    StateOpened(BtifAvStateMachine& sm)
+        : State(sm, kStateOpened), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAvPeer& peer_;
+  };
+
+  class StateStarted : public State {
+   public:
+    StateStarted(BtifAvStateMachine& sm)
+        : State(sm, kStateStarted), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAvPeer& peer_;
+  };
+
+  class StateClosing : public State {
+   public:
+    StateClosing(BtifAvStateMachine& sm)
+        : State(sm, kStateClosing), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAvPeer& peer_;
+  };
+
+  BtifAvStateMachine(BtifAvPeer& btif_av_peer) : peer_(btif_av_peer) {
+    state_idle_ = new StateIdle(*this);
+    state_opening_ = new StateOpening(*this);
+    state_opened_ = new StateOpened(*this);
+    state_started_ = new StateStarted(*this);
+    state_closing_ = new StateClosing(*this);
+
+    AddState(state_idle_);
+    AddState(state_opening_);
+    AddState(state_opened_);
+    AddState(state_started_);
+    AddState(state_closing_);
+    SetInitialState(state_idle_);
+  }
+
+  BtifAvPeer& Peer() { return peer_; }
+
+ private:
+  BtifAvPeer& peer_;
+  StateIdle* state_idle_;
+  StateOpening* state_opening_;
+  StateOpened* state_opened_;
+  StateStarted* state_started_;
+  StateClosing* state_closing_;
+};
+
+class BtifAvPeer {
+ public:
+  enum {
+    kFlagLocalSuspendPending = 0x1,
+    kFlagRemoteSuspend = 0x2,
+    kFlagPendingStart = 0x4,
+    kFlagPendingStop = 0x8,
+  };
+  static constexpr period_ms_t kTimeoutAvOpenOnRcMs = 2 * 1000;  // 2s
+
+  BtifAvPeer(const RawAddress& peer_address, uint8_t peer_sep,
+             tBTA_AV_HNDL bta_handle, uint8_t peer_id);
+  ~BtifAvPeer();
+
+  bt_status_t Init();
+  void Cleanup();
+
+  /**
+   * Check whether the peer can be deleted.
+   *
+   * @return true if the pair can be deleted, otherwise false
+   */
+  bool CanBeDeleted() const;
+
+  const RawAddress& PeerAddress() const { return peer_address_; }
+  bool IsSource() const { return (peer_sep_ == AVDT_TSEP_SRC); }
+  bool IsSink() const { return (peer_sep_ == AVDT_TSEP_SNK); }
+  uint8_t PeerSep() const { return peer_sep_; }
+  /**
+   * Get the local device's Service Class UUID
+   *
+   * @return the local device's Service Class UUID: UUID_SERVCLASS_AUDIO_SOURCE
+   * or UUID_SERVCLASS_AUDIO_SINK
+   */
+  uint16_t LocalUuidServiceClass() const {
+    return (IsSink() ? UUID_SERVCLASS_AUDIO_SOURCE : UUID_SERVCLASS_AUDIO_SINK);
+  }
+  tBTA_AV_HNDL BtaHandle() const { return bta_handle_; }
+  void SetBtaHandle(tBTA_AV_HNDL bta_handle) { bta_handle_ = bta_handle; }
+  uint8_t PeerId() const { return peer_id_; }
+
+  BtifAvStateMachine& StateMachine() { return state_machine_; }
+  const BtifAvStateMachine& StateMachine() const { return state_machine_; }
+  alarm_t* AvOpenOnRcTimer() { return av_open_on_rc_timer_; }
+  const alarm_t* AvOpenOnRcTimer() const { return av_open_on_rc_timer_; }
+
+  void SetEdr(tBTA_AV_EDR edr) { edr_ = edr; }
+  bool IsEdr() const { return (edr_ != 0); }
+  bool Is3Mbps() const { return ((edr_ & BTA_AV_EDR_3MBPS) != 0); }
+
+  bool IsConnected() const;
+  bool IsStreaming() const;
+
+  /**
+   * Check whether any of the flags specified by the bitlags mask is set.
+   *
+   * @param bitflags_mask the bitflags to check
+   * @return true if any of the flags to check is set, otherwise false.
+   */
+  bool CheckFlags(uint8_t bitflags_mask) const {
+    return ((flags_ & bitflags_mask) != 0);
+  }
+
+  /**
+   * Set only the flags as specified by the bitflags mask.
+   *
+   * @param bitflags_mask the bitflags to set
+   */
+  void SetFlags(uint8_t bitflags_mask) { flags_ |= bitflags_mask; }
+
+  /**
+   * Clear only the flags as specified by the bitflags mask.
+   *
+   * @param bitflags_mask the bitflags to clear
+   */
+  void ClearFlags(uint8_t bitflags_mask) { flags_ &= ~bitflags_mask; }
+
+  /**
+   * Clear all flags.
+   */
+  void ClearAllFlags() { flags_ = 0; }
+
+  /**
+   * Get a string representation of the flags that are set.
+   */
+  std::string FlagsToString() const;
+
+  bool SelfInitiatedConnection() const { return self_initiated_connection_; }
+  void SetSelfInitiatedConnection(bool v) { self_initiated_connection_ = v; }
+
+ private:
+  const RawAddress peer_address_;
+  const uint8_t peer_sep_;  // SEP type of peer device
+  tBTA_AV_HNDL bta_handle_;
+  const uint8_t peer_id_;
+  BtifAvStateMachine state_machine_;
+  alarm_t* av_open_on_rc_timer_;
+  tBTA_AV_EDR edr_;
+  uint8_t flags_;
+  bool self_initiated_connection_;
+};
+
+class BtifAvSource {
+ public:
+  // The PeerId is used as AppId for BTA_AvRegister() purpose
+  static constexpr uint8_t kPeerIdMin = 0;
+  static constexpr uint8_t kPeerIdMax = BTA_AV_NUM_STRS;
+
+  BtifAvSource()
+      : callbacks_(nullptr),
+        enabled_(false),
+        max_connected_peers_(kDefaultMaxConnectedAudioDevices) {}
+  ~BtifAvSource();
+
+  btav_source_callbacks_t* Callbacks() { return callbacks_; }
+  bt_status_t Init(
+      btav_source_callbacks_t* callbacks,
+      const std::vector<btav_a2dp_codec_config_t>& codec_priorities);
+  void Cleanup();
+
+  bool Enabled() const { return enabled_; }
+  BtifAvPeer* FindPeer(const RawAddress& peer_address);
+  BtifAvPeer* FindPeerByHandle(tBTA_AV_HNDL bta_handle);
+  BtifAvPeer* FindPeerByPeerId(uint8_t peer_id);
+  BtifAvPeer* FindOrCreatePeer(const RawAddress& peer_address,
+                               tBTA_AV_HNDL bta_handle);
+
+  /**
+   * Check whether a connection to a peer is allowed.
+   * The check considers the maximum number of connected peers.
+   *
+   * @param peer_address the peer address to connect to
+   * @return true if connection is allowed, otherwise false
+   */
+  bool AllowedToConnect(const RawAddress& peer_address) const;
+
+  /**
+   * Delete a peer.
+   *
+   * @param peer_address the peer to delete
+   * @return true on success, otherwise false
+   */
+  bool DeletePeer(const RawAddress& peer_address);
+
+  /**
+   * Delete all peers that have transitioned to Idle state and can be deleted.
+   * If a peer was just created/initialized, then it cannot be deleted yet.
+   */
+  void DeleteIdlePeers();
+
+  const RawAddress& ActivePeer() const { return active_peer_; }
+  bool SetActivePeer(const RawAddress& peer_address) {
+    if (active_peer_ == peer_address) return true;  // Nothing has changed
+    if (bta_av_co_set_active_peer(peer_address)) {
+      active_peer_ = peer_address;
+      return true;
+    }
+    return false;
+  }
+
+  const std::map<RawAddress, BtifAvPeer*>& Peers() const { return peers_; }
+
+  void RegisterAllBtaHandles();
+  void DeregisterAllBtaHandles();
+  void BtaHandleRegistered(uint8_t peer_id, tBTA_AV_HNDL bta_handle);
+
+ private:
+  void CleanupAllPeers();
+
+  btav_source_callbacks_t* callbacks_;
+  bool enabled_;
+  int max_connected_peers_;
+  std::map<RawAddress, BtifAvPeer*> peers_;
+  RawAddress active_peer_;
+  std::map<uint8_t, tBTA_AV_HNDL> peer_id2bta_handle_;
+  std::vector<btav_a2dp_codec_config_t> codec_priorities_;
+};
+
+class BtifAvSink {
+ public:
+  // The PeerId is used as AppId for BTA_AvRegister() purpose
+  static constexpr uint8_t kPeerIdMin = 0;
+  static constexpr uint8_t kPeerIdMax = BTA_AV_NUM_STRS;
+
+  BtifAvSink()
+      : callbacks_(nullptr),
+        enabled_(false),
+        max_connected_peers_(kDefaultMaxConnectedAudioDevices) {}
+  ~BtifAvSink();
+  bt_status_t Init(btav_sink_callbacks_t* callbacks);
+
+  btav_sink_callbacks_t* Callbacks() { return callbacks_; }
+  void Cleanup();
+
+  bool Enabled() const { return enabled_; }
+  BtifAvPeer* FindPeer(const RawAddress& peer_address);
+  BtifAvPeer* FindPeerByHandle(tBTA_AV_HNDL bta_handle);
+  BtifAvPeer* FindPeerByPeerId(uint8_t peer_id);
+  BtifAvPeer* FindOrCreatePeer(const RawAddress& peer_address,
+                               tBTA_AV_HNDL bta_handle);
+
+  /**
+   * Check whether a connection to a peer is allowed.
+   * The check considers the maximum number of connected peers.
+   *
+   * @param peer_address the peer address to connect to
+   * @return true if connection is allowed, otherwise false
+   */
+  bool AllowedToConnect(const RawAddress& peer_address) const;
+
+  /**
+   * Delete a peer.
+   *
+   * @param peer_address the peer to delete
+   * @return true on success, otherwise false
+   */
+  bool DeletePeer(const RawAddress& peer_address);
+
+  /**
+   * Delete all peers that have transitioned to Idle state and can be deleted.
+   * If a peer was just created/initialized, then it cannot be deleted yet.
+   */
+  void DeleteIdlePeers();
+
+  const RawAddress& ActivePeer() const { return active_peer_; }
+  bool SetActivePeer(const RawAddress& peer_address) {
+    if (bta_av_co_set_active_peer(peer_address)) {
+      active_peer_ = peer_address;
+      return true;
+    }
+    return false;
+  }
+
+  const std::map<RawAddress, BtifAvPeer*>& Peers() const { return peers_; }
+
+  void RegisterAllBtaHandles();
+  void DeregisterAllBtaHandles();
+  void BtaHandleRegistered(uint8_t peer_id, tBTA_AV_HNDL bta_handle);
+
+ private:
+  void CleanupAllPeers();
+
+  btav_sink_callbacks_t* callbacks_;
+  bool enabled_;
+  int max_connected_peers_;
+  std::map<RawAddress, BtifAvPeer*> peers_;
+  RawAddress active_peer_;
+  std::map<uint8_t, tBTA_AV_HNDL> peer_id2bta_handle_;
+};
+
 /*****************************************************************************
  *  Static variables
  *****************************************************************************/
-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}}, false, 0, 0, 0, 0, std::vector<btav_a2dp_codec_config_t>()};
-static alarm_t* av_open_on_rc_timer = NULL;
-
-/* both interface and media task needs to be ready to alloc incoming request */
-#define CHECK_BTAV_INIT()                                                    \
-  do {                                                                       \
-    if (((bt_av_src_callbacks == NULL) && (bt_av_sink_callbacks == NULL)) || \
-        (btif_av_cb.sm_handle == NULL)) {                                    \
-      BTIF_TRACE_WARNING("%s: BTAV not initialized", __func__);              \
-      return BT_STATUS_NOT_READY;                                            \
-    }                                                                        \
-  } while (0)
+static BtifAvSource btif_av_source;
+static BtifAvSink btif_av_sink;
 
 /* Helper macro to avoid code duplication in the state machine handlers */
 #define CHECK_RC_EVENT(e, d)       \
@@ -126,44 +480,48 @@
     btif_rc_handler(e, d);         \
   } break;
 
-static bool btif_av_state_idle_handler(btif_sm_event_t event, void* data);
-static bool btif_av_state_opening_handler(btif_sm_event_t event, void* data);
-static bool btif_av_state_opened_handler(btif_sm_event_t event, void* data);
-static bool btif_av_state_started_handler(btif_sm_event_t event, void* data);
-static bool btif_av_state_closing_handler(btif_sm_event_t event, void* data);
+static bt_status_t src_disconnect_sink(const RawAddress& peer_address);
+static bt_status_t sink_disconnect_src(const RawAddress& peer_address);
+static void btif_av_source_dispatch_sm_event(const RawAddress& peer_address,
+                                             btif_av_sm_event_t event);
+static void btif_av_sink_dispatch_sm_event(const RawAddress& peer_address,
+                                           btif_av_sm_event_t event);
+static void btif_av_handle_event(uint8_t peer_sep,
+                                 const RawAddress& peer_address,
+                                 tBTA_AV_HNDL bta_handle,
+                                 const BtifAvEvent& btif_av_event);
+static void btif_report_connection_state(const RawAddress& peer_address,
+                                         btav_connection_state_t state);
+static void btif_report_audio_state(const RawAddress& peer_address,
+                                    btav_audio_state_t state);
+static void btif_av_source_initiate_av_open_timer_timeout(void* data);
+static void btif_av_sink_initiate_av_open_timer_timeout(void* data);
+static void bta_av_sink_media_callback(tBTA_AV_EVT event,
+                                       tBTA_AV_MEDIA* p_data);
 
-static const btif_sm_handler_t btif_av_state_handlers[] = {
-    btif_av_state_idle_handler, btif_av_state_opening_handler,
-    btif_av_state_opened_handler, btif_av_state_started_handler,
-    btif_av_state_closing_handler};
-
-static void btif_av_event_free_data(btif_sm_event_t event, void* p_data);
-
-/*************************************************************************
- * Extern functions
- ************************************************************************/
-extern void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV* p_data);
-extern bool btif_rc_get_connected_peer(RawAddress* peer_addr);
-extern uint8_t btif_rc_get_connected_peer_handle(const RawAddress& peer_addr);
-extern void btif_rc_check_handle_pending_play(const RawAddress& peer_addr,
-                                              bool bSendToApp);
+static BtifAvPeer* btif_av_source_find_peer(const RawAddress& peer_address) {
+  return btif_av_source.FindPeer(peer_address);
+}
+static BtifAvPeer* btif_av_sink_find_peer(const RawAddress& peer_address) {
+  return btif_av_sink.FindPeer(peer_address);
+}
+static BtifAvPeer* btif_av_find_peer(const RawAddress& peer_address) {
+  if (btif_av_source.Enabled()) return btif_av_source_find_peer(peer_address);
+  if (btif_av_sink.Enabled()) return btif_av_sink_find_peer(peer_address);
+  return nullptr;
+}
+static BtifAvPeer* btif_av_find_active_peer() {
+  if (btif_av_source.Enabled())
+    return btif_av_source_find_peer(btif_av_source.ActivePeer());
+  if (btif_av_sink.Enabled())
+    return btif_av_sink_find_peer(btif_av_sink.ActivePeer());
+  return nullptr;
+}
 
 /*****************************************************************************
  * Local helper functions
  *****************************************************************************/
 
-const char* dump_av_sm_state_name(btif_av_state_t state) {
-  switch (state) {
-    CASE_RETURN_STR(BTIF_AV_STATE_IDLE)
-    CASE_RETURN_STR(BTIF_AV_STATE_OPENING)
-    CASE_RETURN_STR(BTIF_AV_STATE_OPENED)
-    CASE_RETURN_STR(BTIF_AV_STATE_STARTED)
-    CASE_RETURN_STR(BTIF_AV_STATE_CLOSING)
-    default:
-      return "UNKNOWN_STATE";
-  }
-}
-
 const char* dump_av_sm_event_name(btif_av_sm_event_t event) {
   switch ((int)event) {
     CASE_RETURN_STR(BTA_AV_ENABLE_EVT)
@@ -189,976 +547,67 @@
     CASE_RETURN_STR(BTA_AV_REJECT_EVT)
     CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT)
     CASE_RETURN_STR(BTA_AV_OFFLOAD_START_RSP_EVT)
-    CASE_RETURN_STR(BTIF_SM_ENTER_EVT)
-    CASE_RETURN_STR(BTIF_SM_EXIT_EVT)
     CASE_RETURN_STR(BTIF_AV_CONNECT_REQ_EVT)
     CASE_RETURN_STR(BTIF_AV_DISCONNECT_REQ_EVT)
     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_ACL_DISCONNECTED)
     CASE_RETURN_STR(BTIF_AV_OFFLOAD_START_REQ_EVT)
+    CASE_RETURN_STR(BTIF_AV_AVRCP_OPEN_EVT)
+    CASE_RETURN_STR(BTIF_AV_AVRCP_CLOSE_EVT)
+    CASE_RETURN_STR(BTIF_AV_AVRCP_REMOTE_PLAY_EVT)
     default:
       return "UNKNOWN_EVENT";
   }
 }
 
-/****************************************************************************
- *  Local helper functions
- ****************************************************************************/
-/*******************************************************************************
- *
- * Function         btif_initiate_av_open_timer_timeout
- *
- * Description      Timer to trigger AV open if the remote headset establishes
- *                  RC connection w/o AV connection. The timer is needed to IOP
- *                  with headsets that do establish AV after RC connection.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btif_initiate_av_open_timer_timeout(UNUSED_ATTR void* data) {
-  RawAddress peer_addr;
-  btif_av_connect_req_t connect_req;
+BtifAvEvent::BtifAvEvent(uint32_t event, const void* p_data, size_t data_length)
+    : event_(event), data_(nullptr), data_length_(0) {
+  DeepCopy(event, p_data, data_length);
+}
 
-  /* is there at least one RC connection - There should be */
-  if (btif_rc_get_connected_peer(&peer_addr)) {
-    BTIF_TRACE_DEBUG("%s: Issuing connect to the remote RC peer", __func__);
-    /* In case of AVRCP connection request, we will initiate SRC connection */
-    connect_req.target_bda = &peer_addr;
-    if (bt_av_sink_callbacks != NULL)
-      connect_req.uuid = UUID_SERVCLASS_AUDIO_SINK;
-    else if (bt_av_src_callbacks != NULL)
-      connect_req.uuid = UUID_SERVCLASS_AUDIO_SOURCE;
-    btif_dispatch_sm_event(BTIF_AV_CONNECT_REQ_EVT, (char*)&connect_req,
-                           sizeof(connect_req));
+BtifAvEvent::BtifAvEvent(const BtifAvEvent& other)
+    : event_(0), data_(nullptr), data_length_(0) {
+  *this = other;
+}
+
+BtifAvEvent& BtifAvEvent::operator=(const BtifAvEvent& other) {
+  DeepFree();
+  DeepCopy(other.Event(), other.Data(), other.DataLength());
+  return *this;
+}
+
+BtifAvEvent::~BtifAvEvent() { DeepFree(); }
+
+std::string BtifAvEvent::ToString() const {
+  return BtifAvEvent::EventName(event_);
+}
+
+std::string BtifAvEvent::EventName(uint32_t event) {
+  std::string name = dump_av_sm_event_name((btif_av_sm_event_t)event);
+  std::stringstream ss_value;
+  ss_value << "(0x" << std::hex << event << ")";
+  return name + ss_value.str();
+}
+
+void BtifAvEvent::DeepCopy(uint32_t event, const void* p_data,
+                           size_t data_length) {
+  event_ = event;
+  data_length_ = data_length;
+  if (data_length == 0) {
+    data_ = nullptr;
   } else {
-    BTIF_TRACE_ERROR("%s: No connected RC peers", __func__);
-  }
-}
-
-/*****************************************************************************
- *  Static functions
- *****************************************************************************/
-
-/*******************************************************************************
- *
- * Function         btif_report_connection_state
- *
- * Description      Updates the components via the callbacks about the
- *                  connection state of a2dp connection.
- *
- * Returns          None
- *
- ******************************************************************************/
-static void btif_report_connection_state(RawAddress* bd_addr,
-                                         btav_connection_state_t state) {
-  if (bt_av_sink_callbacks != NULL) {
-    HAL_CBACK(bt_av_sink_callbacks, connection_state_cb, bd_addr, state);
-  } else if (bt_av_src_callbacks != NULL) {
-    HAL_CBACK(bt_av_src_callbacks, connection_state_cb, bd_addr, state);
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_report_audio_state
- *
- * Description      Updates the components via the callbacks about the audio
- *                  state of a2dp connection. The state is updated when either
- *                  the remote ends starts streaming (started state) or whenever
- *                  it transitions out of started state (to opened or streaming)
- *                  state.
- *
- * Returns          None
- *
- ******************************************************************************/
-static void btif_report_audio_state(RawAddress* bd_addr,
-                                    btav_audio_state_t state) {
-  if (bt_av_sink_callbacks != NULL) {
-    HAL_CBACK(bt_av_sink_callbacks, audio_state_cb, bd_addr, state);
-  } else if (bt_av_src_callbacks != NULL) {
-    HAL_CBACK(bt_av_src_callbacks, audio_state_cb, bd_addr, state);
-  }
-}
-
-static void btif_update_source_codec(void* p_data) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  // copy to avoid alignment problems
-  btav_a2dp_codec_config_t req;
-  memcpy(&req, p_data, sizeof(req));
-
-  btif_a2dp_source_encoder_user_config_update_req(req);
-}
-
-static void btif_report_source_codec_state(void* p_data) {
-  RawAddress* bd_addr = (RawAddress*)p_data;
-  btav_a2dp_codec_config_t codec_config;
-  std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities;
-  std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities;
-
-  A2dpCodecs* a2dp_codecs = bta_av_get_a2dp_codecs();
-  if (a2dp_codecs == nullptr) return;
-  if (!a2dp_codecs->getCodecConfigAndCapabilities(
-          &codec_config, &codecs_local_capabilities,
-          &codecs_selectable_capabilities)) {
-    BTIF_TRACE_WARNING(
-        "%s: error reporting audio source codec state: "
-        "cannot get codec config and capabilities",
-        __func__);
-    return;
-  }
-  if (bt_av_src_callbacks != NULL) {
-    HAL_CBACK(bt_av_src_callbacks, audio_config_cb, bd_addr, codec_config,
-              codecs_local_capabilities, codecs_selectable_capabilities);
-  }
-}
-
-/*****************************************************************************
- *
- * Function     btif_av_state_idle_handler
- *
- * Description  State managing disconnected AV link
- *
- * Returns      true if event was processed, false otherwise
- *
- ******************************************************************************/
-
-static bool btif_av_state_idle_handler(btif_sm_event_t event, void* p_data) {
-  BTIF_TRACE_DEBUG("%s: event=%s flags=0x%x", __func__,
-                   dump_av_sm_event_name((btif_av_sm_event_t)event),
-                   btif_av_cb.flags);
-
-  switch (event) {
-    case BTIF_SM_ENTER_EVT:
-      /* clear the peer_bda */
-      btif_av_cb.peer_bda = RawAddress::kEmpty;
-      btif_av_cb.flags = 0;
-      btif_av_cb.edr = 0;
-      bta_av_co_init(btif_av_cb.codec_priorities);
-      btif_a2dp_on_idle();
-      break;
-
-    case BTIF_SM_EXIT_EVT:
-      break;
-
-    case BTA_AV_ENABLE_EVT:
-      break;
-
-    case BTA_AV_REGISTER_EVT:
-      btif_av_cb.bta_handle = ((tBTA_AV*)p_data)->registr.hndl;
-      break;
-
-    case BTA_AV_PENDING_EVT:
-    case BTIF_AV_CONNECT_REQ_EVT: {
-      if (event == BTIF_AV_CONNECT_REQ_EVT) {
-        btif_av_connect_req_t* connect_req_p = (btif_av_connect_req_t*)p_data;
-        btif_av_cb.peer_bda = *connect_req_p->target_bda;
-        btif_av_cb.self_initiated_connection = true;
-        BTA_AvOpen(btif_av_cb.peer_bda, btif_av_cb.bta_handle, true,
-                   BTA_SEC_AUTHENTICATE, connect_req_p->uuid);
-      } else if (event == BTA_AV_PENDING_EVT) {
-        btif_av_cb.peer_bda = ((tBTA_AV*)p_data)->pend.bd_addr;
-        btif_av_cb.self_initiated_connection = false;
-        if (bt_av_src_callbacks != NULL) {
-          BTA_AvOpen(btif_av_cb.peer_bda, btif_av_cb.bta_handle, true,
-                     BTA_SEC_AUTHENTICATE, UUID_SERVCLASS_AUDIO_SOURCE);
-        }
-        if (bt_av_sink_callbacks != NULL) {
-          BTA_AvOpen(btif_av_cb.peer_bda, btif_av_cb.bta_handle, true,
-                     BTA_SEC_AUTHENTICATE, UUID_SERVCLASS_AUDIO_SINK);
-        }
-      }
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENING);
-    } break;
-
-    case BTA_AV_RC_OPEN_EVT:
-      /* IOP_FIX: Jabra 620 only does RC open without AV open whenever it
-       * connects. So
-       * as per the AV WP, an AVRC connection cannot exist without an AV
-       * connection. Therefore,
-       * we initiate an AV connection if an RC_OPEN_EVT is received when we are
-       * in AV_CLOSED state.
-       * We initiate the AV connection after a small 3s timeout to avoid any
-       * collisions from the
-       * headsets, as some headsets initiate the AVRC connection first and then
-       * immediately initiate the AV connection
-       *
-       * TODO: We may need to do this only on an AVRCP Play. FixMe
-       */
-
-      BTIF_TRACE_WARNING("%s: BTA_AV_RC_OPEN_EVT received w/o AV", __func__);
-      alarm_set_on_mloop(av_open_on_rc_timer, BTIF_TIMEOUT_AV_OPEN_ON_RC_MS,
-                         btif_initiate_av_open_timer_timeout, NULL);
-      btif_rc_handler(event, (tBTA_AV*)p_data);
-      break;
-
-    case BTA_AV_RC_BROWSE_OPEN_EVT:
-      BTIF_TRACE_DEBUG("%s: BTA_AV_RC_BROWSE_OPEN_EVT received", __func__);
-      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
-     * we have to handle config and open event in
-     * idle_state. We hit these scenarios while running
-     * PTS test case for AVRCP Controller
-     */
-    case BTIF_AV_SINK_CONFIG_REQ_EVT: {
-      btif_av_sink_config_req_t req;
-      // copy to avoid alignment problems
-      memcpy(&req, p_data, sizeof(req));
-
-      BTIF_TRACE_WARNING(
-          "%s: BTIF_AV_SINK_CONFIG_REQ_EVT sample_rate=%d "
-          "channel_count=%d",
-          __func__, req.sample_rate, req.channel_count);
-      if (bt_av_sink_callbacks != NULL) {
-        HAL_CBACK(bt_av_sink_callbacks, audio_config_cb, &(req.peer_bd),
-                  req.sample_rate, req.channel_count);
-      }
-    } break;
-
-    case BTA_AV_OPEN_EVT: {
-      tBTA_AV* p_bta_data = (tBTA_AV*)p_data;
-      btav_connection_state_t state;
-      btif_sm_state_t av_state;
-      BTIF_TRACE_WARNING("%s: BTA_AV_OPEN_EVT status=%d, edr=0x%x", __func__,
-                         p_bta_data->open.status, p_bta_data->open.edr);
-
-      if (p_bta_data->open.status == BTA_AV_SUCCESS) {
-        state = BTAV_CONNECTION_STATE_CONNECTED;
-        av_state = BTIF_AV_STATE_OPENED;
-        btif_av_cb.edr = p_bta_data->open.edr;
-
-        btif_av_cb.peer_sep = p_bta_data->open.sep;
-      } else {
-        BTIF_TRACE_WARNING("%s: BTA_AV_OPEN_EVT::FAILED status=%d", __func__,
-                           p_bta_data->open.status);
-        state = BTAV_CONNECTION_STATE_DISCONNECTED;
-        av_state = BTIF_AV_STATE_IDLE;
-      }
-
-      /* inform the application of the event */
-      btif_report_connection_state(&(btif_av_cb.peer_bda), state);
-      /* change state to open/idle based on the status */
-      btif_sm_change_state(btif_av_cb.sm_handle, av_state);
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SNK) {
-        /* if queued PLAY command,  send it now */
-        btif_rc_check_handle_pending_play(
-            p_bta_data->open.bd_addr,
-            (p_bta_data->open.status == BTA_AV_SUCCESS));
-      } else if ((btif_av_cb.peer_sep == AVDT_TSEP_SRC) &&
-                 (p_bta_data->open.status == BTA_AV_SUCCESS)) {
-        /* Bring up AVRCP connection too */
-        BTA_AvOpenRc(btif_av_cb.bta_handle);
-      }
-      btif_queue_advance();
-    } break;
-
-    case BTA_AV_REMOTE_CMD_EVT:
-    case BTA_AV_VENDOR_CMD_EVT:
-    case BTA_AV_META_MSG_EVT:
-    case BTA_AV_RC_FEAT_EVT:
-    case BTA_AV_REMOTE_RSP_EVT:
-      btif_rc_handler(event, (tBTA_AV*)p_data);
-      break;
-
-    case BTA_AV_RC_CLOSE_EVT:
-      BTIF_TRACE_DEBUG("%s: BTA_AV_RC_CLOSE_EVT: Stopping AV timer.", __func__);
-      alarm_cancel(av_open_on_rc_timer);
-      btif_rc_handler(event, (tBTA_AV*)p_data);
-      break;
-
-    case BTIF_AV_OFFLOAD_START_REQ_EVT:
-      BTIF_TRACE_ERROR(
-          "%s: BTIF_AV_OFFLOAD_START_REQ_EVT: Stream not Started IDLE",
-          __func__);
-      btif_a2dp_on_offload_started(BTA_AV_FAIL);
-      break;
-
-    default:
-      BTIF_TRACE_WARNING("%s: unhandled event=%s", __func__,
-                         dump_av_sm_event_name((btif_av_sm_event_t)event));
-      return false;
-  }
-
-  return true;
-}
-/*****************************************************************************
- *
- * Function        btif_av_state_opening_handler
- *
- * Description     Intermediate state managing events during establishment
- *                 of avdtp channel
- *
- * Returns         true if event was processed, false otherwise
- *
- ******************************************************************************/
-
-static bool btif_av_state_opening_handler(btif_sm_event_t event, void* p_data) {
-  BTIF_TRACE_DEBUG("%s: event=%s flags=0x%x", __func__,
-                   dump_av_sm_event_name((btif_av_sm_event_t)event),
-                   btif_av_cb.flags);
-
-  switch (event) {
-    case BTIF_SM_ENTER_EVT:
-      /* inform the application that we are entering connecting state */
-      btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                   BTAV_CONNECTION_STATE_CONNECTING);
-      break;
-
-    case BTIF_SM_EXIT_EVT:
-      break;
-
-    case BTA_AV_REJECT_EVT:
-      BTIF_TRACE_WARNING("%s: Received BTA_AV_REJECT_EVT", __func__);
-      btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                   BTAV_CONNECTION_STATE_DISCONNECTED);
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
-      if (btif_av_cb.self_initiated_connection) {
-        btif_queue_advance();
-      }
-      break;
-
-    case BTA_AV_OPEN_EVT: {
-      tBTA_AV* p_bta_data = (tBTA_AV*)p_data;
-      btav_connection_state_t state;
-      btif_sm_state_t av_state;
-      BTIF_TRACE_WARNING("%s: BTA_AV_OPEN_EVT status=%d, edr=0x%x", __func__,
-                         p_bta_data->open.status, p_bta_data->open.edr);
-
-      if (p_bta_data->open.status == BTA_AV_SUCCESS) {
-        state = BTAV_CONNECTION_STATE_CONNECTED;
-        av_state = BTIF_AV_STATE_OPENED;
-        btif_av_cb.edr = p_bta_data->open.edr;
-
-        btif_av_cb.peer_sep = p_bta_data->open.sep;
-      } else {
-        BTIF_TRACE_WARNING("%s: BTA_AV_OPEN_EVT::FAILED status: %d", __func__,
-                           p_bta_data->open.status);
-        RawAddress peer_addr;
-        uint8_t peer_handle = BTRC_HANDLE_NONE;
-        if (btif_rc_get_connected_peer(&peer_addr) &&
-            btif_av_cb.peer_bda == peer_addr) {
-          /*
-           * Disconnect AVRCP connection, if
-           * A2DP conneciton failed, for any reason
-           */
-          BTIF_TRACE_WARNING("%s: Disconnecting AVRCP: peer_addr=%s", __func__,
-                             peer_addr.ToString().c_str());
-          peer_handle = btif_rc_get_connected_peer_handle(peer_addr);
-          if (peer_handle != BTRC_HANDLE_NONE) {
-            BTA_AvCloseRc(peer_handle);
-          }
-        }
-        state = BTAV_CONNECTION_STATE_DISCONNECTED;
-        av_state = BTIF_AV_STATE_IDLE;
-      }
-
-      /* inform the application of the event */
-      btif_report_connection_state(&(btif_av_cb.peer_bda), state);
-      /* change state to open/idle based on the status */
-      btif_sm_change_state(btif_av_cb.sm_handle, av_state);
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SNK) {
-        /* if queued PLAY command,  send it now */
-        btif_rc_check_handle_pending_play(
-            p_bta_data->open.bd_addr,
-            (p_bta_data->open.status == BTA_AV_SUCCESS));
-      } else if ((btif_av_cb.peer_sep == AVDT_TSEP_SRC) &&
-                 (p_bta_data->open.status == BTA_AV_SUCCESS)) {
-        /* Bring up AVRCP connection too */
-        BTA_AvOpenRc(btif_av_cb.bta_handle);
-      }
-      if (btif_av_cb.self_initiated_connection) {
-        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
-      memcpy(&req, p_data, sizeof(req));
-
-      BTIF_TRACE_WARNING(
-          "%s: BTIF_AV_SINK_CONFIG_REQ_EVT sample_rate=%d "
-          "channel_count=%d",
-          __func__, req.sample_rate, req.channel_count);
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SRC &&
-          bt_av_sink_callbacks != NULL) {
-        HAL_CBACK(bt_av_sink_callbacks, audio_config_cb, &(btif_av_cb.peer_bda),
-                  req.sample_rate, req.channel_count);
-      }
-    } break;
-
-    case BTIF_AV_CONNECT_REQ_EVT: {
-      // Check for device, if same device which moved to opening then ignore
-      // callback
-      btif_av_connect_req_t* connect_req_p = (btif_av_connect_req_t*)p_data;
-      RawAddress& target_bda = *connect_req_p->target_bda;
-      if (btif_av_cb.peer_bda == target_bda) {
-        BTIF_TRACE_WARNING(
-            "%s: device %s is already connecting, ignore Connect request",
-            __func__, btif_av_cb.peer_bda.ToString().c_str());
-      } else {
-        BTIF_TRACE_WARNING(
-            "%s: device %s is already connecting, reject Connect request to %s",
-            __func__, btif_av_cb.peer_bda.ToString().c_str(),
-            target_bda.ToString().c_str());
-        btif_report_connection_state(&target_bda,
-                                     BTAV_CONNECTION_STATE_DISCONNECTED);
-      }
-      // Ignore all connection request if we are already opening
-      btif_queue_advance();
-    } break;
-
-    case BTA_AV_PENDING_EVT: {
-      // Check for device, if same device which moved to opening then ignore
-      // callback
-      const RawAddress& bd_addr = ((tBTA_AV*)p_data)->pend.bd_addr;
-      if (bd_addr == btif_av_cb.peer_bda) {
-        BTIF_TRACE_WARNING(
-            "%s: device %s is already connecting, ignore incoming request",
-            __func__, btif_av_cb.peer_bda.ToString().c_str());
-      } else {
-        BTIF_TRACE_WARNING(
-            "%s: device %s is already connecting, reject incoming request "
-            "from %s",
-            __func__, btif_av_cb.peer_bda.ToString().c_str(),
-            bd_addr.ToString().c_str());
-        BTA_AvDisconnect(bd_addr);
-      }
-    } break;
-
-    case BTIF_AV_OFFLOAD_START_REQ_EVT:
-      BTIF_TRACE_ERROR(
-          "%s: BTIF_AV_OFFLOAD_START_REQ_EVT: Stream not Started OPENING",
-          __func__);
-      btif_a2dp_on_offload_started(BTA_AV_FAIL);
-      break;
-
-    case BTA_AV_CLOSE_EVT:
-      btif_a2dp_on_stopped(NULL);
-      btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                   BTAV_CONNECTION_STATE_DISCONNECTED);
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
-      if (btif_av_cb.self_initiated_connection) {
-        btif_queue_advance();
-      }
-      break;
-
-    case BTIF_AV_DISCONNECT_REQ_EVT:
-      btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                   BTAV_CONNECTION_STATE_DISCONNECTED);
-      BTA_AvClose(btif_av_cb.bta_handle);
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
-      if (btif_av_cb.self_initiated_connection) {
-        btif_queue_advance();
-      }
-      break;
-
-      CHECK_RC_EVENT(event, (tBTA_AV*)p_data);
-
-    default:
-      BTIF_TRACE_WARNING("%s: unhandled event=%s", __func__,
-                         dump_av_sm_event_name((btif_av_sm_event_t)event));
-      return false;
-  }
-  return true;
-}
-
-/*****************************************************************************
- *
- * Function        btif_av_state_closing_handler
- *
- * Description     Intermediate state managing events during closing
- *                 of avdtp channel
- *
- * Returns         true if event was processed, false otherwise
- *
- ******************************************************************************/
-
-static bool btif_av_state_closing_handler(btif_sm_event_t event, void* p_data) {
-  BTIF_TRACE_DEBUG("%s: event=%s flags=0x%x", __func__,
-                   dump_av_sm_event_name((btif_av_sm_event_t)event),
-                   btif_av_cb.flags);
-
-  switch (event) {
-    case BTIF_SM_ENTER_EVT:
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SNK) {
-        /* immediately stop transmission of frames */
-        btif_a2dp_source_set_tx_flush(true);
-        /* wait for audioflinger to stop a2dp */
-      }
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
-        btif_a2dp_sink_set_rx_flush(true);
-      }
-      break;
-
-    case BTA_AV_STOP_EVT:
-    case BTIF_AV_STOP_STREAM_REQ_EVT:
-      btif_a2dp_on_stopped(NULL);
-      break;
-
-    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 */
-      btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                   BTAV_CONNECTION_STATE_DISCONNECTED);
-
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
-      break;
-
-    /* Handle the RC_CLOSE event for the cleanup */
-    case BTA_AV_RC_CLOSE_EVT:
-      btif_rc_handler(event, (tBTA_AV*)p_data);
-      break;
-
-    /* Handle the RC_BROWSE_CLOSE event for tetsing*/
-    case BTA_AV_RC_BROWSE_CLOSE_EVT:
-      btif_rc_handler(event, (tBTA_AV*)p_data);
-      break;
-
-    case BTIF_AV_OFFLOAD_START_REQ_EVT:
-      BTIF_TRACE_ERROR(
-          "%s: BTIF_AV_OFFLOAD_START_REQ_EVT: Stream not Started Closing",
-          __func__);
-      btif_a2dp_on_offload_started(BTA_AV_FAIL);
-      break;
-
-    default:
-      BTIF_TRACE_WARNING("%s: unhandled event=%s", __func__,
-                         dump_av_sm_event_name((btif_av_sm_event_t)event));
-      return false;
-  }
-  return true;
-}
-
-/*****************************************************************************
- *
- * Function     btif_av_state_opened_handler
- *
- * Description  Handles AV events while AVDTP is in OPEN state
- *
- * Returns      true if event was processed, false otherwise
- *
- ******************************************************************************/
-
-static bool btif_av_state_opened_handler(btif_sm_event_t event, void* p_data) {
-  tBTA_AV* p_av = (tBTA_AV*)p_data;
-
-  BTIF_TRACE_DEBUG("%s: event=%s flags=0x%x", __func__,
-                   dump_av_sm_event_name((btif_av_sm_event_t)event),
-                   btif_av_cb.flags);
-
-  if ((event == BTA_AV_REMOTE_CMD_EVT) &&
-      (btif_av_cb.flags & BTIF_AV_FLAG_REMOTE_SUSPEND) &&
-      (p_av->remote_cmd.rc_id == AVRC_ID_PLAY)) {
-    BTIF_TRACE_EVENT("%s: Resetting remote suspend flag on RC PLAY", __func__);
-    btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+    data_ = osi_malloc(data_length_);
+    memcpy(data_, p_data, data_length);
   }
 
   switch (event) {
-    case BTIF_SM_ENTER_EVT:
-      btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_STOP;
-      btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
-      break;
-
-    case BTIF_SM_EXIT_EVT:
-      btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
-      break;
-
-    case BTIF_AV_START_STREAM_REQ_EVT:
-      if (btif_av_cb.peer_sep != AVDT_TSEP_SRC) btif_a2dp_source_setup_codec();
-      BTA_AvStart();
-      btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_START;
-      break;
-
-    case BTA_AV_START_EVT: {
-      BTIF_TRACE_WARNING(
-          "%s: BTA_AV_START_EVT status=%d suspending=%d initiator=%d "
-          "flags=0x%x",
-          __func__, p_av->start.status, p_av->start.suspending,
-          p_av->start.initiator, btif_av_cb.flags);
-
-      if ((p_av->start.status == BTA_SUCCESS) && p_av->start.suspending)
-        return true;
-
-      /* if remote tries to start a2dp when DUT is a2dp source
-       * then suspend. In case a2dp is sink and call is active
-       * then disconnect the AVDTP channel
-       */
-      if (!(btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START)) {
-        if (btif_av_cb.peer_sep == AVDT_TSEP_SNK) {
-          BTIF_TRACE_WARNING("%s: trigger suspend as remote initiated!!",
-                             __func__);
-          btif_dispatch_sm_event(BTIF_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0);
-        }
-      }
-
-      /*  In case peer is A2DP SRC we do not want to ack commands on UIPC*/
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SNK) {
-        if (btif_a2dp_on_started(
-                &p_av->start,
-                ((btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) != 0))) {
-          /* only clear pending flag after acknowledgement */
-          btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
-        }
-      }
-
-      /* remain in open state if status failed */
-      if (p_av->start.status != BTA_AV_SUCCESS) return false;
-
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
-        btif_a2dp_sink_set_rx_flush(
-            false); /*  remove flush state, ready for streaming*/
-      }
-
-      /* change state to started, send acknowledgement if start is pending */
-      if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
-        if (btif_av_cb.peer_sep == AVDT_TSEP_SNK)
-          btif_a2dp_on_started(NULL, true);
-        /* pending start flag will be cleared when exit current state */
-      }
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_STARTED);
-
-    } 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) {
-        BTA_AvCloseRc(btif_av_cb.bta_handle);
-      }
-
-      /* inform the application that we are disconnecting */
-      btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                   BTAV_CONNECTION_STATE_DISCONNECTING);
-      break;
-
-    case BTA_AV_CLOSE_EVT:
-      /* avdtp link is closed */
-      btif_a2dp_on_stopped(NULL);
-
-      /* inform the application that we are disconnected */
-      btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                   BTAV_CONNECTION_STATE_DISCONNECTED);
-
-      /* change state to idle, send acknowledgement if start is pending */
-      if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
-        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
-        /* pending start flag will be cleared when exit current state */
-      }
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
-      break;
-
-    case BTA_AV_RECONFIG_EVT:
-      if ((btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) &&
-          (p_av->reconfig.status == BTA_AV_SUCCESS)) {
-        APPL_TRACE_WARNING("reconfig done BTA_AVstart()");
-        BTA_AvStart();
-      } else if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
-        btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
-        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
-      }
-      break;
-
-    case BTIF_AV_CONNECT_REQ_EVT: {
-      btif_av_connect_req_t* connect_req_p = (btif_av_connect_req_t*)p_data;
-      RawAddress& target_bda = *connect_req_p->target_bda;
-      if (btif_av_cb.peer_bda == target_bda) {
-        BTIF_TRACE_WARNING(
-            "%s: Ignore BTIF_AV_CONNECT_REQ_EVT for same device: target_bda=%s",
-            __func__, target_bda.ToString().c_str());
-      } else {
-        BTIF_TRACE_WARNING(
-            "%s: Moved to opened by Other incoming Connect request: "
-            "target_bda=%s",
-            __func__, target_bda.ToString().c_str());
-        btif_report_connection_state(&target_bda,
-                                     BTAV_CONNECTION_STATE_DISCONNECTED);
-      }
-      btif_queue_advance();
-    } break;
-
-    case BTIF_AV_OFFLOAD_START_REQ_EVT:
-      BTIF_TRACE_ERROR(
-          "%s: BTIF_AV_OFFLOAD_START_REQ_EVT: Stream not Started Opened",
-          __func__);
-      btif_a2dp_on_offload_started(BTA_AV_FAIL);
-      break;
-
-      CHECK_RC_EVENT(event, (tBTA_AV*)p_data);
-
-    default:
-      BTIF_TRACE_WARNING("%s: unhandled event=%s", __func__,
-                         dump_av_sm_event_name((btif_av_sm_event_t)event));
-      return false;
-  }
-  return true;
-}
-
-/*****************************************************************************
- *
- * Function     btif_av_state_started_handler
- *
- * Description  Handles AV events while A2DP stream is started
- *
- * Returns      true if event was processed, false otherwise
- *
- ******************************************************************************/
-
-static bool btif_av_state_started_handler(btif_sm_event_t event, void* p_data) {
-  tBTA_AV* p_av = (tBTA_AV*)p_data;
-
-  BTIF_TRACE_DEBUG("%s: event=%s flags=0x%x", __func__,
-                   dump_av_sm_event_name((btif_av_sm_event_t)event),
-                   btif_av_cb.flags);
-
-  switch (event) {
-    case BTIF_SM_ENTER_EVT:
-
-      /* we are again in started state, clear any remote suspend flags */
-      btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
-
-      /**
-       * Report to components above that we have entered the streaming
-       * stage, this should usually be followed by focus grant.
-       * see update_audio_focus_state()
-       */
-      btif_report_audio_state(&(btif_av_cb.peer_bda), BTAV_AUDIO_STATE_STARTED);
-      break;
-
-    case BTIF_SM_EXIT_EVT:
-      break;
-
-    case BTIF_AV_START_STREAM_REQ_EVT:
-      /* we were remotely started, just ack back the local request */
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SNK)
-        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:
-      BTIF_TRACE_WARNING("%s: event=%s flags=0x%x", __func__,
-                         dump_av_sm_event_name((btif_av_sm_event_t)event),
-                         btif_av_cb.flags);
-      /* set pending flag to ensure btif task is not trying to restart
-         stream while suspend is in progress */
-      btif_av_cb.flags |= BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING;
-
-      /* if we were remotely suspended but suspend locally, local suspend
-         always overrides */
-      btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
-
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SNK) {
-        /*
-         * Immediately stop transmission of frames while suspend is
-         * pending.
-         */
-        btif_a2dp_source_set_tx_flush(true);
-      }
-
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
-        btif_a2dp_on_stopped(NULL);
-      }
-
-      BTA_AvStop(true);
-      break;
-
-    case BTIF_AV_DISCONNECT_REQ_EVT:
-      BTIF_TRACE_WARNING("%s: event=%s flags=0x%x", __func__,
-                         dump_av_sm_event_name((btif_av_sm_event_t)event),
-                         btif_av_cb.flags);
-
-      /* request avdtp to close */
-      BTA_AvClose(btif_av_cb.bta_handle);
-      if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
-        BTA_AvCloseRc(btif_av_cb.bta_handle);
-      }
-
-      /* inform the application that we are disconnecting */
-      btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                   BTAV_CONNECTION_STATE_DISCONNECTING);
-
-      /* wait in closing state until fully closed */
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_CLOSING);
-      break;
-
-    case BTA_AV_SUSPEND_EVT:
-      BTIF_TRACE_WARNING(
-          "%s: BTA_AV_SUSPEND_EVT status=%d initiator=%d flags=0x%x", __func__,
-          p_av->suspend.status, p_av->suspend.initiator, btif_av_cb.flags);
-
-      /* a2dp suspended, stop media task until resumed */
-      btif_a2dp_on_suspended(&p_av->suspend);
-
-      /* if not successful, remain in current state */
-      if (p_av->suspend.status != BTA_AV_SUCCESS) {
-        btif_av_cb.flags &= ~BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING;
-
-        if (btif_av_cb.peer_sep == AVDT_TSEP_SNK) {
-          /* suspend failed, reset back tx flush state */
-          btif_a2dp_source_set_tx_flush(false);
-        }
-        return false;
-      }
-
-      if (p_av->suspend.initiator != true) {
-        /* remote suspend, notify HAL and await audioflinger to
-           suspend/stop stream */
-
-        /* set remote suspend flag to block media task from restarting
-           stream only if we did not already initiate a local suspend */
-        if ((btif_av_cb.flags & BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING) == 0)
-          btif_av_cb.flags |= BTIF_AV_FLAG_REMOTE_SUSPEND;
-
-        btif_report_audio_state(&(btif_av_cb.peer_bda),
-                                BTAV_AUDIO_STATE_REMOTE_SUSPEND);
-      } else {
-        btif_report_audio_state(&(btif_av_cb.peer_bda),
-                                BTAV_AUDIO_STATE_STOPPED);
-      }
-
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENED);
-
-      /* suspend completed and state changed, clear pending status */
-      btif_av_cb.flags &= ~BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING;
-      break;
-
-    case BTA_AV_STOP_EVT:
-      BTIF_TRACE_WARNING("%s: event=%s flags=0x%x", __func__,
-                         dump_av_sm_event_name((btif_av_sm_event_t)event),
-                         btif_av_cb.flags);
-
-      btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_STOP;
-      btif_a2dp_on_stopped(&p_av->suspend);
-
-      btif_report_audio_state(&(btif_av_cb.peer_bda), BTAV_AUDIO_STATE_STOPPED);
-
-      /* if stop was successful, change state to open */
-      if (p_av->suspend.status == BTA_AV_SUCCESS)
-        btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENED);
-
-      break;
-
-    case BTA_AV_CLOSE_EVT:
-      BTIF_TRACE_WARNING("%s: event=%s flags=0x%x", __func__,
-                         dump_av_sm_event_name((btif_av_sm_event_t)event),
-                         btif_av_cb.flags);
-
-      btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_STOP;
-
-      /* avdtp link is closed */
-      btif_a2dp_on_stopped(NULL);
-
-      /* inform the application that we are disconnected */
-      btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                   BTAV_CONNECTION_STATE_DISCONNECTED);
-
-      btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
-      break;
-
-    case BTIF_AV_OFFLOAD_START_REQ_EVT:
-      BTA_AvOffloadStart(btif_av_cb.bta_handle);
-      break;
-
-    case BTA_AV_OFFLOAD_START_RSP_EVT:
-      btif_a2dp_on_offload_started(p_av->status);
-      break;
-
-      CHECK_RC_EVENT(event, (tBTA_AV*)p_data);
-
-    default:
-      BTIF_TRACE_WARNING("%s: unhandled event=%s", __func__,
-                         dump_av_sm_event_name((btif_av_sm_event_t)event));
-      return false;
-  }
-
-  return true;
-}
-
-/*****************************************************************************
- *  Local event handlers
- *****************************************************************************/
-
-static void btif_av_handle_event(uint16_t event, char* p_param) {
-  BTIF_TRACE_EVENT("%s: event=%s", __func__,
-                   dump_av_sm_event_name((btif_av_sm_event_t)event));
-  switch (event) {
-    case BTIF_AV_CLEANUP_REQ_EVT:
-      btif_a2dp_source_shutdown();
-      btif_a2dp_sink_shutdown();
-      break;
-
-    case BTA_AV_REGISTER_EVT:
-      if (btif_av_cb.sm_handle == NULL) {
-        btif_av_cb.bta_handle = ((tBTA_AV*)p_param)->registr.hndl;
-        BTIF_TRACE_DEBUG("%s: BTA AV Handle updated", __func__);
-      }
-    /* FALLTHROUGH */
-    default:
-      btif_sm_dispatch(btif_av_cb.sm_handle, event, (void*)p_param);
-      btif_av_event_free_data(event, p_param);
-  }
-}
-
-void btif_av_event_deep_copy(uint16_t event, char* p_dest, char* p_src) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-  tBTA_AV* av_src = (tBTA_AV*)p_src;
-  tBTA_AV* av_dest = (tBTA_AV*)p_dest;
-
-  // First copy the structure
-  maybe_non_aligned_memcpy(av_dest, av_src, sizeof(*av_src));
-  switch (event) {
-    case BTA_AV_META_MSG_EVT:
+    case BTA_AV_META_MSG_EVT: {
+      CHECK(data_length >= sizeof(tBTA_AV));
+      const tBTA_AV* av_src = (const tBTA_AV*)p_data;
+      tBTA_AV* av_dest = (tBTA_AV*)data_;
       if (av_src->meta_msg.p_data && av_src->meta_msg.len) {
         av_dest->meta_msg.p_data = (uint8_t*)osi_calloc(av_src->meta_msg.len);
         memcpy(av_dest->meta_msg.p_data, av_src->meta_msg.p_data,
@@ -1181,17 +630,17 @@
                  p_msg_src->vendor.p_vendor_data, p_msg_src->vendor.vendor_len);
         }
       }
-      break;
+    } break;
 
     default:
       break;
   }
 }
 
-static void btif_av_event_free_data(btif_sm_event_t event, void* p_data) {
-  switch (event) {
+void BtifAvEvent::DeepFree() {
+  switch (event_) {
     case BTA_AV_META_MSG_EVT: {
-      tBTA_AV* av = (tBTA_AV*)p_data;
+      tBTA_AV* av = (tBTA_AV*)data_;
       osi_free_and_reset((void**)&av->meta_msg.p_data);
 
       if (av->meta_msg.p_msg) {
@@ -1205,46 +654,1663 @@
     default:
       break;
   }
+
+  osi_free_and_reset((void**)&data_);
+  data_length_ = 0;
 }
 
-static void bte_av_callback(tBTA_AV_EVT event, tBTA_AV* p_data) {
-  btif_transfer_context(btif_av_handle_event, event, (char*)p_data,
-                        sizeof(tBTA_AV), btif_av_event_deep_copy);
+BtifAvPeer::BtifAvPeer(const RawAddress& peer_address, uint8_t peer_sep,
+                       tBTA_AV_HNDL bta_handle, uint8_t peer_id)
+    : peer_address_(peer_address),
+      peer_sep_(peer_sep),
+      bta_handle_(bta_handle),
+      peer_id_(peer_id),
+      state_machine_(*this),
+      av_open_on_rc_timer_(nullptr),
+      edr_(0),
+      flags_(0),
+      self_initiated_connection_(false) {}
+
+BtifAvPeer::~BtifAvPeer() { alarm_free(av_open_on_rc_timer_); }
+
+std::string BtifAvPeer::FlagsToString() const {
+  std::string result;
+
+  if (flags_ & BtifAvPeer::kFlagLocalSuspendPending) {
+    if (!result.empty()) result += "|";
+    result += "LOCAL_SUSPEND_PENDING";
+  }
+  if (flags_ & BtifAvPeer::kFlagRemoteSuspend) {
+    if (!result.empty()) result += "|";
+    result += "REMOTE_SUSPEND";
+  }
+  if (flags_ & BtifAvPeer::kFlagPendingStart) {
+    if (!result.empty()) result += "|";
+    result += "PENDING_START";
+  }
+  if (flags_ & BtifAvPeer::kFlagPendingStop) {
+    if (!result.empty()) result += "|";
+    result += "PENDING_STOP";
+  }
+  if (result.empty()) result = "None";
+
+  return base::StringPrintf("0x%x(%s)", flags_, result.c_str());
 }
 
-static void bte_av_sink_media_callback(tBTA_AV_EVT event,
+bt_status_t BtifAvPeer::Init() {
+  alarm_free(av_open_on_rc_timer_);
+  av_open_on_rc_timer_ = alarm_new("btif_av_peer.av_open_on_rc_timer");
+
+  state_machine_.Start();
+  return BT_STATUS_SUCCESS;
+}
+
+void BtifAvPeer::Cleanup() {
+  state_machine_.Quit();
+  alarm_free(av_open_on_rc_timer_);
+  av_open_on_rc_timer_ = nullptr;
+}
+
+bool BtifAvPeer::CanBeDeleted() const {
+  return (
+      (state_machine_.StateId() == BtifAvStateMachine::kStateIdle) &&
+      (state_machine_.PreviousStateId() != BtifAvStateMachine::kStateInvalid));
+}
+
+bool BtifAvPeer::IsConnected() const {
+  int state = state_machine_.StateId();
+  return ((state == BtifAvStateMachine::kStateOpened) ||
+          (state == BtifAvStateMachine::kStateStarted));
+}
+
+bool BtifAvPeer::IsStreaming() const {
+  int state = state_machine_.StateId();
+  return (state == BtifAvStateMachine::kStateStarted);
+}
+
+BtifAvSource::~BtifAvSource() { CleanupAllPeers(); }
+
+bt_status_t BtifAvSource::Init(
+    btav_source_callbacks_t* callbacks,
+    const std::vector<btav_a2dp_codec_config_t>& codec_priorities) {
+  if (enabled_) return BT_STATUS_SUCCESS;
+
+  CleanupAllPeers();
+  max_connected_peers_ =
+      osi_property_get_int32(kBtifAvMaxConnectedAudioDevices.c_str(),
+                             kDefaultMaxConnectedAudioDevices);
+  callbacks_ = callbacks;
+  codec_priorities_ = codec_priorities;
+  bta_av_co_init(codec_priorities_);
+
+  if (!btif_a2dp_source_startup()) {
+    return BT_STATUS_FAIL;  // Already running
+  }
+  btif_enable_service(BTA_A2DP_SOURCE_SERVICE_ID);
+  enabled_ = true;
+  return BT_STATUS_SUCCESS;
+}
+
+void BtifAvSource::Cleanup() {
+  if (!enabled_) return;
+
+  btif_queue_cleanup(UUID_SERVCLASS_AUDIO_SOURCE);
+  do_in_jni_thread(FROM_HERE, base::Bind(&btif_a2dp_source_shutdown));
+
+  btif_disable_service(BTA_A2DP_SOURCE_SERVICE_ID);
+  CleanupAllPeers();
+
+  callbacks_ = nullptr;
+  enabled_ = false;
+}
+
+BtifAvPeer* BtifAvSource::FindPeer(const RawAddress& peer_address) {
+  auto it = peers_.find(peer_address);
+  if (it != peers_.end()) return it->second;
+  return nullptr;
+}
+
+BtifAvPeer* BtifAvSource::FindPeerByHandle(tBTA_AV_HNDL bta_handle) {
+  for (auto it : peers_) {
+    BtifAvPeer* peer = it.second;
+    if (peer->BtaHandle() == bta_handle) {
+      return peer;
+    }
+  }
+  return nullptr;
+}
+
+BtifAvPeer* BtifAvSource::FindPeerByPeerId(uint8_t peer_id) {
+  for (auto it : peers_) {
+    BtifAvPeer* peer = it.second;
+    if (peer->PeerId() == peer_id) {
+      return peer;
+    }
+  }
+  return nullptr;
+}
+
+BtifAvPeer* BtifAvSource::FindOrCreatePeer(const RawAddress& peer_address,
+                                           tBTA_AV_HNDL bta_handle) {
+  BtifAvPeer* peer = FindPeer(peer_address);
+  if (peer != nullptr) return peer;
+
+  // Find next availabie Peer ID to use
+  uint8_t peer_id;
+  for (peer_id = kPeerIdMin; peer_id < kPeerIdMax; peer_id++) {
+    if (FindPeerByPeerId(peer_id) == nullptr) break;
+  }
+  if (peer_id == kPeerIdMax) {
+    BTIF_TRACE_ERROR(
+        "%s: Cannot create peer for peer_address=%s : "
+        "cannot allocate unique Peer ID",
+        __PRETTY_FUNCTION__, peer_address.ToString().c_str());
+    return nullptr;
+  }
+
+  // Get the BTA Handle (if known)
+  if (bta_handle == kBtaHandleUnknown) {
+    auto it = peer_id2bta_handle_.find(peer_id);
+    if (it != peer_id2bta_handle_.end()) {
+      bta_handle = it->second;
+    }
+  }
+
+  peer = new BtifAvPeer(peer_address, AVDT_TSEP_SNK, bta_handle, peer_id);
+  peers_.insert(std::make_pair(peer_address, peer));
+  peer->Init();
+  return peer;
+}
+
+bool BtifAvSource::AllowedToConnect(const RawAddress& peer_address) const {
+  int connected = 0;
+
+  // Count peers that are in the process of connecting or already connected
+  for (auto it : peers_) {
+    const BtifAvPeer* peer = it.second;
+    switch (peer->StateMachine().StateId()) {
+      case BtifAvStateMachine::kStateOpening:
+      case BtifAvStateMachine::kStateOpened:
+      case BtifAvStateMachine::kStateStarted:
+        if (peer->PeerAddress() == peer_address) {
+          return true;  // Already connected or accounted for
+        }
+        connected++;
+        break;
+      default:
+        break;
+    }
+  }
+  return (connected < max_connected_peers_);
+}
+
+bool BtifAvSource::DeletePeer(const RawAddress& peer_address) {
+  auto it = peers_.find(peer_address);
+  if (it == peers_.end()) return false;
+  BtifAvPeer* peer = it->second;
+  peer->Cleanup();
+  peers_.erase(it);
+  delete peer;
+  return true;
+}
+
+void BtifAvSource::DeleteIdlePeers() {
+  for (auto it = peers_.begin(); it != peers_.end();) {
+    BtifAvPeer* peer = it->second;
+    auto prev_it = it++;
+    if (!peer->CanBeDeleted()) continue;
+    BTIF_TRACE_WARNING("%s: Deleting idle peer: %s", __func__,
+                       peer->PeerAddress().ToString().c_str());
+    peer->Cleanup();
+    peers_.erase(prev_it);
+    delete peer;
+  }
+}
+
+void BtifAvSource::CleanupAllPeers() {
+  while (!peers_.empty()) {
+    auto it = peers_.begin();
+    BtifAvPeer* peer = it->second;
+    peer->Cleanup();
+    peers_.erase(it);
+    delete peer;
+  }
+}
+
+void BtifAvSource::RegisterAllBtaHandles() {
+  for (int peer_id = kPeerIdMin; peer_id < kPeerIdMax; peer_id++) {
+    BTA_AvRegister(BTA_AV_CHNL_AUDIO, kBtifAvSourceServiceName.c_str(), peer_id,
+                   nullptr, UUID_SERVCLASS_AUDIO_SOURCE);
+  }
+}
+
+void BtifAvSource::DeregisterAllBtaHandles() {
+  for (auto it : peer_id2bta_handle_) {
+    tBTA_AV_HNDL bta_handle = it.second;
+    BTA_AvDeregister(bta_handle);
+  }
+  peer_id2bta_handle_.clear();
+}
+
+void BtifAvSource::BtaHandleRegistered(uint8_t peer_id,
+                                       tBTA_AV_HNDL bta_handle) {
+  peer_id2bta_handle_.insert(std::make_pair(peer_id, bta_handle));
+
+  // Set the BTA Handle for the Peer (if exists)
+  BtifAvPeer* peer = FindPeerByPeerId(peer_id);
+  if (peer != nullptr) {
+    peer->SetBtaHandle(bta_handle);
+  }
+}
+
+BtifAvSink::~BtifAvSink() { CleanupAllPeers(); }
+
+bt_status_t BtifAvSink::Init(btav_sink_callbacks_t* callbacks) {
+  if (enabled_) return BT_STATUS_SUCCESS;
+
+  CleanupAllPeers();
+  max_connected_peers_ =
+      osi_property_get_int32(kBtifAvMaxConnectedAudioDevices.c_str(),
+                             kDefaultMaxConnectedAudioDevices);
+  callbacks_ = callbacks;
+
+  if (!btif_a2dp_sink_startup()) {
+    return BT_STATUS_FAIL;  // Already running
+  }
+  btif_enable_service(BTA_A2DP_SINK_SERVICE_ID);
+  enabled_ = true;
+  return BT_STATUS_SUCCESS;
+}
+
+void BtifAvSink::Cleanup() {
+  if (!enabled_) return;
+
+  btif_queue_cleanup(UUID_SERVCLASS_AUDIO_SINK);
+  do_in_jni_thread(FROM_HERE, base::Bind(&btif_a2dp_sink_shutdown));
+
+  btif_disable_service(BTA_A2DP_SINK_SERVICE_ID);
+  CleanupAllPeers();
+
+  callbacks_ = nullptr;
+  enabled_ = false;
+}
+
+BtifAvPeer* BtifAvSink::FindPeer(const RawAddress& peer_address) {
+  auto it = peers_.find(peer_address);
+  if (it != peers_.end()) return it->second;
+  return nullptr;
+}
+
+BtifAvPeer* BtifAvSink::FindPeerByHandle(tBTA_AV_HNDL bta_handle) {
+  for (auto it : peers_) {
+    BtifAvPeer* peer = it.second;
+    if (peer->BtaHandle() == bta_handle) {
+      return peer;
+    }
+  }
+  return nullptr;
+}
+
+BtifAvPeer* BtifAvSink::FindPeerByPeerId(uint8_t peer_id) {
+  for (auto it : peers_) {
+    BtifAvPeer* peer = it.second;
+    if (peer->PeerId() == peer_id) {
+      return peer;
+    }
+  }
+  return nullptr;
+}
+
+BtifAvPeer* BtifAvSink::FindOrCreatePeer(const RawAddress& peer_address,
+                                         tBTA_AV_HNDL bta_handle) {
+  BtifAvPeer* peer = FindPeer(peer_address);
+  if (peer != nullptr) return peer;
+
+  // Find next availabie Peer ID to use
+  uint8_t peer_id;
+  for (peer_id = kPeerIdMin; peer_id < kPeerIdMax; peer_id++) {
+    if (FindPeerByPeerId(peer_id) == nullptr) break;
+  }
+  if (peer_id == kPeerIdMax) {
+    BTIF_TRACE_ERROR(
+        "%s: Cannot create peer for peer_address=%s : "
+        "cannot allocate unique Peer ID",
+        __PRETTY_FUNCTION__, peer_address.ToString().c_str());
+    return nullptr;
+  }
+
+  // Get the BTA Handle (if known)
+  if (bta_handle == kBtaHandleUnknown) {
+    auto it = peer_id2bta_handle_.find(peer_id);
+    if (it != peer_id2bta_handle_.end()) {
+      bta_handle = it->second;
+    }
+  }
+
+  peer = new BtifAvPeer(peer_address, AVDT_TSEP_SRC, bta_handle, peer_id);
+  peers_.insert(std::make_pair(peer_address, peer));
+  peer->Init();
+  return peer;
+}
+
+bool BtifAvSink::AllowedToConnect(const RawAddress& peer_address) const {
+  int connected = 0;
+
+  // Count peers that are in the process of connecting or already connected
+  for (auto it : peers_) {
+    const BtifAvPeer* peer = it.second;
+    switch (peer->StateMachine().StateId()) {
+      case BtifAvStateMachine::kStateOpening:
+      case BtifAvStateMachine::kStateOpened:
+      case BtifAvStateMachine::kStateStarted:
+        if (peer->PeerAddress() == peer_address) {
+          return true;  // Already connected or accounted for
+        }
+        connected++;
+        break;
+      default:
+        break;
+    }
+  }
+  return (connected < max_connected_peers_);
+}
+
+bool BtifAvSink::DeletePeer(const RawAddress& peer_address) {
+  auto it = peers_.find(peer_address);
+  if (it == peers_.end()) return false;
+  BtifAvPeer* peer = it->second;
+  peer->Cleanup();
+  peers_.erase(it);
+  delete peer;
+  return true;
+}
+
+void BtifAvSink::DeleteIdlePeers() {
+  for (auto it = peers_.begin(); it != peers_.end();) {
+    BtifAvPeer* peer = it->second;
+    auto prev_it = it++;
+    if (!peer->CanBeDeleted()) continue;
+    BTIF_TRACE_WARNING("%s: Deleting idle peer: %s", __func__,
+                       peer->PeerAddress().ToString().c_str());
+    peer->Cleanup();
+    peers_.erase(prev_it);
+    delete peer;
+  }
+}
+
+void BtifAvSink::CleanupAllPeers() {
+  while (!peers_.empty()) {
+    auto it = peers_.begin();
+    BtifAvPeer* peer = it->second;
+    peer->Cleanup();
+    peers_.erase(it);
+    delete peer;
+  }
+}
+
+void BtifAvSink::RegisterAllBtaHandles() {
+  for (int peer_id = kPeerIdMin; peer_id < kPeerIdMax; peer_id++) {
+    BTA_AvRegister(BTA_AV_CHNL_AUDIO, kBtifAvSinkServiceName.c_str(), peer_id,
+                   bta_av_sink_media_callback, UUID_SERVCLASS_AUDIO_SINK);
+  }
+}
+
+void BtifAvSink::DeregisterAllBtaHandles() {
+  for (auto it : peer_id2bta_handle_) {
+    tBTA_AV_HNDL bta_handle = it.second;
+    BTA_AvDeregister(bta_handle);
+  }
+  peer_id2bta_handle_.clear();
+}
+
+void BtifAvSink::BtaHandleRegistered(uint8_t peer_id, tBTA_AV_HNDL bta_handle) {
+  peer_id2bta_handle_.insert(std::make_pair(peer_id, bta_handle));
+
+  // Set the BTA Handle for the Peer (if exists)
+  BtifAvPeer* peer = FindPeerByPeerId(peer_id);
+  if (peer != nullptr) {
+    peer->SetBtaHandle(bta_handle);
+  }
+}
+
+void BtifAvStateMachine::StateIdle::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+
+  peer_.SetEdr(0);
+  peer_.ClearAllFlags();
+
+  // Stop A2DP if this is the active peer
+  RawAddress active_peer = RawAddress::kEmpty;
+  if (peer_.IsSink()) {
+    active_peer = btif_av_source.ActivePeer();
+  } else if (peer_.IsSource()) {
+    active_peer = btif_av_sink.ActivePeer();
+  }
+  if (peer_.PeerAddress() == active_peer || active_peer.IsEmpty()) {
+    btif_a2dp_on_idle();
+  }
+
+  // Reset the active peer if this was the active peer and
+  // the Idle state was reentered
+  if (peer_.PeerAddress() == active_peer && !active_peer.IsEmpty() &&
+      peer_.CanBeDeleted()) {
+    if (peer_.IsSink()) {
+      btif_av_source.SetActivePeer(RawAddress::kEmpty);
+    } else if (peer_.IsSource()) {
+      btif_av_sink.SetActivePeer(RawAddress::kEmpty);
+    }
+  }
+
+  // Delete peers that are re-entering the Idle state
+  if (peer_.IsSink()) {
+    do_in_jni_thread(FROM_HERE, base::Bind(&BtifAvSource::DeleteIdlePeers,
+                                           base::Unretained(&btif_av_source)));
+  } else if (peer_.IsSource()) {
+    do_in_jni_thread(FROM_HERE, base::Bind(&BtifAvSink::DeleteIdlePeers,
+                                           base::Unretained(&btif_av_sink)));
+  }
+}
+
+void BtifAvStateMachine::StateIdle::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+}
+
+bool BtifAvStateMachine::StateIdle::ProcessEvent(uint32_t event, void* p_data) {
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str(),
+                   BtifAvEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str());
+
+  switch (event) {
+    case BTA_AV_ENABLE_EVT:
+      break;
+
+    case BTIF_AV_STOP_STREAM_REQ_EVT:
+    case BTIF_AV_SUSPEND_STREAM_REQ_EVT:
+    case BTIF_AV_ACL_DISCONNECTED:
+      // Ignore. Just re-enter Idle so the peer can be deleted
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      break;
+
+    case BTIF_AV_DISCONNECT_REQ_EVT:
+      if (peer_.BtaHandle() != kBtaHandleUnknown) {
+        BTA_AvClose(peer_.BtaHandle());
+        if (peer_.IsSource()) {
+          BTA_AvCloseRc(peer_.BtaHandle());
+        }
+      }
+      // Re-enter Idle so the peer can be deleted
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      break;
+
+    case BTIF_AV_CONNECT_REQ_EVT:
+    case BTA_AV_PENDING_EVT: {
+      bool can_connect = true;
+      peer_.SetSelfInitiatedConnection(event == BTIF_AV_CONNECT_REQ_EVT);
+      // Check whether connection is allowed
+      if (peer_.IsSink()) {
+        can_connect = btif_av_source.AllowedToConnect(peer_.PeerAddress());
+        if (!can_connect) src_disconnect_sink(peer_.PeerAddress());
+      } else if (peer_.IsSource()) {
+        can_connect = btif_av_sink.AllowedToConnect(peer_.PeerAddress());
+        if (!can_connect) sink_disconnect_src(peer_.PeerAddress());
+      }
+      if (!can_connect) {
+        BTIF_TRACE_ERROR(
+            "%s: Cannot connect to peer %s: too many connected "
+            "peers",
+            __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+        if (peer_.SelfInitiatedConnection()) {
+          btif_queue_advance();
+        }
+        break;
+      }
+      BTA_AvOpen(peer_.PeerAddress(), peer_.BtaHandle(), true,
+                 BTA_SEC_AUTHENTICATE, peer_.LocalUuidServiceClass());
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpening);
+    } break;
+    case BTIF_AV_AVRCP_OPEN_EVT:
+    case BTA_AV_RC_OPEN_EVT: {
+      // IOP_FIX: Jabra 620 only does AVRCP Open without AV Open whenever it
+      // connects. So as per the AV WP, an AVRCP connection cannot exist
+      // without an AV connection. Therefore, we initiate an AV connection
+      // if an RC_OPEN_EVT is received when we are in AV_CLOSED state.
+      // We initiate the AV connection after a small 3s timeout to avoid any
+      // collisions from the headsets, as some headsets initiate the AVRCP
+      // connection first and then immediately initiate the AV connection
+      //
+      // TODO: We may need to do this only on an AVRCP Play. FixMe
+      BTIF_TRACE_WARNING("%s: Peer %s : event=%s received without AV",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str());
+
+      bool can_connect = true;
+      // Check whether connection is allowed
+      if (peer_.IsSink()) {
+        can_connect = btif_av_source.AllowedToConnect(peer_.PeerAddress());
+        if (!can_connect) src_disconnect_sink(peer_.PeerAddress());
+      } else if (peer_.IsSource()) {
+        can_connect = btif_av_sink.AllowedToConnect(peer_.PeerAddress());
+        if (!can_connect) sink_disconnect_src(peer_.PeerAddress());
+      }
+      if (!can_connect) {
+        BTIF_TRACE_ERROR(
+            "%s: Cannot connect to peer %s: too many connected "
+            "peers",
+            __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+        break;
+      }
+      if (btif_av_source.Enabled()) {
+        alarm_set_on_mloop(
+            peer_.AvOpenOnRcTimer(), BtifAvPeer::kTimeoutAvOpenOnRcMs,
+            btif_av_source_initiate_av_open_timer_timeout, &peer_);
+      } else if (btif_av_sink.Enabled()) {
+        alarm_set_on_mloop(peer_.AvOpenOnRcTimer(),
+                           BtifAvPeer::kTimeoutAvOpenOnRcMs,
+                           btif_av_sink_initiate_av_open_timer_timeout, &peer_);
+      }
+      if (event == BTA_AV_RC_OPEN_EVT) {
+        btif_rc_handler(event, (tBTA_AV*)p_data);
+      }
+    } break;
+
+    case BTA_AV_RC_BROWSE_OPEN_EVT:
+      btif_rc_handler(event, (tBTA_AV*)p_data);
+      break;
+
+    // In case Signalling channel is not down and remote started Streaming
+    // Procedure, we have to handle Config and Open event in Idle state.
+    // We hit these scenarios while running PTS test case for AVRCP Controller.
+    case BTIF_AV_SINK_CONFIG_REQ_EVT: {
+      btif_av_sink_config_req_t req;
+      // Copy to avoid alignment problems
+      memcpy(&req, p_data, sizeof(req));
+
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s sample_rate=%d channel_count=%d",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str(), req.sample_rate,
+          req.channel_count);
+      if (btif_av_sink.Enabled()) {
+        HAL_CBACK(btif_av_sink.Callbacks(), audio_config_cb, req.peer_bd,
+                  req.sample_rate, req.channel_count);
+      }
+    } break;
+
+    case BTA_AV_OPEN_EVT: {
+      tBTA_AV* p_bta_data = (tBTA_AV*)p_data;
+      btav_connection_state_t state;
+      int av_state;
+      tBTA_AV_STATUS status = p_bta_data->open.status;
+      bool can_connect = true;
+
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s flags=%s status=%d(%s) edr=0x%x",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str(), peer_.FlagsToString().c_str(),
+          status, (status == BTA_AV_SUCCESS) ? "SUCCESS" : "FAILED",
+          p_bta_data->open.edr);
+
+      if (p_bta_data->open.status == BTA_AV_SUCCESS) {
+        state = BTAV_CONNECTION_STATE_CONNECTED;
+        av_state = BtifAvStateMachine::kStateOpened;
+        peer_.SetEdr(p_bta_data->open.edr);
+        CHECK(peer_.PeerSep() == p_bta_data->open.sep);
+        // Check whether connection is allowed
+        if (peer_.IsSink()) {
+          can_connect = btif_av_source.AllowedToConnect(peer_.PeerAddress());
+          if (!can_connect) src_disconnect_sink(peer_.PeerAddress());
+        } else if (peer_.IsSource()) {
+          can_connect = btif_av_sink.AllowedToConnect(peer_.PeerAddress());
+          if (!can_connect) sink_disconnect_src(peer_.PeerAddress());
+        }
+      } else {
+        state = BTAV_CONNECTION_STATE_DISCONNECTED;
+        av_state = BtifAvStateMachine::kStateIdle;
+      }
+
+      if (!can_connect) {
+        BTIF_TRACE_ERROR(
+            "%s: Cannot connect to peer %s: too many connected "
+            "peers",
+            __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+      } else {
+        // Report the connection state to the application
+        btif_report_connection_state(peer_.PeerAddress(), state);
+        // Change state to Open/Idle based on the status
+        peer_.StateMachine().TransitionTo(av_state);
+        if (peer_.IsSink()) {
+          // If queued PLAY command, send it now
+          btif_rc_check_handle_pending_play(
+              p_bta_data->open.bd_addr,
+              (p_bta_data->open.status == BTA_AV_SUCCESS));
+        } else if (peer_.IsSource() &&
+                   (p_bta_data->open.status == BTA_AV_SUCCESS)) {
+          // Bring up AVRCP connection as well
+          BTA_AvOpenRc(peer_.BtaHandle());
+        }
+      }
+      btif_queue_advance();
+    } break;
+
+    case BTA_AV_REMOTE_CMD_EVT:
+    case BTA_AV_VENDOR_CMD_EVT:
+    case BTA_AV_META_MSG_EVT:
+    case BTA_AV_RC_FEAT_EVT:
+    case BTA_AV_REMOTE_RSP_EVT:
+      btif_rc_handler(event, (tBTA_AV*)p_data);
+      break;
+
+    case BTIF_AV_AVRCP_CLOSE_EVT:
+    case BTA_AV_RC_CLOSE_EVT: {
+      BTIF_TRACE_DEBUG("%s: Peer %s : event=%s : Stopping AV timer",
+                       __PRETTY_FUNCTION__,
+                       peer_.PeerAddress().ToString().c_str(),
+                       BtifAvEvent::EventName(event).c_str());
+      alarm_cancel(peer_.AvOpenOnRcTimer());
+
+      if (event == BTA_AV_RC_CLOSE_EVT) {
+        btif_rc_handler(event, (tBTA_AV*)p_data);
+      }
+    } break;
+
+    case BTIF_AV_OFFLOAD_START_REQ_EVT:
+      BTIF_TRACE_ERROR("%s: Peer %s : event=%s: stream is not Opened",
+                       __PRETTY_FUNCTION__,
+                       peer_.PeerAddress().ToString().c_str(),
+                       BtifAvEvent::EventName(event).c_str());
+      btif_a2dp_on_offload_started(BTA_AV_FAIL);
+      break;
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str());
+      return false;
+  }
+
+  return true;
+}
+
+void BtifAvStateMachine::StateOpening::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+
+  // Inform the application that we are entering connecting state
+  btif_report_connection_state(peer_.PeerAddress(),
+                               BTAV_CONNECTION_STATE_CONNECTING);
+}
+
+void BtifAvStateMachine::StateOpening::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+}
+
+bool BtifAvStateMachine::StateOpening::ProcessEvent(uint32_t event,
+                                                    void* p_data) {
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str(),
+                   BtifAvEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str());
+
+  switch (event) {
+    case BTIF_AV_STOP_STREAM_REQ_EVT:
+    case BTIF_AV_SUSPEND_STREAM_REQ_EVT:
+      break;  // Ignore
+
+    case BTIF_AV_ACL_DISCONNECTED:
+      // ACL Disconnected needs to be handled only in Opening state, because
+      // it is in an intermediate state. In other states we can handle
+      // incoming/outgoing connect/disconnect requests.
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s: transitioning to Idle due to ACL Disconnect",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str());
+      btif_report_connection_state(peer_.PeerAddress(),
+                                   BTAV_CONNECTION_STATE_DISCONNECTED);
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      if (peer_.SelfInitiatedConnection()) {
+        btif_queue_advance();
+      }
+      break;
+    case BTA_AV_REJECT_EVT:
+      BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str(),
+                         peer_.FlagsToString().c_str());
+      btif_report_connection_state(peer_.PeerAddress(),
+                                   BTAV_CONNECTION_STATE_DISCONNECTED);
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      if (peer_.SelfInitiatedConnection()) {
+        btif_queue_advance();
+      }
+      break;
+
+    case BTA_AV_OPEN_EVT: {
+      tBTA_AV* p_bta_data = (tBTA_AV*)p_data;
+      btav_connection_state_t state;
+      int av_state;
+      tBTA_AV_STATUS status = p_bta_data->open.status;
+
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s flags=%s status=%d(%s) edr=0x%x",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str(), peer_.FlagsToString().c_str(),
+          status, (status == BTA_AV_SUCCESS) ? "SUCCESS" : "FAILED",
+          p_bta_data->open.edr);
+
+      if (p_bta_data->open.status == BTA_AV_SUCCESS) {
+        state = BTAV_CONNECTION_STATE_CONNECTED;
+        av_state = BtifAvStateMachine::kStateOpened;
+        peer_.SetEdr(p_bta_data->open.edr);
+        CHECK(peer_.PeerSep() == p_bta_data->open.sep);
+      } else {
+        if (btif_rc_is_connected_peer(peer_.PeerAddress())) {
+          // Disconnect the AVRCP connection, in case the A2DP connectiton
+          // failed for any reason.
+          BTIF_TRACE_WARNING("%s: Peer %s : Disconnecting AVRCP",
+                             __PRETTY_FUNCTION__,
+                             peer_.PeerAddress().ToString().c_str());
+          uint8_t peer_handle =
+              btif_rc_get_connected_peer_handle(peer_.PeerAddress());
+          if (peer_handle != BTRC_HANDLE_NONE) {
+            BTA_AvCloseRc(peer_handle);
+          }
+        }
+        state = BTAV_CONNECTION_STATE_DISCONNECTED;
+        av_state = BtifAvStateMachine::kStateIdle;
+      }
+
+      // Report the connection state to the application
+      btif_report_connection_state(peer_.PeerAddress(), state);
+      // Change state to Open/Idle based on the status
+      peer_.StateMachine().TransitionTo(av_state);
+      if (peer_.IsSink()) {
+        // If queued PLAY command, send it now
+        btif_rc_check_handle_pending_play(
+            p_bta_data->open.bd_addr,
+            (p_bta_data->open.status == BTA_AV_SUCCESS));
+      } else if (peer_.IsSource() &&
+                 (p_bta_data->open.status == BTA_AV_SUCCESS)) {
+        // Bring up AVRCP connection as well
+        BTA_AvOpenRc(peer_.BtaHandle());
+      }
+      if (peer_.SelfInitiatedConnection()) {
+        btif_queue_advance();
+      }
+    } break;
+
+    case BTIF_AV_SINK_CONFIG_REQ_EVT: {
+      btif_av_sink_config_req_t req;
+      // Copy to avoid alignment problems
+      memcpy(&req, p_data, sizeof(req));
+
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s sample_rate=%d channel_count=%d",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str(), req.sample_rate,
+          req.channel_count);
+      if (peer_.IsSource() && btif_av_sink.Enabled()) {
+        HAL_CBACK(btif_av_sink.Callbacks(), audio_config_cb,
+                  peer_.PeerAddress(), req.sample_rate, req.channel_count);
+      }
+    } break;
+
+    case BTIF_AV_CONNECT_REQ_EVT: {
+      // The device has moved already to Opening, hence don't report the
+      // connection state.
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s : device is already connecting, "
+          "ignore Connect request",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str());
+      btif_queue_advance();
+    } break;
+
+    case BTA_AV_PENDING_EVT: {
+      // The device has moved already to Opening, hence don't report the
+      // connection state.
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s : device is already connecting, "
+          "ignore incoming request",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str());
+    } break;
+
+    case BTIF_AV_OFFLOAD_START_REQ_EVT:
+      BTIF_TRACE_ERROR("%s: Peer %s : event=%s: stream is not Opened",
+                       __PRETTY_FUNCTION__,
+                       peer_.PeerAddress().ToString().c_str(),
+                       BtifAvEvent::EventName(event).c_str());
+      btif_a2dp_on_offload_started(BTA_AV_FAIL);
+      break;
+
+    case BTA_AV_CLOSE_EVT:
+      btif_a2dp_on_stopped(nullptr);
+      btif_report_connection_state(peer_.PeerAddress(),
+                                   BTAV_CONNECTION_STATE_DISCONNECTED);
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      if (peer_.SelfInitiatedConnection()) {
+        btif_queue_advance();
+      }
+      break;
+
+    case BTIF_AV_DISCONNECT_REQ_EVT:
+      btif_report_connection_state(peer_.PeerAddress(),
+                                   BTAV_CONNECTION_STATE_DISCONNECTED);
+      BTA_AvClose(peer_.BtaHandle());
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      if (peer_.SelfInitiatedConnection()) {
+        btif_queue_advance();
+      }
+      break;
+
+      CHECK_RC_EVENT(event, (tBTA_AV*)p_data);
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str());
+      return false;
+  }
+  return true;
+}
+
+void BtifAvStateMachine::StateOpened::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+
+  peer_.ClearFlags(BtifAvPeer::kFlagPendingStart |
+                   BtifAvPeer::kFlagPendingStop);
+
+  // Set the active peer if the first connected device
+  if (peer_.IsSink() && btif_av_source.ActivePeer().IsEmpty()) {
+    if (!btif_av_source.SetActivePeer(peer_.PeerAddress())) {
+      BTIF_TRACE_ERROR("%s: Error setting %s as active Sink peer", __func__,
+                       peer_.PeerAddress().ToString().c_str());
+    }
+  }
+  if (peer_.IsSource() && btif_av_sink.ActivePeer().IsEmpty()) {
+    if (!btif_av_sink.SetActivePeer(peer_.PeerAddress())) {
+      BTIF_TRACE_ERROR("%s: Error setting %s as active Source peer", __func__,
+                       peer_.PeerAddress().ToString().c_str());
+    }
+  }
+}
+
+void BtifAvStateMachine::StateOpened::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+
+  peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
+}
+
+bool BtifAvStateMachine::StateOpened::ProcessEvent(uint32_t event,
+                                                   void* p_data) {
+  tBTA_AV* p_av = (tBTA_AV*)p_data;
+
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str(),
+                   BtifAvEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str());
+
+  if ((event == BTA_AV_REMOTE_CMD_EVT) &&
+      peer_.CheckFlags(BtifAvPeer::kFlagRemoteSuspend) &&
+      (p_av->remote_cmd.rc_id == AVRC_ID_PLAY)) {
+    BTIF_TRACE_EVENT("%s: Peer %s : Resetting remote suspend flag on RC PLAY",
+                     __PRETTY_FUNCTION__,
+                     peer_.PeerAddress().ToString().c_str());
+    peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend);
+  }
+
+  switch (event) {
+    case BTIF_AV_STOP_STREAM_REQ_EVT:
+    case BTIF_AV_SUSPEND_STREAM_REQ_EVT:
+    case BTIF_AV_ACL_DISCONNECTED:
+      break;  // Ignore
+
+    case BTIF_AV_START_STREAM_REQ_EVT:
+      if (peer_.IsSink()) {
+        btif_a2dp_source_setup_codec(peer_.PeerAddress());
+      }
+      BTA_AvStart(peer_.BtaHandle());
+      peer_.SetFlags(BtifAvPeer::kFlagPendingStart);
+      break;
+
+    case BTA_AV_START_EVT: {
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s status=%d suspending=%d "
+          "initiator=%d flags=%s",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str(), p_av->start.status,
+          p_av->start.suspending, p_av->start.initiator,
+          peer_.FlagsToString().c_str());
+
+      if ((p_av->start.status == BTA_SUCCESS) && p_av->start.suspending)
+        return true;
+
+      // If remote tries to start A2DP when DUT is A2DP Source, then Suspend.
+      // If A2DP is Sink and call is active, then disconnect the AVDTP channel.
+      if (peer_.IsSink() && !peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
+        BTIF_TRACE_WARNING("%s: Peer %s : trigger Suspend as remote initiated",
+                           __PRETTY_FUNCTION__,
+                           peer_.PeerAddress().ToString().c_str());
+        btif_av_source_dispatch_sm_event(peer_.PeerAddress(),
+                                         BTIF_AV_SUSPEND_STREAM_REQ_EVT);
+      }
+
+      // If peer is A2DP Source, we do not want to ACK commands on UIPC
+      if (peer_.IsSink() &&
+          btif_a2dp_on_started(
+              peer_.PeerAddress(), &p_av->start,
+              peer_.CheckFlags(BtifAvPeer::kFlagPendingStart))) {
+        // Only clear pending flag after acknowledgement
+        peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
+      }
+
+      // Remain in Open state if status failed
+      if (p_av->start.status != BTA_AV_SUCCESS) return false;
+
+      if (peer_.IsSource()) {
+        // Remove flush state, ready for streaming
+        btif_a2dp_sink_set_rx_flush(false);
+      }
+
+      // Change state to Started, send acknowledgement if start is pending
+      if (peer_.IsSink() && peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
+        btif_a2dp_on_started(peer_.PeerAddress(), nullptr, true);
+        // Pending start flag will be cleared when exit current state
+      }
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateStarted);
+
+    } break;
+
+    case BTIF_AV_DISCONNECT_REQ_EVT:
+      BTA_AvClose(peer_.BtaHandle());
+      if (peer_.IsSource()) {
+        BTA_AvCloseRc(peer_.BtaHandle());
+      }
+
+      // Inform the application that we are disconnecting
+      btif_report_connection_state(peer_.PeerAddress(),
+                                   BTAV_CONNECTION_STATE_DISCONNECTING);
+
+      // Wait in closing state until fully closed
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateClosing);
+      break;
+
+    case BTA_AV_CLOSE_EVT:
+      // AVDTP link is closed
+      btif_a2dp_on_stopped(nullptr);
+
+      // Inform the application that we are disconnected
+      btif_report_connection_state(peer_.PeerAddress(),
+                                   BTAV_CONNECTION_STATE_DISCONNECTED);
+
+      // Change state to Idle, send acknowledgement if start is pending
+      if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
+        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
+        // Pending start flag will be cleared when exit current state
+      }
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      break;
+
+    case BTA_AV_RECONFIG_EVT:
+      if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart) &&
+          (p_av->reconfig.status == BTA_AV_SUCCESS)) {
+        BTIF_TRACE_WARNING(
+            "%s : Peer %s : Reconfig done - calling BTA_AvStart()",
+            __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+        BTA_AvStart(peer_.BtaHandle());
+      } else if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
+        peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
+        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
+      }
+      break;
+
+    case BTIF_AV_CONNECT_REQ_EVT: {
+      BTIF_TRACE_WARNING("%s: Peer %s : Ignore %s for same device",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str());
+      btif_queue_advance();
+    } break;
+
+    case BTIF_AV_OFFLOAD_START_REQ_EVT:
+      BTIF_TRACE_ERROR("%s: Peer %s : event=%s: stream is not Opened",
+                       __PRETTY_FUNCTION__,
+                       peer_.PeerAddress().ToString().c_str(),
+                       BtifAvEvent::EventName(event).c_str());
+      btif_a2dp_on_offload_started(BTA_AV_FAIL);
+      break;
+
+    case BTIF_AV_AVRCP_REMOTE_PLAY_EVT:
+      if (peer_.CheckFlags(BtifAvPeer::kFlagRemoteSuspend)) {
+        BTIF_TRACE_EVENT(
+            "%s: Peer %s : Resetting remote suspend flag on RC PLAY",
+            __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+        peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend);
+      }
+      break;
+
+      CHECK_RC_EVENT(event, (tBTA_AV*)p_data);
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str());
+      return false;
+  }
+  return true;
+}
+
+void BtifAvStateMachine::StateStarted::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+
+  // We are again in started state, clear any remote suspend flags
+  peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend);
+
+  // Report that we have entered the Streaming stage. Usually, this should
+  // be followed by focus grant. See update_audio_focus_state()
+  btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STARTED);
+}
+
+void BtifAvStateMachine::StateStarted::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+}
+
+bool BtifAvStateMachine::StateStarted::ProcessEvent(uint32_t event,
+                                                    void* p_data) {
+  tBTA_AV* p_av = (tBTA_AV*)p_data;
+
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str(),
+                   BtifAvEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str());
+
+  switch (event) {
+    case BTIF_AV_ACL_DISCONNECTED:
+      break;  // Ignore
+
+    case BTIF_AV_START_STREAM_REQ_EVT:
+      // We were remotely started, just ACK back the local request
+      if (peer_.IsSink())
+        btif_a2dp_on_started(peer_.PeerAddress(), nullptr, true);
+      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:
+      BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str(),
+                         peer_.FlagsToString().c_str());
+      // Set pending flag to ensure the BTIF task is not trying to restart
+      // the stream while suspend is in progress.
+      peer_.SetFlags(BtifAvPeer::kFlagLocalSuspendPending);
+
+      // If we were remotely suspended but suspend locally, local suspend
+      // always overrides.
+      peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend);
+
+      if (peer_.IsSink()) {
+        // Immediately stop transmission of frames while suspend is pending
+        btif_a2dp_source_set_tx_flush(true);
+      } else if (peer_.IsSource()) {
+        btif_a2dp_on_stopped(nullptr);
+      }
+      BTA_AvStop(peer_.BtaHandle(), true);
+      break;
+
+    case BTIF_AV_DISCONNECT_REQ_EVT:
+      BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str(),
+                         peer_.FlagsToString().c_str());
+
+      // Request AVDTP to close
+      BTA_AvClose(peer_.BtaHandle());
+      if (peer_.IsSource()) {
+        BTA_AvCloseRc(peer_.BtaHandle());
+      }
+
+      // Inform the application that we are disconnecting
+      btif_report_connection_state(peer_.PeerAddress(),
+                                   BTAV_CONNECTION_STATE_DISCONNECTING);
+
+      // Wait in closing state until fully closed
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateClosing);
+      break;
+
+    case BTA_AV_SUSPEND_EVT:
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s status=%d initiator=%d flags=%s",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAvEvent::EventName(event).c_str(), p_av->suspend.status,
+          p_av->suspend.initiator, peer_.FlagsToString().c_str());
+
+      // A2DP suspended, stop A2DP encoder/decoder until resumed
+      btif_a2dp_on_suspended(&p_av->suspend);
+
+      // If not successful, remain in current state
+      if (p_av->suspend.status != BTA_AV_SUCCESS) {
+        peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending);
+
+        if (peer_.IsSink()) {
+          // Suspend failed, reset back tx flush state
+          btif_a2dp_source_set_tx_flush(false);
+        }
+        return false;
+      }
+
+      if (p_av->suspend.initiator != true) {
+        // Remote suspend, notify HAL and await audioflinger to
+        // suspend/stop stream.
+        //
+        // Set remote suspend flag to block media task from restarting
+        // stream only if we did not already initiate a local suspend.
+        if (!peer_.CheckFlags(BtifAvPeer::kFlagLocalSuspendPending))
+          peer_.SetFlags(BtifAvPeer::kFlagRemoteSuspend);
+
+        btif_report_audio_state(peer_.PeerAddress(),
+                                BTAV_AUDIO_STATE_REMOTE_SUSPEND);
+      } else {
+        btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STOPPED);
+      }
+
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpened);
+
+      // Suspend completed and state changed, clear pending status
+      peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending);
+      break;
+
+    case BTA_AV_STOP_EVT:
+      BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str(),
+                         peer_.FlagsToString().c_str());
+
+      peer_.SetFlags(BtifAvPeer::kFlagPendingStop);
+      btif_a2dp_on_stopped(&p_av->suspend);
+
+      btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STOPPED);
+
+      // If stop was successful, change state to Open
+      if (p_av->suspend.status == BTA_AV_SUCCESS)
+        peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpened);
+
+      break;
+
+    case BTA_AV_CLOSE_EVT:
+      BTIF_TRACE_WARNING("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str(),
+                         peer_.FlagsToString().c_str());
+
+      peer_.SetFlags(BtifAvPeer::kFlagPendingStop);
+
+      // AVDTP link is closed
+      btif_a2dp_on_stopped(nullptr);
+
+      // Inform the application that we are disconnected
+      btif_report_connection_state(peer_.PeerAddress(),
+                                   BTAV_CONNECTION_STATE_DISCONNECTED);
+
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      break;
+
+    case BTIF_AV_OFFLOAD_START_REQ_EVT:
+      BTA_AvOffloadStart(peer_.BtaHandle());
+      break;
+
+    case BTA_AV_OFFLOAD_START_RSP_EVT:
+      btif_a2dp_on_offload_started(p_av->status);
+      break;
+
+      CHECK_RC_EVENT(event, (tBTA_AV*)p_data);
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str());
+      return false;
+  }
+
+  return true;
+}
+
+void BtifAvStateMachine::StateClosing::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+
+  if (peer_.IsSink()) {
+    // Immediately stop transmission of frames
+    btif_a2dp_source_set_tx_flush(true);
+    // Wait for Audio Flinger to stop A2DP
+  } else if (peer_.IsSource()) {
+    btif_a2dp_sink_set_rx_flush(true);
+  }
+}
+
+void BtifAvStateMachine::StateClosing::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+}
+
+bool BtifAvStateMachine::StateClosing::ProcessEvent(uint32_t event,
+                                                    void* p_data) {
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str(),
+                   BtifAvEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str());
+
+  switch (event) {
+    case BTIF_AV_SUSPEND_STREAM_REQ_EVT:
+    case BTIF_AV_ACL_DISCONNECTED:
+      break;  // Ignore
+
+    case BTA_AV_STOP_EVT:
+    case BTIF_AV_STOP_STREAM_REQ_EVT:
+      btif_a2dp_on_stopped(nullptr);
+      break;
+
+    case BTA_AV_CLOSE_EVT:
+      // Inform the application that we are disconnecting
+      btif_report_connection_state(peer_.PeerAddress(),
+                                   BTAV_CONNECTION_STATE_DISCONNECTED);
+
+      peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      break;
+
+    // Handle the RC_CLOSE event for the cleanup
+    case BTA_AV_RC_CLOSE_EVT:
+      btif_rc_handler(event, (tBTA_AV*)p_data);
+      break;
+
+    // Handle the RC_BROWSE_CLOSE event for testing
+    case BTA_AV_RC_BROWSE_CLOSE_EVT:
+      btif_rc_handler(event, (tBTA_AV*)p_data);
+      break;
+
+    case BTIF_AV_OFFLOAD_START_REQ_EVT:
+      BTIF_TRACE_ERROR("%s: Peer %s : event=%s: stream is not Opened",
+                       __PRETTY_FUNCTION__,
+                       peer_.PeerAddress().ToString().c_str(),
+                       BtifAvEvent::EventName(event).c_str());
+      btif_a2dp_on_offload_started(BTA_AV_FAIL);
+      break;
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAvEvent::EventName(event).c_str());
+      return false;
+  }
+  return true;
+}
+
+/**
+ * Timer to trigger AV Open on the Source if the remote Sink device establishes
+ * AVRCP connection without AV connection. The timer is needed to interoperate
+ * with headsets that do establish AV after AVRCP connection.
+ */
+static void btif_av_source_initiate_av_open_timer_timeout(void* data) {
+  BtifAvPeer* peer = (BtifAvPeer*)data;
+
+  BTIF_TRACE_DEBUG("%s: Peer %s", __func__,
+                   peer->PeerAddress().ToString().c_str());
+
+  // Check if AVRCP is connected to the peer
+  if (!btif_rc_is_connected_peer(peer->PeerAddress())) {
+    BTIF_TRACE_ERROR("%s: AVRCP peer %s is not connected", __func__,
+                     peer->PeerAddress().ToString().c_str());
+    return;
+  }
+
+  // Connect to the AVRCP peer
+  if (btif_av_source.Enabled() &&
+      btif_av_source.FindPeer(peer->PeerAddress()) == peer) {
+    BTIF_TRACE_DEBUG("%s: Connecting to AVRCP peer %s", __func__,
+                     peer->PeerAddress().ToString().c_str());
+    btif_av_source_dispatch_sm_event(peer->PeerAddress(),
+                                     BTIF_AV_CONNECT_REQ_EVT);
+  }
+}
+
+/**
+ * Timer to trigger AV Open on the Sink if the remote Source device establishes
+ * AVRCP connection without AV connection.
+ */
+static void btif_av_sink_initiate_av_open_timer_timeout(void* data) {
+  BtifAvPeer* peer = (BtifAvPeer*)data;
+
+  BTIF_TRACE_DEBUG("%s: Peer %s", __func__,
+                   peer->PeerAddress().ToString().c_str());
+
+  // Check if AVRCP is connected to the peer
+  if (!btif_rc_is_connected_peer(peer->PeerAddress())) {
+    BTIF_TRACE_ERROR("%s: AVRCP peer %s is not connected", __func__,
+                     peer->PeerAddress().ToString().c_str());
+    return;
+  }
+
+  // Connect to the AVRCP peer
+  if (btif_av_sink.Enabled() &&
+      btif_av_sink.FindPeer(peer->PeerAddress()) == peer) {
+    BTIF_TRACE_DEBUG("%s: Connecting to AVRCP peer %s", __func__,
+                     peer->PeerAddress().ToString().c_str());
+    btif_av_sink_dispatch_sm_event(peer->PeerAddress(),
+                                   BTIF_AV_CONNECT_REQ_EVT);
+  }
+}
+
+/**
+ * Report the A2DP connection state
+ *
+ * @param peer_address the peer address
+ * @param state the connection state
+ */
+static void btif_report_connection_state(const RawAddress& peer_address,
+                                         btav_connection_state_t state) {
+  BTIF_TRACE_WARNING("%s: peer_address=%s state=%d", __func__,
+                     peer_address.ToString().c_str(), state);
+
+  if (btif_av_source.Enabled()) {
+    HAL_CBACK(btif_av_source.Callbacks(), connection_state_cb, peer_address,
+              state);
+  } else if (btif_av_sink.Enabled()) {
+    HAL_CBACK(btif_av_sink.Callbacks(), connection_state_cb, peer_address,
+              state);
+  }
+}
+
+/**
+ * Report the audio state of the A2DP connection.
+ * The state is updated when either the remote ends starts streaming
+ * (Started state) or whenever it transitions out of Started state
+ * (to Opened or Streaming state).
+ *
+ * @param peer_address the peer address
+ * @param state the audio state
+ */
+static void btif_report_audio_state(const RawAddress& peer_address,
+                                    btav_audio_state_t state) {
+  BTIF_TRACE_WARNING("%s: peer_address=%s state=%d", __func__,
+                     peer_address.ToString().c_str(), state);
+
+  if (btif_av_source.Enabled()) {
+    HAL_CBACK(btif_av_source.Callbacks(), audio_state_cb, peer_address, state);
+  } else if (btif_av_sink.Enabled()) {
+    HAL_CBACK(btif_av_sink.Callbacks(), audio_state_cb, peer_address, state);
+  }
+}
+
+void btif_av_report_source_codec_state(
+    const RawAddress& peer_address,
+    const btav_a2dp_codec_config_t& codec_config,
+    const std::vector<btav_a2dp_codec_config_t>& codecs_local_capabilities,
+    const std::vector<btav_a2dp_codec_config_t>&
+        codecs_selectable_capabilities) {
+  BTIF_TRACE_EVENT("%s: peer_address=%s", __func__,
+                   peer_address.ToString().c_str());
+  if (btif_av_source.Enabled()) {
+    HAL_CBACK(btif_av_source.Callbacks(), audio_config_cb, peer_address,
+              codec_config, codecs_local_capabilities,
+              codecs_selectable_capabilities);
+  }
+}
+
+/**
+ * Process BTIF or BTA AV or BTA AVRCP events. The processing is done on the
+ * JNI thread.
+ *
+ * @param peer_sep the corresponding peer's SEP: AVDT_TSEP_SRC if the peer
+ * is A2DP Source, or AVDT_TSEP_SNK if the peer is A2DP Sink.
+ * @param peer_address the peer address if known, otherwise RawAddress::kEmpty
+ * @param bta_handle the BTA handle for the peer if known, otherwise
+ * kBtaHandleUnknown
+ * @param btif_av_event the corresponding event
+ */
+static void btif_av_handle_event(uint8_t peer_sep,
+                                 const RawAddress& peer_address,
+                                 tBTA_AV_HNDL bta_handle,
+                                 const BtifAvEvent& btif_av_event) {
+  BtifAvPeer* peer = nullptr;
+  BTIF_TRACE_EVENT("%s: peer_sep=%s (%d) peer_address=%s handle=%d event=%s",
+                   __func__, (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink",
+                   peer_sep, peer_address.ToString().c_str(), bta_handle,
+                   btif_av_event.ToString().c_str());
+
+  // Find the peer
+  if (peer_address != RawAddress::kEmpty) {
+    if (peer_sep == AVDT_TSEP_SNK) {
+      peer = btif_av_source.FindOrCreatePeer(peer_address, bta_handle);
+    } else if (peer_sep == AVDT_TSEP_SRC) {
+      peer = btif_av_sink.FindOrCreatePeer(peer_address, bta_handle);
+    }
+  } else if (bta_handle != kBtaHandleUnknown) {
+    if (peer_sep == AVDT_TSEP_SNK) {
+      peer = btif_av_source.FindPeerByHandle(bta_handle);
+    } else if (peer_sep == AVDT_TSEP_SRC) {
+      peer = btif_av_sink.FindPeerByHandle(bta_handle);
+    }
+  }
+  if (peer == nullptr) {
+    BTIF_TRACE_ERROR(
+        "%s: Cannot find or create %s peer for peer_address=%s handle=%d : "
+        "event dropped: %s",
+        __func__, (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink",
+        peer_address.ToString().c_str(), bta_handle,
+        btif_av_event.ToString().c_str());
+    return;
+  }
+
+  peer->StateMachine().ProcessEvent(btif_av_event.Event(),
+                                    btif_av_event.Data());
+}
+
+/**
+ * Process BTA AV or BTA AVRCP events. The processing is done on the JNI
+ * thread.
+ *
+ * @param peer_sep the corresponding peer's SEP: AVDT_TSEP_SRC if the peer
+ * is A2DP Source, or AVDT_TSEP_SNK if the peer is A2DP Sink.
+ * @param btif_av_event the corresponding event
+ */
+static void btif_av_handle_bta_av_event(uint8_t peer_sep,
+                                        const BtifAvEvent& btif_av_event) {
+  RawAddress peer_address = RawAddress::kEmpty;
+  tBTA_AV_HNDL bta_handle = kBtaHandleUnknown;
+  tBTA_AV_EVT event = btif_av_event.Event();
+  tBTA_AV* p_data = (tBTA_AV*)btif_av_event.Data();
+
+  BTIF_TRACE_DEBUG("%s: peer_sep=%s (%d) event=%s", __func__,
+                   (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", peer_sep,
+                   btif_av_event.ToString().c_str());
+
+  switch (event) {
+    case BTA_AV_ENABLE_EVT: {
+      const tBTA_AV_ENABLE& enable = p_data->enable;
+      BTIF_TRACE_DEBUG("%s: features=0x%x", __func__, enable.features);
+      return;  // Nothing to do
+    }
+    case BTA_AV_REGISTER_EVT: {
+      const tBTA_AV_REGISTER& registr = p_data->registr;
+      bta_handle = registr.hndl;
+      uint8_t peer_id = registr.app_id;  // The PeerId is used as AppId
+      BTIF_TRACE_DEBUG("%s: handle=%d app_id=%d", __func__, bta_handle,
+                       registr.app_id);
+      if (peer_sep == AVDT_TSEP_SNK) {
+        btif_av_source.BtaHandleRegistered(peer_id, bta_handle);
+      } else if (peer_sep == AVDT_TSEP_SRC) {
+        btif_av_sink.BtaHandleRegistered(peer_id, bta_handle);
+      }
+      return;  // Nothing else to do
+    }
+    case BTA_AV_OPEN_EVT: {
+      const tBTA_AV_OPEN& open = p_data->open;
+      peer_address = open.bd_addr;
+      bta_handle = open.hndl;
+      break;
+    }
+    case BTA_AV_CLOSE_EVT: {
+      const tBTA_AV_CLOSE& close = p_data->close;
+      bta_handle = close.hndl;
+      break;
+    }
+    case BTA_AV_START_EVT: {
+      const tBTA_AV_START& start = p_data->start;
+      bta_handle = start.hndl;
+      break;
+    }
+    case BTA_AV_SUSPEND_EVT:
+    case BTA_AV_STOP_EVT: {
+      const tBTA_AV_SUSPEND& suspend = p_data->suspend;
+      bta_handle = suspend.hndl;
+      break;
+    }
+    case BTA_AV_PROTECT_REQ_EVT: {
+      const tBTA_AV_PROTECT_REQ& protect_req = p_data->protect_req;
+      bta_handle = protect_req.hndl;
+      break;
+    }
+    case BTA_AV_PROTECT_RSP_EVT: {
+      const tBTA_AV_PROTECT_RSP& protect_rsp = p_data->protect_rsp;
+      bta_handle = protect_rsp.hndl;
+      break;
+    }
+    case BTA_AV_RC_OPEN_EVT: {
+      const tBTA_AV_RC_OPEN& rc_open = p_data->rc_open;
+      peer_address = rc_open.peer_addr;
+      break;
+    }
+    case BTA_AV_RC_CLOSE_EVT: {
+      const tBTA_AV_RC_CLOSE& rc_close = p_data->rc_close;
+      peer_address = rc_close.peer_addr;
+      break;
+    }
+    case BTA_AV_RC_BROWSE_OPEN_EVT: {
+      const tBTA_AV_RC_BROWSE_OPEN& rc_browse_open = p_data->rc_browse_open;
+      peer_address = rc_browse_open.peer_addr;
+      break;
+    }
+    case BTA_AV_RC_BROWSE_CLOSE_EVT: {
+      const tBTA_AV_RC_BROWSE_CLOSE& rc_browse_close = p_data->rc_browse_close;
+      peer_address = rc_browse_close.peer_addr;
+      break;
+    }
+    case BTA_AV_REMOTE_CMD_EVT:
+    case BTA_AV_REMOTE_RSP_EVT:
+    case BTA_AV_VENDOR_CMD_EVT:
+    case BTA_AV_VENDOR_RSP_EVT:
+    case BTA_AV_META_MSG_EVT:
+    case BTA_AV_OFFLOAD_START_RSP_EVT: {
+      // TODO: Might be wrong - this code will be removed once those
+      // events are received from the AVRCP module.
+      if (peer_sep == AVDT_TSEP_SNK) {
+        peer_address = btif_av_source.ActivePeer();
+      } else if (peer_sep == AVDT_TSEP_SRC) {
+        peer_address = btif_av_sink.ActivePeer();
+      }
+      break;
+    }
+    case BTA_AV_RECONFIG_EVT: {
+      const tBTA_AV_RECONFIG& reconfig = p_data->reconfig;
+      bta_handle = reconfig.hndl;
+      break;
+    }
+    case BTA_AV_PENDING_EVT: {
+      const tBTA_AV_PEND& pend = p_data->pend;
+      peer_address = pend.bd_addr;
+      break;
+    }
+    case BTA_AV_REJECT_EVT: {
+      const tBTA_AV_REJECT& reject = p_data->reject;
+      peer_address = reject.bd_addr;
+      bta_handle = reject.hndl;
+      break;
+    }
+    case BTA_AV_RC_FEAT_EVT: {
+      const tBTA_AV_RC_FEAT& rc_feat = p_data->rc_feat;
+      peer_address = rc_feat.peer_addr;
+      break;
+    }
+  }
+  BTIF_TRACE_DEBUG("%s: peer_address=%s handle=%d", __func__,
+                   peer_address.ToString().c_str(), bta_handle);
+
+  btif_av_handle_event(peer_sep, peer_address, bta_handle, btif_av_event);
+}
+
+static void bta_av_source_callback(tBTA_AV_EVT event, tBTA_AV* p_data) {
+  BtifAvEvent btif_av_event(event, p_data, sizeof(tBTA_AV));
+  BTIF_TRACE_EVENT("%s: event=%s", __func__, btif_av_event.ToString().c_str());
+
+  do_in_jni_thread(FROM_HERE,
+                   base::Bind(&btif_av_handle_bta_av_event,
+                              AVDT_TSEP_SNK /* peer_sep */, btif_av_event));
+}
+
+static void bta_av_sink_callback(tBTA_AV_EVT event, tBTA_AV* p_data) {
+  BtifAvEvent btif_av_event(event, p_data, sizeof(tBTA_AV));
+  do_in_jni_thread(FROM_HERE,
+                   base::Bind(&btif_av_handle_bta_av_event,
+                              AVDT_TSEP_SRC /* peer_sep */, btif_av_event));
+}
+
+// TODO: All processing should be done on the JNI thread
+static void bta_av_sink_media_callback(tBTA_AV_EVT event,
                                        tBTA_AV_MEDIA* p_data) {
+  BTIF_TRACE_EVENT("%s: event=%d", __func__, event);
+
   switch (event) {
     case BTA_AV_SINK_MEDIA_DATA_EVT: {
-      btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
-      if ((state == BTIF_AV_STATE_STARTED) || (state == BTIF_AV_STATE_OPENED)) {
-        uint8_t queue_len = btif_a2dp_sink_enqueue_buf((BT_HDR*)p_data);
-        BTIF_TRACE_DEBUG("%s: packets in sink queue %d", __func__, queue_len);
+      BtifAvPeer* peer = btif_av_sink_find_peer(btif_av_sink.ActivePeer());
+      if (peer != nullptr) {
+        int state = peer->StateMachine().StateId();
+        if ((state == BtifAvStateMachine::kStateStarted) ||
+            (state == BtifAvStateMachine::kStateOpened)) {
+          uint8_t queue_len = btif_a2dp_sink_enqueue_buf((BT_HDR*)p_data);
+          BTIF_TRACE_DEBUG("%s: Packets in Sink queue %d", __func__, queue_len);
+        }
       }
       break;
     }
     case BTA_AV_SINK_MEDIA_CFG_EVT: {
       btif_av_sink_config_req_t config_req;
 
-      /* send a command to BT Media Task */
+      // Update the codec info of the A2DP Sink decoder
       btif_a2dp_sink_update_decoder((uint8_t*)(p_data->avk_config.codec_info));
-      /* Switch to BTIF context */
+
       config_req.sample_rate =
           A2DP_GetTrackSampleRate(p_data->avk_config.codec_info);
       if (config_req.sample_rate == -1) {
-        APPL_TRACE_ERROR("%s: cannot get the track frequency", __func__);
+        APPL_TRACE_ERROR("%s: Cannot get the track frequency", __func__);
         break;
       }
       config_req.channel_count =
           A2DP_GetTrackChannelCount(p_data->avk_config.codec_info);
       if (config_req.channel_count == -1) {
-        APPL_TRACE_ERROR("%s: cannot get the channel count", __func__);
+        APPL_TRACE_ERROR("%s: Cannot get the channel count", __func__);
         break;
       }
-
       config_req.peer_bd = p_data->avk_config.bd_addr;
-      btif_transfer_context(btif_av_handle_event, BTIF_AV_SINK_CONFIG_REQ_EVT,
-                            (char*)&config_req, sizeof(config_req), NULL);
+      BtifAvEvent btif_av_event(BTIF_AV_SINK_CONFIG_REQ_EVT, &config_req,
+                                sizeof(config_req));
+      do_in_jni_thread(FROM_HERE, base::Bind(&btif_av_handle_event,
+                                             AVDT_TSEP_SRC,  // peer_sep
+                                             config_req.peer_bd,
+                                             kBtaHandleUnknown, btif_av_event));
       break;
     }
     default:
@@ -1252,173 +2318,168 @@
   }
 }
 
-/*******************************************************************************
- *
- * Function         btif_av_init
- *
- * Description      Initializes btif AV if not already done
- *
- * Returns          bt_status_t
- *
- ******************************************************************************/
-
-bt_status_t btif_av_init(int service_id) {
-  if (btif_av_cb.sm_handle == NULL) {
-    alarm_free(av_open_on_rc_timer);
-    av_open_on_rc_timer = alarm_new("btif_av.av_open_on_rc_timer");
-
-    switch (service_id) {
-      case BTA_A2DP_SOURCE_SERVICE_ID:
-        if (!btif_a2dp_source_startup())
-          return BT_STATUS_FAIL;  // Already running
-        break;
-      case BTA_A2DP_SINK_SERVICE_ID:
-        if (!btif_a2dp_sink_startup())
-          return BT_STATUS_FAIL;  // Already running
-        break;
-      default:
-        break;
-    }
-
-    btif_enable_service(service_id);
-
-    /* Also initialize the AV state machine */
-    btif_av_cb.sm_handle = btif_sm_init(
-        (const btif_sm_handler_t*)btif_av_state_handlers, BTIF_AV_STATE_IDLE);
-  }
-
-  return BT_STATUS_SUCCESS;
-}
-
-/*******************************************************************************
- *
- * Function         init_src
- *
- * Description      Initializes the AV interface for source mode
- *
- * Returns          bt_status_t
- *
- ******************************************************************************/
-
+// Initializes the AV interface for source mode
 static bt_status_t init_src(
     btav_source_callbacks_t* callbacks,
     std::vector<btav_a2dp_codec_config_t> codec_priorities) {
   BTIF_TRACE_EVENT("%s", __func__);
-
-  btif_av_cb.codec_priorities = codec_priorities;
-  bt_status_t status = btif_av_init(BTA_A2DP_SOURCE_SERVICE_ID);
-  if (status == BT_STATUS_SUCCESS) bt_av_src_callbacks = callbacks;
-
-  return status;
+  return btif_av_source.Init(callbacks, codec_priorities);
 }
 
-/*******************************************************************************
- *
- * Function         init_sink
- *
- * Description      Initializes the AV interface for sink mode
- *
- * Returns          bt_status_t
- *
- ******************************************************************************/
-
+// Initializes the AV interface for sink mode
 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);
-  if (status == BT_STATUS_SUCCESS) bt_av_sink_callbacks = callbacks;
-
-  return status;
+  return btif_av_sink.Init(callbacks);
 }
 
-/*******************************************************************************
- *
- * Function         update_audio_focus_state
- *
- * Description      Updates the final focus state reported by components calling
- *                  this module.
- *
- * Returns          None
- *
- ******************************************************************************/
+// Updates the final focus state reported by components calling this module
 static void update_audio_focus_state(int state) {
   BTIF_TRACE_DEBUG("%s: state=%d", __func__, state);
   btif_a2dp_sink_set_focus_state_req((btif_a2dp_sink_focus_state_t)state);
 }
 
-/*******************************************************************************
- *
- * Function         update_audio_track_gain
- *
- * Description      Updates the track gain (used for ducking).
- *
- * Returns          None
- *
- ******************************************************************************/
+// Updates the track gain (used for ducking).
 static void update_audio_track_gain(float gain) {
   BTIF_TRACE_DEBUG("%s: gain=%f", __func__, gain);
   btif_a2dp_sink_set_audio_track_gain(gain);
 }
 
-/*******************************************************************************
- *
- * Function         connect
- *
- * Description      Establishes the AV signalling channel with the remote
- *                  headset
- *
- * Returns          bt_status_t
- *
- ******************************************************************************/
+// Establishes the AV signalling channel with the remote headset
+static bt_status_t connect_int(RawAddress* peer_address, uint16_t uuid) {
+  BTIF_TRACE_EVENT("%s: peer_address=%s uuid=0x%x", __func__,
+                   peer_address->ToString().c_str(), uuid);
 
-static bt_status_t connect_int(RawAddress* bd_addr, uint16_t uuid) {
-  btif_av_connect_req_t connect_req;
-  connect_req.target_bda = bd_addr;
-  connect_req.uuid = uuid;
-  BTIF_TRACE_EVENT("%s", __func__);
-
-  btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT,
-                   (char*)&connect_req);
-
+  BtifAvPeer* peer = nullptr;
+  if (uuid == UUID_SERVCLASS_AUDIO_SOURCE) {
+    peer = btif_av_source.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
+    if (peer == nullptr) {
+      return BT_STATUS_FAIL;
+    }
+  } else if (uuid == UUID_SERVCLASS_AUDIO_SINK) {
+    peer = btif_av_sink.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
+    if (peer == nullptr) {
+      return BT_STATUS_FAIL;
+    }
+  }
+  peer->StateMachine().ProcessEvent(BTIF_AV_CONNECT_REQ_EVT, nullptr);
   return BT_STATUS_SUCCESS;
 }
 
-static bt_status_t src_connect_sink(RawAddress* bd_addr) {
-  BTIF_TRACE_EVENT("%s", __func__);
-  CHECK_BTAV_INIT();
+// Set the active peer
+static void set_active_peer_int(uint8_t peer_sep,
+                                const RawAddress& peer_address) {
+  BTIF_TRACE_EVENT("%s: peer_sep=%s (%d) peer_address=%s", __func__,
+                   (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", peer_sep,
+                   peer_address.ToString().c_str());
 
-  return btif_queue_connect(UUID_SERVCLASS_AUDIO_SOURCE, bd_addr, connect_int);
+  BtifAvPeer* peer = nullptr;
+  if (peer_sep == AVDT_TSEP_SNK) {
+    peer = btif_av_source.FindPeer(peer_address);
+    if (peer != nullptr && peer->IsConnected()) {
+      if (!btif_av_source.SetActivePeer(peer_address)) {
+        BTIF_TRACE_ERROR("%s: Error setting %s as active Sink peer", __func__,
+                         peer_address.ToString().c_str());
+      }
+      return;
+    }
+  }
+  if (peer_sep == AVDT_TSEP_SRC) {
+    peer = btif_av_sink.FindPeer(peer_address);
+    if (peer != nullptr && peer->IsConnected()) {
+      if (!btif_av_sink.SetActivePeer(peer_address)) {
+        BTIF_TRACE_ERROR("%s: Error setting %s as active Source peer", __func__,
+                         peer_address.ToString().c_str());
+      }
+      return;
+    }
+  }
+  // If reached here, we could not set the active peer
+  BTIF_TRACE_ERROR("%s: Cannot set active %s peer to %s: peer not %s", __func__,
+                   (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink",
+                   peer_address.ToString().c_str(),
+                   (peer == nullptr) ? "found" : "connected");
 }
 
-static bt_status_t sink_connect_src(RawAddress* bd_addr) {
-  BTIF_TRACE_EVENT("%s", __func__);
-  CHECK_BTAV_INIT();
+static bt_status_t src_connect_sink(const RawAddress& peer_address) {
+  BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str());
 
-  return btif_queue_connect(UUID_SERVCLASS_AUDIO_SINK, bd_addr, connect_int);
+  if (!btif_av_source.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+
+  RawAddress peer_address_copy(peer_address);
+  return btif_queue_connect(UUID_SERVCLASS_AUDIO_SOURCE, &peer_address_copy,
+                            connect_int);
 }
 
-/*******************************************************************************
- *
- * Function         disconnect
- *
- * Description      Tears down the AV signalling channel with the remote headset
- *
- * Returns          bt_status_t
- *
- ******************************************************************************/
-static bt_status_t disconnect(RawAddress* bd_addr) {
-  BTIF_TRACE_EVENT("%s", __func__);
-  CHECK_BTAV_INIT();
+static bt_status_t sink_connect_src(const RawAddress& peer_address) {
+  BTIF_TRACE_WARNING("%s: Peer %s", __func__, peer_address.ToString().c_str());
 
-  /* Switch to BTIF context */
-  return btif_transfer_context(btif_av_handle_event, BTIF_AV_DISCONNECT_REQ_EVT,
-                               (char*)bd_addr, sizeof(RawAddress), NULL);
+  if (!btif_av_sink.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF AV Sink is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+
+  RawAddress peer_address_copy(peer_address);
+  return btif_queue_connect(UUID_SERVCLASS_AUDIO_SINK, &peer_address_copy,
+                            connect_int);
+}
+
+static bt_status_t src_disconnect_sink(const RawAddress& peer_address) {
+  BTIF_TRACE_WARNING("%s: Peer %s", __func__, peer_address.ToString().c_str());
+
+  if (!btif_av_source.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+
+  BtifAvEvent btif_av_event(BTIF_AV_DISCONNECT_REQ_EVT, &peer_address,
+                            sizeof(peer_address));
+  return do_in_jni_thread(
+      FROM_HERE, base::Bind(&btif_av_handle_event,
+                            AVDT_TSEP_SNK,  // peer_sep
+                            peer_address, kBtaHandleUnknown, btif_av_event));
+}
+
+static bt_status_t sink_disconnect_src(const RawAddress& peer_address) {
+  BTIF_TRACE_WARNING("%s: Peer %s", __func__, peer_address.ToString().c_str());
+
+  if (!btif_av_sink.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF AV Sink is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+
+  BtifAvEvent btif_av_event(BTIF_AV_DISCONNECT_REQ_EVT, &peer_address,
+                            sizeof(peer_address));
+  return do_in_jni_thread(
+      FROM_HERE, base::Bind(&btif_av_handle_event,
+                            AVDT_TSEP_SRC,  // peer_sep
+                            peer_address, kBtaHandleUnknown, btif_av_event));
+}
+
+static bt_status_t src_set_active_sink(const RawAddress& peer_address) {
+  BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str());
+
+  if (!btif_av_source.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+
+  return do_in_jni_thread(FROM_HERE, base::Bind(&set_active_peer_int,
+                                                AVDT_TSEP_SNK,  // peer_sep
+                                                peer_address));
 }
 
 static bt_status_t codec_config_src(
+    const RawAddress& peer_address,
     std::vector<btav_a2dp_codec_config_t> codec_preferences) {
   BTIF_TRACE_EVENT("%s", __func__);
-  CHECK_BTAV_INIT();
+
+  if (!btif_av_source.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
 
   for (auto cp : codec_preferences) {
     BTIF_TRACE_DEBUG(
@@ -1430,365 +2491,421 @@
         __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);
+    do_in_jni_thread(
+        FROM_HERE, base::Bind(&btif_a2dp_source_encoder_user_config_update_req,
+                              peer_address, cp));
   }
 
   return BT_STATUS_SUCCESS;
 }
 
-/*******************************************************************************
- *
- * Function         cleanup
- *
- * Description      Shuts down the AV interface and does the cleanup
- *
- * Returns          None
- *
- ******************************************************************************/
-static void cleanup(int service_uuid) {
-  BTIF_TRACE_EVENT("%s", __func__);
-
-  btif_transfer_context(btif_av_handle_event, BTIF_AV_CLEANUP_REQ_EVT, NULL, 0,
-                        NULL);
-
-  btif_disable_service(service_uuid);
-
-  alarm_free(av_open_on_rc_timer);
-  av_open_on_rc_timer = NULL;
-
-  /* Also shut down the AV state machine */
-  btif_sm_shutdown(btif_av_cb.sm_handle);
-  btif_av_cb.sm_handle = NULL;
-}
-
 static void cleanup_src(void) {
   BTIF_TRACE_EVENT("%s", __func__);
-
-  btif_queue_cleanup(UUID_SERVCLASS_AUDIO_SOURCE);
-  if (bt_av_src_callbacks) {
-    bt_av_src_callbacks = NULL;
-    if (bt_av_sink_callbacks == NULL) cleanup(BTA_A2DP_SOURCE_SERVICE_ID);
-  }
+  do_in_jni_thread(FROM_HERE, base::Bind(&BtifAvSource::Cleanup,
+                                         base::Unretained(&btif_av_source)));
 }
 
 static void cleanup_sink(void) {
   BTIF_TRACE_EVENT("%s", __func__);
-
-  btif_queue_cleanup(UUID_SERVCLASS_AUDIO_SINK);
-  if (bt_av_sink_callbacks) {
-    bt_av_sink_callbacks = NULL;
-    if (bt_av_src_callbacks == NULL) cleanup(BTA_A2DP_SINK_SERVICE_ID);
-  }
+  do_in_jni_thread(FROM_HERE, base::Bind(&BtifAvSink::Cleanup,
+                                         base::Unretained(&btif_av_sink)));
 }
 
 static const btav_source_interface_t bt_av_src_interface = {
     sizeof(btav_source_interface_t),
     init_src,
     src_connect_sink,
-    disconnect,
+    src_disconnect_sink,
+    src_set_active_sink,
     codec_config_src,
     cleanup_src,
 };
 
 static const btav_sink_interface_t bt_av_sink_interface = {
-    sizeof(btav_sink_interface_t),
-    init_sink,
-    sink_connect_src,
-    disconnect,
-    cleanup_sink,
-    update_audio_focus_state,
+    sizeof(btav_sink_interface_t), init_sink,    sink_connect_src,
+    sink_disconnect_src,           cleanup_sink, update_audio_focus_state,
     update_audio_track_gain,
 };
 
-/*******************************************************************************
- *
- * Function         btif_av_get_addr
- *
- * Description      Fetches current AV BD address
- *
- * Returns          BD address
- *
- ******************************************************************************/
-
-RawAddress btif_av_get_addr(void) { return btif_av_cb.peer_bda; }
-
-/*******************************************************************************
- * Function         btif_av_is_sink_enabled
- *
- * Description      Checks if A2DP Sink is enabled or not
- *
- * Returns          true if A2DP Sink is enabled, false otherwise
- *
- ******************************************************************************/
-
-bool btif_av_is_sink_enabled(void) {
-  return (bt_av_sink_callbacks != NULL) ? true : false;
+RawAddress btif_av_source_active_peer(void) {
+  return btif_av_source.ActivePeer();
 }
 
-/*******************************************************************************
- *
- * Function         btif_av_stream_ready
- *
- * Description      Checks whether AV is ready for starting a stream
- *
- * Returns          None
- *
- ******************************************************************************/
+RawAddress btif_av_sink_active_peer(void) { return btif_av_sink.ActivePeer(); }
+
+bool btif_av_is_sink_enabled(void) { return btif_av_sink.Enabled(); }
+
+void btif_av_stream_start(void) {
+  btif_av_source_dispatch_sm_event(btif_av_source_active_peer(),
+                                   BTIF_AV_START_STREAM_REQ_EVT);
+}
+
+void btif_av_stream_stop(void) {
+  // The active peer might have changed and we might be in the process
+  // of reconfiguring the stream. We need to stop the appopriate peer(s).
+  for (auto it : btif_av_source.Peers()) {
+    const BtifAvPeer* peer = it.second;
+    btif_av_source_dispatch_sm_event(peer->PeerAddress(),
+                                     BTIF_AV_STOP_STREAM_REQ_EVT);
+  }
+}
+
+void btif_av_stream_suspend(void) {
+  // The active peer might have changed and we might be in the process
+  // of reconfiguring the stream. We need to suspend the appropriate peer(s).
+  for (auto it : btif_av_source.Peers()) {
+    const BtifAvPeer* peer = it.second;
+    btif_av_source_dispatch_sm_event(peer->PeerAddress(),
+                                     BTIF_AV_SUSPEND_STREAM_REQ_EVT);
+  }
+}
+
+void btif_av_stream_start_offload(void) {
+  btif_av_source_dispatch_sm_event(btif_av_source_active_peer(),
+                                   BTIF_AV_OFFLOAD_START_REQ_EVT);
+}
 
 bool btif_av_stream_ready(void) {
-  btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
-
-  BTIF_TRACE_DEBUG("%s: sm_handle=%d, state=%d, flags=0x%x", __func__,
-                   btif_av_cb.sm_handle, state, btif_av_cb.flags);
-
-  /* also make sure main adapter is enabled */
+  // Make sure the main adapter is enabled
   if (btif_is_enabled() == 0) {
-    BTIF_TRACE_EVENT("%s: main adapter not enabled", __func__);
+    BTIF_TRACE_EVENT("%s: Main adapter is not enabled", __func__);
     return false;
   }
 
-  /* check if we are remotely suspended or stop is pending */
-  if (btif_av_cb.flags &
-      (BTIF_AV_FLAG_REMOTE_SUSPEND | BTIF_AV_FLAG_PENDING_STOP))
+  BtifAvPeer* peer = btif_av_find_active_peer();
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: No active peer found", __func__);
     return false;
+  }
 
-  return (state == BTIF_AV_STATE_OPENED);
+  int state = peer->StateMachine().StateId();
+  BTIF_TRACE_DEBUG("%s: Peer %s : state=%d, flags=%s", __func__,
+                   peer->PeerAddress().ToString().c_str(), state,
+                   peer->FlagsToString().c_str());
+  // check if we are remotely suspended or stop is pending
+  if (peer->CheckFlags(BtifAvPeer::kFlagRemoteSuspend |
+                       BtifAvPeer::kFlagPendingStop)) {
+    return false;
+  }
+
+  return (state == BtifAvStateMachine::kStateOpened);
 }
 
-/*******************************************************************************
- *
- * Function         btif_av_stream_started_ready
- *
- * Description      Checks whether AV ready for media start in streaming state
- *
- * Returns          None
- *
- ******************************************************************************/
-
 bool btif_av_stream_started_ready(void) {
-  btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
-  bool ready = false;
-
-  /* disallow media task to start if we have pending actions */
-  if (btif_av_cb.flags &
-      (BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING | BTIF_AV_FLAG_REMOTE_SUSPEND |
-       BTIF_AV_FLAG_PENDING_STOP)) {
-    ready = false;
-  } else {
-    ready = (state == BTIF_AV_STATE_STARTED);
+  BtifAvPeer* peer = btif_av_find_active_peer();
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: No active peer found", __func__);
+    return false;
   }
 
-  BTIF_TRACE_WARNING("%s: sm_handle=%d state=%d flags=0x%x ready=%d", __func__,
-                     btif_av_cb.sm_handle, state, btif_av_cb.flags, ready);
+  int state = peer->StateMachine().StateId();
+  bool ready = false;
+  if (peer->CheckFlags(BtifAvPeer::kFlagLocalSuspendPending |
+                       BtifAvPeer::kFlagRemoteSuspend |
+                       BtifAvPeer::kFlagPendingStop)) {
+    // Disallow media task to start if we have pending actions
+    ready = false;
+  } else {
+    ready = (state == BtifAvStateMachine::kStateStarted);
+  }
+  BTIF_TRACE_WARNING("%s: Peer %s : state=%d flags=%s ready=%d", __func__,
+                     peer->PeerAddress().ToString().c_str(), state,
+                     peer->FlagsToString().c_str(), ready);
 
   return ready;
 }
 
-/*******************************************************************************
- *
- * Function         btif_dispatch_sm_event
- *
- * Description      Send event to AV statemachine
- *
- * Returns          None
- *
- ******************************************************************************/
+static void btif_av_source_dispatch_sm_event(const RawAddress& peer_address,
+                                             btif_av_sm_event_t event) {
+  BtifAvEvent btif_av_event(event, nullptr, 0);
+  BTIF_TRACE_EVENT("%s: peer_address=%s event=%s", __func__,
+                   peer_address.ToString().c_str(),
+                   btif_av_event.ToString().c_str());
 
-/* used to pass events to AV statemachine from other tasks */
-void btif_dispatch_sm_event(btif_av_sm_event_t event, void* p_data, int len) {
-  /* Switch to BTIF context */
-  btif_transfer_context(btif_av_handle_event, event, (char*)p_data, len, NULL);
+  do_in_jni_thread(FROM_HERE,
+                   base::Bind(&btif_av_handle_event,
+                              AVDT_TSEP_SNK,  // peer_sep
+                              peer_address, kBtaHandleUnknown, btif_av_event));
 }
 
-/*******************************************************************************
- *
- * Function         btif_av_execute_service
- *
- * Description      Initializes/Shuts down the service
- *
- * Returns          BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
- *
- ******************************************************************************/
-bt_status_t btif_av_execute_service(bool b_enable) {
-  if (b_enable) {
-/* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not
- * handle this request in order to allow incoming connections to succeed.
- * We need to put this back once support for this is added */
+static void btif_av_sink_dispatch_sm_event(const RawAddress& peer_address,
+                                           btif_av_sm_event_t event) {
+  BtifAvEvent btif_av_event(event, nullptr, 0);
+  BTIF_TRACE_EVENT("%s: peer_address=%s event=%s", __func__,
+                   peer_address.ToString().c_str(),
+                   btif_av_event.ToString().c_str());
 
-/* Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not
- * auto-suspend av streaming on AG events(SCO or Call). The suspend shall
- * be initiated by the app/audioflinger layers */
-/* Support for browsing for SDP record should work only if we enable BROWSE
- * while registering. */
-    BTA_AvEnable(BTA_SEC_AUTHENTICATE,
-                 BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR |
-                     BTA_AV_FEAT_NO_SCO_SSPD
+  do_in_jni_thread(FROM_HERE,
+                   base::Bind(&btif_av_handle_event,
+                              AVDT_TSEP_SRC,  // peer_sep
+                              peer_address, kBtaHandleUnknown, btif_av_event));
+}
+
+bt_status_t btif_av_source_execute_service(bool enable) {
+  BTIF_TRACE_EVENT("%s: Source service: %s", __func__,
+                   (enable) ? "enable" : "disable");
+
+  if (enable) {
+    // TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not
+    // handle this request in order to allow incoming connections to succeed.
+    // We need to put this back once support for this is added.
+
+    // Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not
+    // auto-suspend av streaming on AG events(SCO or Call). The suspend shall
+    // be initiated by the app/audioflinger layers.
+    // Support for browsing for SDP record should work only if we enable BROWSE
+    // while registering.
+    tBTA_AV_FEAT features = BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA |
+                            BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_NO_SCO_SSPD;
 #if (AVRC_ADV_CTRL_INCLUDED == TRUE)
-                     | BTA_AV_FEAT_RCCT | BTA_AV_FEAT_ADV_CTRL |
-                     BTA_AV_FEAT_BROWSE
+    features |= BTA_AV_FEAT_RCCT | BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_BROWSE;
 #endif
-                 ,
-                 bte_av_callback);
-    BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AV_SERVICE_NAME, 0, NULL,
-                   UUID_SERVCLASS_AUDIO_SOURCE);
-  } else {
-    BTA_AvDeregister(btif_av_cb.bta_handle);
-    BTA_AvDisable();
+    BTA_AvEnable(BTA_SEC_AUTHENTICATE, features, bta_av_source_callback);
+    btif_av_source.RegisterAllBtaHandles();
+    return BT_STATUS_SUCCESS;
   }
+
+  // Disable the service
+  btif_av_source.DeregisterAllBtaHandles();
+  BTA_AvDisable();
   return BT_STATUS_SUCCESS;
 }
 
-/*******************************************************************************
- *
- * Function         btif_av_sink_execute_service
- *
- * Description      Initializes/Shuts down the service
- *
- * Returns          BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
- *
- ******************************************************************************/
-bt_status_t btif_av_sink_execute_service(bool b_enable) {
-  if (b_enable) {
-    /* Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not
-     * auto-suspend av streaming on AG events(SCO or Call). The suspend shall
-     * be initiated by the app/audioflinger layers */
-    BTA_AvEnable(BTA_SEC_AUTHENTICATE,
-                 BTA_AV_FEAT_NO_SCO_SSPD | BTA_AV_FEAT_RCCT |
-                     BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR |
-                     BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_RCTG |
-                     BTA_AV_FEAT_BROWSE,
-                 bte_av_callback);
-    BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AVK_SERVICE_NAME, 0,
-                   bte_av_sink_media_callback, UUID_SERVCLASS_AUDIO_SINK);
-  } else {
-    BTA_AvDeregister(btif_av_cb.bta_handle);
-    BTA_AvDisable();
+bt_status_t btif_av_sink_execute_service(bool enable) {
+  BTIF_TRACE_EVENT("%s: Sink service: %s", __func__,
+                   (enable) ? "enable" : "disable");
+
+  if (enable) {
+    // Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not
+    // auto-suspend AV streaming on AG events (SCO or Call). The suspend shall
+    // be initiated by the app/audioflinger layers.
+    tBTA_AV_FEAT features = BTA_AV_FEAT_NO_SCO_SSPD | BTA_AV_FEAT_RCCT |
+                            BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR |
+                            BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_RCTG |
+                            BTA_AV_FEAT_BROWSE;
+    BTA_AvEnable(BTA_SEC_AUTHENTICATE, features, bta_av_sink_callback);
+    btif_av_sink.RegisterAllBtaHandles();
+    return BT_STATUS_SUCCESS;
   }
+
+  // Disable the service
+  btif_av_sink.DeregisterAllBtaHandles();
+  BTA_AvDisable();
   return BT_STATUS_SUCCESS;
 }
 
-/*******************************************************************************
- *
- * Function         btif_av_get_src_interface
- *
- * Description      Get the AV callback interface for A2DP source profile
- *
- * Returns          btav_source_interface_t
- *
- ******************************************************************************/
+// Get the AV callback interface for A2DP source profile
 const btav_source_interface_t* btif_av_get_src_interface(void) {
   BTIF_TRACE_EVENT("%s", __func__);
   return &bt_av_src_interface;
 }
 
-/*******************************************************************************
- *
- * Function         btif_av_get_sink_interface
- *
- * Description      Get the AV callback interface for A2DP sink profile
- *
- * Returns          btav_sink_interface_t
- *
- ******************************************************************************/
+// Get the AV callback interface for A2DP sink profile
 const btav_sink_interface_t* btif_av_get_sink_interface(void) {
   BTIF_TRACE_EVENT("%s", __func__);
   return &bt_av_sink_interface;
 }
 
-/*******************************************************************************
- *
- * Function         btif_av_is_connected
- *
- * Description      Checks if av has a connected sink
- *
- * Returns          bool
- *
- ******************************************************************************/
 bool btif_av_is_connected(void) {
-  btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
-  return ((state == BTIF_AV_STATE_OPENED) || (state == BTIF_AV_STATE_STARTED));
-}
-
-uint8_t btif_av_get_peer_sep(void) { return btif_av_cb.peer_sep; }
-
-/*******************************************************************************
- *
- * Function         btif_av_is_peer_edr
- *
- * Description      Check if the connected a2dp device supports
- *                  EDR or not. Only when connected this function
- *                  will accurately provide a true capability of
- *                  remote peer. If not connected it will always be false.
- *
- * Returns          true if remote device is capable of EDR
- *
- ******************************************************************************/
-bool btif_av_is_peer_edr(void) {
-  ASSERTC(btif_av_is_connected(), "No active a2dp connection", 0);
-
-  if (btif_av_cb.edr)
-    return true;
-  else
+  BtifAvPeer* peer = btif_av_find_active_peer();
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: No active peer found", __func__);
     return false;
-}
-
-/******************************************************************************
- *
- * Function        btif_av_clear_remote_suspend_flag
- *
- * Description     Clears btif_av_cd.flags if BTIF_AV_FLAG_REMOTE_SUSPEND is set
- *
- * Returns          void
- *****************************************************************************/
-void btif_av_clear_remote_suspend_flag(void) {
-  BTIF_TRACE_DEBUG("%s: flags=0x%x", __func__, btif_av_cb.flags);
-  btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
-}
-
-/*******************************************************************************
- *
- * Function         btif_av_peer_supports_3mbps
- *
- * Description      Check if the connected A2DP device supports
- *                  3 Mbps EDR. This function only works if connected.
- *                  If not connected it will always be false.
- *
- * Returns          true if remote device is EDR and supports 3 Mbps
- *
- ******************************************************************************/
-bool btif_av_peer_supports_3mbps(void) {
-  bool is3mbps = ((btif_av_cb.edr & BTA_AV_EDR_3MBPS) != 0);
-  BTIF_TRACE_DEBUG("%s: connected %d, edr_3mbps %d", __func__,
-                   btif_av_is_connected(), is3mbps);
-  return (btif_av_is_connected() && is3mbps);
-}
-
-/*******************************************************************************
- *
- * Function         btif_av_move_idle
- *
- * Description      Opening state is intermediate state. It cannot handle
- *                  incoming/outgoing connect/disconnect requests.When ACL
- *                  is disconnected and we are in opening state then move back
- *                  to idle state which is proper to handle connections.
- *
- * Returns          Void
- *
- ******************************************************************************/
-void btif_av_move_idle(RawAddress bd_addr) {
-  /* inform the application that ACL is disconnected and move to idle state */
-  btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
-  BTIF_TRACE_WARNING("%s: ACL Disconnected state %d bd_addr=%s peer_bda=%s",
-                     __func__, state, bd_addr.ToString().c_str(),
-                     btif_av_cb.peer_bda.ToString().c_str());
-
-  if (state == BTIF_AV_STATE_OPENING && (bd_addr == btif_av_cb.peer_bda)) {
-    BTIF_TRACE_DEBUG(
-        "%s: Moving State from Opening to Idle due to ACL disconnect",
-        __func__);
-    btif_report_connection_state(&(btif_av_cb.peer_bda),
-                                 BTAV_CONNECTION_STATE_DISCONNECTED);
-    btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
   }
+
+  bool connected = peer->IsConnected();
+  BTIF_TRACE_DEBUG("%s: Peer %s is %s", __func__,
+                   peer->PeerAddress().ToString().c_str(),
+                   (connected) ? "connected" : "not connected");
+  return connected;
+}
+
+uint8_t btif_av_get_peer_sep(void) {
+  BtifAvPeer* peer = btif_av_find_active_peer();
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: No active peer found", __func__);
+    return AVDT_TSEP_SNK;
+  }
+
+  uint8_t peer_sep = peer->PeerSep();
+  BTIF_TRACE_DEBUG("%s: Peer %s SEP is %s (%d)", __func__,
+                   peer->PeerAddress().ToString().c_str(),
+                   (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", peer_sep);
+  return peer_sep;
+}
+
+void btif_av_clear_remote_suspend_flag(void) {
+  BtifAvPeer* peer = btif_av_find_active_peer();
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: No active peer found", __func__);
+    return;
+  }
+
+  BTIF_TRACE_DEBUG("%s: Peer %s : flags=%s are cleared", __func__,
+                   peer->PeerAddress().ToString().c_str(),
+                   peer->FlagsToString().c_str());
+  peer->ClearFlags(BtifAvPeer::kFlagRemoteSuspend);
+}
+
+void btif_av_avrcp_event_open(const RawAddress& peer_address) {
+  // TODO: We need a better demultipexing mechanism whether the remote device
+  // is an A2DP Source or a Sink.
+  if (btif_av_source.Enabled()) {
+    BtifAvPeer* peer =
+        btif_av_source.FindOrCreatePeer(peer_address, kBtaHandleUnknown);
+    if (peer != nullptr) {
+      btif_av_source_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_OPEN_EVT);
+      return;
+    }
+  } else if (btif_av_sink.Enabled()) {
+    BtifAvPeer* peer =
+        btif_av_sink.FindOrCreatePeer(peer_address, kBtaHandleUnknown);
+    if (peer != nullptr) {
+      btif_av_sink_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_OPEN_EVT);
+      return;
+    }
+  }
+  BTIF_TRACE_ERROR("%s: event ignored: cannot find or create peer state for %s",
+                   __func__, peer_address.ToString().c_str());
+}
+
+void btif_av_avrcp_event_close(const RawAddress& peer_address) {
+  // TODO: We need a better demultipexing mechanism whether the remote device
+  // is an A2DP Source or a Sink.
+  if (btif_av_source.Enabled()) {
+    btif_av_source_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_CLOSE_EVT);
+  } else if (btif_av_sink.Enabled()) {
+    btif_av_sink_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_CLOSE_EVT);
+  }
+}
+
+void btif_av_avrcp_event_remote_play(const RawAddress& peer_address) {
+  // TODO: We need a better demultipexing mechanism whether the remote device
+  // is an A2DP Source or a Sink.
+  if (btif_av_source.Enabled()) {
+    btif_av_source_dispatch_sm_event(peer_address,
+                                     BTIF_AV_AVRCP_REMOTE_PLAY_EVT);
+  } else if (btif_av_sink.Enabled()) {
+    btif_av_sink_dispatch_sm_event(peer_address, BTIF_AV_AVRCP_REMOTE_PLAY_EVT);
+  }
+}
+
+bool btif_av_is_peer_edr(const RawAddress& peer_address) {
+  BtifAvPeer* peer = btif_av_find_peer(peer_address);
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: No peer found for peer_address=%s", __func__,
+                       peer_address.ToString().c_str());
+    return false;
+  }
+  if (!peer->IsConnected()) {
+    BTIF_TRACE_WARNING("%s: Peer %s is not connected", __func__,
+                       peer_address.ToString().c_str());
+    return false;
+  }
+
+  bool is_edr = peer->IsEdr();
+  BTIF_TRACE_DEBUG("%s: Peer %s : is_edr=%d", __func__,
+                   peer_address.ToString().c_str(), is_edr);
+  return is_edr;
+}
+
+bool btif_av_peer_supports_3mbps(const RawAddress& peer_address) {
+  BtifAvPeer* peer = btif_av_find_peer(peer_address);
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: No peer found for peer_address=%s", __func__,
+                       peer_address.ToString().c_str());
+    return false;
+  }
+
+  bool is3mbps = peer->Is3Mbps();
+  bool is_connected = peer->IsConnected();
+  BTIF_TRACE_DEBUG("%s: Peer %s : connected=%d, edr_3mbps=%d", __func__,
+                   peer_address.ToString().c_str(), is_connected, is3mbps);
+  return (is_connected && is3mbps);
+}
+
+void btif_av_acl_disconnected(const RawAddress& peer_address) {
+  // Inform the application that ACL is disconnected and move to idle state
+  BTIF_TRACE_WARNING("%s: Peer %s : ACL Disconnected", __func__,
+                     peer_address.ToString().c_str());
+
+  if (btif_av_source.Enabled()) {
+    btif_av_source_dispatch_sm_event(peer_address, BTIF_AV_ACL_DISCONNECTED);
+  } else if (btif_av_sink.Enabled()) {
+    btif_av_sink_dispatch_sm_event(peer_address, BTIF_AV_ACL_DISCONNECTED);
+  }
+}
+
+static void btif_debug_av_peer_dump(int fd, const BtifAvPeer& peer) {
+  std::string state_str;
+  int state = peer.StateMachine().StateId();
+  switch (state) {
+    case BtifAvStateMachine::kStateIdle:
+      state_str = "Idle";
+      break;
+    case BtifAvStateMachine::kStateOpening:
+      state_str = "Opening";
+      break;
+    case BtifAvStateMachine::kStateOpened:
+      state_str = "Opened";
+      break;
+    case BtifAvStateMachine::kStateStarted:
+      state_str = "Started";
+      break;
+    case BtifAvStateMachine::kStateClosing:
+      state_str = "Closing";
+      break;
+    default:
+      state_str = "Unknown(" + std::to_string(state) + ")";
+      break;
+  }
+
+  dprintf(fd, "  Peer: %s\n", peer.PeerAddress().ToString().c_str());
+  dprintf(fd, "    Connected: %s\n", peer.IsConnected() ? "true" : "false");
+  dprintf(fd, "    Streaming: %s\n", peer.IsStreaming() ? "true" : "false");
+  dprintf(fd, "    SEP: %d(%s)\n", peer.PeerSep(),
+          (peer.IsSource()) ? "Source" : "Sink");
+  dprintf(fd, "    State Machine: %s\n", state_str.c_str());
+  dprintf(fd, "    Flags: %s\n", peer.FlagsToString().c_str());
+  dprintf(fd, "    OpenOnRcTimer: %s\n",
+          alarm_is_scheduled(peer.AvOpenOnRcTimer()) ? "Scheduled"
+                                                     : "Not scheduled");
+  dprintf(fd, "    BTA Handle: %d\n", peer.BtaHandle());
+  dprintf(fd, "    Peer ID: %d\n", peer.PeerId());
+  dprintf(fd, "    EDR: %s\n", peer.IsEdr() ? "true" : "false");
+  dprintf(fd, "    Support 3Mbps: %s\n", peer.Is3Mbps() ? "true" : "false");
+  dprintf(fd, "    Self Initiated Connection: %s\n",
+          peer.SelfInitiatedConnection() ? "true" : "false");
+}
+
+static void btif_debug_av_source_dump(int fd) {
+  bool enabled = btif_av_source.Enabled();
+
+  dprintf(fd, "\nA2DP Source State: %s\n", (enabled) ? "Enabled" : "Disabled");
+  if (!enabled) return;
+  dprintf(fd, "  Active peer: %s\n",
+          btif_av_source.ActivePeer().ToString().c_str());
+  for (auto it : btif_av_source.Peers()) {
+    const BtifAvPeer* peer = it.second;
+    btif_debug_av_peer_dump(fd, *peer);
+  }
+}
+
+static void btif_debug_av_sink_dump(int fd) {
+  bool enabled = btif_av_sink.Enabled();
+
+  dprintf(fd, "\nA2DP Sink State: %s\n", (enabled) ? "Enabled" : "Disabled");
+  if (!enabled) return;
+  dprintf(fd, "  Active peer: %s\n",
+          btif_av_sink.ActivePeer().ToString().c_str());
+  dprintf(fd, "  Peers:\n");
+  for (auto it : btif_av_sink.Peers()) {
+    const BtifAvPeer* peer = it.second;
+    btif_debug_av_peer_dump(fd, *peer);
+  }
+}
+
+void btif_debug_av_dump(int fd) {
+  btif_debug_av_source_dump(fd);
+  btif_debug_av_sink_dump(fd);
 }
diff --git a/btif/src/btif_dm.cc b/btif/src/btif_dm.cc
index 48d89c9..4783163 100644
--- a/btif/src/btif_dm.cc
+++ b/btif/src/btif_dm.cc
@@ -49,6 +49,7 @@
 #include "bta_closure_api.h"
 #include "bta_gatt_api.h"
 #include "btif_api.h"
+#include "btif_av.h"
 #include "btif_config.h"
 #include "btif_dm.h"
 #include "btif_hd.h"
@@ -250,7 +251,6 @@
 extern bt_status_t btif_hf_client_execute_service(bool b_enable);
 extern bt_status_t btif_sdp_execute_service(bool b_enable);
 extern int btif_hh_connect(const RawAddress* bd_addr);
-extern void btif_av_move_idle(RawAddress bd_addr);
 extern bt_status_t btif_hd_execute_service(bool b_enable);
 
 /******************************************************************************
@@ -304,7 +304,7 @@
       bluetooth::headset::ExecuteService(b_enable);
     } break;
     case BTA_A2DP_SOURCE_SERVICE_ID: {
-      btif_av_execute_service(b_enable);
+      btif_av_source_execute_service(b_enable);
     } break;
     case BTA_A2DP_SINK_SERVICE_ID: {
       btif_av_sink_execute_service(b_enable);
@@ -1683,7 +1683,7 @@
     case BTA_DM_LINK_DOWN_EVT:
       bd_addr = p_data->link_down.bd_addr;
       btm_set_bond_type_dev(p_data->link_down.bd_addr, BOND_TYPE_UNKNOWN);
-      btif_av_move_idle(bd_addr);
+      btif_av_acl_disconnected(bd_addr);
       BTIF_TRACE_DEBUG(
           "BTA_DM_LINK_DOWN_EVT. Sending BT_ACL_STATE_DISCONNECTED");
       HAL_CBACK(bt_hal_cbacks, acl_state_changed_cb, BT_STATUS_SUCCESS,
@@ -1891,7 +1891,7 @@
     case BTA_DM_ROLE_CHG_EVT:
 
     default:
-      BTIF_TRACE_WARNING("btif_dm_cback : unhandled event (%d)", event);
+      BTIF_TRACE_WARNING("%s: unhandled event (%d)", __func__, event);
       break;
   }
 
diff --git a/btif/src/btif_rc.cc b/btif/src/btif_rc.cc
index e26684b..6ac38fe 100644
--- a/btif/src/btif_rc.cc
+++ b/btif/src/btif_rc.cc
@@ -42,12 +42,14 @@
 #include "bta_av_api.h"
 #include "btif_av.h"
 #include "btif_common.h"
+#include "btif_rc.h"
 #include "btif_util.h"
 #include "btu.h"
 #include "device/include/interop.h"
 #include "osi/include/list.h"
 #include "osi/include/osi.h"
 #include "osi/include/properties.h"
+
 #define RC_INVALID_TRACK_ID (0xFFFFFFFFFFFFFFFFULL)
 
 /*****************************************************************************
@@ -489,13 +491,21 @@
   CHECK(bt_rc_callbacks);
 
   btrc_remote_features_t rc_features = BTRC_FEAT_NONE;
-  RawAddress avdtp_addr = btif_av_get_addr();
+  RawAddress avdtp_source_active_peer_addr = btif_av_source_active_peer();
+  RawAddress avdtp_sink_active_peer_addr = btif_av_sink_active_peer();
 
-  BTIF_TRACE_DEBUG("%s: AVDTP Address: %s AVCTP address: %s", __func__,
-                   avdtp_addr.ToString().c_str(), rc_addr.ToString().c_str());
+  BTIF_TRACE_DEBUG(
+      "%s: AVDTP Source Active Peer Address: %s "
+      "AVDTP Sink Active Peer Address: %s "
+      "AVCTP address: %s",
+      __func__, avdtp_source_active_peer_addr.ToString().c_str(),
+      avdtp_sink_active_peer_addr.ToString().c_str(),
+      rc_addr.ToString().c_str());
 
   if (interop_match_addr(INTEROP_DISABLE_ABSOLUTE_VOLUME, &rc_addr) ||
-      absolute_volume_disabled() || avdtp_addr != rc_addr) {
+      absolute_volume_disabled() ||
+      (avdtp_source_active_peer_addr != rc_addr &&
+       avdtp_sink_active_peer_addr != rc_addr)) {
     p_dev->rc_features &= ~BTA_AV_FEAT_ADV_CTRL;
   }
 
@@ -1072,18 +1082,11 @@
   }
 }
 
-/***************************************************************************
- **
- ** Function       btif_rc_get_connected_peer
- **
- ** Description    Fetches the connected headset's address if any
- **
- ***************************************************************************/
-bool btif_rc_get_connected_peer(RawAddress* peer_addr) {
+bool btif_rc_is_connected_peer(const RawAddress& peer_addr) {
   for (int idx = 0; idx < BTIF_RC_NUM_CONN; idx++) {
     btif_rc_device_cb_t* p_dev = get_connected_device(idx);
-    if (p_dev != NULL && (p_dev->rc_connected == TRUE)) {
-      *peer_addr = p_dev->rc_addr;
+    if (p_dev != NULL && (p_dev->rc_connected == TRUE) &&
+        peer_addr == p_dev->rc_addr) {
       return true;
     }
   }
diff --git a/btif/src/btif_sm.cc b/btif/src/btif_sm.cc
deleted file mode 100644
index e5a6889..0000000
--- a/btif/src/btif_sm.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2009-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.
- *
- ******************************************************************************/
-
-/*****************************************************************************
- *
- *  Filename:      btif_sm.c
- *
- *  Description:   Generic BTIF state machine API
- *
- *****************************************************************************/
-
-#define LOG_TAG "bt_btif"
-
-#include "btif_sm.h"
-
-#include "bt_common.h"
-#include "btif_common.h"
-#include "osi/include/allocator.h"
-
-/*****************************************************************************
- *  Local type definitions
- *****************************************************************************/
-typedef struct {
-  btif_sm_state_t state;
-  btif_sm_handler_t* p_handlers;
-} btif_sm_cb_t;
-
-/*****************************************************************************
- *  Functions
- *****************************************************************************/
-
-/*****************************************************************************
- *
- * Function     btif_sm_init
- *
- * Description  Initializes the state machine with the state handlers
- *              The caller should ensure that the table and the corresponding
- *              states match. The location that 'p_handlers' points to shall
- *              be available until the btif_sm_shutdown API is invoked.
- *
- * Returns      Returns a pointer to the initialized state machine handle.
- *
- *****************************************************************************/
-
-btif_sm_handle_t btif_sm_init(const btif_sm_handler_t* p_handlers,
-                              btif_sm_state_t initial_state) {
-  if (p_handlers == NULL) {
-    BTIF_TRACE_ERROR("%s : p_handlers is NULL", __func__);
-    return NULL;
-  }
-
-  btif_sm_cb_t* p_cb = (btif_sm_cb_t*)osi_malloc(sizeof(btif_sm_cb_t));
-  p_cb->state = initial_state;
-  p_cb->p_handlers = (btif_sm_handler_t*)p_handlers;
-
-  /* Send BTIF_SM_ENTER_EVT to the initial state */
-  p_cb->p_handlers[initial_state](BTIF_SM_ENTER_EVT, NULL);
-
-  return (btif_sm_handle_t)p_cb;
-}
-
-/*****************************************************************************
- *
- * Function     btif_sm_shutdown
- *
- * Description  Tears down the state machine
- *
- * Returns      None
- *
- *****************************************************************************/
-void btif_sm_shutdown(btif_sm_handle_t handle) {
-  btif_sm_cb_t* p_cb = (btif_sm_cb_t*)handle;
-
-  if (p_cb == NULL) {
-    BTIF_TRACE_ERROR("%s : Invalid handle", __func__);
-    return;
-  }
-  osi_free(p_cb);
-}
-
-/*****************************************************************************
- *
- * Function     btif_sm_get_state
- *
- * Description  Fetches the current state of the state machine
- *
- * Returns      Current state
- *
- *****************************************************************************/
-btif_sm_state_t btif_sm_get_state(btif_sm_handle_t handle) {
-  btif_sm_cb_t* p_cb = (btif_sm_cb_t*)handle;
-
-  if (p_cb == NULL) {
-    BTIF_TRACE_ERROR("%s : Invalid handle", __func__);
-    return 0;
-  }
-
-  return p_cb->state;
-}
-
-/*****************************************************************************
- *
- * Function     btif_sm_dispatch
- *
- * Description  Dispatches the 'event' along with 'data' to the current state
- *              handler
- *
- * Returns      BT_STATUS_SUCCESS on success
- *              BT_STATUS_UNHANDLED if event was not processed
- *              BT_STATUS_FAIL otherwise
- *
- *****************************************************************************/
-bt_status_t btif_sm_dispatch(btif_sm_handle_t handle, btif_sm_event_t event,
-                             void* data) {
-  bt_status_t status = BT_STATUS_SUCCESS;
-
-  btif_sm_cb_t* p_cb = (btif_sm_cb_t*)handle;
-
-  if (p_cb == NULL) {
-    BTIF_TRACE_ERROR("%s : Invalid handle", __func__);
-    return BT_STATUS_FAIL;
-  }
-
-  if (!p_cb->p_handlers[p_cb->state](event, data)) return BT_STATUS_UNHANDLED;
-
-  return status;
-}
-
-/*****************************************************************************
- *
- * Function     btif_sm_change_state
- *
- * Description  Make a transition to the new 'state'. The 'BTIF_SM_EXIT_EVT'
- *              shall be invoked before exiting the current state. The
- *              'BTIF_SM_ENTER_EVT' shall be invoked before entering the new
- *              state
- *
- * Returns      BT_STATUS_SUCCESS on success
- *              BT_STATUS_UNHANDLED if event was not processed
- *              BT_STATUS_FAIL otherwise
- *
- *****************************************************************************/
-bt_status_t btif_sm_change_state(btif_sm_handle_t handle,
-                                 btif_sm_state_t state) {
-  bt_status_t status = BT_STATUS_SUCCESS;
-  btif_sm_cb_t* p_cb = (btif_sm_cb_t*)handle;
-
-  if (p_cb == NULL) {
-    BTIF_TRACE_ERROR("%s : Invalid handle", __func__);
-    return BT_STATUS_FAIL;
-  }
-
-  /* Send exit event to the current state */
-  if (!p_cb->p_handlers[p_cb->state](BTIF_SM_EXIT_EVT, NULL))
-    status = BT_STATUS_UNHANDLED;
-
-  /* Change to the new state */
-  p_cb->state = state;
-
-  /* Send enter event to the new state */
-  if (!p_cb->p_handlers[p_cb->state](BTIF_SM_ENTER_EVT, NULL))
-    status = BT_STATUS_UNHANDLED;
-
-  return status;
-}
diff --git a/btif/test/btif_state_machine_test.cc b/btif/test/btif_state_machine_test.cc
new file mode 100644
index 0000000..d413a4a
--- /dev/null
+++ b/btif/test/btif_state_machine_test.cc
@@ -0,0 +1,261 @@
+/******************************************************************************
+ *
+ *  Copyright 2017 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gtest/gtest.h>
+
+#include "btif/include/btif_state_machine.h"
+
+namespace {
+static constexpr uint32_t kInvalidEvent = 0xffffffff;
+static constexpr uint32_t kEventZero = 0;
+static constexpr uint32_t kEventOne = 1;
+static constexpr uint32_t kEventTwo = 2;
+
+static char dataZero = 0;
+static char dataOne = 1;
+static char dataTwo = 2;
+}  // namespace
+
+class BtifStateMachineImpl : public BtifStateMachine {
+ public:
+  enum {
+    kStateZero,
+    kStateOne,
+    kStateTwo,
+  };
+
+  class StateZero : public State {
+   public:
+    StateZero(BtifStateMachine& sm)
+        : State(sm, kStateZero),
+          on_enter_(false),
+          on_exit_(false),
+          event_(kInvalidEvent),
+          data_(nullptr) {}
+    void OnEnter() override {
+      on_enter_ = true;
+      on_exit_ = false;
+    }
+    void OnExit() override {
+      on_exit_ = true;
+      on_enter_ = false;
+    }
+    bool ProcessEvent(uint32_t event, void* p_data) override {
+      event_ = event;
+      data_ = p_data;
+      TransitionTo(kStateOne);
+      return true;
+    }
+
+    bool on_enter_;
+    bool on_exit_;
+    uint32_t event_;
+    void* data_;
+  };
+
+  class StateOne : public State {
+   public:
+    StateOne(BtifStateMachine& sm)
+        : State(sm, kStateOne),
+          on_enter_(false),
+          on_exit_(false),
+          event_(kInvalidEvent),
+          data_(nullptr) {}
+    void OnEnter() override {
+      on_enter_ = true;
+      on_exit_ = false;
+    }
+    void OnExit() override {
+      on_exit_ = true;
+      on_enter_ = false;
+    }
+    bool ProcessEvent(uint32_t event, void* p_data) override {
+      event_ = event;
+      data_ = p_data;
+      TransitionTo(kStateTwo);
+      return true;
+    }
+
+    bool on_enter_;
+    bool on_exit_;
+    uint32_t event_;
+    void* data_;
+  };
+
+  class StateTwo : public State {
+   public:
+    StateTwo(BtifStateMachine& sm)
+        : State(sm, kStateTwo),
+          on_enter_(false),
+          on_exit_(false),
+          event_(kInvalidEvent),
+          data_(nullptr) {}
+    void OnEnter() override {
+      on_enter_ = true;
+      on_exit_ = false;
+    }
+    void OnExit() override {
+      on_exit_ = true;
+      on_enter_ = false;
+    }
+    bool ProcessEvent(uint32_t event, void* p_data) override {
+      event_ = event;
+      data_ = p_data;
+      TransitionTo(kStateZero);
+      return true;
+    }
+
+    bool on_enter_;
+    bool on_exit_;
+    uint32_t event_;
+    void* data_;
+  };
+
+  BtifStateMachineImpl() {
+    state_zero_ = new StateZero(*this);
+    state_one_ = new StateOne(*this);
+    state_two_ = new StateTwo(*this);
+
+    AddState(state_zero_);
+    AddState(state_one_);
+    AddState(state_two_);
+    SetInitialState(state_zero_);
+  }
+
+  StateZero* state_zero_;
+  StateOne* state_one_;
+  StateTwo* state_two_;
+};
+
+class BtifStateMachineTest : public ::testing::Test {
+ protected:
+  BtifStateMachineTest() {}
+
+  void SetUp() override { sm_.Start(); }
+
+  void TearDown() override { sm_.Quit(); }
+
+  BtifStateMachineImpl sm_;
+};
+
+TEST_F(BtifStateMachineTest, test_initial_state) {
+  ASSERT_EQ(sm_.kStateZero, sm_.StateId());
+  ASSERT_EQ(sm_.kStateInvalid, sm_.PreviousStateId());
+}
+
+TEST_F(BtifStateMachineTest, test_invalid_state) {
+  sm_.Quit();
+  ASSERT_EQ(sm_.kStateInvalid, sm_.StateId());
+  ASSERT_EQ(sm_.kStateInvalid, sm_.PreviousStateId());
+  sm_.Start();
+  ASSERT_EQ(sm_.kStateZero, sm_.StateId());
+  ASSERT_EQ(sm_.kStateInvalid, sm_.PreviousStateId());
+}
+
+TEST_F(BtifStateMachineTest, test_transition_to) {
+  // Initial state: StateZero
+  ASSERT_EQ(sm_.kStateZero, sm_.StateId());
+  ASSERT_EQ(sm_.kStateInvalid, sm_.PreviousStateId());
+  ASSERT_TRUE(sm_.state_zero_->on_enter_);
+  ASSERT_FALSE(sm_.state_zero_->on_exit_);
+
+  // Transition to StateOne
+  ASSERT_FALSE(sm_.state_one_->on_enter_);
+  ASSERT_FALSE(sm_.state_one_->on_exit_);
+  sm_.TransitionTo(sm_.kStateOne);
+  ASSERT_EQ(sm_.kStateOne, sm_.StateId());
+  ASSERT_EQ(sm_.kStateZero, sm_.PreviousStateId());
+  ASSERT_TRUE(sm_.state_zero_->on_exit_);
+  ASSERT_TRUE(sm_.state_one_->on_enter_);
+  ASSERT_FALSE(sm_.state_one_->on_exit_);
+
+  // Transition to StateTwo
+  ASSERT_FALSE(sm_.state_two_->on_enter_);
+  ASSERT_FALSE(sm_.state_two_->on_exit_);
+  sm_.TransitionTo(sm_.kStateTwo);
+  ASSERT_EQ(sm_.kStateTwo, sm_.StateId());
+  ASSERT_EQ(sm_.kStateOne, sm_.PreviousStateId());
+  ASSERT_TRUE(sm_.state_one_->on_exit_);
+  ASSERT_TRUE(sm_.state_two_->on_enter_);
+  ASSERT_FALSE(sm_.state_two_->on_exit_);
+}
+
+TEST_F(BtifStateMachineTest, test_process_event) {
+  // Initial state: StateZero
+  ASSERT_EQ(sm_.kStateZero, sm_.StateId());
+  ASSERT_EQ(sm_.kStateInvalid, sm_.PreviousStateId());
+  ASSERT_TRUE(sm_.state_zero_->on_enter_);
+  ASSERT_FALSE(sm_.state_zero_->on_exit_);
+  ASSERT_EQ(sm_.state_zero_->event_, kInvalidEvent);
+  ASSERT_EQ(sm_.state_zero_->data_, nullptr);
+
+  // Process an event and transition to StateOne
+  ASSERT_FALSE(sm_.state_one_->on_enter_);
+  ASSERT_FALSE(sm_.state_one_->on_exit_);
+  ASSERT_EQ(sm_.state_one_->event_, kInvalidEvent);
+  ASSERT_EQ(sm_.state_one_->data_, nullptr);
+  ASSERT_TRUE(sm_.ProcessEvent(kEventZero, &dataZero));
+  ASSERT_EQ(sm_.kStateOne, sm_.StateId());
+  ASSERT_EQ(sm_.kStateZero, sm_.PreviousStateId());
+  // Check StateZero
+  ASSERT_EQ(sm_.state_zero_->event_, kEventZero);
+  ASSERT_EQ(sm_.state_zero_->data_, &dataZero);
+  ASSERT_TRUE(sm_.state_zero_->on_exit_);
+  // Check StateOne
+  ASSERT_TRUE(sm_.state_one_->on_enter_);
+  ASSERT_FALSE(sm_.state_one_->on_exit_);
+  ASSERT_EQ(sm_.state_one_->event_, kInvalidEvent);
+  ASSERT_EQ(sm_.state_one_->data_, nullptr);
+
+  // Process an event and transition to StateTwo
+  ASSERT_FALSE(sm_.state_two_->on_enter_);
+  ASSERT_FALSE(sm_.state_two_->on_exit_);
+  ASSERT_EQ(sm_.state_two_->event_, kInvalidEvent);
+  ASSERT_EQ(sm_.state_two_->data_, nullptr);
+  ASSERT_TRUE(sm_.ProcessEvent(kEventOne, &dataOne));
+  ASSERT_EQ(sm_.kStateTwo, sm_.StateId());
+  ASSERT_EQ(sm_.kStateOne, sm_.PreviousStateId());
+  // Check StateOne
+  ASSERT_EQ(sm_.state_one_->event_, kEventOne);
+  ASSERT_EQ(sm_.state_one_->data_, &dataOne);
+  ASSERT_TRUE(sm_.state_one_->on_exit_);
+  // Check StateTwo
+  ASSERT_TRUE(sm_.state_two_->on_enter_);
+  ASSERT_FALSE(sm_.state_two_->on_exit_);
+  ASSERT_EQ(sm_.state_two_->event_, kInvalidEvent);
+  ASSERT_EQ(sm_.state_two_->data_, nullptr);
+
+  // Process an event and transition to StateZero
+  // NOTE: StateZero was exited before and has local state
+  ASSERT_FALSE(sm_.state_zero_->on_enter_);
+  ASSERT_TRUE(sm_.state_zero_->on_exit_);  // NOTE: already exited before
+  ASSERT_EQ(sm_.state_zero_->event_, kEventZero);  // NOTE: state from before
+  ASSERT_EQ(sm_.state_zero_->data_, &dataZero);    // NOTE: state from before
+  ASSERT_TRUE(sm_.ProcessEvent(kEventTwo, &dataTwo));
+  ASSERT_EQ(sm_.kStateZero, sm_.StateId());
+  ASSERT_EQ(sm_.kStateTwo, sm_.PreviousStateId());
+  // Check StateTwo
+  ASSERT_EQ(sm_.state_two_->event_, kEventTwo);
+  ASSERT_EQ(sm_.state_two_->data_, &dataTwo);
+  ASSERT_TRUE(sm_.state_two_->on_exit_);
+  // Check StateZero
+  ASSERT_TRUE(sm_.state_zero_->on_enter_);
+  ASSERT_FALSE(sm_.state_zero_->on_exit_);
+  ASSERT_EQ(sm_.state_zero_->event_, kEventZero);  // NOTE: state from before
+  ASSERT_EQ(sm_.state_zero_->data_, &dataZero);    // NOTE: state from before
+}
diff --git a/include/hardware/bt_av.h b/include/hardware/bt_av.h
index 873c36a..8c9d1f9 100644
--- a/include/hardware/bt_av.h
+++ b/include/hardware/bt_av.h
@@ -126,25 +126,120 @@
   int64_t codec_specific_2;  // Codec-specific value 2
   int64_t codec_specific_3;  // Codec-specific value 3
   int64_t codec_specific_4;  // Codec-specific value 4
+
+  std::string ToString() const {
+    std::string codec_name_str;
+
+    switch (codec_type) {
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
+        codec_name_str = "SBC";
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+        codec_name_str = "AAC";
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
+        codec_name_str = "aptX";
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+        codec_name_str = "aptX HD";
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC:
+        codec_name_str = "LDAC";
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
+        codec_name_str = "SBC (Sink)";
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SINK_AAC:
+        codec_name_str = "AAC (Sink)";
+        break;
+      case BTAV_A2DP_CODEC_INDEX_MAX:
+        codec_name_str = "Unknown(CODEC_INDEX_MAX)";
+        break;
+    }
+
+    std::string sample_rate_str;
+    AppendCapability(sample_rate_str,
+                     (sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE), "NONE");
+    AppendCapability(sample_rate_str,
+                     (sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_44100),
+                     "44100");
+    AppendCapability(sample_rate_str,
+                     (sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_48000),
+                     "48000");
+    AppendCapability(sample_rate_str,
+                     (sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_88200),
+                     "88200");
+    AppendCapability(sample_rate_str,
+                     (sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_96000),
+                     "96000");
+    AppendCapability(sample_rate_str,
+                     (sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_176400),
+                     "176400");
+    AppendCapability(sample_rate_str,
+                     (sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_192000),
+                     "192000");
+
+    std::string bits_per_sample_str;
+    AppendCapability(bits_per_sample_str,
+                     (bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE),
+                     "NONE");
+    AppendCapability(bits_per_sample_str,
+                     (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16),
+                     "16");
+    AppendCapability(bits_per_sample_str,
+                     (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24),
+                     "24");
+    AppendCapability(bits_per_sample_str,
+                     (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32),
+                     "32");
+
+    std::string channel_mode_str;
+    AppendCapability(channel_mode_str,
+                     (channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE),
+                     "NONE");
+    AppendCapability(channel_mode_str,
+                     (channel_mode & BTAV_A2DP_CODEC_CHANNEL_MODE_MONO),
+                     "MONO");
+    AppendCapability(channel_mode_str,
+                     (channel_mode & BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO),
+                     "STEREO");
+
+    return "codec: " + codec_name_str + " sample_rate: " + sample_rate_str +
+           " bits_per_sample: " + bits_per_sample_str +
+           " channel_mode: " + channel_mode_str +
+           " codec_specific_1: " + std::to_string(codec_specific_1) +
+           " codec_specific_2: " + std::to_string(codec_specific_2) +
+           " codec_specific_3: " + std::to_string(codec_specific_3) +
+           " codec_specific_4: " + std::to_string(codec_specific_4);
+  }
+
+ private:
+  static std::string AppendCapability(std::string& result, bool append,
+                                      const std::string& name) {
+    if (!append) return result;
+    if (!result.empty()) result += "|";
+    result += name;
+    return result;
+  }
 } btav_a2dp_codec_config_t;
 
 /** Callback for connection state change.
  *  state will have one of the values from btav_connection_state_t
  */
-typedef void (*btav_connection_state_callback)(RawAddress* bd_addr,
+typedef void (*btav_connection_state_callback)(const RawAddress& bd_addr,
                                                btav_connection_state_t state);
 
 /** Callback for audiopath state change.
  *  state will have one of the values from btav_audio_state_t
  */
-typedef void (*btav_audio_state_callback)(RawAddress* bd_addr,
+typedef void (*btav_audio_state_callback)(const RawAddress& bd_addr,
                                           btav_audio_state_t state);
 
 /** Callback for audio configuration change.
  *  Used only for the A2DP Source interface.
  */
 typedef void (*btav_audio_source_config_callback)(
-    RawAddress* bd_addr, btav_a2dp_codec_config_t codec_config,
+    const RawAddress& bd_addr, btav_a2dp_codec_config_t codec_config,
     std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
     std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities);
 
@@ -153,7 +248,7 @@
  *  sample_rate: sample rate in Hz
  *  channel_count: number of channels (1 for mono, 2 for stereo)
  */
-typedef void (*btav_audio_sink_config_callback)(RawAddress* bd_addr,
+typedef void (*btav_audio_sink_config_callback)(const RawAddress& bd_addr,
                                                 uint32_t sample_rate,
                                                 uint8_t channel_count);
 
@@ -198,13 +293,17 @@
                       std::vector<btav_a2dp_codec_config_t> codec_priorities);
 
   /** connect to headset */
-  bt_status_t (*connect)(RawAddress* bd_addr);
+  bt_status_t (*connect)(const RawAddress& bd_addr);
 
   /** dis-connect from headset */
-  bt_status_t (*disconnect)(RawAddress* bd_addr);
+  bt_status_t (*disconnect)(const RawAddress& bd_addr);
+
+  /** sets the connected device as active */
+  bt_status_t (*set_active_device)(const RawAddress& bd_addr);
 
   /** configure the codecs settings preferences */
   bt_status_t (*config_codec)(
+      const RawAddress& bd_addr,
       std::vector<btav_a2dp_codec_config_t> codec_preferences);
 
   /** Closes the interface. */
@@ -223,10 +322,10 @@
   bt_status_t (*init)(btav_sink_callbacks_t* callbacks);
 
   /** connect to headset */
-  bt_status_t (*connect)(RawAddress* bd_addr);
+  bt_status_t (*connect)(const RawAddress& bd_addr);
 
   /** dis-connect from headset */
-  bt_status_t (*disconnect)(RawAddress* bd_addr);
+  bt_status_t (*disconnect)(const RawAddress& bd_addr);
 
   /** Closes the interface. */
   void (*cleanup)(void);
diff --git a/include/hardware/bt_rc.h b/include/hardware/bt_rc.h
index a965a94..a79156c 100644
--- a/include/hardware/bt_rc.h
+++ b/include/hardware/bt_rc.h
@@ -20,7 +20,7 @@
 __BEGIN_DECLS
 
 /* Change this macro to use multiple RC */
-#define BT_RC_NUM_APP 1
+#define BT_RC_NUM_APP 12
 
 /* Macros */
 #define BTRC_MAX_ATTR_STR_LEN (1 << 16)
diff --git a/internal_include/bt_target.h b/internal_include/bt_target.h
index 1b12e37..e81898b 100644
--- a/internal_include/bt_target.h
+++ b/internal_include/bt_target.h
@@ -480,14 +480,14 @@
 
 /* The maximum number of simultaneous links that L2CAP can support. */
 #ifndef MAX_ACL_CONNECTIONS
-#define MAX_L2CAP_LINKS 7
+#define MAX_L2CAP_LINKS 13
 #else
 #define MAX_L2CAP_LINKS MAX_ACL_CONNECTIONS
 #endif
 
 /* The maximum number of simultaneous channels that L2CAP can support. */
 #ifndef MAX_L2CAP_CHANNELS
-#define MAX_L2CAP_CHANNELS 16
+#define MAX_L2CAP_CHANNELS 32
 #endif
 
 /* The maximum number of simultaneous applications that can register with L2CAP.
@@ -995,7 +995,7 @@
 
 /* Number of simultaneous links to different peer devices. */
 #ifndef AVDT_NUM_LINKS
-#define AVDT_NUM_LINKS 2
+#define AVDT_NUM_LINKS 6
 #endif
 
 /* Number of simultaneous stream endpoints. */
@@ -1005,7 +1005,7 @@
 
 /* Number of transport channels setup by AVDT for all media streams */
 #ifndef AVDT_NUM_TC_TBL
-#define AVDT_NUM_TC_TBL 6
+#define AVDT_NUM_TC_TBL (AVDT_NUM_SEPS + AVDT_NUM_LINKS)
 #endif
 
 /* Maximum size in bytes of the content protection information element. */
@@ -1203,12 +1203,12 @@
 
 /* Number of simultaneous ACL links to different peer devices. */
 #ifndef AVCT_NUM_LINKS
-#define AVCT_NUM_LINKS 2
+#define AVCT_NUM_LINKS 6
 #endif
 
 /* Number of simultaneous AVCTP connections. */
 #ifndef AVCT_NUM_CONN
-#define AVCT_NUM_CONN 3
+#define AVCT_NUM_CONN 14  // 2 * MaxDevices + 2
 #endif
 
 /******************************************************************************
diff --git a/internal_include/bt_trace.h b/internal_include/bt_trace.h
index 640ed0a..a0c01f7 100644
--- a/internal_include/bt_trace.h
+++ b/internal_include/bt_trace.h
@@ -478,27 +478,27 @@
 /* AVDTP */
 #define AVDT_TRACE_ERROR(...)                                     \
   {                                                               \
-    if (avdt_cb.trace_level >= BT_TRACE_LEVEL_ERROR)              \
+    if (avdtp_cb.TraceLevel() >= BT_TRACE_LEVEL_ERROR)            \
       BT_TRACE(TRACE_LAYER_AVP, TRACE_TYPE_ERROR, ##__VA_ARGS__); \
   }
 #define AVDT_TRACE_WARNING(...)                                     \
   {                                                                 \
-    if (avdt_cb.trace_level >= BT_TRACE_LEVEL_WARNING)              \
+    if (avdtp_cb.TraceLevel() >= BT_TRACE_LEVEL_WARNING)            \
       BT_TRACE(TRACE_LAYER_AVP, TRACE_TYPE_WARNING, ##__VA_ARGS__); \
   }
 #define AVDT_TRACE_EVENT(...)                                     \
   {                                                               \
-    if (avdt_cb.trace_level >= BT_TRACE_LEVEL_EVENT)              \
+    if (avdtp_cb.TraceLevel() >= BT_TRACE_LEVEL_EVENT)            \
       BT_TRACE(TRACE_LAYER_AVP, TRACE_TYPE_EVENT, ##__VA_ARGS__); \
   }
 #define AVDT_TRACE_DEBUG(...)                                     \
   {                                                               \
-    if (avdt_cb.trace_level >= BT_TRACE_LEVEL_DEBUG)              \
+    if (avdtp_cb.TraceLevel() >= BT_TRACE_LEVEL_DEBUG)            \
       BT_TRACE(TRACE_LAYER_AVP, TRACE_TYPE_DEBUG, ##__VA_ARGS__); \
   }
 #define AVDT_TRACE_API(...)                                     \
   {                                                             \
-    if (avdt_cb.trace_level >= BT_TRACE_LEVEL_API)              \
+    if (avdtp_cb.TraceLevel() >= BT_TRACE_LEVEL_API)            \
       BT_TRACE(TRACE_LAYER_AVP, TRACE_TYPE_API, ##__VA_ARGS__); \
   }
 
diff --git a/main/Android.bp b/main/Android.bp
index 06b2086..3661458 100644
--- a/main/Android.bp
+++ b/main/Android.bp
@@ -91,6 +91,8 @@
     defaults: ["fluoride_defaults"],
 
     srcs: [
+        "bte_conf.cc",
+        "bte_init.cc",
         "bte_init_cpp_logging.cc",
         "bte_logmsg.cc",
         "bte_main.cc",
diff --git a/stack/a2dp/a2dp_aac.cc b/stack/a2dp/a2dp_aac.cc
index 9bb67ea..93307c8 100644
--- a/stack/a2dp/a2dp_aac.cc
+++ b/stack/a2dp/a2dp_aac.cc
@@ -680,7 +680,7 @@
 
 const char* A2DP_CodecIndexStrAacSink(void) { return "AAC SINK"; }
 
-bool A2DP_InitCodecConfigAac(tAVDT_CFG* p_cfg) {
+bool A2DP_InitCodecConfigAac(AvdtpSepConfig* p_cfg) {
   if (A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aac_caps,
                         p_cfg->codec_info) != A2DP_SUCCESS) {
     return false;
@@ -697,7 +697,7 @@
   return true;
 }
 
-bool A2DP_InitCodecConfigAacSink(tAVDT_CFG* p_cfg) {
+bool A2DP_InitCodecConfigAacSink(AvdtpSepConfig* p_cfg) {
   return A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aac_sink_caps,
                            p_cfg->codec_info) == A2DP_SUCCESS;
 }
diff --git a/stack/a2dp/a2dp_codec_config.cc b/stack/a2dp/a2dp_codec_config.cc
index b01c623..08dc5ca 100644
--- a/stack/a2dp/a2dp_codec_config.cc
+++ b/stack/a2dp/a2dp_codec_config.cc
@@ -1236,7 +1236,7 @@
 }
 
 bool A2DP_InitCodecConfig(btav_a2dp_codec_index_t codec_index,
-                          tAVDT_CFG* p_cfg) {
+                          AvdtpSepConfig* p_cfg) {
   LOG_VERBOSE(LOG_TAG, "%s: codec %s", __func__,
               A2DP_CodecIndexStr(codec_index));
 
diff --git a/stack/a2dp/a2dp_sbc.cc b/stack/a2dp/a2dp_sbc.cc
index 38e1b3b..400d6d9 100644
--- a/stack/a2dp/a2dp_sbc.cc
+++ b/stack/a2dp/a2dp_sbc.cc
@@ -857,7 +857,7 @@
 
 const char* A2DP_CodecIndexStrSbcSink(void) { return "SBC SINK"; }
 
-bool A2DP_InitCodecConfigSbc(tAVDT_CFG* p_cfg) {
+bool A2DP_InitCodecConfigSbc(AvdtpSepConfig* p_cfg) {
   if (A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &a2dp_sbc_caps,
                         p_cfg->codec_info) != A2DP_SUCCESS) {
     return false;
@@ -874,7 +874,7 @@
   return true;
 }
 
-bool A2DP_InitCodecConfigSbcSink(tAVDT_CFG* p_cfg) {
+bool A2DP_InitCodecConfigSbcSink(AvdtpSepConfig* p_cfg) {
   if (A2DP_BuildInfoSbc(AVDT_MEDIA_TYPE_AUDIO, &a2dp_sbc_sink_caps,
                         p_cfg->codec_info) != A2DP_SUCCESS) {
     return false;
diff --git a/stack/a2dp/a2dp_vendor.cc b/stack/a2dp/a2dp_vendor.cc
index 2b874e3..57b7a25 100644
--- a/stack/a2dp/a2dp_vendor.cc
+++ b/stack/a2dp/a2dp_vendor.cc
@@ -518,7 +518,7 @@
 }
 
 bool A2DP_VendorInitCodecConfig(btav_a2dp_codec_index_t codec_index,
-                                tAVDT_CFG* p_cfg) {
+                                AvdtpSepConfig* p_cfg) {
   // Add checks based on codec_index
   switch (codec_index) {
     case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
diff --git a/stack/a2dp/a2dp_vendor_aptx.cc b/stack/a2dp/a2dp_vendor_aptx.cc
index b22efab..cf136e3 100644
--- a/stack/a2dp/a2dp_vendor_aptx.cc
+++ b/stack/a2dp/a2dp_vendor_aptx.cc
@@ -387,7 +387,7 @@
 
 const char* A2DP_VendorCodecIndexStrAptx(void) { return "aptX"; }
 
-bool A2DP_VendorInitCodecConfigAptx(tAVDT_CFG* p_cfg) {
+bool A2DP_VendorInitCodecConfigAptx(AvdtpSepConfig* p_cfg) {
   if (A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_caps,
                          p_cfg->codec_info) != A2DP_SUCCESS) {
     return false;
diff --git a/stack/a2dp/a2dp_vendor_aptx_hd.cc b/stack/a2dp/a2dp_vendor_aptx_hd.cc
index 3291947..907712c 100644
--- a/stack/a2dp/a2dp_vendor_aptx_hd.cc
+++ b/stack/a2dp/a2dp_vendor_aptx_hd.cc
@@ -404,7 +404,7 @@
 
 const char* A2DP_VendorCodecIndexStrAptxHd(void) { return "aptX-HD"; }
 
-bool A2DP_VendorInitCodecConfigAptxHd(tAVDT_CFG* p_cfg) {
+bool A2DP_VendorInitCodecConfigAptxHd(AvdtpSepConfig* p_cfg) {
   if (A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_hd_caps,
                            p_cfg->codec_info) != A2DP_SUCCESS) {
     return false;
diff --git a/stack/a2dp/a2dp_vendor_ldac.cc b/stack/a2dp/a2dp_vendor_ldac.cc
index 6eeadb8..7f76e1f 100644
--- a/stack/a2dp/a2dp_vendor_ldac.cc
+++ b/stack/a2dp/a2dp_vendor_ldac.cc
@@ -468,7 +468,7 @@
 
 const char* A2DP_VendorCodecIndexStrLdac(void) { return "LDAC"; }
 
-bool A2DP_VendorInitCodecConfigLdac(tAVDT_CFG* p_cfg) {
+bool A2DP_VendorInitCodecConfigLdac(AvdtpSepConfig* p_cfg) {
   if (A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_ldac_caps,
                          p_cfg->codec_info) != A2DP_SUCCESS) {
     return false;
diff --git a/stack/avdt/avdt_ad.cc b/stack/avdt/avdt_ad.cc
index bedfc58..cc994b0 100644
--- a/stack/avdt/avdt_ad.cc
+++ b/stack/avdt/avdt_ad.cc
@@ -35,6 +35,24 @@
 #include "l2cdefs.h"
 #include "osi/include/osi.h"
 
+AvdtpScb* AvdtpAdaptationLayer::LookupAvdtpScb(
+    const AvdtpTransportChannel& tc) {
+  if (tc.ccb_idx >= AVDT_NUM_LINKS) {
+    AVDT_TRACE_ERROR("%s: AvdtpScb entry not found: invalid ccb_idx:%d",
+                     __func__, tc.ccb_idx);
+    return nullptr;
+  }
+  if (tc.tcid >= AVDT_NUM_RT_TBL) {
+    AVDT_TRACE_ERROR("%s: AvdtpScb entry not found: invalid tcid:%d", __func__,
+                     tc.tcid);
+    return nullptr;
+  }
+  const AvdtpRoutingEntry& re = rt_tbl[tc.ccb_idx][tc.tcid];
+  AVDT_TRACE_DEBUG("%s: ccb_idx:%d tcid:%d scb_hdl:%d", __func__, tc.ccb_idx,
+                   tc.tcid, re.scb_hdl);
+  return avdt_scb_by_hdl(re.scb_hdl);
+}
+
 /*******************************************************************************
  *
  * Function         avdt_ad_type_to_tcid
@@ -45,19 +63,19 @@
  * Returns          TCID value.
  *
  ******************************************************************************/
-uint8_t avdt_ad_type_to_tcid(uint8_t type, tAVDT_SCB* p_scb) {
-  uint8_t scb_idx;
-
+uint8_t avdt_ad_type_to_tcid(uint8_t type, AvdtpScb* p_scb) {
   if (type == AVDT_CHAN_SIG) {
     return 0;
-  } else {
-    scb_idx = avdt_scb_to_hdl(p_scb) - 1;
-    /*
-    AVDT_TRACE_DEBUG("type: %d, tcid: %d", type, ((scb_idx *
-    (AVDT_CHAN_NUM_TYPES - 1)) + type));
-    */
-    return ((scb_idx * (AVDT_CHAN_NUM_TYPES - 1)) + type);
   }
+  // The SCB Handle is unique in the [1, AVDT_NUM_LINKS * AVDT_NUM_SEPS]
+  // range. The scb_idx computed here is the SCB index for the corresponding
+  // SEP, and it is in the range [0, AVDT_NUM_SEPS) for a particular link.
+  uint8_t scb_idx = (avdt_scb_to_hdl(p_scb) - 1) % AVDT_NUM_LINKS;
+  // There are AVDT_CHAN_NUM_TYPES channel types per SEP. Here we compute
+  // the type index (TCID) from the SEP index and the type itself.
+  uint8_t tcid = (scb_idx * (AVDT_CHAN_NUM_TYPES - 1)) + type;
+  AVDT_TRACE_DEBUG("%s: type:%d, tcid: %d", __func__, type, tcid);
+  return tcid;
 }
 
 /*******************************************************************************
@@ -99,8 +117,8 @@
  ******************************************************************************/
 void avdt_ad_init(void) {
   int i;
-  tAVDT_TC_TBL* p_tbl = avdt_cb.ad.tc_tbl;
-  memset(&avdt_cb.ad, 0, sizeof(tAVDT_AD));
+  AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
+  avdtp_cb.ad.Reset();
 
   /* make sure the peer_mtu is a valid value */
   for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
@@ -121,10 +139,10 @@
  *                  first matching entry (there could be more than one).
  *
  ******************************************************************************/
-tAVDT_TC_TBL* avdt_ad_tc_tbl_by_st(uint8_t type, tAVDT_CCB* p_ccb,
-                                   uint8_t state) {
+AvdtpTransportChannel* avdt_ad_tc_tbl_by_st(uint8_t type, AvdtpCcb* p_ccb,
+                                            uint8_t state) {
   int i;
-  tAVDT_TC_TBL* p_tbl = avdt_cb.ad.tc_tbl;
+  AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
   uint8_t ccb_idx;
 
   if (p_ccb == NULL) {
@@ -173,13 +191,13 @@
  * Returns          Pointer to entry.
  *
  ******************************************************************************/
-tAVDT_TC_TBL* avdt_ad_tc_tbl_by_lcid(uint16_t lcid) {
+AvdtpTransportChannel* avdt_ad_tc_tbl_by_lcid(uint16_t lcid) {
   uint8_t idx;
 
-  idx = avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID];
+  idx = avdtp_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID];
 
   if (idx < AVDT_NUM_TC_TBL) {
-    return &avdt_cb.ad.tc_tbl[idx];
+    return &avdtp_cb.ad.tc_tbl[idx];
   } else {
     return NULL;
   }
@@ -196,11 +214,11 @@
  * Returns          Pointer to transport channel table entry.
  *
  ******************************************************************************/
-tAVDT_TC_TBL* avdt_ad_tc_tbl_by_type(uint8_t type, tAVDT_CCB* p_ccb,
-                                     tAVDT_SCB* p_scb) {
+AvdtpTransportChannel* avdt_ad_tc_tbl_by_type(uint8_t type, AvdtpCcb* p_ccb,
+                                              AvdtpScb* p_scb) {
   uint8_t tcid;
   int i;
-  tAVDT_TC_TBL* p_tbl = avdt_cb.ad.tc_tbl;
+  AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
   uint8_t ccb_idx = avdt_ccb_to_idx(p_ccb);
 
   /* get tcid from type, scb */
@@ -227,9 +245,9 @@
  * Returns          Pointer to entry.
  *
  ******************************************************************************/
-tAVDT_TC_TBL* avdt_ad_tc_tbl_alloc(tAVDT_CCB* p_ccb) {
+AvdtpTransportChannel* avdt_ad_tc_tbl_alloc(AvdtpCcb* p_ccb) {
   int i;
-  tAVDT_TC_TBL* p_tbl = avdt_cb.ad.tc_tbl;
+  AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
 
   /* find next free entry in tc table */
   for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
@@ -259,10 +277,10 @@
  * Returns          Index value.
  *
  ******************************************************************************/
-uint8_t avdt_ad_tc_tbl_to_idx(tAVDT_TC_TBL* p_tbl) {
-  AVDT_TRACE_DEBUG("avdt_ad_tc_tbl_to_idx: %d", (p_tbl - avdt_cb.ad.tc_tbl));
+uint8_t avdt_ad_tc_tbl_to_idx(AvdtpTransportChannel* p_tbl) {
+  AVDT_TRACE_DEBUG("avdt_ad_tc_tbl_to_idx: %d", (p_tbl - avdtp_cb.ad.tc_tbl));
   /* use array arithmetic to determine index */
-  return (uint8_t)(p_tbl - avdt_cb.ad.tc_tbl);
+  return (uint8_t)(p_tbl - avdtp_cb.ad.tc_tbl);
 }
 
 /*******************************************************************************
@@ -279,9 +297,10 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_ad_tc_close_ind(tAVDT_TC_TBL* p_tbl, UNUSED_ATTR uint16_t reason) {
-  tAVDT_CCB* p_ccb;
-  tAVDT_SCB* p_scb;
+void avdt_ad_tc_close_ind(AvdtpTransportChannel* p_tbl,
+                          UNUSED_ATTR uint16_t reason) {
+  AvdtpCcb* p_ccb;
+  AvdtpScb* p_scb;
   tAVDT_SCB_TC_CLOSE close;
 
   close.old_tc_state = p_tbl->state;
@@ -290,26 +309,27 @@
   p_tbl->cfg_flags = 0;
   p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
 
-  AVDT_TRACE_DEBUG("avdt_ad_tc_close_ind tcid: %d, old: %d", p_tbl->tcid,
+  AVDT_TRACE_DEBUG("%s: tcid: %d, old: %d", __func__, p_tbl->tcid,
                    close.old_tc_state);
   /* if signaling channel, notify ccb that channel open */
   if (p_tbl->tcid == 0) {
     p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
     avdt_ccb_event(p_ccb, AVDT_CCB_LL_CLOSE_EVT, NULL);
+    return;
   }
   /* if media or other channel, notify scb that channel close */
-  else {
-    /* look up scb in stream routing table by ccb, tcid */
-    p_scb =
-        avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
-    if (p_scb != NULL) {
-      close.tcid = p_tbl->tcid;
-      close.type = avdt_ad_tcid_to_type(p_tbl->tcid);
-      tAVDT_SCB_EVT avdt_scb_evt;
-      avdt_scb_evt.close = close;
-      avdt_scb_event(p_scb, AVDT_SCB_TC_CLOSE_EVT, &avdt_scb_evt);
-    }
+  /* look up scb in stream routing table by ccb, tcid */
+  p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
+  if (p_scb == nullptr) {
+    AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
+                     __func__, p_tbl->ccb_idx, p_tbl->tcid);
+    return;
   }
+  close.tcid = p_tbl->tcid;
+  close.type = avdt_ad_tcid_to_type(p_tbl->tcid);
+  tAVDT_SCB_EVT avdt_scb_evt;
+  avdt_scb_evt.close = close;
+  avdt_scb_event(p_scb, AVDT_SCB_TC_CLOSE_EVT, &avdt_scb_evt);
 }
 
 /*******************************************************************************
@@ -324,18 +344,22 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_ad_tc_open_ind(tAVDT_TC_TBL* p_tbl) {
-  tAVDT_CCB* p_ccb;
-  tAVDT_SCB* p_scb;
+void avdt_ad_tc_open_ind(AvdtpTransportChannel* p_tbl) {
+  AvdtpCcb* p_ccb;
+  AvdtpScb* p_scb;
   tAVDT_OPEN open;
   tAVDT_EVT_HDR evt;
 
+  AVDT_TRACE_DEBUG("%s: p_tbl:%p state:%d ccb_idx:%d tcid:%d scb_hdl:%d",
+                   __func__, p_tbl, p_tbl->state, p_tbl->ccb_idx, p_tbl->tcid,
+                   avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
+
   p_tbl->state = AVDT_AD_ST_OPEN;
 
   /* if signaling channel, notify ccb that channel open */
   if (p_tbl->tcid == 0) {
     /* set the signal channel to use high priority within the ACL link */
-    L2CA_SetTxPriority(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][AVDT_CHAN_SIG].lcid,
+    L2CA_SetTxPriority(avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][AVDT_CHAN_SIG].lcid,
                        L2CAP_CHNL_PRIORITY_HIGH);
 
     p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
@@ -348,23 +372,23 @@
     tAVDT_CCB_EVT avdt_ccb_evt;
     avdt_ccb_evt.msg.hdr = evt;
     avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, &avdt_ccb_evt);
+    return;
   }
   /* if media or other channel, notify scb that channel open */
-  else {
-    /* look up scb in stream routing table by ccb, tcid */
-    p_scb =
-        avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
-
-    /* put lcid in event data */
-    if (p_scb != NULL) {
-      open.peer_mtu = p_tbl->peer_mtu;
-      open.lcid = avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].lcid;
-      open.hdr.err_code = avdt_ad_tcid_to_type(p_tbl->tcid);
-      tAVDT_SCB_EVT avdt_scb_evt;
-      avdt_scb_evt.open = open;
-      avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, &avdt_scb_evt);
-    }
+  /* look up scb in stream routing table by ccb, tcid */
+  p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
+  if (p_scb == nullptr) {
+    AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
+                     __func__, p_tbl->ccb_idx, p_tbl->tcid);
+    return;
   }
+  /* put lcid in event data */
+  open.peer_mtu = p_tbl->peer_mtu;
+  open.lcid = avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].lcid;
+  open.hdr.err_code = avdt_ad_tcid_to_type(p_tbl->tcid);
+  tAVDT_SCB_EVT avdt_scb_evt;
+  avdt_scb_evt.open = open;
+  avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, &avdt_scb_evt);
 }
 
 /*******************************************************************************
@@ -381,9 +405,9 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_ad_tc_cong_ind(tAVDT_TC_TBL* p_tbl, bool is_congested) {
-  tAVDT_CCB* p_ccb;
-  tAVDT_SCB* p_scb;
+void avdt_ad_tc_cong_ind(AvdtpTransportChannel* p_tbl, bool is_congested) {
+  AvdtpCcb* p_ccb;
+  AvdtpScb* p_scb;
 
   /* if signaling channel, notify ccb of congestion */
   if (p_tbl->tcid == 0) {
@@ -391,18 +415,19 @@
     tAVDT_CCB_EVT avdt_ccb_evt;
     avdt_ccb_evt.llcong = is_congested;
     avdt_ccb_event(p_ccb, AVDT_CCB_LL_CONG_EVT, &avdt_ccb_evt);
+    return;
   }
   /* if media or other channel, notify scb that channel open */
-  else {
-    /* look up scb in stream routing table by ccb, tcid */
-    p_scb =
-        avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
-    if (p_scb != NULL) {
-      tAVDT_SCB_EVT avdt_scb_evt;
-      avdt_scb_evt.llcong = is_congested;
-      avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, &avdt_scb_evt);
-    }
+  /* look up scb in stream routing table by ccb, tcid */
+  p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
+  if (p_scb == nullptr) {
+    AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
+                     __func__, p_tbl->ccb_idx, p_tbl->tcid);
+    return;
   }
+  tAVDT_SCB_EVT avdt_scb_evt;
+  avdt_scb_evt.llcong = is_congested;
+  avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, &avdt_scb_evt);
 }
 
 /*******************************************************************************
@@ -417,9 +442,9 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_ad_tc_data_ind(tAVDT_TC_TBL* p_tbl, BT_HDR* p_buf) {
-  tAVDT_CCB* p_ccb;
-  tAVDT_SCB* p_scb;
+void avdt_ad_tc_data_ind(AvdtpTransportChannel* p_tbl, BT_HDR* p_buf) {
+  AvdtpCcb* p_ccb;
+  AvdtpScb* p_scb;
 
   /* store type (media, recovery, reporting) */
   p_buf->layer_specific = avdt_ad_tcid_to_type(p_tbl->tcid);
@@ -428,18 +453,18 @@
   if (p_tbl->tcid == 0) {
     p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
     avdt_msg_ind(p_ccb, p_buf);
+    return;
   }
   /* if media or other channel, send event to scb */
-  else {
-    p_scb =
-        avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
-    if (p_scb != NULL) {
-      avdt_scb_event(p_scb, AVDT_SCB_TC_DATA_EVT, (tAVDT_SCB_EVT*)&p_buf);
-    } else {
-      osi_free(p_buf);
-      AVDT_TRACE_ERROR(" avdt_ad_tc_data_ind buffer freed");
-    }
+  p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
+  if (p_scb == nullptr) {
+    AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
+                     __func__, p_tbl->ccb_idx, p_tbl->tcid);
+    osi_free(p_buf);
+    AVDT_TRACE_ERROR("%s: buffer freed", __func__);
+    return;
   }
+  avdt_scb_event(p_scb, AVDT_SCB_TC_DATA_EVT, (tAVDT_SCB_EVT*)&p_buf);
 }
 
 /*******************************************************************************
@@ -458,14 +483,14 @@
  *                  AVDT_AD_FAILED, if error
  *
  ******************************************************************************/
-uint8_t avdt_ad_write_req(uint8_t type, tAVDT_CCB* p_ccb, tAVDT_SCB* p_scb,
+uint8_t avdt_ad_write_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb,
                           BT_HDR* p_buf) {
   uint8_t tcid;
 
   /* get tcid from type, scb */
   tcid = avdt_ad_type_to_tcid(type, p_scb);
 
-  return L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid,
+  return L2CA_DataWrite(avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid,
                         p_buf);
 }
 
@@ -485,9 +510,9 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_ad_open_req(uint8_t type, tAVDT_CCB* p_ccb, tAVDT_SCB* p_scb,
+void avdt_ad_open_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb,
                       uint8_t role) {
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
   uint16_t lcid;
 
   p_tbl = avdt_ad_tc_tbl_alloc(p_ccb);
@@ -502,17 +527,17 @@
 
   if (type == AVDT_CHAN_SIG) {
     /* if signaling, get mtu from registration control block */
-    p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu;
+    p_tbl->my_mtu = avdtp_cb.rcb.ctrl_mtu;
     p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO;
   } else {
     /* otherwise get mtu from scb */
-    p_tbl->my_mtu = p_scb->cs.mtu;
-    p_tbl->my_flush_to = p_scb->cs.flush_to;
+    p_tbl->my_mtu = p_scb->stream_config.mtu;
+    p_tbl->my_flush_to = p_scb->stream_config.flush_to;
 
     /* also set scb_hdl in rt_tbl */
-    avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].scb_hdl =
+    avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].scb_hdl =
         avdt_scb_to_hdl(p_scb);
-    AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].scb_hdl = %d",
+    AVDT_TRACE_DEBUG("avdtp_cb.ad.rt_tbl[%d][%d].scb_hdl = %d",
                      avdt_ccb_to_idx(p_ccb), p_tbl->tcid,
                      avdt_scb_to_hdl(p_scb));
   }
@@ -529,14 +554,14 @@
     lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr);
     if (lcid != 0) {
       /* if connect req ok, store tcid in lcid table  */
-      avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] =
+      avdtp_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] =
           avdt_ad_tc_tbl_to_idx(p_tbl);
-      AVDT_TRACE_DEBUG("avdt_cb.ad.lcid_tbl[%d] = %d",
+      AVDT_TRACE_DEBUG("avdtp_cb.ad.lcid_tbl[%d] = %d",
                        (lcid - L2CAP_BASE_APPL_CID),
                        avdt_ad_tc_tbl_to_idx(p_tbl));
 
-      avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid;
-      AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].lcid = 0x%x",
+      avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid;
+      AVDT_TRACE_DEBUG("avdtp_cb.ad.rt_tbl[%d][%d].lcid = 0x%x",
                        avdt_ccb_to_idx(p_ccb), p_tbl->tcid, lcid);
     } else {
       /* if connect req failed, call avdt_ad_tc_close_ind() */
@@ -557,9 +582,9 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_ad_close_req(uint8_t type, tAVDT_CCB* p_ccb, tAVDT_SCB* p_scb) {
+void avdt_ad_close_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb) {
   uint8_t tcid;
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
 
   p_tbl = avdt_ad_tc_tbl_by_type(type, p_ccb, p_scb);
   AVDT_TRACE_DEBUG("avdt_ad_close_req state: %d", p_tbl->state);
@@ -577,6 +602,6 @@
       tcid = avdt_ad_type_to_tcid(type, p_scb);
 
       /* call l2cap disconnect req */
-      L2CA_DisconnectReq(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid);
+      L2CA_DisconnectReq(avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid);
   }
 }
diff --git a/stack/avdt/avdt_api.cc b/stack/avdt/avdt_api.cc
index e1277d0..a635567 100644
--- a/stack/avdt/avdt_api.cc
+++ b/stack/avdt/avdt_api.cc
@@ -34,11 +34,11 @@
 #include "l2c_api.h"
 #include "stack/include/a2dp_codec_api.h"
 
-/* Control block for AVDT */
-tAVDT_CB avdt_cb;
+/* Control block for AVDTP */
+AvdtpCb avdtp_cb;
 
 void avdt_ccb_idle_ccb_timer_timeout(void* data) {
-  tAVDT_CCB* p_ccb = (tAVDT_CCB*)data;
+  AvdtpCcb* p_ccb = (AvdtpCcb*)data;
   uint8_t avdt_event = AVDT_CCB_IDLE_TOUT_EVT;
   uint8_t err_code = AVDT_ERR_TIMEOUT;
 
@@ -48,7 +48,7 @@
 }
 
 void avdt_ccb_ret_ccb_timer_timeout(void* data) {
-  tAVDT_CCB* p_ccb = (tAVDT_CCB*)data;
+  AvdtpCcb* p_ccb = (AvdtpCcb*)data;
   uint8_t avdt_event = AVDT_CCB_RET_TOUT_EVT;
   uint8_t err_code = AVDT_ERR_TIMEOUT;
 
@@ -58,7 +58,7 @@
 }
 
 void avdt_ccb_rsp_ccb_timer_timeout(void* data) {
-  tAVDT_CCB* p_ccb = (tAVDT_CCB*)data;
+  AvdtpCcb* p_ccb = (AvdtpCcb*)data;
   uint8_t avdt_event = AVDT_CCB_RSP_TOUT_EVT;
   uint8_t err_code = AVDT_ERR_TIMEOUT;
 
@@ -68,7 +68,7 @@
 }
 
 void avdt_scb_transport_channel_timer_timeout(void* data) {
-  tAVDT_SCB* p_scb = (tAVDT_SCB*)data;
+  AvdtpScb* p_scb = (AvdtpScb*)data;
   uint8_t avdt_event = AVDT_SCB_TC_TOUT_EVT;
 
   avdt_scb_event(p_scb, avdt_event, NULL);
@@ -88,7 +88,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void AVDT_Register(tAVDT_REG* p_reg, tAVDT_CTRL_CBACK* p_cback) {
+void AVDT_Register(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback) {
   /* register PSM with L2CAP */
   L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO*)&avdt_l2c_appl);
 
@@ -116,8 +116,8 @@
   avdt_ad_init();
 
   /* copy registration struct */
-  memcpy(&avdt_cb.rcb, p_reg, sizeof(tAVDT_REG));
-  avdt_cb.p_conn_cback = p_cback;
+  avdtp_cb.rcb = *p_reg;
+  avdtp_cb.p_conn_cback = p_cback;
 }
 
 /*******************************************************************************
@@ -142,7 +142,7 @@
 void AVDT_AbortReq(uint8_t handle) {
   AVDT_TRACE_WARNING("%s: handle=%d", __func__, handle);
 
-  tAVDT_SCB* p_scb = avdt_scb_by_hdl(handle);
+  AvdtpScb* p_scb = avdt_scb_by_hdl(handle);
   if (p_scb != NULL) {
     avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_REQ_EVT, NULL);
   } else {
@@ -164,20 +164,21 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-uint16_t AVDT_CreateStream(uint8_t* p_handle, tAVDT_CS* p_cs) {
+uint16_t AVDT_CreateStream(uint8_t peer_id, uint8_t* p_handle,
+                           const AvdtpStreamConfig& avdtp_stream_config) {
   uint16_t result = AVDT_SUCCESS;
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
 
-  AVDT_TRACE_DEBUG("%s", __func__);
+  AVDT_TRACE_DEBUG("%s: peer_id=%d", __func__, peer_id);
 
   /* Verify parameters; if invalid, return failure */
-  if (((p_cs->cfg.psc_mask & (~AVDT_PSC)) != 0) ||
-      (p_cs->p_ctrl_cback == NULL)) {
+  if (((avdtp_stream_config.cfg.psc_mask & (~AVDT_PSC)) != 0) ||
+      (avdtp_stream_config.p_avdt_ctrl_cback == NULL)) {
     result = AVDT_BAD_PARAMS;
   }
   /* Allocate scb; if no scbs, return failure */
   else {
-    p_scb = avdt_scb_alloc(p_cs);
+    p_scb = avdt_scb_alloc(peer_id, avdtp_stream_config);
     if (p_scb == NULL) {
       result = AVDT_NO_RESOURCES;
     } else {
@@ -185,7 +186,8 @@
     }
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
+  AVDT_TRACE_DEBUG("%s: result=%d handle=%d scb_index=%d", __func__, result,
+                   *p_handle, avdtp_stream_config.scb_index);
 
   return result;
 }
@@ -206,7 +208,7 @@
  ******************************************************************************/
 uint16_t AVDT_RemoveStream(uint8_t handle) {
   uint16_t result = AVDT_SUCCESS;
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
 
   AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle);
 
@@ -250,9 +252,10 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-uint16_t AVDT_DiscoverReq(const RawAddress& bd_addr, tAVDT_SEP_INFO* p_sep_info,
-                          uint8_t max_seps, tAVDT_CTRL_CBACK* p_cback) {
-  tAVDT_CCB* p_ccb;
+uint16_t AVDT_DiscoverReq(const RawAddress& bd_addr, uint8_t channel_index,
+                          tAVDT_SEP_INFO* p_sep_info, uint8_t max_seps,
+                          tAVDT_CTRL_CBACK* p_cback) {
+  AvdtpCcb* p_ccb;
   uint16_t result = AVDT_SUCCESS;
   tAVDT_CCB_EVT evt;
 
@@ -261,7 +264,7 @@
   /* find channel control block for this bd addr; if none, allocate one */
   p_ccb = avdt_ccb_by_bd(bd_addr);
   if (p_ccb == NULL) {
-    p_ccb = avdt_ccb_alloc(bd_addr);
+    p_ccb = avdt_ccb_alloc_by_channel_index(bd_addr, channel_index);
     if (p_ccb == NULL) {
       /* could not allocate channel control block */
       result = AVDT_NO_RESOURCES;
@@ -291,15 +294,15 @@
  *
  * Function         avdt_get_cap_req
  *
- * Description      internal function to serve both AVDT_GetCapReq and
- *                  AVDT_GetAllCapReq
+ * Description      internal function to serve AVDT_GetCapReq
  *
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
 static uint16_t avdt_get_cap_req(const RawAddress& bd_addr,
+                                 uint8_t channel_index,
                                  tAVDT_CCB_API_GETCAP* p_evt) {
-  tAVDT_CCB* p_ccb = NULL;
+  AvdtpCcb* p_ccb = NULL;
   uint16_t result = AVDT_SUCCESS;
 
   AVDT_TRACE_DEBUG("%s", __func__);
@@ -314,7 +317,7 @@
   else {
     p_ccb = avdt_ccb_by_bd(bd_addr);
     if (p_ccb == NULL) {
-      p_ccb = avdt_ccb_alloc(bd_addr);
+      p_ccb = avdt_ccb_alloc_by_channel_index(bd_addr, channel_index);
       if (p_ccb == NULL) {
         /* could not allocate channel control block */
         result = AVDT_NO_RESOURCES;
@@ -362,60 +365,23 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-uint16_t AVDT_GetCapReq(const RawAddress& bd_addr, uint8_t seid,
-                        tAVDT_CFG* p_cfg, tAVDT_CTRL_CBACK* p_cback) {
+uint16_t AVDT_GetCapReq(const RawAddress& bd_addr, uint8_t channel_index,
+                        uint8_t seid, AvdtpSepConfig* p_cfg,
+                        tAVDT_CTRL_CBACK* p_cback, bool get_all_cap) {
   tAVDT_CCB_API_GETCAP getcap;
   uint16_t result = AVDT_SUCCESS;
 
   AVDT_TRACE_DEBUG("%s", __func__);
 
   getcap.single.seid = seid;
-  getcap.single.sig_id = AVDT_SIG_GETCAP;
+  if (get_all_cap) {
+    getcap.single.sig_id = AVDT_SIG_GET_ALLCAP;
+  } else {
+    getcap.single.sig_id = AVDT_SIG_GETCAP;
+  }
   getcap.p_cfg = p_cfg;
   getcap.p_cback = p_cback;
-  result = avdt_get_cap_req(bd_addr, &getcap);
-
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
-  return result;
-}
-
-/*******************************************************************************
- *
- * Function         AVDT_GetAllCapReq
- *
- * Description      This function initiates a connection to the AVDTP service
- *                  on the peer device, if not already present, and gets the
- *                  capabilities of a stream endpoint on the peer device.
- *                  This function can be called at any time regardless of
- *                  whether there is an AVDTP connection to the peer device.
- *
- *                  When the procedure is complete, an AVDT_GETCAP_CFM_EVT is
- *                  sent to the application via its callback function.  The
- *                  application must not call AVDT_GetCapReq() or
- *                  AVDT_DiscoverReq() again until the procedure is complete.
- *
- *                  The memory pointed to by p_cfg is allocated by the
- *                  application.  This memory is written to by AVDTP as part
- *                  of the get capabilities procedure.  This memory must
- *                  remain accessible until the application receives
- *                  the AVDT_GETCAP_CFM_EVT.
- *
- * Returns          AVDT_SUCCESS if successful, otherwise error.
- *
- ******************************************************************************/
-uint16_t AVDT_GetAllCapReq(const RawAddress& bd_addr, uint8_t seid,
-                           tAVDT_CFG* p_cfg, tAVDT_CTRL_CBACK* p_cback) {
-  tAVDT_CCB_API_GETCAP getcap;
-  uint16_t result = AVDT_SUCCESS;
-
-  AVDT_TRACE_DEBUG("%s", __func__);
-
-  getcap.single.seid = seid;
-  getcap.single.sig_id = AVDT_SIG_GET_ALLCAP;
-  getcap.p_cfg = p_cfg;
-  getcap.p_cback = p_cback;
-  result = avdt_get_cap_req(bd_addr, &getcap);
+  result = avdt_get_cap_req(bd_addr, channel_index, &getcap);
 
   AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
 
@@ -434,7 +400,7 @@
  *
  ******************************************************************************/
 uint16_t AVDT_DelayReport(uint8_t handle, uint8_t seid, uint16_t delay) {
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
@@ -472,10 +438,11 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-uint16_t AVDT_OpenReq(uint8_t handle, const RawAddress& bd_addr, uint8_t seid,
-                      tAVDT_CFG* p_cfg) {
-  tAVDT_CCB* p_ccb = NULL;
-  tAVDT_SCB* p_scb = NULL;
+uint16_t AVDT_OpenReq(uint8_t handle, const RawAddress& bd_addr,
+                      uint8_t channel_index, uint8_t seid,
+                      AvdtpSepConfig* p_cfg) {
+  AvdtpCcb* p_ccb = NULL;
+  AvdtpScb* p_scb = NULL;
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
@@ -495,7 +462,7 @@
     else {
       p_ccb = avdt_ccb_by_bd(bd_addr);
       if (p_ccb == NULL) {
-        p_ccb = avdt_ccb_alloc(bd_addr);
+        p_ccb = avdt_ccb_alloc_by_channel_index(bd_addr, channel_index);
         if (p_ccb == NULL) {
           /* could not allocate channel control block */
           result = AVDT_NO_RESOURCES;
@@ -534,13 +501,13 @@
  ******************************************************************************/
 uint16_t AVDT_ConfigRsp(uint8_t handle, uint8_t label, uint8_t error_code,
                         uint8_t category) {
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   tAVDT_SCB_EVT evt;
   uint16_t result = AVDT_SUCCESS;
   uint8_t event_code;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=%d category=%d", __func__,
-                   handle, label, error_code, category);
+  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=0x%x category=%d",
+                   __func__, handle, label, error_code, category);
 
   /* map handle to scb */
   p_scb = avdt_scb_by_hdl(handle);
@@ -587,7 +554,7 @@
  *
  ******************************************************************************/
 uint16_t AVDT_StartReq(uint8_t* p_handles, uint8_t num_handles) {
-  tAVDT_SCB* p_scb = NULL;
+  AvdtpScb* p_scb = NULL;
   tAVDT_CCB_EVT evt;
   uint16_t result = AVDT_SUCCESS;
   int i;
@@ -639,7 +606,7 @@
  *
  ******************************************************************************/
 uint16_t AVDT_SuspendReq(uint8_t* p_handles, uint8_t num_handles) {
-  tAVDT_SCB* p_scb = NULL;
+  AvdtpScb* p_scb = NULL;
   tAVDT_CCB_EVT evt;
   uint16_t result = AVDT_SUCCESS;
   int i;
@@ -690,7 +657,7 @@
  *
  ******************************************************************************/
 uint16_t AVDT_CloseReq(uint8_t handle) {
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   uint16_t result = AVDT_SUCCESS;
 
   AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle);
@@ -727,8 +694,8 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-uint16_t AVDT_ReconfigReq(uint8_t handle, tAVDT_CFG* p_cfg) {
-  tAVDT_SCB* p_scb;
+uint16_t AVDT_ReconfigReq(uint8_t handle, AvdtpSepConfig* p_cfg) {
+  AvdtpScb* p_scb;
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
@@ -766,12 +733,12 @@
  ******************************************************************************/
 uint16_t AVDT_ReconfigRsp(uint8_t handle, uint8_t label, uint8_t error_code,
                           uint8_t category) {
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   tAVDT_SCB_EVT evt;
   uint16_t result = AVDT_SUCCESS;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=%d category=%d", __func__,
-                   handle, label, error_code, category);
+  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=0x%x category=%d",
+                   __func__, handle, label, error_code, category);
 
   /* map handle to scb */
   p_scb = avdt_scb_by_hdl(handle);
@@ -806,7 +773,7 @@
  *
  ******************************************************************************/
 uint16_t AVDT_SecurityReq(uint8_t handle, uint8_t* p_data, uint16_t len) {
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
@@ -845,11 +812,11 @@
  ******************************************************************************/
 uint16_t AVDT_SecurityRsp(uint8_t handle, uint8_t label, uint8_t error_code,
                           uint8_t* p_data, uint16_t len) {
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=%d len=%d", __func__,
+  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=0x%x len=%d", __func__,
                    handle, label, error_code, len);
 
   /* map handle to scb */
@@ -910,7 +877,7 @@
  ******************************************************************************/
 uint16_t AVDT_WriteReqOpt(uint8_t handle, BT_HDR* p_pkt, uint32_t time_stamp,
                           uint8_t m_pt, tAVDT_DATA_OPT_MASK opt) {
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   tAVDT_SCB_EVT evt;
   uint16_t result = AVDT_SUCCESS;
 
@@ -988,18 +955,19 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-uint16_t AVDT_ConnectReq(const RawAddress& bd_addr, uint8_t sec_mask,
-                         tAVDT_CTRL_CBACK* p_cback) {
-  tAVDT_CCB* p_ccb = NULL;
+uint16_t AVDT_ConnectReq(const RawAddress& bd_addr, uint8_t channel_index,
+                         uint8_t sec_mask, tAVDT_CTRL_CBACK* p_cback) {
+  AvdtpCcb* p_ccb = NULL;
   uint16_t result = AVDT_SUCCESS;
   tAVDT_CCB_EVT evt;
 
-  AVDT_TRACE_DEBUG("%s: sec_mask=0x%x", __func__, sec_mask);
+  AVDT_TRACE_WARNING("%s: address=%s channel_index=%d sec_mask=0x%x", __func__,
+                     bd_addr.ToString().c_str(), channel_index, sec_mask);
 
   /* find channel control block for this bd addr; if none, allocate one */
   p_ccb = avdt_ccb_by_bd(bd_addr);
   if (p_ccb == NULL) {
-    p_ccb = avdt_ccb_alloc(bd_addr);
+    p_ccb = avdt_ccb_alloc_by_channel_index(bd_addr, channel_index);
     if (p_ccb == NULL) {
       /* could not allocate channel control block */
       result = AVDT_NO_RESOURCES;
@@ -1018,7 +986,8 @@
     avdt_ccb_event(p_ccb, AVDT_CCB_API_CONNECT_REQ_EVT, &evt);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
+  AVDT_TRACE_WARNING("%s: address=%s result=%d", __func__,
+                     bd_addr.ToString().c_str(), result);
 
   return result;
 }
@@ -1037,7 +1006,7 @@
  ******************************************************************************/
 uint16_t AVDT_DisconnectReq(const RawAddress& bd_addr,
                             tAVDT_CTRL_CBACK* p_cback) {
-  tAVDT_CCB* p_ccb = NULL;
+  AvdtpCcb* p_ccb = NULL;
   uint16_t result = AVDT_SUCCESS;
   tAVDT_CCB_EVT evt;
 
@@ -1071,8 +1040,8 @@
  *
  ******************************************************************************/
 uint16_t AVDT_GetL2CapChannel(uint8_t handle) {
-  tAVDT_SCB* p_scb;
-  tAVDT_CCB* p_ccb;
+  AvdtpScb* p_scb;
+  AvdtpCcb* p_ccb;
   uint8_t tcid;
   uint16_t lcid = 0;
 
@@ -1082,7 +1051,7 @@
     /* get tcid from type, scb */
     tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb);
 
-    lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
+    lcid = avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
   }
 
   return (lcid);
@@ -1099,19 +1068,19 @@
  *
  ******************************************************************************/
 uint16_t AVDT_GetSignalChannel(uint8_t handle, const RawAddress& bd_addr) {
-  tAVDT_SCB* p_scb;
-  tAVDT_CCB* p_ccb;
+  AvdtpScb* p_scb;
+  AvdtpCcb* p_ccb;
   uint8_t tcid = 0; /* tcid is always 0 for signal channel */
   uint16_t lcid = 0;
 
   /* map handle to scb */
   if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) &&
       ((p_ccb = p_scb->p_ccb) != NULL)) {
-    lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
+    lcid = avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
   } else {
     p_ccb = avdt_ccb_by_bd(bd_addr);
     if (p_ccb != NULL) {
-      lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
+      lcid = avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
     }
   }
 
@@ -1131,9 +1100,9 @@
  ******************************************************************************/
 uint16_t AVDT_SendReport(uint8_t handle, AVDT_REPORT_TYPE type,
                          tAVDT_REPORT_DATA* p_data) {
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   uint16_t result = AVDT_BAD_PARAMS;
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
   uint8_t *p, *plen, *pm1, *p_end;
   uint32_t ssrc;
   uint16_t len;
@@ -1142,8 +1111,10 @@
 
   /* map handle to scb && verify parameters */
   if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) && (p_scb->p_ccb != NULL) &&
-      (((type == AVDT_RTCP_PT_SR) && (p_scb->cs.tsep == AVDT_TSEP_SRC)) ||
-       ((type == AVDT_RTCP_PT_RR) && (p_scb->cs.tsep == AVDT_TSEP_SNK)) ||
+      (((type == AVDT_RTCP_PT_SR) &&
+        (p_scb->stream_config.tsep == AVDT_TSEP_SRC)) ||
+       ((type == AVDT_RTCP_PT_RR) &&
+        (p_scb->stream_config.tsep == AVDT_TSEP_SNK)) ||
        (type == AVDT_RTCP_PT_SDES))) {
     result = AVDT_NO_RESOURCES;
 
@@ -1234,7 +1205,63 @@
  *
  *****************************************************************************/
 uint8_t AVDT_SetTraceLevel(uint8_t new_level) {
-  if (new_level != 0xFF) avdt_cb.trace_level = new_level;
+  if (new_level != 0xFF) avdtp_cb.SetTraceLevel(new_level);
 
-  return (avdt_cb.trace_level);
+  return avdtp_cb.TraceLevel();
+}
+
+void stack_debug_avdtp_api_dump(int fd) {
+  dprintf(fd, "\nAVDTP Stack State:\n");
+  dprintf(fd, "  AVDTP signalling L2CAP channel MTU: %d\n",
+          avdtp_cb.rcb.ctrl_mtu);
+  dprintf(fd, "  Security mask: 0x%x\n", avdtp_cb.rcb.sec_mask);
+
+  for (size_t i = 0; i < AVDT_NUM_LINKS; i++) {
+    const AvdtpCcb& ccb = avdtp_cb.ccb[i];
+    dprintf(fd, "\n  Channel control block: %zu peer: %s\n", i,
+            ccb.peer_addr.ToString().c_str());
+    dprintf(fd, "    Allocated: %s\n", ccb.allocated ? "true" : "false");
+    dprintf(fd, "    State: %d\n", ccb.state);
+    dprintf(fd, "    Link-layer opened: %s\n",
+            ccb.ll_opened ? "true" : "false");
+    dprintf(fd, "    Discover in progress: %s\n",
+            ccb.proc_busy ? "true" : "false");
+    dprintf(fd, "    Congested: %s\n", ccb.cong ? "true" : "false");
+    dprintf(fd, "    Reinitiate connection on idle: %s\n",
+            ccb.reconn ? "true" : "false");
+    dprintf(fd, "    Command retransmission count: %d\n", ccb.ret_count);
+    dprintf(fd, "    BTA AV SCB index: %d\n", ccb.BtaAvScbIndex());
+
+    for (size_t i = 0; i < AVDT_NUM_SEPS; i++) {
+      const AvdtpScb& scb = ccb.scb[i];
+      dprintf(fd, "\n    Stream control block: %zu\n", i);
+      dprintf(fd, "      SEP codec: %s\n",
+              A2DP_CodecName(scb.stream_config.cfg.codec_info));
+      dprintf(fd, "      SEP type: 0x%x\n", scb.stream_config.tsep);
+      dprintf(fd, "      Media type: 0x%x\n", scb.stream_config.media_type);
+      dprintf(fd, "      MTU: %d\n", scb.stream_config.mtu);
+      dprintf(fd, "      SCB handle: %d\n", scb.ScbHandle());
+      dprintf(fd, "      SCB index: %d\n", scb.stream_config.scb_index);
+      dprintf(fd, "      Configured codec: %s\n",
+              A2DP_CodecName(scb.curr_cfg.codec_info));
+      dprintf(fd, "      Requested codec: %s\n",
+              A2DP_CodecName(scb.req_cfg.codec_info));
+      dprintf(fd, "      Transport channel connect timer: %s\n",
+              alarm_is_scheduled(scb.transport_channel_timer)
+                  ? "Scheduled"
+                  : "Not scheduled");
+      dprintf(fd, "      Channel control block peer: %s\n",
+              (scb.p_ccb != nullptr) ? scb.p_ccb->peer_addr.ToString().c_str()
+                                     : "null");
+      dprintf(fd, "      Allocated: %s\n", scb.allocated ? "true" : "false");
+      dprintf(fd, "      In use: %s\n", scb.in_use ? "true" : "false");
+      dprintf(fd, "      Role: 0x%x\n", scb.role);
+      dprintf(fd, "      Remove: %s\n", scb.remove ? "true" : "false");
+      dprintf(fd, "      State: %d\n", scb.state);
+      dprintf(fd, "      Peer SEID: %d\n", scb.peer_seid);
+      dprintf(fd, "      Current event: %d\n", scb.curr_evt);
+      dprintf(fd, "      Congested: %s\n", scb.cong ? "true" : "false");
+      dprintf(fd, "      Close response code: %d\n", scb.close_code);
+    }
+  }
 }
diff --git a/stack/avdt/avdt_ccb.cc b/stack/avdt/avdt_ccb.cc
index bca7aaf..990d0b8 100644
--- a/stack/avdt/avdt_ccb.cc
+++ b/stack/avdt/avdt_ccb.cc
@@ -348,8 +348,10 @@
  *
  ******************************************************************************/
 void avdt_ccb_init(void) {
-  memset(&avdt_cb.ccb[0], 0, sizeof(tAVDT_CCB) * AVDT_NUM_LINKS);
-  avdt_cb.p_ccb_act = avdt_ccb_action;
+  for (size_t i = 0; i < AVDT_NUM_LINKS; i++) {
+    avdtp_cb.ccb[i].Reset(i);
+  }
+  avdtp_cb.p_ccb_act = avdt_ccb_action;
 }
 
 /*******************************************************************************
@@ -362,15 +364,15 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_ccb_event(tAVDT_CCB* p_ccb, uint8_t event, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_event(AvdtpCcb* p_ccb, uint8_t event, tAVDT_CCB_EVT* p_data) {
   tAVDT_CCB_ST_TBL state_table;
   uint8_t action;
   int i;
 
 #if (AVDT_DEBUG == TRUE)
-  AVDT_TRACE_EVENT("%s: CCB ccb=%d event=%s state=%s", __func__,
+  AVDT_TRACE_EVENT("%s: CCB ccb=%d event=%s state=%s p_ccb=%p", __func__,
                    avdt_ccb_to_idx(p_ccb), avdt_ccb_evt_str[event],
-                   avdt_ccb_st_str[p_ccb->state]);
+                   avdt_ccb_st_str[p_ccb->state], p_ccb);
 #endif
 
   /* look up the state table for the current state */
@@ -388,7 +390,7 @@
                      avdt_ccb_evt_str[event], avdt_ccb_st_str[p_ccb->state],
                      action);
     if (action != AVDT_CCB_IGNORE) {
-      (*avdt_cb.p_ccb_act[action])(p_ccb, p_data);
+      (*avdtp_cb.p_ccb_act[action])(p_ccb, p_data);
     } else {
       break;
     }
@@ -405,8 +407,8 @@
  * Returns          pointer to the ccb, or NULL if none found.
  *
  ******************************************************************************/
-tAVDT_CCB* avdt_ccb_by_bd(const RawAddress& bd_addr) {
-  tAVDT_CCB* p_ccb = &avdt_cb.ccb[0];
+AvdtpCcb* avdt_ccb_by_bd(const RawAddress& bd_addr) {
+  AvdtpCcb* p_ccb = &avdtp_cb.ccb[0];
   int i;
 
   for (i = 0; i < AVDT_NUM_LINKS; i++, p_ccb++) {
@@ -435,32 +437,52 @@
  * Returns          pointer to the ccb, or NULL if none could be allocated.
  *
  ******************************************************************************/
-tAVDT_CCB* avdt_ccb_alloc(const RawAddress& bd_addr) {
-  tAVDT_CCB* p_ccb = &avdt_cb.ccb[0];
-  int i;
-
-  for (i = 0; i < AVDT_NUM_LINKS; i++, p_ccb++) {
+AvdtpCcb* avdt_ccb_alloc(const RawAddress& bd_addr) {
+  // Find available entry
+  AvdtpCcb* p_ccb = &avdtp_cb.ccb[0];
+  for (int i = 0; i < AVDT_NUM_LINKS; i++, p_ccb++) {
     if (!p_ccb->allocated) {
-      p_ccb->allocated = true;
-      p_ccb->peer_addr = bd_addr;
-      p_ccb->cmd_q = fixed_queue_new(SIZE_MAX);
-      p_ccb->rsp_q = fixed_queue_new(SIZE_MAX);
-      p_ccb->idle_ccb_timer = alarm_new("avdt_ccb.idle_ccb_timer");
-      p_ccb->ret_ccb_timer = alarm_new("avdt_ccb.ret_ccb_timer");
-      p_ccb->rsp_ccb_timer = alarm_new("avdt_ccb.rsp_ccb_timer");
-      AVDT_TRACE_DEBUG("avdt_ccb_alloc %d", i);
-      break;
+      p_ccb->Allocate(bd_addr);
+      AVDT_TRACE_DEBUG("%s: allocated (index %d)", __func__, i);
+      return p_ccb;
     }
   }
 
-  if (i == AVDT_NUM_LINKS) {
-    /* out of ccbs */
-    p_ccb = NULL;
-    AVDT_TRACE_WARNING("Out of ccbs");
+  AVDT_TRACE_WARNING("%s: out of AvdtpCcb entries", __func__);
+  return nullptr;
+}
+
+AvdtpCcb* avdt_ccb_alloc_by_channel_index(const RawAddress& bd_addr,
+                                          uint8_t channel_index) {
+  // Allocate the entry for the specified channel index
+  if (channel_index >= AVDT_NUM_LINKS) {
+    AVDT_TRACE_ERROR("%s: peer %s invalid channel index %d (max %d)", __func__,
+                     bd_addr.ToString().c_str(), channel_index, AVDT_NUM_LINKS);
+    return nullptr;
   }
+  AvdtpCcb* p_ccb = &avdtp_cb.ccb[channel_index];
+  if (p_ccb->allocated) {
+    AVDT_TRACE_ERROR("%s: peer %s channel index %d already allocated", __func__,
+                     bd_addr.ToString().c_str(), channel_index);
+    return nullptr;
+  }
+  p_ccb->Allocate(bd_addr);
+  AVDT_TRACE_DEBUG("%s: allocated (index %d) peer=%s p_ccb=%p", __func__,
+                   channel_index, p_ccb->peer_addr.ToString().c_str(), p_ccb);
   return p_ccb;
 }
 
+void AvdtpCcb::Allocate(const RawAddress& peer_address) {
+  ResetCcb();
+  peer_addr = peer_address;
+  cmd_q = fixed_queue_new(SIZE_MAX);
+  rsp_q = fixed_queue_new(SIZE_MAX);
+  idle_ccb_timer = alarm_new("avdtp_ccb.idle_ccb_timer");
+  ret_ccb_timer = alarm_new("avdtp_ccb.ret_ccb_timer");
+  rsp_ccb_timer = alarm_new("avdtp_ccb.rsp_ccb_timer");
+  allocated = true;
+}
+
 /*******************************************************************************
  *
  * Function         avdt_ccb_dealloc
@@ -471,14 +493,11 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_dealloc(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
-  AVDT_TRACE_DEBUG("avdt_ccb_dealloc %d", avdt_ccb_to_idx(p_ccb));
-  alarm_free(p_ccb->idle_ccb_timer);
-  alarm_free(p_ccb->ret_ccb_timer);
-  alarm_free(p_ccb->rsp_ccb_timer);
-  fixed_queue_free(p_ccb->cmd_q, NULL);
-  fixed_queue_free(p_ccb->rsp_q, NULL);
-  memset(p_ccb, 0, sizeof(tAVDT_CCB));
+void avdt_ccb_dealloc(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+  AVDT_TRACE_DEBUG("%s: deallocated (index %d) peer=%s p_ccb=%p", __func__,
+                   avdt_ccb_to_idx(p_ccb), p_ccb->peer_addr.ToString().c_str(),
+                   p_ccb);
+  p_ccb->ResetCcb();
 }
 
 /*******************************************************************************
@@ -491,9 +510,9 @@
  * Returns          Index of ccb.
  *
  ******************************************************************************/
-uint8_t avdt_ccb_to_idx(tAVDT_CCB* p_ccb) {
+uint8_t avdt_ccb_to_idx(AvdtpCcb* p_ccb) {
   /* use array arithmetic to determine index */
-  return (uint8_t)(p_ccb - avdt_cb.ccb);
+  return (uint8_t)(p_ccb - avdtp_cb.ccb);
 }
 
 /*******************************************************************************
@@ -506,12 +525,12 @@
  * Returns          pointer to the ccb, or NULL if none found.
  *
  ******************************************************************************/
-tAVDT_CCB* avdt_ccb_by_idx(uint8_t idx) {
-  tAVDT_CCB* p_ccb;
+AvdtpCcb* avdt_ccb_by_idx(uint8_t idx) {
+  AvdtpCcb* p_ccb;
 
   /* verify index */
   if (idx < AVDT_NUM_LINKS) {
-    p_ccb = &avdt_cb.ccb[idx];
+    p_ccb = &avdtp_cb.ccb[idx];
   } else {
     p_ccb = NULL;
     AVDT_TRACE_WARNING("No ccb for idx %d", idx);
diff --git a/stack/avdt/avdt_ccb_act.cc b/stack/avdt/avdt_ccb_act.cc
index 4fdbee0..52f3857 100644
--- a/stack/avdt/avdt_ccb_act.cc
+++ b/stack/avdt/avdt_ccb_act.cc
@@ -46,7 +46,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-static void avdt_ccb_clear_ccb(tAVDT_CCB* p_ccb) {
+static void avdt_ccb_clear_ccb(AvdtpCcb* p_ccb) {
   BT_HDR* p_buf;
 
   /* clear certain ccb variables */
@@ -75,7 +75,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_chan_open(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_chan_open(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   BTM_SetOutService(p_ccb->peer_addr, BTM_SEC_SERVICE_AVDTP, AVDT_CHAN_SIG);
   avdt_ad_open_req(AVDT_CHAN_SIG, p_ccb, NULL, AVDT_INT);
 }
@@ -91,7 +91,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_chan_close(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_chan_close(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   /* close the transport channel used by this CCB */
   avdt_ad_close_req(AVDT_CHAN_SIG, p_ccb, NULL);
 }
@@ -107,9 +107,9 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_chk_close(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_chk_close(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   int i;
-  tAVDT_SCB* p_scb = &avdt_cb.scb[0];
+  AvdtpScb* p_scb = &(p_ccb->scb[0]);
 
   /* see if there are any active scbs associated with this ccb */
   for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) {
@@ -122,7 +122,7 @@
   if (i == AVDT_NUM_SEPS) {
     alarm_cancel(p_ccb->ret_ccb_timer);
     alarm_cancel(p_ccb->rsp_ccb_timer);
-    period_ms_t interval_ms = avdt_cb.rcb.idle_tout * 1000;
+    period_ms_t interval_ms = avdtp_cb.rcb.idle_tout * 1000;
     alarm_set_on_mloop(p_ccb->idle_ccb_timer, interval_ms,
                        avdt_ccb_idle_ccb_timer_timeout, p_ccb);
   }
@@ -141,23 +141,25 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_hdl_discover_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_hdl_discover_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   tAVDT_SEP_INFO sep_info[AVDT_NUM_SEPS];
-  tAVDT_SCB* p_scb = &avdt_cb.scb[0];
-  int i;
+  AvdtpScb* p_scb = &(p_ccb->scb[0]);
+
+  AVDT_TRACE_DEBUG("%s: p_ccb index=%d", __func__, avdt_ccb_to_idx(p_ccb));
 
   p_data->msg.discover_rsp.p_sep_info = sep_info;
   p_data->msg.discover_rsp.num_seps = 0;
 
   /* for all allocated scbs */
-  for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) {
+  for (int i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) {
     if (p_scb->allocated) {
       /* copy sep info */
       sep_info[p_data->msg.discover_rsp.num_seps].in_use = p_scb->in_use;
-      sep_info[p_data->msg.discover_rsp.num_seps].seid = i + 1;
+      sep_info[p_data->msg.discover_rsp.num_seps].seid = p_scb->ScbHandle();
       sep_info[p_data->msg.discover_rsp.num_seps].media_type =
-          p_scb->cs.media_type;
-      sep_info[p_data->msg.discover_rsp.num_seps].tsep = p_scb->cs.tsep;
+          p_scb->stream_config.media_type;
+      sep_info[p_data->msg.discover_rsp.num_seps].tsep =
+          p_scb->stream_config.tsep;
 
       p_data->msg.discover_rsp.num_seps++;
     }
@@ -179,13 +181,14 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_hdl_discover_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_hdl_discover_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   /* we're done with procedure */
   p_ccb->proc_busy = false;
 
   /* call app callback with results */
-  (*p_ccb->proc_cback)(0, &p_ccb->peer_addr, AVDT_DISCOVER_CFM_EVT,
-                       (tAVDT_CTRL*)(&p_data->msg.discover_rsp));
+  (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_DISCOVER_CFM_EVT,
+                       (tAVDT_CTRL*)(&p_data->msg.discover_rsp),
+                       p_ccb->BtaAvScbIndex());
 }
 
 /*******************************************************************************
@@ -201,13 +204,13 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_hdl_getcap_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
-  tAVDT_SCB* p_scb;
+void avdt_ccb_hdl_getcap_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
+  AvdtpScb* p_scb;
 
   /* look up scb for seid sent to us */
   p_scb = avdt_scb_by_hdl(p_data->msg.single.seid);
 
-  p_data->msg.svccap.p_cfg = &p_scb->cs.cfg;
+  p_data->msg.svccap.p_cfg = &p_scb->stream_config.cfg;
 
   avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_RSP_EVT, p_data);
 }
@@ -224,13 +227,14 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_hdl_getcap_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_hdl_getcap_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   /* we're done with procedure */
   p_ccb->proc_busy = false;
 
   /* call app callback with results */
-  (*p_ccb->proc_cback)(0, &p_ccb->peer_addr, AVDT_GETCAP_CFM_EVT,
-                       (tAVDT_CTRL*)(&p_data->msg.svccap));
+  (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_GETCAP_CFM_EVT,
+                       (tAVDT_CTRL*)(&p_data->msg.svccap),
+                       p_ccb->BtaAvScbIndex());
 }
 
 /*******************************************************************************
@@ -246,7 +250,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_hdl_start_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_hdl_start_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   uint8_t err_code = 0;
 
   /* verify all streams in the right state */
@@ -277,11 +281,11 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_hdl_start_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_hdl_start_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   uint8_t event;
   int i;
   uint8_t* p;
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
 
   /* determine rsp or rej event */
   event = (p_data->msg.hdr.err_code == 0) ? AVDT_SCB_MSG_START_RSP_EVT
@@ -313,7 +317,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_hdl_suspend_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_hdl_suspend_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   uint8_t seid;
   uint8_t err_code = 0;
 
@@ -347,11 +351,11 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_hdl_suspend_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_hdl_suspend_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   uint8_t event;
   int i;
   uint8_t* p;
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
 
   /* determine rsp or rej event */
   event = (p_data->msg.hdr.err_code == 0) ? AVDT_SCB_MSG_SUSPEND_RSP_EVT
@@ -382,7 +386,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_discover_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_snd_discover_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   /* store info in ccb struct */
   p_ccb->p_proc_data = p_data->discover.p_sep_info;
   p_ccb->proc_cback = p_data->discover.p_cback;
@@ -407,7 +411,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_discover_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_snd_discover_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   /* send response */
   avdt_msg_send_rsp(p_ccb, AVDT_SIG_DISCOVER, &p_data->msg);
 }
@@ -425,7 +429,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_getcap_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_snd_getcap_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   uint8_t sig_id = AVDT_SIG_GETCAP;
 
   /* store info in ccb struct */
@@ -454,7 +458,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_getcap_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_snd_getcap_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   uint8_t sig_id = AVDT_SIG_GETCAP;
 
   if (p_data->msg.hdr.sig_id == AVDT_SIG_GET_ALLCAP)
@@ -477,9 +481,9 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_start_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_snd_start_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   int i;
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   tAVDT_MSG avdt_msg;
   uint8_t seid_list[AVDT_NUM_SEPS];
 
@@ -527,8 +531,8 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_start_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
-  tAVDT_SCB* p_scb;
+void avdt_ccb_snd_start_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
+  AvdtpScb* p_scb;
   int i;
 
   /* send response message */
@@ -557,9 +561,9 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_suspend_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_snd_suspend_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   int i;
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   tAVDT_MSG avdt_msg;
   uint8_t seid_list[AVDT_NUM_SEPS];
 
@@ -602,8 +606,8 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_suspend_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
-  tAVDT_SCB* p_scb;
+void avdt_ccb_snd_suspend_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
+  AvdtpScb* p_scb;
   int i;
 
   /* send response message */
@@ -632,9 +636,9 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_clear_cmds(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_clear_cmds(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   int i;
-  tAVDT_SCB* p_scb = &avdt_cb.scb[0];
+  AvdtpScb* p_scb = &(p_ccb->scb[0]);
   uint8_t err_code = AVDT_ERR_CONNECT;
 
   /* clear the ccb */
@@ -675,10 +679,10 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_cmd_fail(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_cmd_fail(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   tAVDT_MSG msg;
   uint8_t evt;
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
 
   if (p_ccb->p_curr_cmd != NULL) {
     /* set up data */
@@ -718,7 +722,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_free_cmd(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_free_cmd(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   osi_free_and_reset((void**)&p_ccb->p_curr_cmd);
 }
 
@@ -733,7 +737,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_cong_state(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_cong_state(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   p_ccb->cong = p_data->llcong;
 }
 
@@ -750,7 +754,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_ret_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_ret_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   p_ccb->ret_count++;
   if (p_ccb->ret_count == AVDT_RET_MAX) {
     /* command failed */
@@ -776,7 +780,7 @@
     /* restart ret timer */
     alarm_cancel(p_ccb->idle_ccb_timer);
     alarm_cancel(p_ccb->rsp_ccb_timer);
-    period_ms_t interval_ms = avdt_cb.rcb.ret_tout * 1000;
+    period_ms_t interval_ms = avdtp_cb.rcb.ret_tout * 1000;
     alarm_set_on_mloop(p_ccb->ret_ccb_timer, interval_ms,
                        avdt_ccb_ret_ccb_timer_timeout, p_ccb);
   }
@@ -793,7 +797,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_cmd(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_snd_cmd(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   BT_HDR* p_msg;
 
   /* do we have commands to send?  send next command;  make sure we're clear;
@@ -822,7 +826,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_snd_msg(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_snd_msg(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   BT_HDR* p_msg;
 
   /* if not congested */
@@ -858,7 +862,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_set_reconn(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_set_reconn(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   p_ccb->reconn = true;
 }
 
@@ -872,7 +876,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_clr_reconn(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_clr_reconn(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   p_ccb->reconn = false;
 }
 
@@ -888,7 +892,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_chk_reconn(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_chk_reconn(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   if (p_ccb->reconn) {
     p_ccb->reconn = false;
 
@@ -919,7 +923,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_chk_timer(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_chk_timer(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   alarm_cancel(p_ccb->idle_ccb_timer);
 }
 
@@ -933,7 +937,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_set_conn(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_set_conn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   /* save callback */
   p_ccb->p_conn_cback = p_data->connect.p_cback;
 
@@ -953,7 +957,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_set_disconn(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_set_disconn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   /*
   AVDT_TRACE_EVENT("avdt_ccb_set_disconn:conn:x%x, api:x%x",
       p_ccb->p_conn_cback, p_data->disconnect.p_cback);
@@ -973,7 +977,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_do_disconn(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_do_disconn(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   /* clear any pending commands */
   avdt_ccb_clear_cmds(p_ccb, NULL);
 
@@ -991,17 +995,20 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_ll_closed(tAVDT_CCB* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_ll_closed(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) {
   tAVDT_CTRL_CBACK* p_cback;
   tAVDT_CTRL avdt_ctrl;
 
+  AVDT_TRACE_DEBUG("%s peer %s", __func__, p_ccb->peer_addr.ToString().c_str());
+
   /* clear any pending commands */
   avdt_ccb_clear_cmds(p_ccb, NULL);
 
   /* save callback pointer, bd addr */
   p_cback = p_ccb->p_conn_cback;
-  if (!p_cback) p_cback = avdt_cb.p_conn_cback;
+  if (!p_cback) p_cback = avdtp_cb.p_conn_cback;
   RawAddress bd_addr = p_ccb->peer_addr;
+  uint8_t bta_av_scb_index = p_ccb->BtaAvScbIndex();
 
   /* dealloc ccb */
   avdt_ccb_dealloc(p_ccb, NULL);
@@ -1009,7 +1016,8 @@
   /* call callback */
   if (p_cback) {
     avdt_ctrl.hdr.err_code = 0;
-    (*p_cback)(0, &bd_addr, AVDT_DISCONNECT_IND_EVT, &avdt_ctrl);
+    (*p_cback)(0, bd_addr, AVDT_DISCONNECT_IND_EVT, &avdt_ctrl,
+               bta_av_scb_index);
   }
 }
 
@@ -1023,18 +1031,21 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_ccb_ll_opened(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data) {
+void avdt_ccb_ll_opened(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) {
   tAVDT_CTRL avdt_ctrl;
 
+  AVDT_TRACE_DEBUG("%s peer %s BtaAvScbIndex=%d p_ccb=%p", __func__,
+                   p_ccb->peer_addr.ToString().c_str(), p_ccb->BtaAvScbIndex(),
+                   p_ccb);
   p_ccb->ll_opened = true;
 
-  if (!p_ccb->p_conn_cback) p_ccb->p_conn_cback = avdt_cb.p_conn_cback;
+  if (!p_ccb->p_conn_cback) p_ccb->p_conn_cback = avdtp_cb.p_conn_cback;
 
   /* call callback */
   if (p_ccb->p_conn_cback) {
     avdt_ctrl.hdr.err_code = 0;
     avdt_ctrl.hdr.err_param = p_data->msg.hdr.err_param;
-    (*p_ccb->p_conn_cback)(0, &p_ccb->peer_addr, AVDT_CONNECT_IND_EVT,
-                           &avdt_ctrl);
+    (*p_ccb->p_conn_cback)(0, p_ccb->peer_addr, AVDT_CONNECT_IND_EVT,
+                           &avdt_ctrl, p_ccb->BtaAvScbIndex());
   }
 }
diff --git a/stack/avdt/avdt_int.h b/stack/avdt/avdt_int.h
index 0a65cfb..1c35448 100644
--- a/stack/avdt/avdt_int.h
+++ b/stack/avdt/avdt_int.h
@@ -304,7 +304,7 @@
 
 /* adaption layer number of stream routing table entries */
 /* 2 channels(1 media, 1 report) for each SEP and one for signalling */
-#define AVDT_NUM_RT_TBL ((AVDT_NUM_SEPS << 1) + 1)
+#define AVDT_NUM_RT_TBL (AVDT_NUM_SEPS * AVDT_CHAN_NUM_TYPES + 1)
 
 /* adaption layer number of transport channel table entries - moved to target.h
 #define AVDT_NUM_TC_TBL     (AVDT_NUM_SEPS + AVDT_NUM_LINKS) */
@@ -320,7 +320,7 @@
 #define AVDT_AD_ST_SEC_INT 7 /* Security process as INT */
 #define AVDT_AD_ST_SEC_ACP 8 /* Security process as ACP */
 
-/* Configuration flags. tAVDT_TC_TBL.cfg_flags */
+/* Configuration flags. AvdtpTransportChannel.cfg_flags */
 #define AVDT_L2C_CFG_IND_DONE (1 << 0)
 #define AVDT_L2C_CFG_CFM_DONE (1 << 1)
 #define AVDT_L2C_CFG_CONN_INT (1 << 2)
@@ -360,7 +360,7 @@
 typedef struct {
   tAVDT_EVT_HDR single;
   tAVDT_CTRL_CBACK* p_cback;
-  tAVDT_CFG* p_cfg;
+  AvdtpSepConfig* p_cfg;
 } tAVDT_CCB_API_GETCAP;
 
 /* data type for AVDT_CCB_API_CONNECT_REQ_EVT */
@@ -383,42 +383,6 @@
   uint8_t err_code;
 } tAVDT_CCB_EVT;
 
-/* channel control block type */
-typedef struct {
-  RawAddress peer_addr; /* BD address of peer */
-  /*
-   * NOTE: idle_ccb_timer, ret_ccb_timer and rsp_ccb_timer are mutually
-   * exclusive - no more than one timer should be running at the same time.
-   */
-  alarm_t* idle_ccb_timer; /* Idle CCB timer entry */
-  alarm_t* ret_ccb_timer;  /* Ret CCB timer entry */
-  alarm_t* rsp_ccb_timer;  /* Rsp CCB timer entry */
-  fixed_queue_t* cmd_q;    /* Queue for outgoing command messages */
-  fixed_queue_t* rsp_q;    /* Queue for outgoing response and reject messages */
-  tAVDT_CTRL_CBACK* proc_cback; /* Procedure callback function */
-  tAVDT_CTRL_CBACK*
-      p_conn_cback;   /* Connection/disconnection callback function */
-  void* p_proc_data;  /* Pointer to data storage for procedure */
-  BT_HDR* p_curr_cmd; /* Current command being sent awaiting response */
-  BT_HDR* p_curr_msg; /* Current message being sent */
-  BT_HDR* p_rx_msg;   /* Current message being received */
-  bool allocated;     /* Whether ccb is allocated */
-  uint8_t state;      /* The CCB state machine state */
-  bool ll_opened;     /* true if LL is opened */
-  bool proc_busy;     /* true when a discover or get capabilities procedure in
-                         progress */
-  uint8_t proc_param; /* Procedure parameter; either SEID for get capabilities
-                         or number of SEPS for discover */
-  bool cong;          /* Whether signaling channel is congested */
-  uint8_t label;      /* Message header "label" (sequence number) */
-  bool reconn;        /* If true, reinitiate connection after transitioning from
-                         CLOSING to IDLE state */
-  uint8_t ret_count;  /* Command retransmission count */
-} tAVDT_CCB;
-
-/* type for action functions */
-typedef void (*tAVDT_CCB_ACTION)(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-
 /* type for AVDT_SCB_API_WRITE_REQ_EVT */
 typedef struct {
   BT_HDR* p_buf;
@@ -445,67 +409,404 @@
   BT_HDR* p_pkt;
 } tAVDT_SCB_EVT;
 
-/* stream control block type */
-typedef struct {
-  tAVDT_CS cs;                      /* stream creation struct */
-  tAVDT_CFG curr_cfg;               /* current configuration */
-  tAVDT_CFG req_cfg;                /* requested configuration */
-  alarm_t* transport_channel_timer; /* transport channel connect timer */
-  BT_HDR* p_pkt;                    /* packet waiting to be sent */
-  tAVDT_CCB* p_ccb;                 /* ccb associated with this scb */
-  uint16_t media_seq;               /* media packet sequence number */
-  bool allocated;                   /* whether scb is allocated or unused */
-  bool in_use;                      /* whether stream being used by peer */
-  uint8_t role;       /* initiator/acceptor role in current procedure */
-  bool remove;        /* whether CB is marked for removal */
-  uint8_t state;      /* state machine state */
-  uint8_t peer_seid;  /* SEID of peer stream */
-  uint8_t curr_evt;   /* current event; set only by state machine */
-  bool cong;          /* Whether media transport channel is congested */
-  uint8_t close_code; /* Error code received in close response */
-} tAVDT_SCB;
+class AvdtpCcb;
 
-/* type for action functions */
-typedef void (*tAVDT_SCB_ACTION)(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
+/**
+ * AVDTP Stream Control Block.
+ */
+class AvdtpScb {
+ public:
+  AvdtpScb()
+      : transport_channel_timer(nullptr),
+        p_pkt(nullptr),
+        p_ccb(nullptr),
+        media_seq(0),
+        allocated(false),
+        in_use(false),
+        role(0),
+        remove(false),
+        state(0),
+        peer_seid(0),
+        curr_evt(0),
+        cong(false),
+        close_code(0),
+        scb_handle_(0) {}
 
-/* adaption layer type for transport channel table */
-typedef struct {
-  uint16_t peer_mtu;    /* L2CAP mtu of the peer device */
-  uint16_t my_mtu;      /* Our MTU for this channel */
-  uint16_t my_flush_to; /* Our flush timeout for this channel */
+  /**
+   * Allocate the entry for usage.
+   * Previous state will be reset and initialized.
+   *
+   * @param p_avdtp_ccb the AvdtCcb entry to use
+   * @param avdtp_stream_config the stream config to use
+   */
+  void Allocate(AvdtpCcb* p_avdtp_ccb,
+                const AvdtpStreamConfig& avdtp_stream_config);
+
+  /**
+   * Recycle the entry by resetting it, mark it as allocated and keeping
+   * the following state:
+   *  - stream_config
+   *  - p_ccb
+   */
+  void Recycle() {
+    AvdtpStreamConfig stream_config_saved = stream_config;
+    AvdtpCcb* p_ccb_saved = p_ccb;
+    Allocate(p_ccb_saved, stream_config_saved);
+  }
+
+  /**
+   * Reset all the state.
+   *
+   * @param scb_handle the AVDTP SCB handle to use
+   */
+  void Reset(uint8_t scb_handle) {
+    stream_config.Reset();
+    curr_cfg.Reset();
+    req_cfg.Reset();
+
+    alarm_free(transport_channel_timer);
+    transport_channel_timer = nullptr;
+
+    p_pkt = nullptr;
+    p_ccb = nullptr;
+    media_seq = 0;
+    allocated = false;
+    in_use = false;
+    role = 0;
+    remove = false;
+    state = 0;
+    peer_seid = 0;
+    curr_evt = 0;
+    cong = false;
+    close_code = 0;
+    scb_handle_ = scb_handle;
+  }
+
+  /**
+   * Get the AVDTP SCB handle for this entry.
+   */
+  uint8_t ScbHandle() const { return scb_handle_; }
+
+  AvdtpStreamConfig stream_config;   // Stream configuration
+  AvdtpSepConfig curr_cfg;           // Current configuration
+  AvdtpSepConfig req_cfg;            // Requested configuration
+  alarm_t* transport_channel_timer;  // Transport channel connect timer
+  BT_HDR* p_pkt;                     // Packet waiting to be sent
+  AvdtpCcb* p_ccb;                   // CCB associated with this SCB
+  uint16_t media_seq;                // Media packet sequence number
+  bool allocated;                    // True if the SCB is allocated
+  bool in_use;                       // True if used by peer
+  uint8_t role;        // Initiator/acceptor role in current procedure
+  bool remove;         // True if the SCB is marked for removal
+  uint8_t state;       // State machine state
+  uint8_t peer_seid;   // SEID of peer stream
+  uint8_t curr_evt;    // current event; set only by the state machine
+  bool cong;           // True if the media transport channel is congested
+  uint8_t close_code;  // Error code received in close response
+
+ private:
+  uint8_t scb_handle_;  // Unique handle for this AvdtpScb entry
+};
+
+/**
+ * AVDTP Channel Control Block.
+ */
+class AvdtpCcb {
+ public:
+  AvdtpCcb()
+      : peer_addr(RawAddress::kEmpty),
+        scb{},
+        idle_ccb_timer(nullptr),
+        ret_ccb_timer(nullptr),
+        rsp_ccb_timer(nullptr),
+        cmd_q(nullptr),
+        rsp_q(nullptr),
+        proc_cback(nullptr),
+        p_conn_cback(nullptr),
+        p_proc_data(nullptr),
+        p_curr_cmd(nullptr),
+        p_curr_msg(nullptr),
+        p_rx_msg(nullptr),
+        allocated(false),
+        state(0),
+        ll_opened(false),
+        proc_busy(false),
+        proc_param(0),
+        cong(false),
+        label(0),
+        reconn(false),
+        ret_count(0),
+        bta_av_scb_index_(0) {}
+
+  /**
+   * Allocate the entry for usage.
+   *
+   * NOTE: The corresponding AvdtpScb entries are allocated independently.
+   * @param peer_address the peer address
+   */
+  void Allocate(const RawAddress& peer_address);
+
+  /**
+   * Reset all the state.
+   *
+   * @param bta_av_scb_index the BTA AV SCB index to use
+   */
+  void Reset(uint8_t bta_av_scb_index) {
+    bta_av_scb_index_ = bta_av_scb_index;
+    ResetCcb();
+    for (size_t i = 0; i < AVDT_NUM_SEPS; i++) {
+      scb[i].Reset(0);
+    }
+  }
+
+  /**
+   * Reset only the Channel Control Block state without the Stream
+   * Control Block entries. The bta_av_scb_index_ is also preserved.
+   */
+  void ResetCcb() {
+    peer_addr = RawAddress::kEmpty;
+
+    alarm_free(idle_ccb_timer);
+    idle_ccb_timer = nullptr;
+
+    alarm_free(ret_ccb_timer);
+    ret_ccb_timer = nullptr;
+
+    alarm_free(rsp_ccb_timer);
+    rsp_ccb_timer = nullptr;
+
+    fixed_queue_free(cmd_q, nullptr);
+    cmd_q = nullptr;
+
+    fixed_queue_free(rsp_q, nullptr);
+    rsp_q = nullptr;
+
+    proc_cback = nullptr;
+    p_conn_cback = nullptr;
+    p_proc_data = nullptr;
+    p_curr_cmd = nullptr;
+    p_curr_msg = nullptr;
+    p_rx_msg = nullptr;
+    allocated = false;
+    state = 0;
+    ll_opened = false;
+    proc_busy = false;
+    proc_param = 0;
+    cong = false;
+    label = 0;
+    reconn = false;
+    ret_count = 0;
+  }
+
+  /**
+   * Get the corresponding BTA AV stream control block index for this entry.
+   */
+  uint8_t BtaAvScbIndex() const { return bta_av_scb_index_; }
+
+  RawAddress peer_addr;         // Bluetooth address of peer
+  AvdtpScb scb[AVDT_NUM_SEPS];  // The AVDTP stream control blocks
+
+  /*
+   * NOTE: idle_ccb_timer, ret_ccb_timer and rsp_ccb_timer are mutually
+   * exclusive - no more than one timer should be running at the same time.
+   */
+  alarm_t* idle_ccb_timer;  // Idle CCB timer entry
+  alarm_t* ret_ccb_timer;   // Ret CCB timer entry
+  alarm_t* rsp_ccb_timer;   // Rsp CCB timer entry
+  fixed_queue_t* cmd_q;     // Queue for outgoing command messages
+  fixed_queue_t* rsp_q;     // Queue for outgoing response and reject messages
+  tAVDT_CTRL_CBACK* proc_cback;    // Procedure callback function
+  tAVDT_CTRL_CBACK* p_conn_cback;  // Connection/disconnection callback function
+  void* p_proc_data;               // Pointer to data storage for procedure
+  BT_HDR* p_curr_cmd;  // Current command being sent awaiting response
+  BT_HDR* p_curr_msg;  // Current message being sent
+  BT_HDR* p_rx_msg;    // Current message being received
+  bool allocated;      // Whether ccb is allocated
+  uint8_t state;       // The CCB state machine state
+  bool ll_opened;      // True if LL is opened
+  bool proc_busy;      // True when a discover or get capabilities procedure in
+                       // progress
+  uint8_t proc_param;  // Procedure parameter; either SEID for get capabilities
+                       // or number of SEPS for discover
+  bool cong;           // True if the signaling channel is congested
+  uint8_t label;       // Message header "label" (sequence number)
+  bool reconn;        // If true, reinitiate connection after transitioning from
+                      // CLOSING to IDLE state
+  uint8_t ret_count;  // Command retransmission count
+
+ private:
+  // The corresponding BTA AV stream control block index for this entry
+  uint8_t bta_av_scb_index_;
+};
+
+/**
+ * AVDTP transport channel entry.
+ * Used in the transport channel table in the adaptation layer.
+ */
+class AvdtpTransportChannel {
+ public:
+  AvdtpTransportChannel()
+      : peer_mtu(0),
+        my_mtu(0),
+        my_flush_to(0),
+        lcid(0),
+        tcid(0),
+        ccb_idx(0),
+        state(0),
+        cfg_flags(0),
+        id(0) {}
+
+  void Reset() {
+    peer_mtu = 0;
+    my_mtu = 0;
+    my_flush_to = 0;
+    lcid = 0;
+    tcid = 0;
+    ccb_idx = 0;
+    state = 0;
+    cfg_flags = 0;
+    id = 0;
+  }
+
+  uint16_t peer_mtu;     // L2CAP MTU of the peer device
+  uint16_t my_mtu;       // Our MTU for this channel
+  uint16_t my_flush_to;  // Our flush timeout for this channel
   uint16_t lcid;
-  uint8_t tcid;      /* transport channel id */
-  uint8_t ccb_idx;   /* channel control block associated with this tc */
-  uint8_t state;     /* transport channel state */
-  uint8_t cfg_flags; /* L2CAP configuration flags */
+  uint8_t tcid;       // Transport channel ID
+  uint8_t ccb_idx;    // Channel control block for with this transport channel
+  uint8_t state;      // Transport channel state
+  uint8_t cfg_flags;  // L2CAP configuration flags
   uint8_t id;
-} tAVDT_TC_TBL;
+};
 
-/* adaption layer type for stream routing table */
-typedef struct {
-  uint16_t lcid;   /* L2CAP LCID of the associated transport channel */
-  uint8_t scb_hdl; /* stream control block associated with this tc */
-} tAVDT_RT_TBL;
+/**
+ * AVDTP stream routing entry.
+ * Used in the routing table in the adaption layer.
+ */
+class AvdtpRoutingEntry {
+ public:
+  AvdtpRoutingEntry() : lcid(0), scb_hdl(0) {}
 
-/* adaption layer control block */
-typedef struct {
-  tAVDT_RT_TBL rt_tbl[AVDT_NUM_LINKS][AVDT_NUM_RT_TBL];
-  tAVDT_TC_TBL tc_tbl[AVDT_NUM_TC_TBL];
-  uint8_t lcid_tbl[MAX_L2CAP_CHANNELS]; /* map LCID to tc_tbl index */
-} tAVDT_AD;
+  void Reset() {
+    lcid = 0;
+    scb_hdl = 0;
+  }
 
-/* Control block for AVDT */
-typedef struct {
-  tAVDT_REG rcb;                   /* registration control block */
-  tAVDT_CCB ccb[AVDT_NUM_LINKS];   /* channel control blocks */
-  tAVDT_SCB scb[AVDT_NUM_SEPS];    /* stream control blocks */
-  tAVDT_AD ad;                     /* adaption layer control block */
-  tAVDTC_CTRL_CBACK* p_conf_cback; /* conformance callback function */
-  const tAVDT_CCB_ACTION* p_ccb_act; /* pointer to CCB action functions */
-  const tAVDT_SCB_ACTION* p_scb_act; /* pointer to SCB action functions */
-  tAVDT_CTRL_CBACK* p_conn_cback;  /* connection callback function */
-  uint8_t trace_level;             /* trace level */
-} tAVDT_CB;
+  uint16_t lcid;    // L2CAP LCID of the associated transport channel
+  uint8_t scb_hdl;  // Stream control block for this transport channel
+};
+
+/**
+ * AVDTP adaption layer control block.
+ */
+class AvdtpAdaptationLayer {
+ public:
+  AvdtpAdaptationLayer() : lcid_tbl{} {}
+
+  void Reset() {
+    for (size_t i = 0; i < AVDT_NUM_LINKS; i++) {
+      for (size_t j = 0; j < AVDT_NUM_RT_TBL; j++) {
+        rt_tbl[i][j].Reset();
+      }
+    }
+    for (size_t i = 0; i < AVDT_NUM_TC_TBL; i++) {
+      tc_tbl[i].Reset();
+    }
+    memset(lcid_tbl, 0, sizeof(lcid_tbl));
+  }
+
+  /**
+   * Lookup AvdtpScb entry for a transport channel.
+   *
+   * @param tc the transport channel
+   * @return the corresponding AvdtpScb entry or null of the transport
+   * channel is invalid.
+   */
+  AvdtpScb* LookupAvdtpScb(const AvdtpTransportChannel& tc);
+
+  AvdtpRoutingEntry rt_tbl[AVDT_NUM_LINKS][AVDT_NUM_RT_TBL];
+  AvdtpTransportChannel tc_tbl[AVDT_NUM_TC_TBL];
+  uint8_t lcid_tbl[MAX_L2CAP_CHANNELS];  // Map LCID to tc_tbl index
+};
+
+/**
+ * Types for action functions.
+ */
+typedef void (*tAVDT_CCB_ACTION)(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+typedef void (*tAVDT_SCB_ACTION)(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+
+/**
+ * Control block for AVDTP.
+ */
+class AvdtpCb {
+ public:
+  AvdtpCb()
+      : p_conf_cback(nullptr),
+        p_ccb_act(nullptr),
+        p_scb_act(nullptr),
+        p_conn_cback(nullptr),
+        trace_level_(0) {}
+
+  void Reset() {
+    rcb.Reset();
+    for (size_t i = 0; i < AVDT_NUM_LINKS; i++) {
+      ccb[i].Reset(i);
+    }
+    ad.Reset();
+    p_conf_cback = nullptr;
+    p_ccb_act = nullptr;
+    p_scb_act = nullptr;
+    p_conn_cback = nullptr;
+    trace_level_ = 0;
+  }
+
+  AvdtpRcb rcb;                       // Registration control block
+  AvdtpCcb ccb[AVDT_NUM_LINKS];       // Channel control blocks
+  AvdtpAdaptationLayer ad;            // Adaption layer control block
+  tAVDTC_CTRL_CBACK* p_conf_cback;    // Conformance callback function
+  const tAVDT_CCB_ACTION* p_ccb_act;  // Pointer to CCB action functions
+  const tAVDT_SCB_ACTION* p_scb_act;  // Pointer to SCB action functions
+  tAVDT_CTRL_CBACK* p_conn_cback;     // Connection callback function
+
+  /**
+   * Compute the SCB handle for a given AvdtpScb entry.
+   *
+   * @param p_scb the entry to use
+   * @return the computed SCB handle or 0 if the entry is invalid.
+   */
+  uint8_t ComputeScbHandle(const AvdtpScb* p_scb) const {
+    uint8_t scb_handle = 0;
+
+    // Find the entry and in the process compute the unique index
+    // TODO: This mechanism is sub-efficient and should be refactored.
+    for (size_t i = 0; i < AVDT_NUM_LINKS; i++) {
+      for (size_t j = 0; j < AVDT_NUM_SEPS; j++) {
+        scb_handle++;
+        if (&ccb[i].scb[j] == p_scb) {
+          return scb_handle;
+        }
+      }
+    }
+    return 0;  // Not found
+  }
+
+  /**
+   * Get the current trace level used for logging.
+   *
+   * @return the current trace level
+   */
+  uint8_t TraceLevel() const { return trace_level_; }
+
+  /**
+   * Set the current trace level used for logging.
+   *
+   * @param trace_level the trace level to set. Should be in the range [1, 6].
+   */
+  void SetTraceLevel(uint8_t trace_level) { trace_level_ = trace_level; }
+
+ private:
+  uint8_t trace_level_; /* trace level */
+};
 
 /*****************************************************************************
  * function declarations
@@ -513,156 +814,162 @@
 
 /* CCB function declarations */
 extern void avdt_ccb_init(void);
-extern void avdt_ccb_event(tAVDT_CCB* p_ccb, uint8_t event,
+extern void avdt_ccb_event(AvdtpCcb* p_ccb, uint8_t event,
                            tAVDT_CCB_EVT* p_data);
-extern tAVDT_CCB* avdt_ccb_by_bd(const RawAddress& bd_addr);
-extern tAVDT_CCB* avdt_ccb_alloc(const RawAddress& bd_addr);
-extern void avdt_ccb_dealloc(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern uint8_t avdt_ccb_to_idx(tAVDT_CCB* p_ccb);
-extern tAVDT_CCB* avdt_ccb_by_idx(uint8_t idx);
+extern AvdtpCcb* avdt_ccb_by_bd(const RawAddress& bd_addr);
+extern AvdtpCcb* avdt_ccb_alloc(const RawAddress& bd_addr);
+extern AvdtpCcb* avdt_ccb_alloc_by_channel_index(const RawAddress& bd_addr,
+                                                 uint8_t channel_index);
+extern void avdt_ccb_dealloc(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern uint8_t avdt_ccb_to_idx(AvdtpCcb* p_ccb);
+extern AvdtpCcb* avdt_ccb_by_idx(uint8_t idx);
 
 /* CCB action functions */
-extern void avdt_ccb_chan_open(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_chan_close(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_chk_close(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_hdl_discover_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_hdl_discover_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_hdl_getcap_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_hdl_getcap_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_hdl_start_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_hdl_start_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_hdl_suspend_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_hdl_suspend_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_discover_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_discover_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_getcap_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_getcap_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_start_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_start_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_suspend_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_suspend_rsp(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_clear_cmds(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_cmd_fail(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_free_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_cong_state(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_ret_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_cmd(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_snd_msg(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_set_reconn(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_clr_reconn(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_chk_reconn(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_chk_timer(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_set_conn(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_set_disconn(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_do_disconn(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_ll_closed(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
-extern void avdt_ccb_ll_opened(tAVDT_CCB* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_chan_open(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_chan_close(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_chk_close(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_hdl_discover_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_hdl_discover_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_hdl_getcap_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_hdl_getcap_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_hdl_start_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_hdl_start_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_hdl_suspend_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_hdl_suspend_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_discover_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_discover_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_getcap_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_getcap_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_start_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_start_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_suspend_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_suspend_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_clear_cmds(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_cmd_fail(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_free_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_cong_state(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_ret_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_snd_msg(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_set_reconn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_clr_reconn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_chk_reconn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_chk_timer(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_set_conn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_set_disconn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_do_disconn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_ll_closed(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
+extern void avdt_ccb_ll_opened(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data);
 
 /* SCB function prototypes */
-extern void avdt_scb_event(tAVDT_SCB* p_scb, uint8_t event,
+extern void avdt_scb_event(AvdtpScb* p_scb, uint8_t event,
                            tAVDT_SCB_EVT* p_data);
 extern void avdt_scb_init(void);
-extern tAVDT_SCB* avdt_scb_alloc(tAVDT_CS* p_cs);
-extern void avdt_scb_dealloc(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern uint8_t avdt_scb_to_hdl(tAVDT_SCB* p_scb);
-extern tAVDT_SCB* avdt_scb_by_hdl(uint8_t hdl);
-extern uint8_t avdt_scb_verify(tAVDT_CCB* p_ccb, uint8_t state, uint8_t* p_seid,
+extern AvdtpScb* avdt_scb_alloc(uint8_t peer_id,
+                                const AvdtpStreamConfig& avdtp_stream_config);
+extern void avdt_scb_dealloc(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern uint8_t avdt_scb_to_hdl(AvdtpScb* p_scb);
+extern AvdtpScb* avdt_scb_by_hdl(uint8_t hdl);
+extern uint8_t avdt_scb_verify(AvdtpCcb* p_ccb, uint8_t state, uint8_t* p_seid,
                                uint16_t num_seid, uint8_t* p_err_code);
 extern void avdt_scb_peer_seid_list(tAVDT_MULTI* p_multi);
-extern uint32_t avdt_scb_gen_ssrc(tAVDT_SCB* p_scb);
+extern uint32_t avdt_scb_gen_ssrc(AvdtpScb* p_scb);
 
 /* SCB action functions */
-extern void avdt_scb_hdl_abort_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_abort_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_close_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_close_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_getconfig_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_getconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_open_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_open_rej(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_open_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_pkt(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_drop_pkt(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_reconfig_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_reconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_security_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_security_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_setconfig_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_setconfig_rej(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_start_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_start_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_suspend_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_suspend_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_delay_rpt_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_delay_rpt_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_delay_rpt_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_tc_close(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_tc_open(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_tc_close_sto(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_tc_open_sto(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_hdl_write_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_abort_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_abort_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_close_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_stream_close(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_close_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_getconfig_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_getconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_open_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_open_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_reconfig_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_reconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_security_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_security_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_setconfig_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_setconfig_rej(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_setconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_snd_tc_close(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_cb_err(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_cong_state(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_rej_state(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_rej_in_use(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_rej_not_in_use(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_set_remove(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_free_pkt(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_chk_snd_pkt(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_clr_pkt(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_transport_channel_timer(tAVDT_SCB* p_scb,
+extern void avdt_scb_hdl_abort_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_abort_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_close_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_close_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_getconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_getconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_open_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_open_rej(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_open_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_pkt(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_drop_pkt(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_reconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_reconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_security_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_security_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_setconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_setconfig_rej(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_setconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_start_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_start_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_suspend_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_suspend_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_delay_rpt_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_delay_rpt_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_delay_rpt_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_tc_close(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_tc_open(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_tc_close_sto(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_tc_open_sto(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_hdl_write_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_abort_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_abort_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_close_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_stream_close(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_close_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_getconfig_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_getconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_open_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_open_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_reconfig_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_reconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_security_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_security_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_setconfig_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_setconfig_rej(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_setconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_snd_tc_close(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_cb_err(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_cong_state(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_rej_state(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_rej_in_use(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_rej_not_in_use(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_set_remove(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_free_pkt(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_chk_snd_pkt(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_clr_pkt(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_transport_channel_timer(AvdtpScb* p_scb,
                                              tAVDT_SCB_EVT* p_data);
-extern void avdt_scb_clr_vars(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data);
+extern void avdt_scb_clr_vars(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data);
 
 /* msg function declarations */
-extern bool avdt_msg_send(tAVDT_CCB* p_ccb, BT_HDR* p_msg);
-extern void avdt_msg_send_cmd(tAVDT_CCB* p_ccb, void* p_scb, uint8_t sig_id,
+extern bool avdt_msg_send(AvdtpCcb* p_ccb, BT_HDR* p_msg);
+extern void avdt_msg_send_cmd(AvdtpCcb* p_ccb, void* p_scb, uint8_t sig_id,
                               tAVDT_MSG* p_params);
-extern void avdt_msg_send_rsp(tAVDT_CCB* p_ccb, uint8_t sig_id,
+extern void avdt_msg_send_rsp(AvdtpCcb* p_ccb, uint8_t sig_id,
                               tAVDT_MSG* p_params);
-extern void avdt_msg_send_rej(tAVDT_CCB* p_ccb, uint8_t sig_id,
+extern void avdt_msg_send_rej(AvdtpCcb* p_ccb, uint8_t sig_id,
                               tAVDT_MSG* p_params);
-extern void avdt_msg_send_grej(tAVDT_CCB* p_ccb, uint8_t sig_id,
+extern void avdt_msg_send_grej(AvdtpCcb* p_ccb, uint8_t sig_id,
                                tAVDT_MSG* p_params);
-extern void avdt_msg_ind(tAVDT_CCB* p_ccb, BT_HDR* p_buf);
+extern void avdt_msg_ind(AvdtpCcb* p_ccb, BT_HDR* p_buf);
 
 /* adaption layer function declarations */
 extern void avdt_ad_init(void);
-extern uint8_t avdt_ad_type_to_tcid(uint8_t type, tAVDT_SCB* p_scb);
-extern tAVDT_TC_TBL* avdt_ad_tc_tbl_by_st(uint8_t type, tAVDT_CCB* p_ccb,
-                                          uint8_t state);
-extern tAVDT_TC_TBL* avdt_ad_tc_tbl_by_lcid(uint16_t lcid);
-extern tAVDT_TC_TBL* avdt_ad_tc_tbl_alloc(tAVDT_CCB* p_ccb);
-extern uint8_t avdt_ad_tc_tbl_to_idx(tAVDT_TC_TBL* p_tbl);
-extern void avdt_ad_tc_close_ind(tAVDT_TC_TBL* p_tbl, uint16_t reason);
-extern void avdt_ad_tc_open_ind(tAVDT_TC_TBL* p_tbl);
-extern void avdt_ad_tc_cong_ind(tAVDT_TC_TBL* p_tbl, bool is_congested);
-extern void avdt_ad_tc_data_ind(tAVDT_TC_TBL* p_tbl, BT_HDR* p_buf);
-extern tAVDT_TC_TBL* avdt_ad_tc_tbl_by_type(uint8_t type, tAVDT_CCB* p_ccb,
-                                            tAVDT_SCB* p_scb);
-extern uint8_t avdt_ad_write_req(uint8_t type, tAVDT_CCB* p_ccb,
-                                 tAVDT_SCB* p_scb, BT_HDR* p_buf);
-extern void avdt_ad_open_req(uint8_t type, tAVDT_CCB* p_ccb, tAVDT_SCB* p_scb,
+extern uint8_t avdt_ad_type_to_tcid(uint8_t type, AvdtpScb* p_scb);
+extern AvdtpTransportChannel* avdt_ad_tc_tbl_by_st(uint8_t type,
+                                                   AvdtpCcb* p_ccb,
+                                                   uint8_t state);
+extern AvdtpTransportChannel* avdt_ad_tc_tbl_by_lcid(uint16_t lcid);
+extern AvdtpTransportChannel* avdt_ad_tc_tbl_alloc(AvdtpCcb* p_ccb);
+extern uint8_t avdt_ad_tc_tbl_to_idx(AvdtpTransportChannel* p_tbl);
+extern void avdt_ad_tc_close_ind(AvdtpTransportChannel* p_tbl, uint16_t reason);
+extern void avdt_ad_tc_open_ind(AvdtpTransportChannel* p_tbl);
+extern void avdt_ad_tc_cong_ind(AvdtpTransportChannel* p_tbl,
+                                bool is_congested);
+extern void avdt_ad_tc_data_ind(AvdtpTransportChannel* p_tbl, BT_HDR* p_buf);
+extern AvdtpTransportChannel* avdt_ad_tc_tbl_by_type(uint8_t type,
+                                                     AvdtpCcb* p_ccb,
+                                                     AvdtpScb* p_scb);
+extern uint8_t avdt_ad_write_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb,
+                                 BT_HDR* p_buf);
+extern void avdt_ad_open_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb,
                              uint8_t role);
-extern void avdt_ad_close_req(uint8_t type, tAVDT_CCB* p_ccb, tAVDT_SCB* p_scb);
+extern void avdt_ad_close_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb);
 
 extern void avdt_ccb_idle_ccb_timer_timeout(void* data);
 extern void avdt_ccb_ret_ccb_timer_timeout(void* data);
@@ -689,7 +996,7 @@
 /******************************************************************************
  * Main Control Block
  ******************************************************************************/
-extern tAVDT_CB avdt_cb;
+extern AvdtpCb avdtp_cb;
 
 /* L2CAP callback registration structure */
 extern const tL2CAP_APPL_INFO avdt_l2c_appl;
diff --git a/stack/avdt/avdt_l2c.cc b/stack/avdt/avdt_l2c.cc
index 787e302..1489859 100644
--- a/stack/avdt/avdt_l2c.cc
+++ b/stack/avdt/avdt_l2c.cc
@@ -76,9 +76,9 @@
                                          tBT_TRANSPORT transport,
                                          UNUSED_ATTR void* p_ref_data,
                                          uint8_t res) {
-  tAVDT_CCB* p_ccb = NULL;
+  AvdtpCcb* p_ccb = NULL;
   tL2CAP_CFG_INFO cfg;
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
 
   AVDT_TRACE_DEBUG("avdt_sec_check_complete_term res: %d", res);
   p_ccb = avdt_ccb_by_bd(*bd_addr);
@@ -92,9 +92,9 @@
                     L2CAP_CONN_OK);
 
     /* store idx in LCID table, store LCID in routing table */
-    avdt_cb.ad.lcid_tbl[p_tbl->lcid - L2CAP_BASE_APPL_CID] =
+    avdtp_cb.ad.lcid_tbl[p_tbl->lcid - L2CAP_BASE_APPL_CID] =
         avdt_ad_tc_tbl_to_idx(p_tbl);
-    avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = p_tbl->lcid;
+    avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = p_tbl->lcid;
 
     /* transition to configuration state */
     p_tbl->state = AVDT_AD_ST_CFG;
@@ -127,9 +127,9 @@
                                          tBT_TRANSPORT trasnport,
                                          UNUSED_ATTR void* p_ref_data,
                                          uint8_t res) {
-  tAVDT_CCB* p_ccb = NULL;
+  AvdtpCcb* p_ccb = NULL;
   tL2CAP_CFG_INFO cfg;
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
 
   AVDT_TRACE_DEBUG("avdt_sec_check_complete_orig res: %d", res);
   if (bd_addr) p_ccb = avdt_ccb_by_bd(*bd_addr);
@@ -164,8 +164,8 @@
  ******************************************************************************/
 void avdt_l2c_connect_ind_cback(const RawAddress& bd_addr, uint16_t lcid,
                                 UNUSED_ATTR uint16_t psm, uint8_t id) {
-  tAVDT_CCB* p_ccb;
-  tAVDT_TC_TBL* p_tbl = NULL;
+  AvdtpCcb* p_ccb;
+  AvdtpTransportChannel* p_tbl = NULL;
   uint16_t result;
   tL2CAP_CFG_INFO cfg;
   tBTM_STATUS rc;
@@ -181,7 +181,7 @@
     } else {
       /* allocate and set up entry; first channel is always signaling */
       p_tbl = avdt_ad_tc_tbl_alloc(p_ccb);
-      p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu;
+      p_tbl->my_mtu = avdtp_cb.rcb.ctrl_mtu;
       p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO;
       p_tbl->tcid = AVDT_CHAN_SIG;
       p_tbl->lcid = lcid;
@@ -244,9 +244,9 @@
   /* if result ok, proceed with connection */
   if (result == L2CAP_CONN_OK) {
     /* store idx in LCID table, store LCID in routing table */
-    avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] =
+    avdtp_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] =
         avdt_ad_tc_tbl_to_idx(p_tbl);
-    avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid;
+    avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid;
 
     /* transition to configuration state */
     p_tbl->state = AVDT_AD_ST_CFG;
@@ -272,9 +272,9 @@
  *
  ******************************************************************************/
 void avdt_l2c_connect_cfm_cback(uint16_t lcid, uint16_t result) {
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
   tL2CAP_CFG_INFO cfg;
-  tAVDT_CCB* p_ccb;
+  AvdtpCcb* p_ccb;
 
   AVDT_TRACE_DEBUG("avdt_l2c_connect_cfm_cback lcid: %d, result: %d", lcid,
                    result);
@@ -345,7 +345,9 @@
  *
  ******************************************************************************/
 void avdt_l2c_config_cfm_cback(uint16_t lcid, tL2CAP_CFG_INFO* p_cfg) {
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
+
+  AVDT_TRACE_DEBUG("%s: lcid: %d", __func__, lcid);
 
   /* look up info for this channel */
   p_tbl = avdt_ad_tc_tbl_by_lcid(lcid);
@@ -384,7 +386,9 @@
  *
  ******************************************************************************/
 void avdt_l2c_config_ind_cback(uint16_t lcid, tL2CAP_CFG_INFO* p_cfg) {
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
+
+  AVDT_TRACE_DEBUG("%s: lcid: %d", __func__, lcid);
 
   /* look up info for this channel */
   p_tbl = avdt_ad_tc_tbl_by_lcid(lcid);
@@ -395,7 +399,8 @@
     } else {
       p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
     }
-    AVDT_TRACE_DEBUG("peer_mtu: %d, lcid: x%x", p_tbl->peer_mtu, lcid);
+    AVDT_TRACE_DEBUG("%s: peer_mtu: %d, lcid: %d", __func__, p_tbl->peer_mtu,
+                     lcid);
 
     /* send L2CAP configure response */
     memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO));
@@ -426,7 +431,7 @@
  *
  ******************************************************************************/
 void avdt_l2c_disconnect_ind_cback(uint16_t lcid, bool ack_needed) {
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
 
   AVDT_TRACE_DEBUG("avdt_l2c_disconnect_ind_cback lcid: %d, ack_needed: %d",
                    lcid, ack_needed);
@@ -453,7 +458,7 @@
  *
  ******************************************************************************/
 void avdt_l2c_disconnect_cfm_cback(uint16_t lcid, uint16_t result) {
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
 
   AVDT_TRACE_DEBUG("avdt_l2c_disconnect_cfm_cback lcid: %d, result: %d", lcid,
                    result);
@@ -475,7 +480,7 @@
  *
  ******************************************************************************/
 void avdt_l2c_congestion_ind_cback(uint16_t lcid, bool is_congested) {
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
 
   /* look up info for this channel */
   p_tbl = avdt_ad_tc_tbl_by_lcid(lcid);
@@ -495,7 +500,7 @@
  *
  ******************************************************************************/
 void avdt_l2c_data_ind_cback(uint16_t lcid, BT_HDR* p_buf) {
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
 
   /* look up info for this channel */
   p_tbl = avdt_ad_tc_tbl_by_lcid(lcid);
diff --git a/stack/avdt/avdt_msg.cc b/stack/avdt/avdt_msg.cc
index 908b923..618424f 100644
--- a/stack/avdt/avdt_msg.cc
+++ b/stack/avdt/avdt_msg.cc
@@ -271,7 +271,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-static void avdt_msg_bld_cfg(uint8_t** p, tAVDT_CFG* p_cfg) {
+static void avdt_msg_bld_cfg(uint8_t** p, AvdtpSepConfig* p_cfg) {
   uint8_t len;
 
   /* for now, just build media transport, codec, and content protection, and
@@ -467,10 +467,9 @@
  *
  ******************************************************************************/
 static void avdt_msg_bld_svccap(uint8_t** p, tAVDT_MSG* p_msg) {
-  tAVDT_CFG cfg;
+  AvdtpSepConfig cfg = *p_msg->svccap.p_cfg;
 
   /* make sure the delay report category is not reported */
-  memcpy(&cfg, p_msg->svccap.p_cfg, sizeof(tAVDT_CFG));
   cfg.psc_mask &= ~AVDT_PSC_DELAY_RPT;
   avdt_msg_bld_cfg(p, &cfg);
 }
@@ -518,7 +517,7 @@
  *                  in p_elem.
  *
  ******************************************************************************/
-static uint8_t avdt_msg_prs_cfg(tAVDT_CFG* p_cfg, uint8_t* p, uint16_t len,
+static uint8_t avdt_msg_prs_cfg(AvdtpSepConfig* p_cfg, uint8_t* p, uint16_t len,
                                 uint8_t* p_elem, uint8_t sig_id) {
   uint8_t* p_end;
   uint8_t elem = 0;
@@ -1023,11 +1022,11 @@
  * Returns          Congested state; true if CCB congested, false if not.
  *
  ******************************************************************************/
-bool avdt_msg_send(tAVDT_CCB* p_ccb, BT_HDR* p_msg) {
+bool avdt_msg_send(AvdtpCcb* p_ccb, BT_HDR* p_msg) {
   uint16_t curr_msg_len;
   uint8_t pkt_type;
   uint8_t hdr_len;
-  tAVDT_TC_TBL* p_tbl;
+  AvdtpTransportChannel* p_tbl;
   BT_HDR* p_buf;
   uint8_t* p;
   uint8_t label;
@@ -1119,16 +1118,16 @@
       if (msg == AVDT_MSG_TYPE_CMD) {
         /* if retransmit timeout set to zero, sig doesn't use retransmit */
         if ((sig == AVDT_SIG_DISCOVER) || (sig == AVDT_SIG_GETCAP) ||
-            (sig == AVDT_SIG_SECURITY) || (avdt_cb.rcb.ret_tout == 0)) {
+            (sig == AVDT_SIG_SECURITY) || (avdtp_cb.rcb.ret_tout == 0)) {
           alarm_cancel(p_ccb->idle_ccb_timer);
           alarm_cancel(p_ccb->ret_ccb_timer);
-          period_ms_t interval_ms = avdt_cb.rcb.sig_tout * 1000;
+          period_ms_t interval_ms = avdtp_cb.rcb.sig_tout * 1000;
           alarm_set_on_mloop(p_ccb->rsp_ccb_timer, interval_ms,
                              avdt_ccb_rsp_ccb_timer_timeout, p_ccb);
         } else if (sig != AVDT_SIG_DELAY_RPT) {
           alarm_cancel(p_ccb->idle_ccb_timer);
           alarm_cancel(p_ccb->rsp_ccb_timer);
-          period_ms_t interval_ms = avdt_cb.rcb.ret_tout * 1000;
+          period_ms_t interval_ms = avdtp_cb.rcb.ret_tout * 1000;
           alarm_set_on_mloop(p_ccb->ret_ccb_timer, interval_ms,
                              avdt_ccb_ret_ccb_timer_timeout, p_ccb);
         }
@@ -1171,7 +1170,7 @@
  *                  available.
  *
  ******************************************************************************/
-BT_HDR* avdt_msg_asmbl(tAVDT_CCB* p_ccb, BT_HDR* p_buf) {
+BT_HDR* avdt_msg_asmbl(AvdtpCcb* p_ccb, BT_HDR* p_buf) {
   uint8_t* p;
   uint8_t pkt_type;
   BT_HDR* p_ret;
@@ -1292,7 +1291,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_msg_send_cmd(tAVDT_CCB* p_ccb, void* p_scb, uint8_t sig_id,
+void avdt_msg_send_cmd(AvdtpCcb* p_ccb, void* p_scb, uint8_t sig_id,
                        tAVDT_MSG* p_params) {
   uint8_t* p;
   uint8_t* p_start;
@@ -1318,7 +1317,7 @@
     }
     /* for all others, p_scb points to scb as usual */
     else {
-      *p = avdt_scb_to_hdl((tAVDT_SCB*)p_scb);
+      *p = avdt_scb_to_hdl((AvdtpScb*)p_scb);
     }
   }
 
@@ -1349,7 +1348,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_msg_send_rsp(tAVDT_CCB* p_ccb, uint8_t sig_id, tAVDT_MSG* p_params) {
+void avdt_msg_send_rsp(AvdtpCcb* p_ccb, uint8_t sig_id, tAVDT_MSG* p_params) {
   uint8_t* p;
   uint8_t* p_start;
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE);
@@ -1389,7 +1388,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_msg_send_rej(tAVDT_CCB* p_ccb, uint8_t sig_id, tAVDT_MSG* p_params) {
+void avdt_msg_send_rej(AvdtpCcb* p_ccb, uint8_t sig_id, tAVDT_MSG* p_params) {
   uint8_t* p;
   uint8_t* p_start;
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE);
@@ -1440,7 +1439,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_msg_send_grej(tAVDT_CCB* p_ccb, uint8_t sig_id, tAVDT_MSG* p_params) {
+void avdt_msg_send_grej(AvdtpCcb* p_ccb, uint8_t sig_id, tAVDT_MSG* p_params) {
   uint8_t* p;
   uint8_t* p_start;
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE);
@@ -1476,8 +1475,8 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_msg_ind(tAVDT_CCB* p_ccb, BT_HDR* p_buf) {
-  tAVDT_SCB* p_scb;
+void avdt_msg_ind(AvdtpCcb* p_ccb, BT_HDR* p_buf) {
+  AvdtpScb* p_scb;
   uint8_t* p;
   bool ok = true;
   bool handle_rsp = false;
@@ -1487,7 +1486,7 @@
   uint8_t msg_type;
   uint8_t sig = 0;
   tAVDT_MSG msg;
-  tAVDT_CFG cfg;
+  AvdtpSepConfig cfg;
   uint8_t err;
   uint8_t evt = 0;
   uint8_t scb_hdl;
@@ -1552,7 +1551,7 @@
     } else if ((msg_type == AVDT_MSG_TYPE_RSP) &&
                ((sig == AVDT_SIG_GETCAP) || (sig == AVDT_SIG_GET_ALLCAP))) {
       /* parse discover rsp message to struct supplied by app */
-      msg.svccap.p_cfg = (tAVDT_CFG*)p_ccb->p_proc_data;
+      msg.svccap.p_cfg = (AvdtpSepConfig*)p_ccb->p_proc_data;
     } else if ((msg_type == AVDT_MSG_TYPE_RSP) && (sig == AVDT_SIG_GETCONFIG)) {
       /* parse get config rsp message to struct allocated locally */
       msg.svccap.p_cfg = &cfg;
diff --git a/stack/avdt/avdt_scb.cc b/stack/avdt/avdt_scb.cc
index c0772f1..eacdf83 100644
--- a/stack/avdt/avdt_scb.cc
+++ b/stack/avdt/avdt_scb.cc
@@ -755,15 +755,16 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_event(tAVDT_SCB* p_scb, uint8_t event, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_event(AvdtpScb* p_scb, uint8_t event, tAVDT_SCB_EVT* p_data) {
   tAVDT_SCB_ST_TBL state_table;
   uint8_t action;
   int i;
 
 #if (AVDT_DEBUG == TRUE)
-  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]);
+  AVDT_TRACE_EVENT(
+      "%s: SCB hdl=%d event=%d/%s state=%s p_avdt_scb=%p scb_index=%d",
+      __func__, avdt_scb_to_hdl(p_scb), event, avdt_scb_evt_str[event],
+      avdt_scb_st_str[p_scb->state], p_scb, p_scb->stream_config.scb_index);
 #endif
   /* set current event */
   p_scb->curr_evt = event;
@@ -780,7 +781,7 @@
   for (i = 0; i < AVDT_SCB_ACTIONS; i++) {
     action = state_table[event][i];
     if (action != AVDT_SCB_IGNORE) {
-      (*avdt_cb.p_scb_act[action])(p_scb, p_data);
+      (*avdtp_cb.p_scb_act[action])(p_scb, p_data);
     } else {
       break;
     }
@@ -798,8 +799,13 @@
  *
  ******************************************************************************/
 void avdt_scb_init(void) {
-  memset(avdt_cb.scb, 0, sizeof(tAVDT_SCB) * AVDT_NUM_SEPS);
-  avdt_cb.p_scb_act = avdt_scb_action;
+  for (size_t i = 0; i < AVDT_NUM_LINKS; i++) {
+    for (size_t j = 0; j < AVDT_NUM_SEPS; j++) {
+      avdtp_cb.ccb[i].scb[j].Reset(0);
+    }
+  }
+
+  avdtp_cb.p_scb_act = avdt_scb_action;
 }
 
 /*******************************************************************************
@@ -812,33 +818,34 @@
  * Returns          pointer to the scb, or NULL if none could be allocated.
  *
  ******************************************************************************/
-tAVDT_SCB* avdt_scb_alloc(tAVDT_CS* p_cs) {
-  tAVDT_SCB* p_scb = &avdt_cb.scb[0];
-  int i;
+AvdtpScb* avdt_scb_alloc(uint8_t peer_id,
+                         const AvdtpStreamConfig& avdtp_stream_config) {
+  CHECK(peer_id < AVDT_NUM_LINKS);
 
-  /* find available scb */
-  for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) {
+  // Find available entry
+  AvdtpScb* p_scb = &avdtp_cb.ccb[peer_id].scb[0];
+  for (int i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) {
     if (!p_scb->allocated) {
-      memset(p_scb, 0, sizeof(tAVDT_SCB));
-      p_scb->allocated = true;
-      p_scb->p_ccb = NULL;
-
-      memcpy(&p_scb->cs, p_cs, sizeof(tAVDT_CS));
-      p_scb->transport_channel_timer =
-          alarm_new("avdt_scb.transport_channel_timer");
-      AVDT_TRACE_DEBUG("%s: hdl=%d, psc_mask:0x%x", __func__, i + 1,
-                       p_cs->cfg.psc_mask);
-      break;
+      p_scb->Allocate(&avdtp_cb.ccb[peer_id], avdtp_stream_config);
+      AVDT_TRACE_DEBUG("%s: allocated (handle=%d, psc_mask:0x%x)", __func__,
+                       p_scb->ScbHandle(), avdtp_stream_config.cfg.psc_mask);
+      return p_scb;
     }
   }
 
-  if (i == AVDT_NUM_SEPS) {
-    /* out of ccbs */
-    p_scb = NULL;
-    AVDT_TRACE_WARNING("Out of scbs");
-  }
+  AVDT_TRACE_WARNING("%s: out of AvdtScb entries for peer_id %d", __func__,
+                     peer_id);
+  return nullptr;
+}
 
-  return p_scb;
+void AvdtpScb::Allocate(AvdtpCcb* p_avdtp_ccb,
+                        const AvdtpStreamConfig& avdtp_stream_config) {
+  uint8_t scb_handle = avdtp_cb.ComputeScbHandle(this);
+  Reset(scb_handle);
+  p_ccb = p_avdtp_ccb;
+  stream_config = avdtp_stream_config;
+  transport_channel_timer = alarm_new("avdtp_scb.transport_channel_timer");
+  allocated = true;
 }
 
 /*******************************************************************************
@@ -851,10 +858,9 @@
  * Returns          void.
  *
  ******************************************************************************/
-void avdt_scb_dealloc(tAVDT_SCB* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
+void avdt_scb_dealloc(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   AVDT_TRACE_DEBUG("%s: hdl=%d", __func__, avdt_scb_to_hdl(p_scb));
-  alarm_free(p_scb->transport_channel_timer);
-  memset(p_scb, 0, sizeof(tAVDT_SCB));
+  p_scb->Recycle();
 }
 
 /*******************************************************************************
@@ -867,9 +873,7 @@
  * Returns          Index of scb.
  *
  ******************************************************************************/
-uint8_t avdt_scb_to_hdl(tAVDT_SCB* p_scb) {
-  return (uint8_t)(p_scb - avdt_cb.scb + 1);
-}
+uint8_t avdt_scb_to_hdl(AvdtpScb* p_scb) { return p_scb->ScbHandle(); }
 
 /*******************************************************************************
  *
@@ -882,22 +886,26 @@
  *                  is not allocated.
  *
  ******************************************************************************/
-tAVDT_SCB* avdt_scb_by_hdl(uint8_t hdl) {
-  tAVDT_SCB* p_scb;
-
-  /* verify index */
-  if ((hdl > 0) && (hdl <= AVDT_NUM_SEPS)) {
-    p_scb = &avdt_cb.scb[hdl - 1];
-
-    /* verify scb is allocated */
-    if (!p_scb->allocated) {
-      p_scb = NULL;
-      AVDT_TRACE_WARNING("scb hdl %d not allocated", hdl);
-    }
-  } else {
-    p_scb = NULL;
-    AVDT_TRACE_WARNING("scb hdl %d out of range", hdl);
+AvdtpScb* avdt_scb_by_hdl(uint8_t hdl) {
+  // Verify the index
+  if ((hdl < 1) || (hdl > AVDT_NUM_LINKS * AVDT_NUM_SEPS)) {
+    AVDT_TRACE_WARNING("%s: SCB handle %d out of range", __func__, hdl);
+    return nullptr;
   }
+
+  uint8_t index = hdl - 1;
+  size_t i = index / AVDT_NUM_LINKS;
+  size_t j = index % AVDT_NUM_SEPS;
+
+  AvdtpScb* p_scb = &avdtp_cb.ccb[i].scb[j];
+  // Verify the whether the scb is allocated
+  if (!p_scb->allocated) {
+    AVDT_TRACE_WARNING("%s: SCB handle %d not allocated", __func__, hdl);
+    return nullptr;
+  }
+
+  AVDT_TRACE_DEBUG("%s: SCB for handle %d found: p_scb=%p scb_index=%d",
+                   __func__, hdl, p_scb, p_scb->stream_config.scb_index);
   return p_scb;
 }
 
@@ -911,10 +919,10 @@
  * Returns          SEID that failed, or 0 if success.
  *
  ******************************************************************************/
-uint8_t avdt_scb_verify(tAVDT_CCB* p_ccb, uint8_t state, uint8_t* p_seid,
+uint8_t avdt_scb_verify(AvdtpCcb* p_ccb, uint8_t state, uint8_t* p_seid,
                         uint16_t num_seid, uint8_t* p_err_code) {
   int i;
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
   uint8_t nsc_mask;
   uint8_t ret = 0;
 
@@ -922,7 +930,9 @@
   /* set nonsupported command mask */
   /* translate public state into private state */
   nsc_mask = 0;
-  if (state == AVDT_VERIFY_SUSPEND) nsc_mask = AVDT_NSC_SUSPEND;
+  if (state == AVDT_VERIFY_SUSPEND) {
+    nsc_mask = AvdtpStreamConfig::AVDT_NSC_SUSPEND;
+  }
 
   /* verify every scb */
   for (i = 0, *p_err_code = 0;
@@ -932,7 +942,7 @@
       *p_err_code = AVDT_ERR_BAD_STATE;
     else if (p_scb->p_ccb != p_ccb)
       *p_err_code = AVDT_ERR_BAD_STATE;
-    else if (p_scb->cs.nsc_mask & nsc_mask)
+    else if (p_scb->stream_config.nsc_mask & nsc_mask)
       *p_err_code = AVDT_ERR_NSC;
 
     switch (state) {
@@ -971,7 +981,7 @@
  ******************************************************************************/
 void avdt_scb_peer_seid_list(tAVDT_MULTI* p_multi) {
   int i;
-  tAVDT_SCB* p_scb;
+  AvdtpScb* p_scb;
 
   for (i = 0; i < p_multi->num_seps; i++) {
     p_scb = avdt_scb_by_hdl(p_multi->seid_list[i]);
diff --git a/stack/avdt/avdt_scb_act.cc b/stack/avdt/avdt_scb_act.cc
index 826e4b9..824dbcb 100644
--- a/stack/avdt/avdt_scb_act.cc
+++ b/stack/avdt/avdt_scb_act.cc
@@ -62,10 +62,10 @@
  * Returns          SSRC value.
  *
  ******************************************************************************/
-uint32_t avdt_scb_gen_ssrc(tAVDT_SCB* p_scb) {
+uint32_t avdt_scb_gen_ssrc(AvdtpScb* p_scb) {
   /* combine the value of the media type and codec type of the SCB */
-  return (
-      (uint32_t)(p_scb->cs.cfg.codec_info[1] | p_scb->cs.cfg.codec_info[2]));
+  return ((uint32_t)(p_scb->stream_config.cfg.codec_info[1] |
+                     p_scb->stream_config.cfg.codec_info[2]));
 }
 
 /*******************************************************************************
@@ -78,7 +78,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_abort_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_abort_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   p_scb->role = AVDT_CLOSE_ACP;
   avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_RSP_EVT, p_data);
 }
@@ -93,7 +93,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_abort_rsp(UNUSED_ATTR tAVDT_SCB* p_scb,
+void avdt_scb_hdl_abort_rsp(UNUSED_ATTR AvdtpScb* p_scb,
                             UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   return;
 }
@@ -108,7 +108,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_close_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_close_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   p_scb->role = AVDT_CLOSE_ACP;
   avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_RSP_EVT, p_data);
 }
@@ -123,7 +123,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_close_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_close_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   p_scb->close_code = p_data->msg.hdr.err_code;
 }
 
@@ -138,7 +138,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_getconfig_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_getconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   p_data->msg.svccap.p_cfg = &p_scb->curr_cfg;
 
   avdt_scb_event(p_scb, AVDT_SCB_API_GETCONFIG_RSP_EVT, p_data);
@@ -154,7 +154,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_getconfig_rsp(UNUSED_ATTR tAVDT_SCB* p_scb,
+void avdt_scb_hdl_getconfig_rsp(UNUSED_ATTR AvdtpScb* p_scb,
                                 UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   return;
 }
@@ -169,7 +169,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_open_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_open_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_RSP_EVT, p_data);
 }
 
@@ -185,7 +185,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_open_rej(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_open_rej(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   /* do exactly same as setconfig reject */
   avdt_scb_hdl_setconfig_rej(p_scb, p_data);
 }
@@ -200,8 +200,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_open_rsp(tAVDT_SCB* p_scb,
-                           UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_open_rsp(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   /* initiate opening of trans channels for this SEID */
   p_scb->role = AVDT_OPEN_INT;
   avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_INT);
@@ -221,7 +220,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_pkt_no_frag(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_pkt_no_frag(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   uint8_t *p, *p_start;
   uint8_t o_v, o_p, o_x, o_cc;
   uint8_t m_pt;
@@ -271,12 +270,12 @@
     p_data->p_pkt->len -= (offset + pad_len);
     p_data->p_pkt->offset += offset;
 
-    if (p_scb->cs.p_sink_data_cback != NULL) {
+    if (p_scb->stream_config.p_sink_data_cback != NULL) {
       /* report sequence number */
       p_data->p_pkt->layer_specific = seq;
-      (*p_scb->cs.p_sink_data_cback)(avdt_scb_to_hdl(p_scb), p_data->p_pkt,
-                                     time_stamp,
-                                     (uint8_t)(m_pt | (marker << 7)));
+      (*p_scb->stream_config.p_sink_data_cback)(
+          avdt_scb_to_hdl(p_scb), p_data->p_pkt, time_stamp,
+          (uint8_t)(m_pt | (marker << 7)));
     } else {
       osi_free_and_reset((void**)&p_data->p_pkt);
     }
@@ -292,7 +291,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-uint8_t* avdt_scb_hdl_report(tAVDT_SCB* p_scb, uint8_t* p, uint16_t len) {
+uint8_t* avdt_scb_hdl_report(AvdtpScb* p_scb, uint8_t* p, uint16_t len) {
   uint16_t result = AVDT_SUCCESS;
   uint8_t* p_start = p;
   uint32_t ssrc;
@@ -301,7 +300,7 @@
   tAVDT_REPORT_DATA report;
 
   AVDT_TRACE_DEBUG("%s", __func__);
-  if (p_scb->cs.p_report_cback) {
+  if (p_scb->stream_config.p_report_cback) {
     /* parse report packet header */
     AVDT_MSG_PRS_RPT_OCTET1(p, o_v, o_p, o_cc);
     pt = *p++;
@@ -351,7 +350,8 @@
     }
 
     if (result == AVDT_SUCCESS)
-      (*p_scb->cs.p_report_cback)(avdt_scb_to_hdl(p_scb), pt, &report);
+      (*p_scb->stream_config.p_report_cback)(avdt_scb_to_hdl(p_scb), pt,
+                                             &report);
   }
   p_start += len;
   return p_start;
@@ -366,7 +366,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_pkt(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_pkt(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   if (p_data->p_pkt->layer_specific == AVDT_CHAN_REPORT) {
     uint8_t* p = (uint8_t*)(p_data->p_pkt + 1) + p_data->p_pkt->offset;
     avdt_scb_hdl_report(p_scb, p, p_data->p_pkt->len);
@@ -386,7 +386,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_drop_pkt(UNUSED_ATTR tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_drop_pkt(UNUSED_ATTR AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   AVDT_TRACE_ERROR("%s dropped incoming media packet", __func__);
   osi_free_and_reset((void**)&p_data->p_pkt);
 }
@@ -401,21 +401,21 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_reconfig_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_reconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   /* if command not supported */
-  if (p_scb->cs.nsc_mask & AVDT_NSC_RECONFIG) {
+  if (p_scb->stream_config.nsc_mask & AvdtpStreamConfig::AVDT_NSC_RECONFIG) {
     /* send reject */
     p_data->msg.hdr.err_code = AVDT_ERR_NSC;
     p_data->msg.hdr.err_param = 0;
     avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, p_data);
   } else {
     /* store requested configuration */
-    memcpy(&p_scb->req_cfg, p_data->msg.reconfig_cmd.p_cfg, sizeof(tAVDT_CFG));
+    p_scb->req_cfg = *p_data->msg.reconfig_cmd.p_cfg;
 
     /* call application callback */
-    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL,
-                              AVDT_RECONFIG_IND_EVT,
-                              (tAVDT_CTRL*)&p_data->msg.reconfig_cmd);
+    (*p_scb->stream_config.p_avdt_ctrl_cback)(
+        avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_RECONFIG_IND_EVT,
+        (tAVDT_CTRL*)&p_data->msg.reconfig_cmd, p_scb->stream_config.scb_index);
   }
 }
 
@@ -429,7 +429,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_reconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_reconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   if (p_data->msg.hdr.err_code == 0) {
     /* store new configuration */
     if (p_scb->req_cfg.num_codec > 0) {
@@ -447,8 +447,9 @@
   p_data->msg.svccap.p_cfg = &p_scb->curr_cfg;
 
   /* call application callback */
-  (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_RECONFIG_CFM_EVT,
-                            (tAVDT_CTRL*)&p_data->msg.svccap);
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_RECONFIG_CFM_EVT,
+      (tAVDT_CTRL*)&p_data->msg.svccap, p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -461,17 +462,17 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_security_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_security_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   /* if command not supported */
-  if (p_scb->cs.nsc_mask & AVDT_NSC_SECURITY) {
+  if (p_scb->stream_config.nsc_mask & AvdtpStreamConfig::AVDT_NSC_SECURITY) {
     /* send reject */
     p_data->msg.hdr.err_code = AVDT_ERR_NSC;
     avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, p_data);
   } else {
     /* call application callback */
-    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL,
-                              AVDT_SECURITY_IND_EVT,
-                              (tAVDT_CTRL*)&p_data->msg.security_cmd);
+    (*p_scb->stream_config.p_avdt_ctrl_cback)(
+        avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_SECURITY_IND_EVT,
+        (tAVDT_CTRL*)&p_data->msg.security_cmd, p_scb->stream_config.scb_index);
   }
 }
 
@@ -485,10 +486,11 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_security_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_security_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   /* call application callback */
-  (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_SECURITY_CFM_EVT,
-                            (tAVDT_CTRL*)&p_data->msg.security_cmd);
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_SECURITY_CFM_EVT,
+      (tAVDT_CTRL*)&p_data->msg.security_cmd, p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -502,30 +504,40 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_setconfig_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
-  tAVDT_CFG* p_cfg;
-
-  AVDT_TRACE_DEBUG("%s: p_scb->in_use=%d", __func__, p_scb->in_use);
+void avdt_scb_hdl_setconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
+  AVDT_TRACE_DEBUG("%s: p_scb->in_use=%d p_avdt_scb=%p scb_index=%d", __func__,
+                   p_scb->in_use, p_scb, p_scb->stream_config.scb_index);
 
   if (!p_scb->in_use) {
-    A2DP_DumpCodecInfo(p_scb->cs.cfg.codec_info);
+    A2DP_DumpCodecInfo(p_scb->stream_config.cfg.codec_info);
     A2DP_DumpCodecInfo(p_data->msg.config_cmd.p_cfg->codec_info);
-    p_cfg = p_data->msg.config_cmd.p_cfg;
-    if (A2DP_GetCodecType(p_scb->cs.cfg.codec_info) ==
+    AvdtpSepConfig* p_cfg = p_data->msg.config_cmd.p_cfg;
+    if (A2DP_GetCodecType(p_scb->stream_config.cfg.codec_info) ==
         A2DP_GetCodecType(p_cfg->codec_info)) {
+      /* copy info to scb */
+      AvdtpCcb* p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx);
+      if (p_scb->p_ccb != p_ccb) {
+        AVDT_TRACE_ERROR(
+            "%s: mismatch in AVDTP SCB/CCB state: (p_scb->p_ccb=%p != "
+            "p_ccb=%p): "
+            "p_scb=%p scb_handle=%d ccb_idx=%d",
+            __func__, p_scb->p_ccb, p_ccb, p_scb, p_scb->ScbHandle(),
+            p_data->msg.config_cmd.hdr.ccb_idx);
+        avdt_scb_rej_not_in_use(p_scb, p_data);
+        return;
+      }
       /* set sep as in use */
       p_scb->in_use = true;
 
-      /* copy info to scb */
-      p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx);
       p_scb->peer_seid = p_data->msg.config_cmd.int_seid;
-      memcpy(&p_scb->req_cfg, p_cfg, sizeof(tAVDT_CFG));
+      p_scb->req_cfg = *p_cfg;
       /* call app callback */
       /* handle of scb- which is same as sep handle of bta_av_cb.p_scb*/
-      (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
-                                p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-                                AVDT_CONFIG_IND_EVT,
-                                (tAVDT_CTRL*)&p_data->msg.config_cmd);
+      (*p_scb->stream_config.p_avdt_ctrl_cback)(
+          avdt_scb_to_hdl(p_scb),
+          p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty,
+          AVDT_CONFIG_IND_EVT, (tAVDT_CTRL*)&p_data->msg.config_cmd,
+          p_scb->stream_config.scb_index);
     } else {
       p_data->msg.hdr.err_code = AVDT_ERR_UNSUP_CFG;
       p_data->msg.hdr.err_param = 0;
@@ -549,7 +561,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_setconfig_rej(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_setconfig_rej(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   /* clear scb variables */
   avdt_scb_clr_vars(p_scb, p_data);
 
@@ -558,8 +570,9 @@
                  AVDT_CCB_UL_CLOSE_EVT, NULL);
 
   /* call application callback */
-  (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_OPEN_CFM_EVT,
-                            (tAVDT_CTRL*)&p_data->msg.hdr);
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_OPEN_CFM_EVT,
+      (tAVDT_CTRL*)&p_data->msg.hdr, p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -572,13 +585,13 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB* p_scb,
+void avdt_scb_hdl_setconfig_rsp(AvdtpScb* p_scb,
                                 UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   tAVDT_EVT_HDR single;
 
   if (p_scb->p_ccb != NULL) {
     /* save configuration */
-    memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG));
+    p_scb->curr_cfg = p_scb->req_cfg;
 
     /* initiate open */
     single.seid = p_scb->peer_seid;
@@ -598,11 +611,12 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_start_cmd(tAVDT_SCB* p_scb,
+void avdt_scb_hdl_start_cmd(AvdtpScb* p_scb,
                             UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
-  (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
-                            p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-                            AVDT_START_IND_EVT, NULL);
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb),
+      p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty,
+      AVDT_START_IND_EVT, NULL, p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -615,10 +629,12 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_start_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
-  (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
-                            p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-                            AVDT_START_CFM_EVT, (tAVDT_CTRL*)&p_data->msg.hdr);
+void avdt_scb_hdl_start_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb),
+      p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty,
+      AVDT_START_CFM_EVT, (tAVDT_CTRL*)&p_data->msg.hdr,
+      p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -631,11 +647,12 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_suspend_cmd(tAVDT_SCB* p_scb,
+void avdt_scb_hdl_suspend_cmd(AvdtpScb* p_scb,
                               UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
-  (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
-                            p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-                            AVDT_SUSPEND_IND_EVT, NULL);
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb),
+      p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty,
+      AVDT_SUSPEND_IND_EVT, NULL, p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -648,10 +665,12 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_suspend_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
-  (*p_scb->cs.p_ctrl_cback)(
-      avdt_scb_to_hdl(p_scb), p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-      AVDT_SUSPEND_CFM_EVT, (tAVDT_CTRL*)&p_data->msg.hdr);
+void avdt_scb_hdl_suspend_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb),
+      p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty,
+      AVDT_SUSPEND_CFM_EVT, (tAVDT_CTRL*)&p_data->msg.hdr,
+      p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -670,13 +689,14 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_tc_close(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_tc_close(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   uint8_t hdl = avdt_scb_to_hdl(p_scb);
-  tAVDT_CTRL_CBACK* p_ctrl_cback = p_scb->cs.p_ctrl_cback;
+  tAVDT_CTRL_CBACK* p_avdt_ctrl_cback = p_scb->stream_config.p_avdt_ctrl_cback;
   tAVDT_CTRL avdt_ctrl;
   uint8_t event;
-  tAVDT_CCB* p_ccb = p_scb->p_ccb;
+  AvdtpCcb* p_ccb = p_scb->p_ccb;
   RawAddress remote_addr = p_ccb->peer_addr;
+  uint8_t scb_index = p_scb->stream_config.scb_index;
 
   /* set up hdr */
   avdt_ctrl.hdr.err_code = p_scb->close_code;
@@ -704,7 +724,7 @@
   }
 
   /* call app callback */
-  (*p_ctrl_cback)(hdl, &remote_addr, event, &avdt_ctrl);
+  (*p_avdt_ctrl_cback)(hdl, remote_addr, event, &avdt_ctrl, scb_index);
 }
 
 /*******************************************************************************
@@ -717,7 +737,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_delay_rpt_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_delay_rpt_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_DELAY_RPT,
                     (tAVDT_MSG*)&p_data->apidelay);
 }
@@ -732,10 +752,12 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_delay_rpt_cmd(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
-  (*p_scb->cs.p_ctrl_cback)(
-      avdt_scb_to_hdl(p_scb), p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-      AVDT_DELAY_REPORT_EVT, (tAVDT_CTRL*)&p_data->msg.hdr);
+void avdt_scb_hdl_delay_rpt_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb),
+      p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty,
+      AVDT_DELAY_REPORT_EVT, (tAVDT_CTRL*)&p_data->msg.hdr,
+      p_scb->stream_config.scb_index);
 
   if (p_scb->p_ccb)
     avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_DELAY_RPT, &p_data->msg);
@@ -753,10 +775,12 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_delay_rpt_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
-  (*p_scb->cs.p_ctrl_cback)(
-      avdt_scb_to_hdl(p_scb), p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-      AVDT_DELAY_REPORT_CFM_EVT, (tAVDT_CTRL*)&p_data->msg.hdr);
+void avdt_scb_hdl_delay_rpt_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb),
+      p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty,
+      AVDT_DELAY_REPORT_CFM_EVT, (tAVDT_CTRL*)&p_data->msg.hdr,
+      p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -769,7 +793,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_tc_close_sto(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_tc_close_sto(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   tAVDT_CTRL avdt_ctrl;
   /* AVDT_CHAN_SIG does not visit this action */
   if (p_data && p_data->close.type != AVDT_CHAN_MEDIA) {
@@ -779,9 +803,10 @@
       avdt_ctrl.hdr.err_code = 0;
       avdt_ctrl.hdr.err_param = 0;
       /* call app callback */
-      (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
-                                p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-                                AVDT_REPORT_DISCONN_EVT, &avdt_ctrl);
+      (*p_scb->stream_config.p_avdt_ctrl_cback)(
+          avdt_scb_to_hdl(p_scb),
+          p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty,
+          AVDT_REPORT_DISCONN_EVT, &avdt_ctrl, p_scb->stream_config.scb_index);
     }
   } else {
     /* must be in OPEN state. need to go back to idle */
@@ -802,7 +827,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_tc_open(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_tc_open(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   uint8_t event;
   uint8_t role;
 
@@ -812,8 +837,8 @@
       (p_scb->role == AVDT_OPEN_INT) ? AVDT_OPEN_CFM_EVT : AVDT_OPEN_IND_EVT;
   p_data->open.hdr.err_code = 0;
 
-  AVDT_TRACE_DEBUG("psc_mask: cfg: 0x%x, req:0x%x, cur: 0x%x",
-                   p_scb->cs.cfg.psc_mask, p_scb->req_cfg.psc_mask,
+  AVDT_TRACE_DEBUG("%s: psc_mask: cfg: 0x%x, req:0x%x, cur: 0x%x", __func__,
+                   p_scb->stream_config.cfg.psc_mask, p_scb->req_cfg.psc_mask,
                    p_scb->curr_cfg.psc_mask);
   if (p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) {
     /* open the reporting channel, if both devices support it */
@@ -822,9 +847,10 @@
   }
 
   /* call app callback */
-  (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
-                            p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-                            event, (tAVDT_CTRL*)&p_data->open);
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb),
+      p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, event,
+      (tAVDT_CTRL*)&p_data->open, p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -839,7 +865,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_tc_open_sto(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_tc_open_sto(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   tAVDT_CTRL avdt_ctrl;
   /* open reporting channel here, when it is implemented */
 
@@ -847,9 +873,10 @@
   if (p_data->open.hdr.err_code == AVDT_CHAN_REPORT) {
     avdt_ctrl.hdr.err_code = 0;
     avdt_ctrl.hdr.err_param = 1;
-    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
-                              p_scb->p_ccb ? &p_scb->p_ccb->peer_addr : NULL,
-                              AVDT_REPORT_CONN_EVT, &avdt_ctrl);
+    (*p_scb->stream_config.p_avdt_ctrl_cback)(
+        avdt_scb_to_hdl(p_scb),
+        p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty,
+        AVDT_REPORT_CONN_EVT, &avdt_ctrl, p_scb->stream_config.scb_index);
   }
 }
 
@@ -864,7 +891,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_hdl_write_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_hdl_write_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   uint8_t* p;
   uint32_t ssrc;
   bool add_rtp_header = !(p_data->apiwrite.opt & AVDT_DATA_OPT_NO_RTP);
@@ -912,7 +939,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_abort_req(tAVDT_SCB* p_scb,
+void avdt_scb_snd_abort_req(AvdtpScb* p_scb,
                             UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   tAVDT_EVT_HDR hdr;
 
@@ -938,7 +965,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_abort_rsp(UNUSED_ATTR tAVDT_SCB* p_scb,
+void avdt_scb_snd_abort_rsp(UNUSED_ATTR AvdtpScb* p_scb,
                             tAVDT_SCB_EVT* p_data) {
   avdt_msg_send_rsp(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_SIG_ABORT,
                     &p_data->msg);
@@ -953,7 +980,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_close_req(tAVDT_SCB* p_scb,
+void avdt_scb_snd_close_req(AvdtpScb* p_scb,
                             UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   tAVDT_EVT_HDR hdr;
 
@@ -975,7 +1002,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_stream_close(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_stream_close(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   osi_free_and_reset((void**)&p_scb->p_pkt);
   avdt_scb_snd_close_req(p_scb, p_data);
 }
@@ -989,7 +1016,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_close_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_close_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_CLOSE, &p_data->msg);
 }
 
@@ -1002,7 +1029,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_getconfig_req(tAVDT_SCB* p_scb,
+void avdt_scb_snd_getconfig_req(AvdtpScb* p_scb,
                                 UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   tAVDT_EVT_HDR hdr;
 
@@ -1022,7 +1049,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_getconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_getconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_GETCONFIG, &p_data->msg);
 }
 
@@ -1035,8 +1062,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_open_req(tAVDT_SCB* p_scb,
-                           UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_open_req(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   tAVDT_EVT_HDR hdr;
 
   hdr.seid = p_scb->peer_seid;
@@ -1057,7 +1083,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_open_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_open_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   /* notify adaption that we're waiting for transport channel open */
   p_scb->role = AVDT_OPEN_ACP;
   avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_ACP);
@@ -1080,12 +1106,12 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_reconfig_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_reconfig_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   AVDT_TRACE_DEBUG("%s: p_scb->peer_seid=%d p_data->msg.hdr.seid=%d", __func__,
                    p_scb->peer_seid, p_data->msg.hdr.seid);
   A2DP_DumpCodecInfo(p_data->msg.config_cmd.p_cfg->codec_info);
 
-  memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG));
+  p_scb->req_cfg = *p_data->msg.config_cmd.p_cfg;
   p_data->msg.hdr.seid = p_scb->peer_seid;
   avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_RECONFIG, &p_data->msg);
 }
@@ -1100,7 +1126,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_reconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_reconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   if (p_data->msg.hdr.err_code == 0) {
     /* store new configuration */
     if (p_scb->req_cfg.num_codec > 0) {
@@ -1131,7 +1157,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_security_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_security_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   p_data->msg.hdr.seid = p_scb->peer_seid;
   avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SECURITY, &p_data->msg);
 }
@@ -1145,7 +1171,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_security_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_security_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   if (p_data->msg.hdr.err_code == 0) {
     avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg);
   } else {
@@ -1163,7 +1189,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_setconfig_rej(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_setconfig_rej(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   if (p_scb->p_ccb != NULL) {
     avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg);
 
@@ -1184,19 +1210,29 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_setconfig_req(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
-  tAVDT_CFG *p_req, *p_cfg;
+void avdt_scb_snd_setconfig_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
+  AvdtpSepConfig *p_req, *p_cfg;
 
   AVDT_TRACE_DEBUG("%s", __func__);
   A2DP_DumpCodecInfo(p_data->msg.config_cmd.p_cfg->codec_info);
 
   /* copy API parameters to scb, set scb as in use */
+
+  AvdtpCcb* p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx);
+  if (p_scb->p_ccb != p_ccb) {
+    AVDT_TRACE_ERROR(
+        "%s: mismatch in AVDTP SCB/CCB state: (p_scb->p_ccb=%p != p_ccb=%p): "
+        "p_scb=%p scb_handle=%d ccb_idx=%d",
+        __func__, p_scb->p_ccb, p_ccb, p_scb, p_scb->ScbHandle(),
+        p_data->msg.config_cmd.hdr.ccb_idx);
+    avdt_scb_rej_not_in_use(p_scb, p_data);
+    return;
+  }
   p_scb->in_use = true;
-  p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx);
   p_scb->peer_seid = p_data->msg.config_cmd.hdr.seid;
   p_req = p_data->msg.config_cmd.p_cfg;
-  p_cfg = &p_scb->cs.cfg;
-  memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG));
+  p_cfg = &p_scb->stream_config.cfg;
+  p_scb->req_cfg = *p_data->msg.config_cmd.p_cfg;
 
   avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SETCONFIG, &p_data->msg);
 
@@ -1215,9 +1251,9 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_setconfig_rsp(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_setconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   if (p_scb->p_ccb != NULL) {
-    memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG));
+    p_scb->curr_cfg = p_scb->req_cfg;
 
     avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg);
   }
@@ -1233,8 +1269,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_snd_tc_close(tAVDT_SCB* p_scb,
-                           UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
+void avdt_scb_snd_tc_close(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   if (p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT)
     avdt_ad_close_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb);
   avdt_ad_close_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb);
@@ -1250,7 +1285,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_cb_err(tAVDT_SCB* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
+void avdt_scb_cb_err(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   tAVDT_CTRL avdt_ctrl;
 
   /* set error code and parameter */
@@ -1258,8 +1293,10 @@
   avdt_ctrl.hdr.err_param = 0;
 
   /* call callback, using lookup table to get callback event */
-  (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL,
-                            avdt_scb_cback_evt[p_scb->curr_evt], &avdt_ctrl);
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb), RawAddress::kEmpty,
+      avdt_scb_cback_evt[p_scb->curr_evt], &avdt_ctrl,
+      p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -1272,7 +1309,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_cong_state(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_cong_state(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   p_scb->cong = p_data->llcong;
 }
 
@@ -1286,7 +1323,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_rej_state(UNUSED_ATTR tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_rej_state(UNUSED_ATTR AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   p_data->msg.hdr.err_code = AVDT_ERR_BAD_STATE;
   p_data->msg.hdr.err_param = 0;
   avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
@@ -1303,7 +1340,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_rej_in_use(UNUSED_ATTR tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_rej_in_use(UNUSED_ATTR AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   p_data->msg.hdr.err_code = AVDT_ERR_IN_USE;
   p_data->msg.hdr.err_param = 0;
   avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
@@ -1320,7 +1357,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_rej_not_in_use(UNUSED_ATTR tAVDT_SCB* p_scb,
+void avdt_scb_rej_not_in_use(UNUSED_ATTR AvdtpScb* p_scb,
                              tAVDT_SCB_EVT* p_data) {
   p_data->msg.hdr.err_code = AVDT_ERR_NOT_IN_USE;
   p_data->msg.hdr.err_param = 0;
@@ -1337,7 +1374,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_set_remove(tAVDT_SCB* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
+void avdt_scb_set_remove(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   p_scb->remove = true;
 }
 
@@ -1350,7 +1387,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_free_pkt(tAVDT_SCB* p_scb, tAVDT_SCB_EVT* p_data) {
+void avdt_scb_free_pkt(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
   tAVDT_CTRL avdt_ctrl;
 
   /* set error code and parameter */
@@ -1362,8 +1399,9 @@
   AVDT_TRACE_WARNING("Dropped media packet");
 
   /* we need to call callback to keep data flow going */
-  (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT,
-                            &avdt_ctrl);
+  (*p_scb->stream_config.p_avdt_ctrl_cback)(
+      avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_WRITE_CFM_EVT,
+      &avdt_ctrl, p_scb->stream_config.scb_index);
 }
 
 /*******************************************************************************
@@ -1375,9 +1413,9 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_clr_pkt(tAVDT_SCB* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
+void avdt_scb_clr_pkt(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   tAVDT_CTRL avdt_ctrl;
-  tAVDT_CCB* p_ccb;
+  AvdtpCcb* p_ccb;
   uint8_t tcid;
   uint16_t lcid;
 
@@ -1390,7 +1428,7 @@
     /* get tcid from type, scb */
     tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb);
 
-    lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
+    lcid = avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
     L2CA_FlushChannel(lcid, L2CAP_FLUSH_CHANS_ALL);
   }
 
@@ -1400,8 +1438,9 @@
     AVDT_TRACE_DEBUG("Dropped stored media packet");
 
     /* we need to call callback to keep data flow going */
-    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT,
-                              &avdt_ctrl);
+    (*p_scb->stream_config.p_avdt_ctrl_cback)(
+        avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_WRITE_CFM_EVT,
+        &avdt_ctrl, p_scb->stream_config.scb_index);
   }
 }
 
@@ -1417,7 +1456,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_chk_snd_pkt(tAVDT_SCB* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
+void avdt_scb_chk_snd_pkt(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   tAVDT_CTRL avdt_ctrl;
   BT_HDR* p_pkt;
 
@@ -1429,8 +1468,9 @@
       p_scb->p_pkt = NULL;
       avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt);
 
-      (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL,
-                                AVDT_WRITE_CFM_EVT, &avdt_ctrl);
+      (*p_scb->stream_config.p_avdt_ctrl_cback)(
+          avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_WRITE_CFM_EVT,
+          &avdt_ctrl, p_scb->stream_config.scb_index);
     }
   }
 }
@@ -1446,7 +1486,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_transport_channel_timer(tAVDT_SCB* p_scb,
+void avdt_scb_transport_channel_timer(AvdtpScb* p_scb,
                                       UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   alarm_set_on_mloop(p_scb->transport_channel_timer,
                      AVDT_SCB_TC_DISC_TIMEOUT_MS,
@@ -1462,8 +1502,7 @@
  * Returns          Nothing.
  *
  ******************************************************************************/
-void avdt_scb_clr_vars(tAVDT_SCB* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
+void avdt_scb_clr_vars(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) {
   p_scb->in_use = false;
-  p_scb->p_ccb = NULL;
   p_scb->peer_seid = 0;
 }
diff --git a/stack/btm/btm_acl.cc b/stack/btm/btm_acl.cc
index 5697fe2..10ac2a5 100644
--- a/stack/btm/btm_acl.cc
+++ b/stack/btm/btm_acl.cc
@@ -191,8 +191,9 @@
   tACL_CONN* p;
   uint8_t xx;
 
-  BTM_TRACE_DEBUG("btm_acl_created hci_handle=%d link_role=%d  transport=%d",
-                  hci_handle, link_role, transport);
+  BTM_TRACE_DEBUG("%s: peer %s hci_handle=%d link_role=%d  transport=%d",
+                  __func__, bda.ToString().c_str(), hci_handle, link_role,
+                  transport);
   /* Ensure we don't have duplicates */
   p = btm_bda_to_acl(bda, transport);
   if (p != (tACL_CONN*)NULL) {
@@ -240,7 +241,8 @@
       p_dev_rec = btm_find_dev_by_handle(hci_handle);
 
       if (p_dev_rec) {
-        BTM_TRACE_DEBUG("device_type=0x%x", p_dev_rec->device_type);
+        BTM_TRACE_DEBUG("%s: peer %s device_type=0x%x", __func__,
+                        bda.ToString().c_str(), p_dev_rec->device_type);
       }
 
       if (p_dev_rec && !(transport == BT_TRANSPORT_LE)) {
@@ -690,8 +692,8 @@
       (*btm_cb.p_bl_changed_cb)(&btm_bl_event_data);
 
       BTM_TRACE_DEBUG(
-          "Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d",
-          evt.new_role, evt.hci_status, p->switch_role_state);
+          "%s: Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d",
+          __func__, evt.new_role, evt.hci_status, p->switch_role_state);
     }
 
 #if (BTM_DISC_DURING_RS == TRUE)
@@ -1435,7 +1437,9 @@
   tBTM_ROLE_SWITCH_CMPL* p_data = &btm_cb.devcb.switch_role_ref_data;
   tBTM_SEC_DEV_REC* p_dev_rec;
 
-  BTM_TRACE_DEBUG("btm_acl_role_changed");
+  BTM_TRACE_DEBUG("%s: peer %s hci_status:0x%x new_role:%d", __func__,
+                  (p_bda != nullptr) ? bd_addr->ToString().c_str() : "nullptr",
+                  hci_status, new_role);
   /* Ignore any stray events */
   if (p == NULL) {
     /* it could be a failure */
@@ -1498,7 +1502,9 @@
   }
 
   BTM_TRACE_DEBUG(
-      "Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d",
+      "%s: peer %s Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, "
+      "rs_st:%d",
+      __func__, (p_bda != nullptr) ? p_bda->ToString().c_str() : "nullptr",
       p_data->role, p_data->hci_status, p->switch_role_state);
 
 #if (BTM_DISC_DURING_RS == TRUE)
@@ -1507,10 +1513,13 @@
   if (p_dev_rec != NULL) {
     if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING) {
       BTM_TRACE_WARNING(
-          "btm_acl_role_changed -> Issuing delayed HCI_Disconnect!!!");
+          "%s peer %s Issuing delayed HCI_Disconnect!!!", __func__,
+          (p_bda != nullptr) ? p_bda->ToString().c_str() : "nullptr");
       btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER);
     }
-    BTM_TRACE_ERROR("tBTM_SEC_DEV:0x%x rs_disc_pending=%d",
+    BTM_TRACE_ERROR("%s: peer %s tBTM_SEC_DEV:0x%x rs_disc_pending=%d",
+                    __func__,
+                    (p_bda != nullptr) ? p_bda->ToString().c_str() : "nullptr",
                     PTR_TO_UINT(p_dev_rec), p_dev_rec->rs_disc_pending);
     p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
   }
diff --git a/stack/include/a2dp_aac.h b/stack/include/a2dp_aac.h
index f2d2553..a3a53b1 100644
--- a/stack/include/a2dp_aac.h
+++ b/stack/include/a2dp_aac.h
@@ -243,12 +243,12 @@
 // Gets the A2DP AAC Sink codec name.
 const char* A2DP_CodecIndexStrAacSink(void);
 
-// Initializes A2DP AAC Source codec information into |tAVDT_CFG|
+// Initializes A2DP AAC Source codec information into |AvdtpSepConfig|
 // configuration entry pointed by |p_cfg|.
-bool A2DP_InitCodecConfigAac(tAVDT_CFG* p_cfg);
+bool A2DP_InitCodecConfigAac(AvdtpSepConfig* p_cfg);
 
-// Initializes A2DP AAC Sink codec information into |tAVDT_CFG|
+// Initializes A2DP AAC Sink codec information into |AvdtpSepConfig|
 // configuration entry pointed by |p_cfg|.
-bool A2DP_InitCodecConfigAacSink(tAVDT_CFG* p_cfg);
+bool A2DP_InitCodecConfigAacSink(AvdtpSepConfig* p_cfg);
 
 #endif  // A2DP_AAC_H
diff --git a/stack/include/a2dp_codec_api.h b/stack/include/a2dp_codec_api.h
index ba62fbe..0db1f16 100644
--- a/stack/include/a2dp_codec_api.h
+++ b/stack/include/a2dp_codec_api.h
@@ -658,11 +658,12 @@
 // 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|.
+// Initializes A2DP codec-specific information into |AvdtpSepConfig|
+// 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);
+                          AvdtpSepConfig* p_cfg);
 
 // Decodes and displays A2DP codec info when using |LOG_DEBUG|.
 // |p_codec_info| is a pointer to the codec_info to decode and display.
diff --git a/stack/include/a2dp_sbc.h b/stack/include/a2dp_sbc.h
index 51632ad..393a6e9 100644
--- a/stack/include/a2dp_sbc.h
+++ b/stack/include/a2dp_sbc.h
@@ -250,12 +250,12 @@
 // 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 Source codec information into |AvdtpSepConfig|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_InitCodecConfigSbc(AvdtpSepConfig* p_cfg);
 
-// Initializes A2DP SBC Sink codec information into |tAVDT_CFG| configuration
-// entry pointed by |p_cfg|.
-bool A2DP_InitCodecConfigSbcSink(tAVDT_CFG* p_cfg);
+// Initializes A2DP SBC Sink codec information into |AvdtpSepConfig|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_InitCodecConfigSbcSink(AvdtpSepConfig* p_cfg);
 
 #endif  // A2DP_SBC_H
diff --git a/stack/include/a2dp_vendor.h b/stack/include/a2dp_vendor.h
index 0547d46..9d0b635 100644
--- a/stack/include/a2dp_vendor.h
+++ b/stack/include/a2dp_vendor.h
@@ -187,12 +187,12 @@
 // 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|
+// Initializes A2DP vendor codec-specific information into |AvdtpSepConfig|
 // 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);
+                                AvdtpSepConfig* p_cfg);
 
 // Decodes and displays A2DP vendor codec info when using |LOG_DEBUG|.
 // |p_codec_info| is a pointer to the codec_info to decode and display.
diff --git a/stack/include/a2dp_vendor_aptx.h b/stack/include/a2dp_vendor_aptx.h
index a9420f1..2cece9b 100644
--- a/stack/include/a2dp_vendor_aptx.h
+++ b/stack/include/a2dp_vendor_aptx.h
@@ -139,8 +139,8 @@
 // Gets the A2DP aptX Source codec name.
 const char* A2DP_VendorCodecIndexStrAptx(void);
 
-// Initializes A2DP aptX Source codec information into |tAVDT_CFG|
+// Initializes A2DP aptX Source codec information into |AvdtpSepConfig|
 // configuration entry pointed by |p_cfg|.
-bool A2DP_VendorInitCodecConfigAptx(tAVDT_CFG* p_cfg);
+bool A2DP_VendorInitCodecConfigAptx(AvdtpSepConfig* p_cfg);
 
 #endif  // A2DP_VENDOR_APTX_H
diff --git a/stack/include/a2dp_vendor_aptx_hd.h b/stack/include/a2dp_vendor_aptx_hd.h
index e45fa0c..3508040 100644
--- a/stack/include/a2dp_vendor_aptx_hd.h
+++ b/stack/include/a2dp_vendor_aptx_hd.h
@@ -140,8 +140,8 @@
 // Gets the A2DP aptX-HD Source codec name.
 const char* A2DP_VendorCodecIndexStrAptxHd(void);
 
-// Initializes A2DP aptX-HD Source codec information into |tAVDT_CFG|
+// Initializes A2DP aptX-HD Source codec information into |AvdtpSepConfig|
 // configuration entry pointed by |p_cfg|.
-bool A2DP_VendorInitCodecConfigAptxHd(tAVDT_CFG* p_cfg);
+bool A2DP_VendorInitCodecConfigAptxHd(AvdtpSepConfig* p_cfg);
 
 #endif  // A2DP_VENDOR_APTX_HD_H
diff --git a/stack/include/a2dp_vendor_ldac.h b/stack/include/a2dp_vendor_ldac.h
index e8689e5..9732d07 100644
--- a/stack/include/a2dp_vendor_ldac.h
+++ b/stack/include/a2dp_vendor_ldac.h
@@ -146,8 +146,8 @@
 // Gets the A2DP LDAC Source codec name.
 const char* A2DP_VendorCodecIndexStrLdac(void);
 
-// Initializes A2DP LDAC Source codec information into |tAVDT_CFG|
+// Initializes A2DP LDAC Source codec information into |AvdtpSepConfig|
 // configuration entry pointed by |p_cfg|.
-bool A2DP_VendorInitCodecConfigLdac(tAVDT_CFG* p_cfg);
+bool A2DP_VendorInitCodecConfigLdac(AvdtpSepConfig* p_cfg);
 
 #endif  // A2DP_VENDOR_LDAC_H
diff --git a/stack/include/avdt_api.h b/stack/include/avdt_api.h
index dd96856..8cbbb6c 100644
--- a/stack/include/avdt_api.h
+++ b/stack/include/avdt_api.h
@@ -236,11 +236,6 @@
 /* PSM for AVDT */
 #define AVDT_PSM 0x0019
 
-/* Nonsupported protocol command messages.  This value is used in tAVDT_CS */
-#define AVDT_NSC_SUSPEND 0x01  /* Suspend command not supported */
-#define AVDT_NSC_RECONFIG 0x02 /* Reconfigure command not supported */
-#define AVDT_NSC_SECURITY 0x04 /* Security command not supported */
-
 /*****************************************************************************
  *  Type Definitions
  ****************************************************************************/
@@ -270,14 +265,36 @@
   uint8_t cname[AVDT_MAX_CNAME_SIZE + 1];
 } tAVDT_REPORT_DATA;
 
-/* This structure contains parameters which are set at registration. */
-typedef struct {
+/**
+ * AVDTP Registration Control Block.
+ */
+class AvdtpRcb {
+ public:
+  AvdtpRcb()
+      : ctrl_mtu(0),
+        ret_tout(0),
+        sig_tout(0),
+        idle_tout(0),
+        sec_mask(0),
+        scb_index(0) {}
+  AvdtpRcb& operator=(const AvdtpRcb&) = default;
+
+  void Reset() {
+    ctrl_mtu = 0;
+    ret_tout = 0;
+    sig_tout = 0;
+    idle_tout = 0;
+    sec_mask = 0;
+    scb_index = 0;
+  }
+
   uint16_t ctrl_mtu; /* L2CAP MTU of the AVDTP signaling channel */
   uint8_t ret_tout;  /* AVDTP signaling retransmission timeout */
   uint8_t sig_tout;  /* AVDTP signaling message timeout */
   uint8_t idle_tout; /* AVDTP idle signaling channel timeout */
   uint8_t sec_mask;  /* Security mask for BTM_SetSecurityLevel() */
-} tAVDT_REG;
+  uint8_t scb_index; /* The Stream Control Block index */
+};
 
 /* This structure contains the SEP information.  This information is
  * transferred during the discovery procedure.
@@ -289,8 +306,35 @@
   uint8_t tsep;       /* SEP type */
 } tAVDT_SEP_INFO;
 
-/* This structure contains the SEP configuration. */
-typedef struct {
+/**
+ * AVDTP SEP Configuration.
+ */
+class AvdtpSepConfig {
+ public:
+  AvdtpSepConfig()
+      : codec_info{},
+        protect_info{},
+        num_codec(0),
+        num_protect(0),
+        psc_mask(0),
+        recov_type(0),
+        recov_mrws(0),
+        recov_mnmp(0),
+        hdrcmp_mask(0) {}
+  AvdtpSepConfig& operator=(const AvdtpSepConfig&) = default;
+
+  void Reset() {
+    memset(codec_info, 0, sizeof(codec_info));
+    memset(protect_info, 0, sizeof(protect_info));
+    num_codec = 0;
+    num_protect = 0;
+    psc_mask = 0;
+    recov_type = 0;
+    recov_mrws = 0;
+    recov_mnmp = 0;
+    hdrcmp_mask = 0;
+  }
+
   uint8_t codec_info[AVDT_CODEC_SIZE];     /* Codec capabilities array */
   uint8_t protect_info[AVDT_PROTECT_SIZE]; /* Content protection capabilities */
   uint8_t num_codec;   /* Number of media codec information elements */
@@ -300,7 +344,7 @@
   uint8_t recov_mrws;  /* Maximum recovery window size */
   uint8_t recov_mnmp;  /* Recovery maximum number of media packets */
   uint8_t hdrcmp_mask; /* Header compression capabilities */
-} tAVDT_CFG;
+};
 
 /* Header structure for callback event parameters. */
 typedef struct {
@@ -318,13 +362,13 @@
 */
 typedef struct {
   tAVDT_EVT_HDR hdr; /* Event header */
-  tAVDT_CFG* p_cfg;  /* Pointer to configuration for this SEP */
+  AvdtpSepConfig* p_cfg; /* Pointer to configuration for this SEP */
 } tAVDT_CONFIG;
 
 /* This data structure is associated with the AVDT_CONFIG_IND_EVT. */
 typedef struct {
   tAVDT_EVT_HDR hdr; /* Event header */
-  tAVDT_CFG* p_cfg;  /* Pointer to configuration for this SEP */
+  AvdtpSepConfig* p_cfg; /* Pointer to configuration for this SEP */
   uint8_t int_seid;  /* Stream endpoint ID of stream initiating the operation */
 } tAVDT_SETCONFIG;
 
@@ -384,8 +428,9 @@
  * endpoints and for the AVDT_DiscoverReq() and AVDT_GetCapReq() functions.
  *
 */
-typedef void(tAVDT_CTRL_CBACK)(uint8_t handle, const RawAddress* bd_addr,
-                               uint8_t event, tAVDT_CTRL* p_data);
+typedef void(tAVDT_CTRL_CBACK)(uint8_t handle, const RawAddress& bd_addr,
+                               uint8_t event, tAVDT_CTRL* p_data,
+                               uint8_t scb_index);
 
 /* This is the data callback function.  It is executed when AVDTP has a media
  * packet ready for the application.  This function is required for SNK
@@ -401,23 +446,57 @@
 typedef void(tAVDT_REPORT_CBACK)(uint8_t handle, AVDT_REPORT_TYPE type,
                                  tAVDT_REPORT_DATA* p_data);
 
-typedef uint16_t(tAVDT_GETCAP_REQ)(const RawAddress& bd_addr, uint8_t seid,
-                                   tAVDT_CFG* p_cfg, tAVDT_CTRL_CBACK* p_cback);
+/**
+ * AVDTP Stream Configuration.
+ * The information is used when a stream is created.
+ */
+class AvdtpStreamConfig {
+ public:
+  //
+  // Non-supported protocol command messages
+  //
+  // Suspend command not supported
+  static constexpr int AVDT_NSC_SUSPEND = 0x01;
+  // Reconfigure command not supported
+  static constexpr int AVDT_NSC_RECONFIG = 0x02;
+  // Security command not supported
+  static constexpr int AVDT_NSC_SECURITY = 0x04;
 
-/* This structure contains information required when a stream is created.
- * It is passed to the AVDT_CreateStream() function.
-*/
-typedef struct {
-  tAVDT_CFG cfg;                            /* SEP configuration */
-  tAVDT_CTRL_CBACK* p_ctrl_cback;           /* Control callback function */
-  tAVDT_SINK_DATA_CBACK* p_sink_data_cback; /* Sink data callback function */
-  tAVDT_REPORT_CBACK* p_report_cback; /* Report callback function. */
-  uint16_t mtu;       /* The L2CAP MTU of the transport channel */
-  uint16_t flush_to;  /* The L2CAP flush timeout of the transport channel */
-  uint8_t tsep;       /* SEP type */
-  uint8_t media_type; /* Media type: AVDT_MEDIA_TYPE_* */
-  uint16_t nsc_mask;  /* Nonsupported protocol command messages */
-} tAVDT_CS;
+  AvdtpStreamConfig()
+      : p_avdt_ctrl_cback(nullptr),
+        scb_index(0),
+        p_sink_data_cback(nullptr),
+        p_report_cback(nullptr),
+        mtu(0),
+        flush_to(0),
+        tsep(0),
+        media_type(0),
+        nsc_mask(0) {}
+
+  void Reset() {
+    cfg.Reset();
+    p_avdt_ctrl_cback = nullptr;
+    scb_index = 0;
+    p_sink_data_cback = nullptr;
+    p_report_cback = nullptr;
+    mtu = 0;
+    flush_to = 0;
+    tsep = 0;
+    media_type = 0;
+    nsc_mask = 0;
+  }
+
+  AvdtpSepConfig cfg;                   // SEP configuration
+  tAVDT_CTRL_CBACK* p_avdt_ctrl_cback;  // Control callback function
+  uint8_t scb_index;  // The index to the bta_av_cb.p_scb[] entry
+  tAVDT_SINK_DATA_CBACK* p_sink_data_cback;  // Sink data callback function
+  tAVDT_REPORT_CBACK* p_report_cback;        // Report callback function
+  uint16_t mtu;        // The L2CAP MTU of the transport channel
+  uint16_t flush_to;   // The L2CAP flush timeout of the transport channel
+  uint8_t tsep;        // SEP type
+  uint8_t media_type;  // Media type: AVDT_MEDIA_TYPE_*
+  uint16_t nsc_mask;   // Nonsupported protocol command messages
+};
 
 /* AVDT data option mask is used in the write request */
 #define AVDT_DATA_OPT_NONE 0x00          /* No option still add RTP header */
@@ -443,7 +522,7 @@
  * Returns          void
  *
  ******************************************************************************/
-extern void AVDT_Register(tAVDT_REG* p_reg, tAVDT_CTRL_CBACK* p_cback);
+extern void AVDT_Register(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback);
 
 /*******************************************************************************
  *
@@ -485,7 +564,8 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-extern uint16_t AVDT_CreateStream(uint8_t* p_handle, tAVDT_CS* p_cs);
+extern uint16_t AVDT_CreateStream(uint8_t peer_id, uint8_t* p_handle,
+                                  const AvdtpStreamConfig& avdtp_stream_config);
 
 /*******************************************************************************
  *
@@ -530,6 +610,7 @@
  *
  ******************************************************************************/
 extern uint16_t AVDT_DiscoverReq(const RawAddress& bd_addr,
+                                 uint8_t channel_index,
                                  tAVDT_SEP_INFO* p_sep_info, uint8_t max_seps,
                                  tAVDT_CTRL_CBACK* p_cback);
 
@@ -557,35 +638,9 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-extern uint16_t AVDT_GetCapReq(const RawAddress& bd_addr, uint8_t seid,
-                               tAVDT_CFG* p_cfg, tAVDT_CTRL_CBACK* p_cback);
-
-/*******************************************************************************
- *
- * Function         AVDT_GetAllCapReq
- *
- * Description      This function initiates a connection to the AVDTP service
- *                  on the peer device, if not already present, and gets the
- *                  capabilities of a stream endpoint on the peer device.
- *                  This function can be called at any time regardless of
- *                  whether there is an AVDTP connection to the peer device.
- *
- *                  When the procedure is complete, an AVDT_GETCAP_CFM_EVT is
- *                  sent to the application via its callback function.  The
- *                  application must not call AVDT_GetCapReq() or
- *                  AVDT_DiscoverReq() again until the procedure is complete.
- *
- *                  The memory pointed to by p_cfg is allocated by the
- *                  application.  This memory is written to by AVDTP as part
- *                  of the get capabilities procedure.  This memory must
- *                  remain accessible until the application receives
- *                  the AVDT_GETCAP_CFM_EVT.
- *
- * Returns          AVDT_SUCCESS if successful, otherwise error.
- *
- ******************************************************************************/
-extern uint16_t AVDT_GetAllCapReq(const RawAddress& bd_addr, uint8_t seid,
-                                  tAVDT_CFG* p_cfg, tAVDT_CTRL_CBACK* p_cback);
+extern uint16_t AVDT_GetCapReq(const RawAddress& bd_addr, uint8_t channel_index,
+                               uint8_t seid, AvdtpSepConfig* p_cfg,
+                               tAVDT_CTRL_CBACK* p_cback, bool get_all_cap);
 
 /*******************************************************************************
  *
@@ -615,7 +670,8 @@
  *
  ******************************************************************************/
 extern uint16_t AVDT_OpenReq(uint8_t handle, const RawAddress& bd_addr,
-                             uint8_t seid, tAVDT_CFG* p_cfg);
+                             uint8_t channel_index, uint8_t seid,
+                             AvdtpSepConfig* p_cfg);
 
 /*******************************************************************************
  *
@@ -699,7 +755,7 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-extern uint16_t AVDT_ReconfigReq(uint8_t handle, tAVDT_CFG* p_cfg);
+extern uint16_t AVDT_ReconfigReq(uint8_t handle, AvdtpSepConfig* p_cfg);
 
 /*******************************************************************************
  *
@@ -841,7 +897,8 @@
  * Returns          AVDT_SUCCESS if successful, otherwise error.
  *
  ******************************************************************************/
-extern uint16_t AVDT_ConnectReq(const RawAddress& bd_addr, uint8_t sec_mask,
+extern uint16_t AVDT_ConnectReq(const RawAddress& bd_addr,
+                                uint8_t channel_index, uint8_t sec_mask,
                                 tAVDT_CTRL_CBACK* p_cback);
 
 /*******************************************************************************
@@ -920,4 +977,12 @@
  *****************************************************************************/
 extern uint8_t AVDT_SetTraceLevel(uint8_t new_level);
 
+/**
+ * Dump debug-related information for the Stack AVDTP module.
+ *
+ * @param fd the file descriptor to use for writing the ASCII formatted
+ * information
+ */
+void stack_debug_avdtp_api_dump(int fd);
+
 #endif /* AVDT_API_H */
diff --git a/stack/include/avdtc_api.h b/stack/include/avdtc_api.h
index 71f2472..6948f83 100644
--- a/stack/include/avdtc_api.h
+++ b/stack/include/avdtc_api.h
@@ -107,7 +107,7 @@
  *
  ******************************************************************************/
 extern void AVDTC_GetCapRsp(const RawAddress& bd_addr, uint8_t label,
-                            tAVDT_CFG* p_cap);
+                            AvdtpSepConfig* p_cap);
 
 /*******************************************************************************
  *
@@ -119,7 +119,7 @@
  *
  ******************************************************************************/
 extern void AVDTC_GetAllCapRsp(const RawAddress& bd_addr, uint8_t label,
-                               tAVDT_CFG* p_cap);
+                               AvdtpSepConfig* p_cap);
 
 /*******************************************************************************
  *
@@ -141,7 +141,8 @@
  * Returns          void
  *
  ******************************************************************************/
-extern void AVDTC_GetConfigRsp(uint8_t handle, uint8_t label, tAVDT_CFG* p_cfg);
+extern void AVDTC_GetConfigRsp(uint8_t handle, uint8_t label,
+                               AvdtpSepConfig* p_cfg);
 
 /*******************************************************************************
  *
diff --git a/stack/test/stack_a2dp_test.cc b/stack/test/stack_a2dp_test.cc
index 91c1c83..d53818d 100644
--- a/stack/test/stack_a2dp_test.cc
+++ b/stack/test/stack_a2dp_test.cc
@@ -773,7 +773,7 @@
 }
 
 TEST_F(StackA2dpTest, test_a2dp_init_codec_config) {
-  tAVDT_CFG avdt_cfg;
+  AvdtpSepConfig avdt_cfg;
 
   //
   // Test for SBC Source
diff --git a/test/run_host_unit_tests.py b/test/run_host_unit_tests.py
index e5e392c..7c7c9d0 100755
--- a/test/run_host_unit_tests.py
+++ b/test/run_host_unit_tests.py
@@ -22,6 +22,7 @@
 # Must have 'host_supported: true'
 HOST_TESTS = [
   'bluetoothtbd_test',
+  'net_test_btif_state_machine',
   'net_test_btcore',
   'net_test_types',
 ]
diff --git a/test/run_unit_tests.sh b/test/run_unit_tests.sh
index 4c390bf..3b5a731 100755
--- a/test/run_unit_tests.sh
+++ b/test/run_unit_tests.sh
@@ -8,6 +8,7 @@
   net_test_bta
   net_test_btif
   net_test_btif_profile_queue
+  net_test_btif_state_machine
   net_test_device
   net_test_hci
   net_test_stack