Merge "Fix Mobly adb pull error when pull logs from device"
diff --git a/binder/android/bluetooth/IBluetooth.aidl b/binder/android/bluetooth/IBluetooth.aidl
index 0efa972..3e78f5b 100644
--- a/binder/android/bluetooth/IBluetooth.aidl
+++ b/binder/android/bluetooth/IBluetooth.aidl
@@ -196,6 +196,10 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
     boolean isLePeriodicAdvertisingSupported();
     @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
+    int isCisCentralSupported();
+    @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
+    int isLePeriodicAdvertisingSyncTransferSenderSupported();
+    @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
     int getLeMaximumAdvertisingDataLength();
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
diff --git a/bta/dm/bta_dm_api.cc b/bta/dm/bta_dm_api.cc
index b884b96..c1bdf0e 100644
--- a/bta/dm/bta_dm_api.cc
+++ b/bta/dm/bta_dm_api.cc
@@ -33,6 +33,7 @@
 #include "stack/btm/btm_sec.h"
 #include "stack/include/bt_octets.h"
 #include "stack/include/btm_api.h"
+#include "stack/include/btm_client_interface.h"
 #include "stack/include/btu.h"  // do_in_main_thread
 #include "types/bluetooth/uuid.h"
 #include "types/raw_address.h"
@@ -279,7 +280,7 @@
  *
  ******************************************************************************/
 extern const uint16_t bta_service_id_to_uuid_lkup_tbl[];
-void BTA_GetEirService(uint8_t* p_eir, size_t eir_len,
+void BTA_GetEirService(const uint8_t* p_eir, size_t eir_len,
                        tBTA_SERVICE_MASK* p_services) {
   uint8_t xx, yy;
   uint8_t num_uuid, max_num_uuid = 32;
@@ -287,8 +288,8 @@
   uint16_t* p_uuid16 = (uint16_t*)uuid_list;
   tBTA_SERVICE_MASK mask;
 
-  BTM_GetEirUuidList(p_eir, eir_len, Uuid::kNumBytes16, &num_uuid, uuid_list,
-                     max_num_uuid);
+  get_btm_client_interface().eir.BTM_GetEirUuidList(
+      p_eir, eir_len, Uuid::kNumBytes16, &num_uuid, uuid_list, max_num_uuid);
   for (xx = 0; xx < num_uuid; xx++) {
     mask = 1;
     for (yy = 0; yy < BTA_MAX_SERVICE_ID; yy++) {
diff --git a/bta/include/bta_api.h b/bta/include/bta_api.h
index 995798f..80a1421 100644
--- a/bta/include/bta_api.h
+++ b/bta/include/bta_api.h
@@ -866,7 +866,7 @@
  * Returns          None
  *
  ******************************************************************************/
-extern void BTA_GetEirService(uint8_t* p_eir, size_t eir_len,
+extern void BTA_GetEirService(const uint8_t* p_eir, size_t eir_len,
                               tBTA_SERVICE_MASK* p_services);
 
 /*******************************************************************************
diff --git a/bta/le_audio/client.cc b/bta/le_audio/client.cc
index 54e6017..6522d76 100644
--- a/bta/le_audio/client.cc
+++ b/bta/le_audio/client.cc
@@ -718,7 +718,30 @@
       group_add_node(group_id, address);
     }
 
-    if (autoconnect) Connect(address);
+    if (autoconnect) {
+      BTA_GATTC_Open(gatt_if_, address, false, false);
+    }
+  }
+
+  void BackgroundConnectIfGroupConnected(LeAudioDevice* leAudioDevice) {
+    DLOG(INFO) << __func__ << leAudioDevice->address_ ;
+    auto group = aseGroups_.FindById(leAudioDevice->group_id_);
+    if (!group) {
+      DLOG(INFO) << __func__ << " Device is not yet part of the group. ";
+      return;
+    }
+
+    if (!group->IsAnyDeviceConnected()) {
+      DLOG(INFO) << __func__ << " group: " << leAudioDevice->group_id_
+                 << " is not connected";
+      return;
+    }
+
+    DLOG(INFO) << __func__ << "Add " << leAudioDevice->address_
+               << " to background connect to connected group: "
+               << leAudioDevice->group_id_;
+
+    BTA_GATTC_Open(gatt_if_, leAudioDevice->address_, false, false);
   }
 
   void Disconnect(const RawAddress& address) override {
@@ -731,19 +754,23 @@
     }
 
     /* cancel pending direct connect */
-    if (leAudioDevice->connecting_actively_)
+    if (leAudioDevice->connecting_actively_) {
       BTA_GATTC_CancelOpen(gatt_if_, address, true);
+      leAudioDevice->connecting_actively_ = false;
+    }
 
     /* Removes all registrations for connection */
     BTA_GATTC_CancelOpen(0, address, false);
 
-    if (leAudioDevice->conn_id_ == GATT_INVALID_CONN_ID) {
-      LOG(ERROR) << __func__ << ", leAudioDevice not connected (" << address
-                 << ")";
+    if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID) {
+      DisconnectDevice(leAudioDevice);
       return;
     }
 
-    DisconnectDevice(leAudioDevice);
+    /* If this is a device which is a part of the group which is connected,
+     * lets start backgroup connect
+     */
+    BackgroundConnectIfGroupConnected(leAudioDevice);
   }
 
   void DisconnectDevice(LeAudioDevice* leAudioDevice,
@@ -2130,15 +2157,23 @@
             context_type, le_audio::types::kLeAudioDirectionSource);
 
     if (source_configuration) {
+      bool send_active = false;
       /* Stream configuration differs from previous one */
       if (!current_source_codec_config.IsInvalid() &&
-          (*source_configuration != current_source_codec_config))
+          (*source_configuration != current_source_codec_config)) {
+        callbacks_->OnGroupStatus(group_id, GroupStatus::INACTIVE);
+        send_active = true;
         LeAudioClientAudioSource::Stop();
+      }
 
       current_source_codec_config = *source_configuration;
 
       LeAudioClientAudioSource::Start(current_source_codec_config,
                                       audioSinkReceiver);
+      if (send_active) {
+        callbacks_->OnGroupStatus(group_id, GroupStatus::ACTIVE);
+      }
+
     } else {
       if (!current_source_codec_config.IsInvalid()) {
         LeAudioClientAudioSource::Stop();
@@ -2152,15 +2187,22 @@
     }
 
     if (sink_configuration) {
+      bool send_active = false;
       /* Stream configuration differs from previous one */
       if (!current_sink_codec_config.IsInvalid() &&
-          (*sink_configuration != current_sink_codec_config))
+          (*sink_configuration != current_sink_codec_config)) {
+        callbacks_->OnGroupStatus(group_id, GroupStatus::INACTIVE);
+        send_active = true;
         LeAudioClientAudioSink::Stop();
+      }
 
       current_sink_codec_config = *sink_configuration;
 
       LeAudioClientAudioSink::Start(current_sink_codec_config,
                                     audioSourceReceiver);
+      if (send_active) {
+        callbacks_->OnGroupStatus(group_id, GroupStatus::ACTIVE);
+      }
     } else {
       if (!current_sink_codec_config.IsInvalid()) {
         LeAudioClientAudioSink::Stop();
diff --git a/bta/le_audio/le_audio_client_test.cc b/bta/le_audio/le_audio_client_test.cc
index 7a0b318..8158ef2 100644
--- a/bta/le_audio/le_audio_client_test.cc
+++ b/bta/le_audio/le_audio_client_test.cc
@@ -1847,7 +1847,7 @@
       .Times(1);
   ON_CALL(mock_btm_interface_, BTM_IsEncrypted(test_address0, _))
       .WillByDefault(DoAll(Return(true)));
-  EXPECT_CALL(mock_gatt_interface_, Open(gatt_if, test_address0, true, _))
+  EXPECT_CALL(mock_gatt_interface_, Open(gatt_if, test_address0, false, _))
       .Times(1);
 
   // Expect stored device1 to connect automatically
@@ -1856,7 +1856,7 @@
       .Times(1);
   ON_CALL(mock_btm_interface_, BTM_IsEncrypted(test_address1, _))
       .WillByDefault(DoAll(Return(true)));
-  EXPECT_CALL(mock_gatt_interface_, Open(gatt_if, test_address1, true, _))
+  EXPECT_CALL(mock_gatt_interface_, Open(gatt_if, test_address1, false, _))
       .Times(1);
 
   ON_CALL(mock_groups_module_, GetGroupId(_, _))
@@ -1942,7 +1942,7 @@
       .Times(1);
   ON_CALL(mock_btm_interface_, BTM_IsEncrypted(test_address0, _))
       .WillByDefault(DoAll(Return(true)));
-  EXPECT_CALL(mock_gatt_interface_, Open(gatt_if, test_address0, true, _))
+  EXPECT_CALL(mock_gatt_interface_, Open(gatt_if, test_address0, false, _))
       .Times(1);
 
   // Expect stored device1 to NOT connect automatically
@@ -1951,7 +1951,7 @@
       .Times(0);
   ON_CALL(mock_btm_interface_, BTM_IsEncrypted(test_address1, _))
       .WillByDefault(DoAll(Return(true)));
-  EXPECT_CALL(mock_gatt_interface_, Open(gatt_if, test_address1, true, _))
+  EXPECT_CALL(mock_gatt_interface_, Open(gatt_if, test_address1, false, _))
       .Times(0);
 
   // Initialize
diff --git a/bta/vc/vc.cc b/bta/vc/vc.cc
index dd558dc..7bc7fab 100644
--- a/bta/vc/vc.cc
+++ b/bta/vc/vc.cc
@@ -159,7 +159,7 @@
       return;
     }
 
-    LOG(INFO) << __func__ << " " << address << " status: " << success;
+    LOG(INFO) << __func__ << " " << address << " status: " << +success;
 
     if (device->HasHandles()) {
       device->EnqueueInitialRequests(gatt_if_, chrc_read_callback_static,
@@ -743,7 +743,7 @@
       } break;
 
       case BTA_GATTC_ENC_CMPL_CB_EVT:
-        OnEncryptionComplete(p_data->enc_cmpl.remote_bda, true);
+        OnEncryptionComplete(p_data->enc_cmpl.remote_bda, BTM_SUCCESS);
         break;
 
       case BTA_GATTC_SRVC_CHG_EVT:
diff --git a/btif/src/btif_dm.cc b/btif/src/btif_dm.cc
index a8b33a7..712a64b 100644
--- a/btif/src/btif_dm.cc
+++ b/btif/src/btif_dm.cc
@@ -36,6 +36,7 @@
 #include <hardware/bt_csis.h>
 #include <hardware/bt_hearing_aid.h>
 #include <hardware/bt_le_audio.h>
+#include <hardware/bt_vc.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -51,6 +52,7 @@
 #include "bta_dm_int.h"
 #include "bta_gatt_api.h"
 #include "bta_le_audio_api.h"
+#include "bta_vc_api.h"
 #include "btif/include/stack_manager.h"
 #include "btif_api.h"
 #include "btif_av.h"
@@ -250,6 +252,8 @@
 extern bluetooth::csis::CsisClientInterface* btif_csis_client_get_interface();
 extern bluetooth::le_audio::LeAudioClientInterface*
 btif_le_audio_get_interface();
+extern bluetooth::vc::VolumeControlInterface*
+btif_volume_control_get_interface();
 
 /******************************************************************************
  *  Functions
@@ -1598,6 +1602,10 @@
       if (LeAudioClient::IsLeAudioClientRunning())
         btif_le_audio_get_interface()->RemoveDevice(bd_addr);
 
+      if (VolumeControl::IsVolumeControlRunning()) {
+        btif_volume_control_get_interface()->RemoveDevice(bd_addr);
+      }
+
       btif_storage_remove_bonded_device(&bd_addr);
       bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_NONE);
       break;
diff --git a/gd/Android.bp b/gd/Android.bp
index fadab26..46cd7d9 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -118,6 +118,18 @@
     ],
 }
 
+// Clang is targeted for android core libraries but other base libraries
+// may not support clang tidy recommendations (e.g. MacOS)
+cc_defaults {
+    name: "gd_clang_tidy_ignore_android",
+    tidy: true,
+    tidy_checks: [
+        "-android-cloexec-pipe2", //  warning: 'pipe2' should use O_CLOEXEC where possible
+        "-android-cloexec-accept", // warning: prefer accept4() to accept() because accept4() allows SOCK_CLOEXEC
+        "-android-cloexec-socket",  // warning: 'pipe2' should use O_CLOEXEC where possible
+    ],
+}
+
 cc_defaults {
     name: "libbluetooth_gd_defaults",
     defaults: [
diff --git a/gd/hci/acl_manager.cc b/gd/hci/acl_manager.cc
index eefb83b..08b14dc 100644
--- a/gd/hci/acl_manager.cc
+++ b/gd/hci/acl_manager.cc
@@ -220,6 +220,10 @@
   CallOn(pimpl_->le_impl_, &le_impl::add_device_to_connect_list, address_with_type);
 }
 
+void AclManager::RemoveDeviceFromConnectList(AddressWithType address_with_type) {
+  CallOn(pimpl_->le_impl_, &le_impl::remove_device_from_connect_list, address_with_type);
+}
+
 void AclManager::AddDeviceToResolvingList(
     AddressWithType address_with_type,
     const std::array<uint8_t, 16>& peer_irk,
@@ -227,14 +231,14 @@
   CallOn(pimpl_->le_impl_, &le_impl::add_device_to_resolving_list, address_with_type, peer_irk, local_irk);
 }
 
-void AclManager::RemoveDeviceFromConnectList(AddressWithType address_with_type) {
-  CallOn(pimpl_->le_impl_, &le_impl::remove_device_from_connect_list, address_with_type);
-}
-
 void AclManager::RemoveDeviceFromResolvingList(AddressWithType address_with_type) {
   CallOn(pimpl_->le_impl_, &le_impl::remove_device_from_resolving_list, address_with_type);
 }
 
+void AclManager::ClearResolvingList() {
+  CallOn(pimpl_->le_impl_, &le_impl::clear_resolving_list);
+}
+
 void AclManager::CentralLinkKey(KeyFlag key_flag) {
   CallOn(pimpl_->classic_impl_, &classic_impl::central_link_key, key_flag);
 }
diff --git a/gd/hci/acl_manager.h b/gd/hci/acl_manager.h
index effa6db..6791722 100644
--- a/gd/hci/acl_manager.h
+++ b/gd/hci/acl_manager.h
@@ -101,12 +101,13 @@
 
  virtual void CancelLeConnect(AddressWithType address_with_type);
  virtual void AddDeviceToConnectList(AddressWithType address_with_type);
+ virtual void RemoveDeviceFromConnectList(AddressWithType address_with_type);
  virtual void AddDeviceToResolvingList(
      AddressWithType address_with_type,
      const std::array<uint8_t, 16>& peer_irk,
      const std::array<uint8_t, 16>& local_irk);
- virtual void RemoveDeviceFromConnectList(AddressWithType address_with_type);
  virtual void RemoveDeviceFromResolvingList(AddressWithType address_with_type);
+ virtual void ClearResolvingList();
 
  virtual void CentralLinkKey(KeyFlag key_flag);
  virtual void SwitchRole(Address address, Role role);
diff --git a/gd/hci/acl_manager/le_impl.h b/gd/hci/acl_manager/le_impl.h
index 356db1c..6fb1de3 100644
--- a/gd/hci/acl_manager/le_impl.h
+++ b/gd/hci/acl_manager/le_impl.h
@@ -625,6 +625,10 @@
     }
   }
 
+  void clear_resolving_list() {
+    le_address_manager_->ClearResolvingList();
+  }
+
   void set_privacy_policy_for_initiator_address(
       LeAddressManager::AddressPolicy address_policy,
       AddressWithType fixed_address,
diff --git a/gd/rust/topshim/hfp/hfp_shim.cc b/gd/rust/topshim/hfp/hfp_shim.cc
index d2742de..363c2c3 100644
--- a/gd/rust/topshim/hfp/hfp_shim.cc
+++ b/gd/rust/topshim/hfp/hfp_shim.cc
@@ -14,21 +14,100 @@
  * limitations under the License.
  */
 
-#include "hfp/hfp_shim.h"
+#include "gd/rust/topshim/hfp/hfp_shim.h"
 
 #include "btif/include/btif_hf.h"
+#include "types/raw_address.h"
+
+namespace bluetooth::headset {
+class DBusHeadsetCallbacks : public Callbacks {
+ public:
+  static Callbacks* GetInstance() {
+    static Callbacks* instance = new DBusHeadsetCallbacks();
+    return instance;
+  }
+
+  void ConnectionStateCallback(
+      [[maybe_unused]] bthf_connection_state_t state, [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void AudioStateCallback([[maybe_unused]] bthf_audio_state_t state, [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void VoiceRecognitionCallback([[maybe_unused]] bthf_vr_state_t state, [[maybe_unused]] RawAddress* bd_addr) override {
+  }
+
+  void AnswerCallCallback([[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void HangupCallCallback([[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void VolumeControlCallback(
+      [[maybe_unused]] bthf_volume_type_t type,
+      [[maybe_unused]] int volume,
+      [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void DialCallCallback([[maybe_unused]] char* number, [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void DtmfCmdCallback([[maybe_unused]] char tone, [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void NoiseReductionCallback([[maybe_unused]] bthf_nrec_t nrec, [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void WbsCallback([[maybe_unused]] bthf_wbs_config_t wbs, [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void AtChldCallback([[maybe_unused]] bthf_chld_type_t chld, [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void AtCnumCallback([[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void AtCindCallback([[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void AtCopsCallback([[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void AtClccCallback([[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void UnknownAtCallback([[maybe_unused]] char* at_string, [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void KeyPressedCallback([[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void AtBindCallback([[maybe_unused]] char* at_string, [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void AtBievCallback(
+      [[maybe_unused]] bthf_hf_ind_type_t ind_id,
+      [[maybe_unused]] int ind_value,
+      [[maybe_unused]] RawAddress* bd_addr) override {}
+
+  void AtBiaCallback(
+      [[maybe_unused]] bool service,
+      [[maybe_unused]] bool roam,
+      [[maybe_unused]] bool signal,
+      [[maybe_unused]] bool battery,
+      [[maybe_unused]] RawAddress* bd_addr) override {}
+};
+}  // namespace bluetooth::headset
 
 namespace bluetooth {
 namespace topshim {
 namespace rust {
+namespace internal {
+static HfpIntf* g_hfpif;
+
+}  // namespace internal
 
 int HfpIntf::init() {
-  intf_->Init(nullptr, 1, false);
-  return 0;
+  return intf_->Init(headset::DBusHeadsetCallbacks::GetInstance(), 1, false);
 }
 
 void HfpIntf::cleanup() {}
 
+std::unique_ptr<HfpIntf> GetHfpProfile(const unsigned char* btif) {
+  if (internal::g_hfpif) std::abort();
+
+  const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif);
+
+  auto hfpif = std::make_unique<HfpIntf>(const_cast<headset::Interface*>(
+      reinterpret_cast<const headset::Interface*>(btif_->get_profile_interface("handsfree"))));
+  internal::g_hfpif = hfpif.get();
+
+  return hfpif;
+}
+
 }  // namespace rust
 }  // namespace topshim
 }  // namespace bluetooth
diff --git a/gd/rust/topshim/hfp/hfp_shim.h b/gd/rust/topshim/hfp/hfp_shim.h
index 0198130..a924fda 100644
--- a/gd/rust/topshim/hfp/hfp_shim.h
+++ b/gd/rust/topshim/hfp/hfp_shim.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include "btif/include/btif_hf.h"
+#include "include/hardware/bluetooth_headset_callbacks.h"
 
 namespace bluetooth {
 namespace topshim {
@@ -24,15 +25,16 @@
 
 class HfpIntf {
  public:
-  // interface for Settings
+  HfpIntf(headset::Interface* intf) : intf_(intf){};
+
   int init();
   void cleanup();
 
  private:
-  bluetooth::headset::Interface* intf_ = nullptr;
+  headset::Interface* intf_;
 };
 
-std::unique_ptr<HfpIntf> GetHfpProfile();
+std::unique_ptr<HfpIntf> GetHfpProfile(const unsigned char* btif);
 
 }  // namespace rust
 }  // namespace topshim
diff --git a/le_audio/certification_tool/bap_uclient_test_tool/Android.bp b/le_audio/certification_tool/bap_uclient_test_tool/Android.bp
new file mode 100644
index 0000000..098039f
--- /dev/null
+++ b/le_audio/certification_tool/bap_uclient_test_tool/Android.bp
@@ -0,0 +1,46 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+cc_binary {
+    name: "bap_uclient_test",
+    system_ext_specific: true,
+    enabled: false,
+    srcs: ["bap_uclient_test.cpp"],
+
+    include_dirs: [
+        ".",
+        "system/bt/include",
+        "system/bt/types",
+        "vendor/qcom/opensource/commonsys/system/bt/stack/l2cap",
+        "vendor/qcom/opensource/commonsys/system/bt/utils/include",
+        "vendor/qcom/opensource/commonsys/system/bt",
+        "vendor/qcom/opensource/commonsys/bluetooth_lea/vhal/include",
+        "external/libchrome",
+    ],
+
+    cflags: ["-DHAS_NO_BDROID_BUILDCFG"],
+
+    shared_libs: [
+        "libcutils",
+        "libchrome",
+        "libutils",
+    ],
+
+    static_libs: ["libbluetooth-types-qti"],
+
+}
diff --git a/le_audio/certification_tool/bap_uclient_test_tool/bap_uclient_test.cpp b/le_audio/certification_tool/bap_uclient_test_tool/bap_uclient_test.cpp
new file mode 100644
index 0000000..78db703
--- /dev/null
+++ b/le_audio/certification_tool/bap_uclient_test_tool/bap_uclient_test.cpp
@@ -0,0 +1,1904 @@
+/***********************************************************************
+ *
+ *  Copyright (c) 2014-2015, 2020 The Linux Foundation. All rights reserved.
+ *
+ *  Copyright (C) 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:      bap_uclient_test.cpp
+ *
+ *  Description:   bap unicast client test application
+ *
+
+*******************************************************************************
+****/
+
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <cstring>
+#include <stdio.h>
+#include <stdint.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#include <map>
+#include <iomanip>
+#include <private/android_filesystem_config.h>
+#include <android/log.h>
+#include <hardware/bt_gatt_types.h>
+#include <hardware/hardware.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_bap_uclient.h>
+#include <hardware/bt_pacs_client.h>
+#include <hardware/bt_ascs_client.h>
+
+#include <signal.h>
+#include <time.h>
+
+#include <base/bind.h>
+#include <base/callback.h>
+
+using bluetooth::Uuid;
+
+constexpr uint8_t ASE_DIRECTION_SINK           = 0x01 << 0;
+constexpr uint8_t ASE_DIRECTION_SRC            = 0x01 << 1;
+
+constexpr uint8_t ASE_SINK_STEREO     = 0x01 << 0;
+constexpr uint8_t ASE_SRC_STEREO      = 0x01 << 1;
+
+#ifndef BAP_UNICAST_TEST_APP_INTERFACE
+#define BAP_UNICAST_TEST_APP_INTERFACE
+/******************************************************************************
+******
+**  Constants & Macros
+*******************************************************************************
+*****/
+
+#ifndef TRUE
+#define     TRUE       1
+#endif
+#ifndef FALSE
+#define     FALSE      0
+#endif
+
+#define PID_FILE "/data/.bdt_pid"
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define CASE_RETURN_STR(const) case const: return #const;
+
+
+/******************************************************************************
+******
+**  Local type definitions
+*******************************************************************************
+*****/
+
+
+/******************************************************************************
+******
+**  Static variables
+*******************************************************************************
+*****/
+
+static unsigned char main_done = 0;
+static int status;
+
+#define LE_ACL_MAX_BUFF_SIZE 4096
+static int num_frames = 1;
+static unsigned long g_delay = 1; /* Default delay before data transfer */
+static int count = 1;
+static uint16_t g_BleEncKeySize = 16;
+static int g_le_coc_if = 0;
+static int rcv_itration = 0;
+static volatile bool cong_status = FALSE;
+
+
+/* Main API */
+const bt_interface_t* sBtInterface = NULL;
+
+static gid_t groups[] = { AID_NET_BT, AID_INET, AID_NET_BT_ADMIN,
+                          AID_SYSTEM, AID_MISC, AID_SDCARD_RW,
+                          AID_NET_ADMIN, AID_VPN};
+
+enum {
+   DISCONNECT,
+   CONNECTING,
+   CONNECTED,
+   DISCONNECTING
+};
+
+static unsigned char bt_enabled = 0;
+static int  g_ConnectionState   = DISCONNECT;
+static int  g_AdapterState      = BT_STATE_OFF;
+static int  g_PairState         = BT_BOND_STATE_NONE;
+
+static int  g_conn_id        = 0;
+static int  g_client_if      = 0;
+static int  g_server_if      = 0;
+static int  g_client_if_scan = 0;
+static int  g_server_if_scan = 0;
+
+
+RawAddress* remote_bd_address;
+
+static uint16_t g_SecLevel = 0;
+static bool g_ConnType = TRUE;//DUT is initiating connection
+
+/******************************************************************************
+******
+**  Static functions
+*******************************************************************************
+*****/
+
+static void process_cmd(char *p, unsigned char is_job);
+//static void job_handler(void *param);
+static void bdt_log(const char *fmt_str, ...);
+static void l2c_connect(RawAddress bd_addr);
+static uint16_t do_l2cap_connect(RawAddress bd_addr);
+
+int GetBdAddr(char *p, RawAddress* pbd_addr);
+void bdt_init(void);
+int reg_inst_id = -1;
+int reg_status = -1;
+
+
+/******************************************************************************
+******
+**  ASCS Client Callbacks
+*******************************************************************************
+*****/
+
+
+/******************************************************************************
+******
+**  PACS client Callbacks
+*******************************************************************************
+*****/
+
+
+
+/******************************************************************************
+******
+**  BAP Unicast client Callbacks
+*******************************************************************************
+*****/
+
+/******************************************************************************
+******
+**  Shutdown helper functions
+*******************************************************************************
+*****/
+
+static void bdt_shutdown(void)
+{
+    bdt_log("shutdown bdroid test app.\n");
+    main_done = 1;
+}
+
+
+/*****************************************************************************
+** Android's init.rc does not yet support applying linux capabilities
+*****************************************************************************/
+
+static void config_permissions(void)
+{
+    struct __user_cap_header_struct header;
+    struct __user_cap_data_struct cap[2];
+
+    bdt_log("set_aid_and_cap : pid %d, uid %d gid %d", getpid(), getuid(), getgid());
+
+    header.pid = 0;
+
+    prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+    setuid(AID_BLUETOOTH);
+    setgid(AID_BLUETOOTH);
+
+    header.version = _LINUX_CAPABILITY_VERSION_3;
+
+    cap[CAP_TO_INDEX(CAP_NET_RAW)].permitted |= CAP_TO_MASK(CAP_NET_RAW);
+    cap[CAP_TO_INDEX(CAP_NET_ADMIN)].permitted |= CAP_TO_MASK(CAP_NET_ADMIN);
+    cap[CAP_TO_INDEX(CAP_NET_BIND_SERVICE)].permitted |= CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+    cap[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted |= CAP_TO_MASK(CAP_SYS_RAWIO);
+    cap[CAP_TO_INDEX(CAP_SYS_NICE)].permitted |= CAP_TO_MASK(CAP_SYS_NICE);
+    cap[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
+    cap[CAP_TO_INDEX(CAP_WAKE_ALARM)].permitted |= CAP_TO_MASK(CAP_WAKE_ALARM);
+
+    cap[CAP_TO_INDEX(CAP_NET_RAW)].effective |= CAP_TO_MASK(CAP_NET_RAW);
+    cap[CAP_TO_INDEX(CAP_NET_ADMIN)].effective |= CAP_TO_MASK(CAP_NET_ADMIN);
+    cap[CAP_TO_INDEX(CAP_NET_BIND_SERVICE)].effective |= CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+    cap[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective |= CAP_TO_MASK(CAP_SYS_RAWIO);
+    cap[CAP_TO_INDEX(CAP_SYS_NICE)].effective |= CAP_TO_MASK(CAP_SYS_NICE);
+    cap[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID);
+    cap[CAP_TO_INDEX(CAP_WAKE_ALARM)].effective |= CAP_TO_MASK(CAP_WAKE_ALARM);
+
+    capset(&header, &cap[0]);
+    setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+}
+
+
+/*****************************************************************************
+**   Logger API
+*****************************************************************************/
+
+void bdt_log(const char *fmt_str, ...)
+{
+    static char buffer[1024];
+    va_list ap;
+
+    va_start(ap, fmt_str);
+    vsnprintf(buffer, 1024, fmt_str, ap);
+    va_end(ap);
+
+    fprintf(stdout, "%s\n", buffer);
+}
+
+/******************************************************************************
+*
+ ** Misc helper functions
+
+*******************************************************************************/
+static const char* dump_bt_status(int status)
+{
+    switch(status)
+    {
+        CASE_RETURN_STR(BT_STATUS_SUCCESS)
+        CASE_RETURN_STR(BT_STATUS_FAIL)
+        CASE_RETURN_STR(BT_STATUS_NOT_READY)
+        CASE_RETURN_STR(BT_STATUS_NOMEM)
+        CASE_RETURN_STR(BT_STATUS_BUSY)
+        CASE_RETURN_STR(BT_STATUS_UNSUPPORTED)
+
+        default:
+            return "unknown status code";
+    }
+}
+
+
+/******************************************************************************
+*
+ ** Console helper functions
+
+*******************************************************************************/
+
+void skip_blanks(char **p)
+{
+    while (**p == ' ')
+    (*p)++;
+}
+
+uint32_t get_int(char **p, int DefaultValue)
+{
+    uint32_t Value = 0;
+    unsigned char   UseDefault;
+
+    UseDefault = 1;
+    skip_blanks(p);
+
+    while ( ((**p)<= '9' && (**p)>= '0') )
+    {
+        Value = Value * 10 + (**p) - '0';
+        UseDefault = 0;
+        (*p)++;
+    }
+   if (UseDefault)
+       return DefaultValue;
+   else
+       return Value;
+}
+
+int get_signed_int(char **p, int DefaultValue)
+{
+    int    Value = 0;
+    unsigned char   UseDefault;
+    unsigned char  NegativeNum = 0;
+
+    UseDefault = 1;
+    skip_blanks(p);
+
+    if ((**p) == '-')
+    {
+        NegativeNum = 1;
+        (*p)++;
+    }
+    while ( ((**p)<= '9' && (**p)>= '0') )
+    {
+        Value = Value * 10 + (**p) - '0';
+        UseDefault = 0;
+        (*p)++;
+    }
+
+    if (UseDefault)
+        return DefaultValue;
+    else
+        return ((NegativeNum == 0)? Value : -Value);
+}
+
+void get_str_1(char **p, char *Buffer)
+{
+    skip_blanks(p);
+    while (**p != 0 && **p != '\0')
+    {
+        *Buffer = **p;
+        (*p)++;
+        Buffer++;
+    }
+
+    *Buffer = 0;
+}
+
+void get_str(char **p, char *Buffer)
+{
+    skip_blanks(p);
+    while (**p != 0 && **p != ' ')
+    {
+        *Buffer = **p;
+        (*p)++;
+        Buffer++;
+    }
+
+    *Buffer = 0;
+}
+
+
+
+#define is_cmd(str) ((strlen(str) == strlen(cmd)) && strncmp((const char *)&cmd, str, strlen(str)) == 0)
+#define if_cmd(str)  if (is_cmd(str))
+
+typedef void (t_console_cmd_handler) (char *p);
+
+typedef struct {
+    const char *name;
+    t_console_cmd_handler *handler;
+    const char *help;
+    unsigned char is_job;
+} t_cmd;
+
+void do_help(char *p);
+void do_quit(char *p);
+void do_init(char *p);
+void do_enable(char *p);
+void do_disable(char *p);
+void do_cleanup(char *p);
+void do_pairing(char *p);
+void do_pacs_discovery(char *p);
+void do_ascs_discovery(char *p);
+void do_bap_connect(char *p);
+void do_bap_disconnect(char *p);
+void do_bap_start(char *p);
+void do_bap_stop(char *p);
+void do_bap_disc_in_connecting(char *p);
+void do_bap_disc_in_starting(char *p);
+void do_bap_disc_in_stopping(char *p);
+void do_bap_stop_in_starting(char *p);
+void do_bap_update_stream(char *p);
+
+/*******************************************************************
+ *
+ *  CONSOLE COMMAND TABLE
+ *
+*/
+
+const t_cmd console_cmd_list[] =
+{
+    /*
+     * INTERNAL
+     */
+
+    { "help", do_help, "lists all available console commands", 0 },
+    { "quit", do_quit, "", 0},
+
+    /*
+     * API CONSOLE COMMANDS
+     */
+
+     /* Init and Cleanup shall be called automatically */
+    { "enable", do_enable, "cmd :: enable", 0 },
+    { "disable", do_disable, "cmd  :: disable", 0 },
+    { "pair", do_pairing, "cmd :: pair <BdAddr as 00112233445566>", 0 },
+    { "pacs_discovery", do_pacs_discovery, "cmd :: pacs_discovery <BdAddr>", 0 },
+    { "ascs_discovery", do_ascs_discovery, "cmd :: ascs_discovery <BdAddr>", 0 },
+    { "bap_connect", do_bap_connect, "cmd :: bap_connect <CodecConfig> <AudioConfig> <BdAddr> <profile> <direction> <context>", 0 },
+    { "bap_disconnect", do_bap_disconnect, "cmd :: bap_disconnect <BdAddr> <profile> <direction> <context>", 0 },
+    { "bap_start", do_bap_start, "cmd :: bap_start <BdAddr> <profile> <direction> <context>", 0 },
+    { "bap_stop", do_bap_stop, "cmd :: bap_stop <BdAddr> <profile> <direction> <context>", 0 },
+    { "bap_disc_in_connecting", do_bap_disc_in_connecting, "cmd :: bap_disc_in_connecting <CodecConfig> <AudioConfig> <BdAddr> <profile> <direction> <context>", 0 },
+    { "bap_disc_in_starting", do_bap_disc_in_starting, "cmd :: bap_disc_in_starting <BdAddr> <profile> <direction> <context>", 0 },
+    { "bap_disc_in_stopping", do_bap_disc_in_stopping, "cmd :: bap_disc_in_stopping <BdAddr> <profile> <direction> <context>", 0 },
+    { "bap_stop_in_starting", do_bap_stop_in_starting, "cmd :: bap_stop_in_starting <BdAddr> <profile> <direction> <context>", 0 },
+    { "bap_update_stream", do_bap_update_stream, "cmd :: bap_update_stream <BdAddr> <profile> <direction> <new context>", 0 },
+    /* last entry */
+    {NULL, NULL, "", 0},
+};
+
+
+static int console_cmd_maxlen = 0;
+
+static void *cmdjob_handler(void *param)
+{
+    char *job_cmd = (char*)param;
+
+    bdt_log("cmdjob starting (%s)", job_cmd);
+
+    process_cmd(job_cmd, 1);
+
+    bdt_log("cmdjob terminating");
+
+    free(job_cmd);
+    return NULL;
+}
+
+static int create_cmdjob(char *cmd)
+{
+    pthread_t thread_id;
+    char *job_cmd;
+
+    job_cmd = (char*)calloc(1, strlen(cmd)+1); /* freed in job handler */
+    if (job_cmd) {
+       strlcpy(job_cmd, cmd,(strlen(cmd)+1));
+       if (pthread_create(&thread_id, NULL, cmdjob_handler, (void *)job_cmd) != 0)
+      /*if (pthread_create(&thread_id, NULL,
+                       (void*)cmdjob_handler, (void*)job_cmd) !=0)*/
+         perror("pthread_create");
+      return 0;
+    }
+    else
+       perror("create_Cmdjob malloc failed ");
+    return -1;
+}
+
+/******************************************************************************
+*
+ ** Load stack lib
+
+*******************************************************************************/
+#define BLUETOOTH_LIBRARY_NAME "libbluetooth_qti.so"
+int load_bt_lib(const bt_interface_t** interface) {
+    const char* sym = BLUETOOTH_INTERFACE_STRING;
+  bt_interface_t* itf = nullptr;
+
+  // Always try to load the default Bluetooth stack on GN builds.
+  const char* path = BLUETOOTH_LIBRARY_NAME;
+  void* handle = dlopen(path, RTLD_NOW);
+  if (!handle) {
+    //const char* err_str = dlerror();
+    printf("failed to load Bluetooth library\n");
+    goto error;
+  }
+
+  // Get the address of the bt_interface_t.
+  itf = (bt_interface_t*)dlsym(handle, sym);
+  if (!itf) {
+    printf("failed to load symbol from Bluetooth library\n");
+    goto error;
+  }
+
+  // Success.
+  printf(" loaded HAL Success\n");
+  *interface = itf;
+  return 0;
+
+error:
+  *interface = NULL;
+  if (handle) dlclose(handle);
+
+  return -EINVAL;
+}
+
+int HAL_load(void)
+{
+    if (load_bt_lib((bt_interface_t const**)&sBtInterface)) {
+        printf("No Bluetooth Library found\n");
+        return -1;
+    }
+    return 0;
+}
+
+int HAL_unload(void)
+{
+    int err = 0;
+
+    bdt_log("Unloading HAL lib");
+
+    sBtInterface = NULL;
+
+    bdt_log("HAL library unloaded (%s)", strerror(err));
+
+    return err;
+}
+
+/******************************************************************************
+*
+ ** HAL test functions & callbacks
+
+*******************************************************************************/
+
+void setup_test_env(void)
+{
+    int i = 0;
+
+    while (console_cmd_list[i].name != NULL)
+    {
+        console_cmd_maxlen = MAX(console_cmd_maxlen, (int)strlen(console_cmd_list[i].name));
+        i++;
+    }
+}
+
+void check_return_status(int status)
+{
+    if (status != BT_STATUS_SUCCESS)
+    {
+        bdt_log("HAL REQUEST FAILED status : %d (%s)", status, dump_bt_status(status));
+    }
+    else
+    {
+        bdt_log("HAL REQUEST SUCCESS");
+    }
+}
+
+static void do_set_localname(char *p)
+{
+    printf("set name in progress: %s\n", p);
+    bt_property_t property = {BT_PROPERTY_BDNAME, static_cast<int>(strlen(p)), p};
+    status =  sBtInterface->set_adapter_property(&property);
+}
+
+static void adapter_state_changed(bt_state_t state)
+{
+    int V1 = 1000, V2=2;
+    char V3[] = "bap_uclient_test";
+    bt_property_t property = {(bt_property_type_t)9 /*
+BT_PROPERTY_DISCOVERY_TIMEOUT*/, 4, &V1};
+    bt_property_t property1 = {(bt_property_type_t)7 /*SCAN*/, 2, &V2};
+    bt_property_t property2 ={(bt_property_type_t)1,9, &V3};
+    printf("ADAPTER STATE UPDATED : %s\n", (state == BT_STATE_OFF)?"OFF":"ON");
+
+    g_AdapterState = state;
+
+    if (state == BT_STATE_ON) {
+        bt_enabled = 1;
+        status = sBtInterface->set_adapter_property(&property1);
+        status = sBtInterface->set_adapter_property(&property);
+        status = sBtInterface->set_adapter_property(&property2);
+    } else {
+        bt_enabled = 0;
+    }
+}
+
+static void adapter_properties_changed(bt_status_t status,
+         int num_properties, bt_property_t *properties)
+{
+ char Bd_addr[15] = {0};
+    if(NULL == properties)
+    {
+        printf("properties is null\n");
+        return;
+    }
+    switch(properties->type)
+    {
+    case BT_PROPERTY_BDADDR:
+        memcpy(Bd_addr, properties->val, properties->len);
+        break;
+    default:
+        printf("property type not used\n");
+    }
+    return;
+}
+
+static void discovery_state_changed(bt_discovery_state_t state)
+{
+    printf("Discovery State Updated : %s\n",
+         (state == BT_DISCOVERY_STOPPED)?"STOPPED":"STARTED");
+}
+
+
+static void pin_request_cb(RawAddress* remote_bd_addr, bt_bdname_t *bd_name,
+      uint32_t cod, bool min_16_digit )
+{
+    remote_bd_address = remote_bd_addr;
+    //bt_pin_code_t pincode = {{0x31, 0x32, 0x33, 0x34}};
+    printf("Enter the pin key displayed in the remote device and terminate the key entry with .\n");
+
+    /*if(BT_STATUS_SUCCESS != sBtInterface->pin_reply(remote_bd_addr, TRUE, 4
+, &pincode))
+    {
+        printf("Pin Reply failed\n");
+    }*/
+}
+static void ssp_request_cb(RawAddress* remote_bd_addr, bt_bdname_t *bd_name,
+                           uint32_t cod, bt_ssp_variant_t pairing_variant,
+uint32_t pass_key)
+{
+    printf("ssp_request_cb : name=%s variant=%d passkey=%u\n", bd_name->name,
+pairing_variant, pass_key);
+    if(BT_STATUS_SUCCESS != sBtInterface->ssp_reply(remote_bd_addr,
+pairing_variant, TRUE, pass_key))
+    {
+        printf("SSP Reply failed\n");
+    }
+}
+
+static void bond_state_changed_cb(bt_status_t status, RawAddress*
+remote_bd_addr, bt_bond_state_t state)
+{
+    g_PairState = state;
+}
+
+static void acl_state_changed(bt_status_t status, RawAddress* remote_bd_addr,
+bt_acl_state_t state,
+                              bt_hci_error_code_t hci_reason)
+{
+    printf("acl_state_changed : remote_bd_addr=%02x:%02x:%02x:%02x:%02x:%02x, \
+           acl status=%s \n",
+    remote_bd_addr->address[0], remote_bd_addr->address[1], remote_bd_addr->address[2],
+    remote_bd_addr->address[3], remote_bd_addr->address[4], remote_bd_addr->address[5],
+    (state == BT_ACL_STATE_CONNECTED)?"ACL Connected" :"ACL Disconnected");
+}
+static void dut_mode_recv(uint16_t opcode, uint8_t *buf, uint8_t len)
+{
+    bdt_log("DUT MODE RECV : NOT IMPLEMENTED");
+}
+
+static void le_test_mode(bt_status_t status, uint16_t packet_count)
+{
+    bdt_log("LE TEST MODE END status:%s number_of_packets:%d",
+         dump_bt_status(status), packet_count);
+}
+
+extern int timer_create (clockid_t, struct sigevent *__restrict, timer_t *
+__restrict);
+extern int timer_settime (timer_t, int, const struct itimerspec *__restrict,
+struct itimerspec *__restrict);
+
+static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb
+cb, void *data)
+{
+
+   static timer_t timer;
+   static bool timer_created;
+
+   if (!timer_created) {
+      struct sigevent sigevent;
+      memset(&sigevent, 0, sizeof(sigevent));
+      sigevent.sigev_notify = SIGEV_THREAD;
+      sigevent.sigev_notify_function = (void (*)(union sigval))cb;
+      sigevent.sigev_value.sival_ptr = data;
+      timer_create(CLOCK_MONOTONIC, &sigevent, &timer);
+      timer_created = true;
+   }
+
+   struct itimerspec new_value;
+   new_value.it_value.tv_sec = delay_millis / 1000;
+   new_value.it_value.tv_nsec = (delay_millis % 1000) * 1000 * 1000;
+   new_value.it_interval.tv_sec = 0;
+   new_value.it_interval.tv_nsec = 0;
+   timer_settime(timer, 0, &new_value, NULL);
+
+  return TRUE;
+}
+
+static int acquire_wake_lock(const char *lock_name)
+{
+    return BT_STATUS_SUCCESS;
+}
+
+static int release_wake_lock(const char *lock_name)
+{
+    return BT_STATUS_SUCCESS;
+}
+
+static bt_callbacks_t bt_callbacks = {
+    sizeof(bt_callbacks_t),
+    adapter_state_changed,
+    adapter_properties_changed, /*adapter_properties_cb */
+    NULL, /* remote_device_properties_cb */
+    NULL, /* device_found_cb */
+    discovery_state_changed, /* discovery_state_changed_cb */
+    pin_request_cb, /* pin_request_cb  */
+    ssp_request_cb, /* ssp_request_cb  */
+    bond_state_changed_cb, /*bond_state_changed_cb */
+    acl_state_changed, /* acl_state_changed_cb */
+    NULL, /* thread_evt_cb */
+    dut_mode_recv, /*dut_mode_recv_cb */
+    le_test_mode, /* le_test_mode_cb */
+    NULL      /*energy_info_cb*/
+};
+
+static bt_os_callouts_t bt_os_callbacks = {
+     sizeof(bt_os_callouts_t),
+     set_wake_alarm,
+     acquire_wake_lock,
+     release_wake_lock
+};
+
+
+void bdt_enable(void)
+{
+    bdt_log("ENABLE BT");
+    if (bt_enabled) {
+        bdt_log("Bluetooth is already enabled");
+        return;
+    }
+    status = sBtInterface->enable();
+
+    check_return_status(status);
+}
+
+void bdt_disable(void)
+{
+    bdt_log("DISABLE BT");
+    if (!bt_enabled) {
+        bdt_log("Bluetooth is already disabled");
+        return;
+    }
+    status = sBtInterface->disable();
+
+    check_return_status(status);
+}
+
+void do_pairing(char *p)
+{
+    RawAddress bd_addr = {{0}};
+    int transport = GATT_TRANSPORT_LE;
+    if(FALSE == GetBdAddr(p, &bd_addr))    return;    // arg1
+    if(BT_STATUS_SUCCESS != sBtInterface->create_bond(&bd_addr, transport))
+    {
+        printf("Failed to Initiate Pairing \n");
+        return;
+    }
+}
+
+
+void bdt_cleanup(void)
+{
+    bdt_log("CLEANUP");
+    sBtInterface->cleanup();
+}
+
+/******************************************************************************
+*
+ ** Console commands
+
+*******************************************************************************/
+
+void do_help(char *p)
+{
+    int i = 0;
+    char line[128];
+//    int pos = 0;
+
+    while (console_cmd_list[i].name != NULL)
+    {
+        snprintf(line, 128,"%s", (char*)console_cmd_list[i].name);
+        bdt_log("%s %s\n", (char*)line, (char*)console_cmd_list[i].help);
+        i++;
+    }
+}
+
+void do_quit(char *p)
+{
+    bdt_shutdown();
+}
+
+/*******************************************************************
+ *
+ *  BT TEST  CONSOLE COMMANDS
+ *
+ *  Parses argument lists and passes to API test function
+ *
+*/
+
+void do_init(char *p)
+{
+    bdt_init();
+}
+
+void do_enable(char *p)
+{
+    bdt_enable();
+}
+
+using bluetooth::bap::pacs::PacsClientInterface;
+using bluetooth::bap::pacs::PacsClientCallbacks;
+
+static PacsClientInterface* sPacsClientInterface = nullptr;
+static uint16_t pacs_client_id = 0;
+static uint8_t pacsSearchComplete = 0;
+static uint8_t pacsConnectionComplete = 0;
+static uint8_t bapConnectionComplete = 0;
+static RawAddress pac_bd_addr;
+
+class PacsClientCallbacksImpl : public PacsClientCallbacks {
+ public:
+  ~PacsClientCallbacksImpl() = default;
+  void OnInitialized(int status,
+                     int client_id) override {
+    printf("%d\n", client_id);
+    pacs_client_id = client_id;
+  }
+  void OnConnectionState(const RawAddress& bd_addr,
+                         bluetooth::bap::pacs::ConnectionState state)
+override {
+    printf("%s\n", __func__);
+    if(state == bluetooth::bap::pacs::ConnectionState::CONNECTED)  {
+      printf("%s Connected\n", __func__);
+      pacsConnectionComplete = 1;
+    } else if(state == bluetooth::bap::pacs::ConnectionState::DISCONNECTED)  {
+      printf("%s Disconnected\n", __func__);
+    }
+  }
+  void OnAudioContextAvailable(const RawAddress& bd_addr,
+                        uint32_t available_contexts) override {
+    printf("%s\n", __func__);
+  }
+   void OnSearchComplete(int status, const RawAddress& address,
+            std::vector<bluetooth::bap::pacs::CodecConfig> sink_pac_records,
+            std::vector<bluetooth::bap::pacs::CodecConfig> src_pac_records,
+            uint32_t sink_locations,
+            uint32_t src_locations,
+            uint32_t available_contexts,
+            uint32_t supported_contexts) override {
+    pacsSearchComplete = 1;
+    printf("%s\n", __func__);
+  }
+};
+
+static PacsClientCallbacksImpl sPacsClientCallbacks;
+
+void do_pacs_discovery(char *p)
+{
+  if(FALSE == GetBdAddr(p, &pac_bd_addr))    return;    // arg1
+  sPacsClientInterface = (PacsClientInterface*)
+        sBtInterface->get_profile_interface(BT_PROFILE_PACS_CLIENT_ID);
+  sPacsClientInterface->Init(&sPacsClientCallbacks);
+  sleep(1);
+  printf("%s going for connect\n", __func__);
+  sPacsClientInterface->Connect(pacs_client_id, pac_bd_addr);
+  while(!pacsConnectionComplete) sleep(1);
+  printf("%s going for discovery\n", __func__);
+  sPacsClientInterface->StartDiscovery(pacs_client_id, pac_bd_addr);
+  while(!pacsSearchComplete) sleep(1);
+  printf("%s going for disconnect\n", __func__);
+  sleep(5);
+  sPacsClientInterface->Disconnect(pacs_client_id, pac_bd_addr);
+}
+
+using bluetooth::bap::ascs::AscsClientInterface;
+using bluetooth::bap::ascs::AscsClientCallbacks;
+
+static AscsClientInterface* sAscsClientInterface = nullptr;
+static uint16_t ascs_client_id = 0;
+static uint8_t ascsSearchComplete = 0;
+static uint8_t ascsConnectionComplete = 0;
+static RawAddress ascs_bd_addr;
+
+class AscsClientCallbacksImpl : public AscsClientCallbacks {
+  public:
+    ~AscsClientCallbacksImpl() = default;
+    void OnAscsInitialized(int status, int client_id) override {
+        printf("%d\n", client_id);
+        ascs_client_id = client_id;
+    }
+
+    void OnConnectionState(const RawAddress& address,
+                       bluetooth::bap::ascs::GattState state) override {
+        printf("%s\n", __func__);
+        if(state == bluetooth::bap::ascs::GattState::CONNECTED)  {
+          printf("%s Connected\n", __func__);
+          ascsConnectionComplete = 1;
+        } else if(state == bluetooth::bap::ascs::GattState::DISCONNECTED)  {
+          printf("%s Disconnected\n", __func__);
+        }
+    }
+
+    void OnAseOpFailed(const RawAddress& address,
+        bluetooth::bap::ascs::AseOpId ase_op_id,
+        std::vector<bluetooth::bap::ascs::AseOpStatus> status) {
+        printf("%s\n", __func__);
+
+    }
+
+    void OnAseState(const RawAddress& address,
+                          bluetooth::bap::ascs::AseParams ase) override {
+        printf("%s\n", __func__);
+    }
+
+    void OnSearchComplete(int status, const RawAddress& address,
+        std::vector<bluetooth::bap::ascs::AseParams> sink_ase_list,
+        std::vector<bluetooth::bap::ascs::AseParams> src_ase_list) override {
+        printf("%s\n", __func__);
+        ascsSearchComplete = 1;
+    }
+};
+
+static AscsClientCallbacksImpl sAscsClientCallbacks;
+
+void do_ascs_discovery(char *p)
+{
+  if(FALSE == GetBdAddr(p, &ascs_bd_addr))    return;    // arg1
+  sAscsClientInterface = (AscsClientInterface*)
+        sBtInterface->get_profile_interface(BT_PROFILE_ASCS_CLIENT_ID);
+  sAscsClientInterface->Init(&sAscsClientCallbacks);
+  sleep(1);
+  printf("%s going for connect\n", __func__);
+  sAscsClientInterface->Connect(ascs_client_id, ascs_bd_addr);
+  while(!ascsConnectionComplete) sleep(1);
+  printf("%s going for discovery\n", __func__);
+  sAscsClientInterface->StartDiscovery(ascs_client_id, ascs_bd_addr);
+  while(!ascsSearchComplete) sleep(1);
+  printf("%s going for disconnect\n", __func__);
+  sAscsClientInterface->Disconnect(ascs_client_id, ascs_bd_addr);
+}
+
+template <typename T>
+std::string loghex(T x) {
+   std::stringstream tmp;
+   tmp << "0x" << std::internal << std::hex << std::setfill('0')
+       << std::setw(sizeof(T) * 2) << (unsigned int)x;
+   return tmp.str();
+}
+
+using bluetooth::bap::ucast::UcastClientCallbacks;
+using bluetooth::bap::ucast::UcastClientInterface;
+
+static UcastClientInterface* sUcastClientInterface = nullptr;
+
+class UcastClientCallbacksImpl : public UcastClientCallbacks {
+ public:
+  ~UcastClientCallbacksImpl() = default;
+  void OnStreamState(const RawAddress &address,
+      std::vector<bluetooth::bap::ucast::StreamStateInfo> streams_state_info) override {
+    for (auto it = streams_state_info.begin();
+                         it != streams_state_info.end(); it++) {
+      printf("%s stream type %d\n", __func__, (it->stream_type.type));
+      printf("%s stream dir %s\n", __func__, loghex(it->stream_type.direction).c_str());
+      printf("%s stream state %d\n", __func__, static_cast<int> (it->stream_state));
+      if(static_cast<int> (it->stream_state) == 2 ||
+         static_cast<int> (it->stream_state) == 0) {
+        bapConnectionComplete = 1;
+      }
+    }
+  }
+  void OnStreamConfig(const RawAddress &address,
+      std::vector<bluetooth::bap::ucast::StreamConfigInfo> streams_config_info) override {
+    printf("%s\n",__func__);
+  }
+  void OnStreamAvailable(const RawAddress &address,
+                      uint16_t src_audio_contexts,
+                      uint16_t sink_audio_contexts)  override {
+    printf("%s\n",__func__);
+  }
+};
+
+static UcastClientCallbacksImpl sUcastClientCallbacks;
+
+typedef struct {
+    char bdAddr[13];
+    uint16_t profile;
+    uint16_t context;
+    uint8_t direction;
+} Servers;
+
+typedef struct {
+    uint8_t cnt;
+    char codecConfig[7];
+    char audioConfig[5];
+    std::vector<Servers> serv;
+} UserParms;
+
+typedef struct {
+    uint8_t audio_dir;
+    uint8_t stereo;
+} AudioType;
+
+typedef struct {
+    uint8_t num_servers;
+    uint8_t num_cises;
+    std::vector<AudioType> audio_type;
+} AudioConfigSettings;
+
+//
+
+std::map<std::string, AudioConfigSettings> audioConfigMap = {
+ {"1_1", {1, 1, {{ASE_DIRECTION_SINK, 0}}}},  // EB streaming
+ {"2_1", {1, 1, {{ASE_DIRECTION_SRC, 0}}}},   // EB Recording
+ {"3_1", {1, 1, {{ASE_DIRECTION_SRC|ASE_DIRECTION_SINK, 0}}}}, // EB Call Mono Bi-Dir CIS
+ {"4_1", {1, 1, {{ASE_DIRECTION_SINK, ASE_SINK_STEREO}}}}, // Stereo Headset stereo streaming
+
+ {"5_1", {1, 1, {{ASE_DIRECTION_SRC|ASE_DIRECTION_SINK, ASE_SINK_STEREO}}}}, // EB Call with speaker stereo  mono mic
+
+ {"6_1", {1, 2, {{ASE_DIRECTION_SINK, 0}, {ASE_DIRECTION_SINK, 0}}}}, // TWM Streaming
+ {"6_2", {2, 2, {{ASE_DIRECTION_SINK, 0}, {ASE_DIRECTION_SINK, 0}}}}, // EBP Streaming same as 1_1
+ {"7_1", {1, 2, {{ASE_DIRECTION_SINK, 0}, {ASE_DIRECTION_SRC, 0}}}},  // EB Call with dual CIS ( same as 3_1)
+ {"7_2", {2, 2, {{ASE_DIRECTION_SINK, 0}, {ASE_DIRECTION_SRC, 0}}}},  // EBP Call with speaker on EB1 and mic on EB2
+ {"8_1", {1, 2, {{ASE_DIRECTION_SINK, 0}, {ASE_DIRECTION_SRC|ASE_DIRECTION_SINK, 0}}}}, // Headset Call with single mic
+ {"8_2", {2, 2, {{ASE_DIRECTION_SRC|ASE_DIRECTION_SINK, 0}, {ASE_DIRECTION_SINK, 0}}}}, // EBP Call with mic from one EB
+ {"9_1", {1, 2, {{ASE_DIRECTION_SRC, 0}, {ASE_DIRECTION_SRC, 0}}}}, // TWM Recording
+ {"9_2", {2, 2, {{ASE_DIRECTION_SRC, 0}, {ASE_DIRECTION_SRC, 0}}}}, // EBP Recording
+{"10_1", {1, 1, {{ASE_DIRECTION_SRC, ASE_SRC_STEREO}}}}, // EB stereo Recording
+{"11_1", {1, 2, {{ASE_DIRECTION_SRC|ASE_DIRECTION_SINK, 0}, {ASE_DIRECTION_SRC|ASE_DIRECTION_SINK, 0}}}}, // TWM Call
+{"11_2", {2, 2, {{ASE_DIRECTION_SRC|ASE_DIRECTION_SINK, 0}, {ASE_DIRECTION_SRC|ASE_DIRECTION_SINK, 0}}}}}; // EBP Call
+
+int getInt(std::string &str)
+{
+    int ret;
+    std::stringstream integer(str);
+    integer >> ret;
+    return ret;
+}
+
+void parse_parms(char *p, UserParms *ptr)
+{
+    std::string line(p);
+    std::vector <std::string> token;
+    std::stringstream check1(line);
+    std::string intermediate;
+    while(getline(check1, intermediate, ' '))
+    {
+        token.push_back(intermediate);
+    }
+    ptr->cnt = token.size();
+    if (ptr->cnt == 11)
+    {
+        memcpy(ptr->codecConfig, token[1].c_str(), token[1].size());
+        memcpy(ptr->audioConfig, token[2].c_str(), token[2].size());
+        Servers serv1, serv2;
+        memcpy(serv1.bdAddr, token[3].c_str(), token[3].size());
+        serv1.profile = static_cast<uint16_t>(getInt(token[4]));
+        serv1.direction = static_cast<uint16_t>(getInt(token[5]));
+        serv1.context = static_cast<uint16_t>(getInt(token[6]));
+        ptr->serv.push_back(serv1);
+        memcpy(serv2.bdAddr, token[7].c_str(), token[7].size());
+        serv2.profile = static_cast<uint16_t>(getInt(token[8]));
+        serv2.direction = static_cast<uint16_t>(getInt(token[9]));
+        serv2.context = static_cast<uint16_t>(getInt(token[10]));
+        ptr->serv.push_back(serv2);
+    }
+    else if (ptr->cnt == 9)
+    {
+        Servers serv1, serv2;
+        memcpy(serv1.bdAddr, token[1].c_str(), token[1].size());
+        serv1.profile = static_cast<uint16_t>(getInt(token[2]));
+        serv1.direction = static_cast<uint16_t>(getInt(token[3]));
+        serv1.context = static_cast<uint16_t>(getInt(token[4]));
+        ptr->serv.push_back(serv1);
+        memcpy(serv2.bdAddr, token[5].c_str(), token[5].size());
+        serv2.profile = static_cast<uint16_t>(getInt(token[6]));
+        serv2.direction = static_cast<uint16_t>(getInt(token[7]));
+        serv2.context = static_cast<uint16_t>(getInt(token[8]));
+        ptr->serv.push_back(serv2);
+    }
+    else if (ptr->cnt == 7)
+    {
+        memcpy(ptr->codecConfig, token[1].c_str(), token[1].size());
+        memcpy(ptr->audioConfig, token[2].c_str(), token[2].size());
+        Servers serv1;
+        memcpy(serv1.bdAddr, token[3].c_str(), token[3].size());
+        serv1.profile = static_cast<uint16_t>(getInt(token[4]));
+        serv1.direction = static_cast<uint16_t>(getInt(token[5]));
+        serv1.context = static_cast<uint16_t>(getInt(token[6]));
+        ptr->serv.push_back(serv1);
+    }
+    else if (ptr->cnt == 5)
+    {
+        Servers serv1;
+        memcpy(serv1.bdAddr, token[1].c_str(), token[1].size());
+        serv1.profile = static_cast<uint16_t>(getInt(token[2]));
+        serv1.direction = static_cast<uint16_t>(getInt(token[3]));
+        serv1.context = static_cast<uint16_t>(getInt(token[4]));
+        ptr->serv.push_back(serv1);
+    }
+    else
+    {
+        printf("%s ERROR: Input\n", __func__);
+    }
+}
+
+constexpr uint8_t  CONFIG_FRAME_DUR_INDEX       = 0x04;
+constexpr uint8_t  CONFIG_OCTS_PER_FRAME_INDEX  = 0x04;
+constexpr uint8_t  CONFIG_PREF_AUDIO_CONT_INDEX = 0x06; // CS1
+
+bool UpdateFrameDuration(bluetooth::bap::pacs::CodecConfig *config ,
+         uint8_t frame_dur) {
+   uint64_t value = 0xFF;
+   config->codec_specific_1 &=
+       ~(value << (CONFIG_FRAME_DUR_INDEX*8));
+   config->codec_specific_1 |=
+       static_cast<uint64_t>(frame_dur) << (CONFIG_FRAME_DUR_INDEX * 8);
+   return true;
+}
+
+
+bool UpdatePreferredAudioContext(bluetooth::bap::pacs::CodecConfig *config ,
+                                    uint16_t pref_audio_context) {
+  uint64_t value = 0xFFFF;
+  config->codec_specific_1 &= ~(value << (CONFIG_PREF_AUDIO_CONT_INDEX*8));
+  config->codec_specific_1 |=  static_cast<uint64_t>(pref_audio_context) <<
+                               (CONFIG_PREF_AUDIO_CONT_INDEX * 8);
+  return true;
+}
+
+bool UpdateOctsPerFrame(bluetooth::bap::pacs::CodecConfig *config ,
+        uint16_t octs_per_frame) {
+    uint64_t value = 0xFFFF;
+    config->codec_specific_2 &=
+        ~(value << (CONFIG_OCTS_PER_FRAME_INDEX * 8));
+    config->codec_specific_2 |=
+        static_cast<uint64_t>(octs_per_frame) << (CONFIG_OCTS_PER_FRAME_INDEX * 8);
+    return true;
+}
+
+void set_conn_info(bluetooth::bap::ucast::StreamConnect *conn_info, int type, int context, int dir)
+{
+    conn_info->stream_type.type = type;
+    conn_info->stream_type.direction = dir;
+    conn_info->stream_type.audio_context = context;
+}
+
+bluetooth::bap::pacs::CodecSampleRate get_sample_rate (char *p)
+{
+    std::string str = p;
+    if (str.find("16_") != std::string::npos)
+        return bluetooth::bap::pacs::CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+    else if (str.find("24_") != std::string::npos)
+        return bluetooth::bap::pacs::CodecSampleRate::CODEC_SAMPLE_RATE_24000;
+    else if (str.find("32_") != std::string::npos)
+        return bluetooth::bap::pacs::CodecSampleRate::CODEC_SAMPLE_RATE_32000;
+    else if (str.find("48_") != std::string::npos)
+        return bluetooth::bap::pacs::CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    else if (str.find("8_") != std::string::npos)
+        return bluetooth::bap::pacs::CodecSampleRate::CODEC_SAMPLE_RATE_8000;
+    else
+        return bluetooth::bap::pacs::CodecSampleRate::CODEC_SAMPLE_RATE_NONE;
+}
+
+int get_frame_duration (char *p)
+{
+    std::string str = p;
+    int ret;
+    if ((str.find("_1_") != std::string::npos) ||
+        (str.find("_3_") != std::string::npos) ||
+        (str.find("_5_") != std::string::npos))
+        ret = static_cast<int>(bluetooth::bap::pacs::CodecFrameDuration::FRAME_DUR_7_5);
+    else if ((str.find("_2_") != std::string::npos) ||
+             (str.find("_4_") != std::string::npos) ||
+             (str.find("_6_") != std::string::npos))
+        ret = static_cast<int>(bluetooth::bap::pacs::CodecFrameDuration::FRAME_DUR_10);
+    else
+        ret = -1;
+    return ret;
+}
+
+int get_sdu_interval (char *p)
+{
+    std::string str = p;
+    int ret;
+    if ((str.find("_1_") != std::string::npos) ||
+        (str.find("_3_") != std::string::npos) ||
+        (str.find("_5_") != std::string::npos))
+        ret = 7500;
+    else if ((str.find("_2_") != std::string::npos) ||
+             (str.find("_4_") != std::string::npos) ||
+             (str.find("_6_") != std::string::npos))
+        ret = 10000;
+    else
+        ret = -1;
+    return ret;
+}
+
+std::map<std::string, int> octetPerFrame =
+{{"8_1", 26},{"8_2", 30},{"16_1", 30},{"16_2", 40},
+{"24_1", 45},{"24_2", 60},{"32_1", 60},{"32_2", 80},
+{"48_1", 75},{"48_2", 100},{"48_3", 90},{"48_4", 120},
+{"48_5", 117},{"48_6", 155}};
+
+int get_octetPerFrame (char *p)
+{
+    std::string str = p;
+    int ret = -1;
+    size_t pos = str.rfind('_');
+    std::string key = str.substr(0, pos);
+    for (std::map<std::string, int>::iterator it =
+        octetPerFrame.begin(); it != octetPerFrame.end(); it++)
+    {
+        if (key.compare(it->first) == 0)
+            ret = it->second;
+    }
+    return ret;
+}
+
+std::map<std::string, int> tport_latency =
+{{"8_1_1", 8},{"16_1_1", 8},{"24_1_1", 8},{"32_1_1", 8},
+ {"8_2_1", 10},{"16_2_1", 10},{"24_2_1", 10},{"32_2_1", 10},
+{"48_1_1", 15},{"48_3_1", 15},{"48_5_1", 15},
+{"48_2_1", 20},{"48_4_1", 20},{"48_6_1", 20},
+ {"8_1_2", 75},{"16_1_2", 75},{"24_1_2", 75},
+{"31_1_2", 75},{"48_1_2", 75},{"48_3_2", 75},{"48_5_2", 75},
+ {"8_2_2", 95},{"16_2_2", 95},{"24_2_2", 95},
+{"32_2_2", 95},{"48_2_2", 95},
+{"48_4_2", 100},{"48_6_2", 100}};
+
+int get_tport_latency (char *p)
+{
+    std::string str = p;
+    for (std::map<std::string, int>::iterator it = tport_latency.begin();
+        it != tport_latency.end(); it++)
+    {
+        if (str.compare(it->first) == 0)
+            return it->second;
+    }
+    return -1;
+}
+
+int get_rtn (char *p)
+{
+    std::string str = p;
+    int ret;
+    size_t pos = str.rfind('_');
+    std::string key = str.substr(pos);
+    if (str.find("_1_2") != std::string::npos ||
+        str.find("_2_2") != std::string::npos ||
+        str.find("_3_2") != std::string::npos ||
+        str.find("_4_2") != std::string::npos ||
+        str.find("_5_2") != std::string::npos ||
+        str.find("_6_2") != std::string::npos ) {
+        ret = 13;
+        return ret;
+    }
+    if (str.find("48_") != std::string::npos)
+        ret = 5;
+    if ((str.find("8_") != std::string::npos) ||
+        (str.find("16_") != std::string::npos) ||
+        (str.find("24_") != std::string::npos) ||
+        (str.find("32_") != std::string::npos))
+        ret = 2;
+    else
+        ret = -1;
+    return ret;
+}
+
+int getAudioConfigSettings(char *p, AudioConfigSettings *ptr)
+{
+    int ret = -1;
+    std::string key = p;
+    for (std::map<std::string, AudioConfigSettings>::iterator it =
+        audioConfigMap.begin();
+        it != audioConfigMap.end(); it++)
+    {
+        if (key.compare(it->first) == 0)
+        {
+            *ptr = it->second;
+            printf(" %s ERROR: audio type 0 %d \n", __func__, ptr->audio_type[0].audio_dir);
+            printf(" %s ERROR: audio type 1 %d \n", __func__, ptr->audio_type[1].audio_dir);
+            //memcpy(ptr, &it->second, sizeof(AudioConfigSettings));
+            ret = 0;
+        }
+    }
+    return ret;
+}
+
+typedef struct
+{
+    uint8_t A;
+    uint8_t B;
+    uint8_t C;
+} setFormat;
+
+void set(void *dest, setFormat src)
+{
+    memcpy(dest, &src, sizeof(setFormat));
+}
+
+void set_codec_qos_config (bluetooth::bap::ucast::CodecQosConfig *codec_qos_config,
+                           char *codecConfig, AudioConfigSettings *acs,
+                           uint8_t audio_direction, uint16_t context,
+                           uint8_t server_id,
+                           uint8_t server_count, uint8_t total_servers)
+{
+    int frameDuration, octetPerFrame, tport_latency, sdu_interval, rtn, cis_t;
+    bluetooth::bap::pacs::CodecSampleRate sampleRate;
+    bool stereo_t = false;
+    bluetooth::bap::ucast::CIGConfig cig_config;
+    sampleRate = get_sample_rate(codecConfig);
+    printf("Sample Rate %d\n", sampleRate);
+    printf("server_id  %d\n", server_id);
+
+    if (sampleRate == bluetooth::bap::pacs::CodecSampleRate::CODEC_SAMPLE_RATE_NONE)
+    {
+        printf(" %s ERROR: sample rate\n", __func__);
+        exit(0);
+    }
+    frameDuration = get_frame_duration(codecConfig);
+    if (frameDuration < 0)
+    {
+        printf(" %s ERROR: frame duration\n", __func__);
+        exit(0);
+    }
+    octetPerFrame = get_octetPerFrame(codecConfig);
+    if (octetPerFrame < 0)
+    {
+        printf(" %s ERROR: octet per frame\n", __func__);
+        exit(0);
+    }
+    tport_latency = get_tport_latency(codecConfig);
+    if (tport_latency < 0)
+    {
+        printf(" %s ERROR: max transport latency\n", __func__);
+        exit(0);
+    }
+    rtn = get_rtn(codecConfig);
+    if (rtn < 0)
+    {
+        printf(" %s ERROR: re-transmission\n", __func__);
+        exit(0);
+    }
+    sdu_interval = get_sdu_interval(codecConfig);
+    codec_qos_config->codec_config.codec_type =
+        bluetooth::bap::pacs::CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config->codec_config.codec_priority =
+        bluetooth::bap::pacs::CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config->codec_config.sample_rate = sampleRate;
+    UpdateFrameDuration(&codec_qos_config->codec_config,
+        static_cast<uint8_t>(frameDuration));
+    UpdatePreferredAudioContext(&codec_qos_config->codec_config, context);
+    cig_config.cig_id = 1;
+    cig_config.cis_count = acs->num_cises;
+    cig_config.packing = 0x01; // interleaved
+    cig_config.framing = 0x00; // unframed
+    cig_config.max_tport_latency_m_to_s = static_cast<uint16_t>(tport_latency);
+    cig_config.max_tport_latency_s_to_m = static_cast<uint16_t>(tport_latency);
+    if (sdu_interval == 7500)
+    {
+        set(&cig_config.sdu_interval_m_to_s, {0x4C, 0x1D, 0x00});
+        set(&cig_config.sdu_interval_s_to_m, {0x4C, 0x1D, 0x00});
+    }
+    else
+    {
+        set(&cig_config.sdu_interval_m_to_s, {0x10, 0x27, 0x00});
+        set(&cig_config.sdu_interval_s_to_m, {0x10, 0x27, 0x00});
+    }
+    memcpy(&codec_qos_config->qos_config.cig_config,
+        &cig_config, sizeof(cig_config));
+    for (uint8_t i = 0; i < acs->num_cises; i++) {
+        bluetooth::bap::ucast::CISConfig cis_config;
+        bluetooth::bap::ucast::ASCSConfig ascs_config;
+        int max_sdu_m_to_s;
+        int max_sdu_s_to_m;
+        cis_config.cis_id = i;
+        ascs_config.cig_id = 1;
+        ascs_config.cis_id = i;
+        max_sdu_m_to_s = max_sdu_s_to_m = get_octetPerFrame(codecConfig);
+        printf("audio_dir %d\n", acs->audio_type[i].audio_dir);
+        printf("max_sdu_m_to_s %d\n", max_sdu_m_to_s);
+        printf("max_sdu_s_to_m %d\n", max_sdu_s_to_m);
+        if(acs->audio_type[i].stereo & ASE_SRC_STEREO) {
+          max_sdu_s_to_m *= 2;
+          if(audio_direction & ASE_DIRECTION_SRC) stereo_t = true;
+        }
+        if(acs->audio_type[i].stereo & ASE_SINK_STEREO) {
+          max_sdu_m_to_s *= 2;
+          if(audio_direction & ASE_DIRECTION_SINK) stereo_t = true;
+        }
+
+        if (acs->audio_type[i].audio_dir == (ASE_DIRECTION_SINK|ASE_DIRECTION_SRC))
+        {
+            printf("i %d   Filling both m to s and s to m \n", i);
+            cis_config.max_sdu_m_to_s = static_cast<uint16_t>(max_sdu_m_to_s);
+            cis_config.max_sdu_s_to_m = static_cast<uint16_t>(max_sdu_s_to_m);
+            ascs_config.bi_directional = true;
+        }
+        else if (acs->audio_type[i].audio_dir == ASE_DIRECTION_SRC)
+        {
+            printf("i %d   Filling s to m \n", i);
+            cis_config.max_sdu_s_to_m = static_cast<uint16_t>(max_sdu_s_to_m);
+            cis_config.max_sdu_m_to_s = 0;
+            ascs_config.bi_directional = false;
+        }
+        else if (acs->audio_type[i].audio_dir == ASE_DIRECTION_SINK)
+        {
+            printf("i %d   Filling m to s  \n", i);
+            cis_config.max_sdu_m_to_s = static_cast<uint16_t>(max_sdu_m_to_s);
+            cis_config.max_sdu_s_to_m = 0;
+            ascs_config.bi_directional = false;
+        }
+        cis_config.phy_m_to_s = 0x02;
+        cis_config.phy_s_to_m = 0x02;
+        cis_config.rtn_m_to_s = static_cast<uint8_t>(rtn);
+        cis_config.rtn_s_to_m = static_cast<uint8_t>(rtn);
+        printf("rtn   %d   \n", rtn);
+        set(&ascs_config.presentation_delay, {0x40, 0x9C, 0x00});
+        codec_qos_config->qos_config.cis_configs.push_back(cis_config);
+
+
+        printf("i %d   server_id   %d \n", i, server_id);
+        if(total_servers == 1) {
+          if(acs->num_cises == 1) {
+            codec_qos_config->qos_config.ascs_configs.push_back(ascs_config);
+          } else if(acs->num_cises == 2) {
+            if(acs->audio_type[i % acs->num_cises].audio_dir ==
+              acs->audio_type[(i + 1) % acs->num_cises].audio_dir) {
+              codec_qos_config->qos_config.ascs_configs.push_back(ascs_config);
+            } else if( i == server_count) {
+              codec_qos_config->qos_config.ascs_configs.push_back(ascs_config);
+            }
+          }
+        } else if(total_servers == 2) {
+           if(i == server_id) {
+             codec_qos_config->qos_config.ascs_configs.push_back(ascs_config);
+           }
+        }
+    }
+    if (stereo_t == true) {
+        codec_qos_config->codec_config.channel_mode =
+            bluetooth::bap::pacs::CodecChannelMode::CODEC_CHANNEL_MODE_STEREO;
+        UpdateOctsPerFrame(&codec_qos_config->codec_config,
+            static_cast<uint16_t>(octetPerFrame*2));
+    } else {
+        codec_qos_config->codec_config.channel_mode =
+            bluetooth::bap::pacs::CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+        UpdateOctsPerFrame(&codec_qos_config->codec_config,
+            static_cast<uint16_t>(octetPerFrame));
+    }
+}
+
+void do_bap_connect (char *p)
+{
+    UserParms args;
+    parse_parms(p, &args);
+    bluetooth::bap::ucast::CodecQosConfig codec_qos_config;
+    bluetooth::bap::ucast::CodecQosConfig codec_qos_config_2;
+    AudioConfigSettings acs;
+    bapConnectionComplete = 0;
+    if (getAudioConfigSettings(args.audioConfig, &acs) < 0)
+    {
+        printf("%s ERROR: AudioConfig\n", __func__);
+        exit(0);
+    }
+    //set_codec_qos_config(&codec_qos_config, args.codecConfig, &acs);
+    for (uint8_t i = 0; i < args.serv.size(); i++) {
+        RawAddress bap_bd_addr;
+        bluetooth::bap::ucast::StreamConnect conn_info;
+        std::vector<bluetooth::bap::ucast::StreamConnect> streams;
+        codec_qos_config.qos_config.cis_configs.clear();
+        codec_qos_config.qos_config.ascs_configs.clear();
+        codec_qos_config_2.qos_config.cis_configs.clear();
+        codec_qos_config_2.qos_config.ascs_configs.clear();
+        if(FALSE == GetBdAddr(args.serv[i].bdAddr, &bap_bd_addr))     return;
+        if (args.serv[i].direction & ASE_DIRECTION_SINK)
+        {
+            set_codec_qos_config(&codec_qos_config, args.codecConfig,
+                                 &acs, ASE_DIRECTION_SINK, args.serv[i].context,
+                                 i, 0,
+                                 args.serv.size());
+            set_conn_info(&conn_info, args.serv[i].profile,
+                args.serv[i].context, ASE_DIRECTION_SINK);
+            printf("%s ERROR: context %d\n", __func__, args.serv[i].context);
+            conn_info.codec_qos_config_pair.push_back(codec_qos_config);
+            streams.push_back(conn_info);
+        }
+        if (args.serv[i].direction & ASE_DIRECTION_SRC)
+        {
+            set_codec_qos_config(&codec_qos_config_2, args.codecConfig,
+                                 &acs, ASE_DIRECTION_SRC, args.serv[i].context,
+                                 i, 1,
+                                 args.serv.size());
+            set_conn_info(&conn_info, args.serv[i].profile,
+                args.serv[i].context, ASE_DIRECTION_SRC);
+            printf("%s ERROR: context %d\n", __func__, args.serv[i].context);
+            conn_info.codec_qos_config_pair.push_back(codec_qos_config_2);
+            streams.push_back(conn_info);
+        }
+        std::vector<RawAddress> address;
+        address.push_back(bap_bd_addr);
+        sUcastClientInterface->Connect(address, true, streams);
+    }
+    while(!bapConnectionComplete) sleep(1);
+}
+
+void do_bap_disconnect (char *p)
+{
+    UserParms args;
+    parse_parms(p, &args);
+    for (uint8_t i = 0; i < ((args.cnt)/4); i++) {
+        RawAddress bap_bd_addr;
+        if(FALSE == GetBdAddr(args.serv[i].bdAddr, &bap_bd_addr)) return;
+        std::vector<bluetooth::bap::ucast::StreamType> streams;
+        if (args.serv[i].direction & 1) {
+            bluetooth::bap::ucast::StreamType type_1 =
+                      { .type = static_cast<uint8_t>(args.serv[i].profile),
+                        .audio_context = args.serv[i].context,
+                        .direction = 1
+                      };
+            streams.push_back(type_1);
+        }
+        if (args.serv[i].direction & 2) {
+                    bluetooth::bap::ucast::StreamType type_1 =
+                      { .type = static_cast<uint8_t>(args.serv[i].profile),
+                        .audio_context = args.serv[i].context,
+                        .direction = 2
+                      };
+            streams.push_back(type_1);
+        }
+        sUcastClientInterface->Disconnect(bap_bd_addr, streams);
+    }
+}
+
+void do_bap_start (char *p)
+{
+    UserParms args;
+    parse_parms(p, &args);
+    for (uint8_t i = 0; i < ((args.cnt)/4); i++) {
+        RawAddress bap_bd_addr;
+        if(FALSE == GetBdAddr(args.serv[i].bdAddr, &bap_bd_addr)) return;
+        std::vector<bluetooth::bap::ucast::StreamType> streams;
+        if (args.serv[i].direction & 1) {
+            bluetooth::bap::ucast::StreamType type_1 =
+                      { .type = static_cast<uint8_t>(args.serv[i].profile),
+                        .audio_context = args.serv[i].context,
+                        .direction = 1
+                      };
+            streams.push_back(type_1);
+        }
+        if (args.serv[i].direction & 2) {
+                    bluetooth::bap::ucast::StreamType type_1 =
+                      { .type = static_cast<uint8_t>(args.serv[i].profile),
+                        .audio_context = args.serv[i].context,
+                        .direction = 2
+                      };
+            streams.push_back(type_1);
+        }
+        sUcastClientInterface->Start(bap_bd_addr, streams);
+    }
+}
+
+void do_bap_stop (char *p)
+{
+    UserParms args;
+    parse_parms(p, &args);
+    for (uint8_t i = 0; i < ((args.cnt)/4); i++) {
+        RawAddress bap_bd_addr;
+        if(FALSE == GetBdAddr(args.serv[i].bdAddr, &bap_bd_addr)) return;
+        std::vector<bluetooth::bap::ucast::StreamType> streams;
+        if (args.serv[i].direction & 1) {
+            bluetooth::bap::ucast::StreamType type_1 =
+                      { .type = static_cast<uint8_t>(args.serv[i].profile),
+                        .audio_context = args.serv[i].context,
+                        .direction = 1
+                      };
+            streams.push_back(type_1);
+        }
+        if (args.serv[i].direction & 2) {
+                    bluetooth::bap::ucast::StreamType type_1 =
+                      { .type = static_cast<uint8_t>(args.serv[i].profile),
+                        .audio_context = args.serv[i].context,
+                        .direction = 2
+                      };
+            streams.push_back(type_1);
+        }
+        sUcastClientInterface->Stop(bap_bd_addr, streams);
+    }
+}
+
+void do_bap_disc_in_connecting (char *p)
+{
+    int del;
+    printf("Enter the delay (ms)> ");
+    std::cin >> del;
+    do_bap_connect(p);
+    usleep(del *1000);
+    do_bap_disconnect(p);
+}
+
+void do_bap_disc_in_starting (char *p)
+{
+    int del;
+    printf("Enter the delay (ms)> ");
+    std::cin >> del;
+    do_bap_start(p);
+    usleep(del *1000);
+    do_bap_disconnect(p);
+}
+
+void do_bap_disc_in_stopping (char *p)
+{
+    int del;
+    printf("Enter the delay (ms)> ");
+    std::cin >> del;
+    do_bap_stop(p);
+    usleep(del *1000);
+    do_bap_disconnect(p);
+}
+
+void do_bap_stop_in_starting (char *p)
+{
+    int del;
+    printf("Enter the delay (ms)> ");
+    std::cin >> del;
+    do_bap_start(p);
+    usleep(del *1000);
+    do_bap_stop(p);
+}
+
+void do_bap_update_stream (char *p)
+{
+    UserParms args;
+    parse_parms(p, &args);
+    for (uint8_t i = 0; i < ((args.cnt)/4); i++) {
+        RawAddress bap_bd_addr;
+        if(FALSE == GetBdAddr(args.serv[i].bdAddr, &bap_bd_addr)) return;
+        std::vector<bluetooth::bap::ucast::StreamUpdate> Update_Stream;
+        if (args.serv[i].direction & 1) {
+            bluetooth::bap::ucast::StreamType type_1 =
+                      { .type = static_cast<uint8_t>(args.serv[i].profile),
+                        .direction = 1
+                      };
+            bluetooth::bap::ucast::StreamUpdate sUpdate =
+                      {
+                        type_1,
+                        bluetooth::bap::ucast::StreamUpdateType::STREAMING_CONTEXT,
+                        args.serv[i].context
+                      };
+            Update_Stream.push_back(sUpdate);
+        }
+        if (args.serv[i].direction & 2) {
+            bluetooth::bap::ucast::StreamType type_1 =
+                      { .type = static_cast<uint8_t>(args.serv[i].profile),
+                        .direction = 2
+                      };
+            bluetooth::bap::ucast::StreamUpdate sUpdate =
+                      {
+                        type_1,
+                        bluetooth::bap::ucast::StreamUpdateType::STREAMING_CONTEXT,
+                        args.serv[i].context
+                      };
+            Update_Stream.push_back(sUpdate);
+        }
+        sUcastClientInterface->UpdateStream(bap_bd_addr, Update_Stream);
+    }
+}
+
+void do_disable(char *p)
+{
+    bdt_disable();
+}
+
+void do_cleanup(char *p)
+{
+    bdt_cleanup();
+}
+
+void bdt_init(void)
+{
+    bdt_log("INIT BT ");
+    status = sBtInterface->init(&bt_callbacks, false, false, 0, nullptr, false);
+    sleep(1);
+    if (status == BT_STATUS_SUCCESS) {
+        status = sBtInterface->set_os_callouts(&bt_os_callbacks);
+    }
+    check_return_status(status);
+}
+
+/******************************************************************************
+*
+ ** GATT SERVER API commands
+
+*******************************************************************************/
+
+/*
+ * Main console command handler
+*/
+
+static void process_cmd(char *p, unsigned char is_job)
+{
+    char cmd[2048];
+    int i = 0;
+    bt_pin_code_t pincode;
+    char *p_saved = p;
+
+    get_str(&p, cmd);
+
+    /* table commands */
+    while (console_cmd_list[i].name != NULL)
+    {
+        if (is_cmd(console_cmd_list[i].name))
+        {
+            if (!is_job && console_cmd_list[i].is_job)
+                create_cmdjob(p_saved);
+            else
+            {
+                console_cmd_list[i].handler(p);
+            }
+            return;
+        }
+        i++;
+    }
+    //pin key
+    if(cmd[6] == '.') {
+        for(i=0; i<6; i++) {
+            pincode.pin[i] = cmd[i];
+        }
+        if(BT_STATUS_SUCCESS != sBtInterface->pin_reply(remote_bd_address,
+TRUE, strlen((const char*)pincode.pin), &pincode)) {
+            printf("Pin Reply failed\n");
+        }
+        //flush the char for pinkey
+        cmd[6] = 0;
+    }
+    else {
+        bdt_log("%s : unknown command\n", p_saved);
+        do_help(NULL);
+    }
+}
+
+int main()
+{
+    config_permissions();
+    bdt_log("\n:::::::::::::::::::::::::::::::::::::::::::::::::::");
+    bdt_log(":: Bluedroid test app starting");
+
+    if ( HAL_load() < 0 ) {
+        perror("HAL failed to initialize, exit\n");
+        unlink(PID_FILE);
+        exit(0);
+    }
+
+    setup_test_env();
+
+    /* Automatically perform the init */
+    bdt_init();
+    sleep(5);
+    bdt_enable();
+    sleep(5);
+
+    sUcastClientInterface = (UcastClientInterface*)
+        sBtInterface->get_profile_interface(BT_PROFILE_BAP_UCLIENT_ID);
+    sUcastClientInterface->Init(&sUcastClientCallbacks);
+    sPacsClientInterface = (PacsClientInterface*)
+        sBtInterface->get_profile_interface(BT_PROFILE_PACS_CLIENT_ID);
+    sPacsClientInterface->Init(&sPacsClientCallbacks);
+    sAscsClientInterface = (AscsClientInterface*)
+        sBtInterface->get_profile_interface(BT_PROFILE_ASCS_CLIENT_ID);
+    sAscsClientInterface->Init(&sAscsClientCallbacks);
+
+    sleep(5);
+    while(!main_done)
+    {
+        char line[2048], *result;
+
+
+        /* command prompt */
+        printf( ">" );
+        fflush(stdout);
+
+        if ((result = fgets (line, 2048, stdin)) == NULL)
+        {
+            printf("ERROR: The string is NULL. code %d\n", errno);
+            exit(0);
+        }
+        else
+        {
+            printf("UserInput\n");
+        }
+
+        if (line[0]!= '\0')
+        {
+            /* remove linefeed */
+            line[strlen(line)-1] = 0;
+
+            process_cmd(line, 0);
+            memset(line, '\0', 2048);
+        }
+    }
+    HAL_unload();
+
+    bdt_log(":: bap uca test app terminating");
+
+    return 0;
+}
+
+int GetFileName(char *p, char *filename)
+{
+//    uint8_t  i;
+    int len;
+
+    skip_blanks(&p);
+
+    printf("Input file name = %s\n", p);
+
+    if (p == NULL)
+    {
+        printf("\nInvalid File Name... Please enter file name\n");
+        return FALSE;
+    }
+    len = strlen(p);
+
+    memcpy(filename, p, len);
+    filename[len] = '\0';
+
+    return TRUE;
+}
+uint8_t check_length(char *p)
+{
+    uint8_t val = 0;
+    while (*p != ' ' && *p != '\0')
+    {
+        val++;
+        p++;
+    }
+    return val;
+}
+int GetBdAddr(char *p, RawAddress* pbd_addr)
+{
+    char Arr[13] = {0};
+    char *pszAddr = NULL;
+    uint8_t k1 = 0;
+    uint8_t k2 = 0;
+    uint8_t  i;
+
+    skip_blanks(&p);
+
+    printf("Input=%s\n", p);
+
+    if(12 != check_length(p))
+    {
+        printf("\nInvalid Bd Address. Format[112233445566]\n");
+        return FALSE;
+    }
+    memcpy(Arr, p, 12);
+
+    for(i=0; i<12; i++)
+    {
+        Arr[i] = tolower(Arr[i]);
+    }
+    pszAddr = Arr;
+
+    for(i=0; i<6; i++)
+    {
+        k1 = (uint8_t) ( (*pszAddr >= 'a') ?
+            ( 10 + (uint8_t)( *pszAddr - 'a' )) : (*pszAddr - '0') );
+        pszAddr++;
+        k2 = (uint8_t) ( (*pszAddr >= 'a') ?
+            ( 10 + (uint8_t)( *pszAddr - 'a' )) : (*pszAddr - '0') );
+        pszAddr++;
+
+        if ( (k1>15)||(k2>15) )
+        {
+            return FALSE;
+        }
+        pbd_addr->address[i] = (k1<<4 | k2);
+    }
+    return TRUE;
+}
+#endif //BAP_UNICAST_TEST_APP_INTERFACE
diff --git a/le_audio/certification_tool/types/Android.bp b/le_audio/certification_tool/types/Android.bp
new file mode 100644
index 0000000..77d1630
--- /dev/null
+++ b/le_audio/certification_tool/types/Android.bp
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+
+cc_library_static {
+    name: "libbluetooth-types-qti",
+    vendor_available: true,
+    enabled: false,
+    defaults: ["fluoride_types_defaults"],
+    cflags: [
+        /* we export all classes, so change default visibility, instead of having EXPORT_SYMBOL on each class*/
+        "-fvisibility=default",
+    ],
+    host_supported: true,
+    srcs: [
+        "class_of_device.cc",
+        "raw_address.cc",
+        "bluetooth/uuid.cc",
+    ],
+}
diff --git a/le_audio/certification_tool/types/bluetooth/uuid.cc b/le_audio/certification_tool/types/bluetooth/uuid.cc
new file mode 100644
index 0000000..d05f437
--- /dev/null
+++ b/le_audio/certification_tool/types/bluetooth/uuid.cc
@@ -0,0 +1,177 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 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 "uuid.h"
+
+#include <base/rand_util.h>
+#include <base/strings/stringprintf.h>
+#include <string.h>
+#include <algorithm>
+
+namespace bluetooth {
+
+static_assert(sizeof(Uuid) == 16, "Uuid must be 16 bytes long!");
+
+using UUID128Bit = Uuid::UUID128Bit;
+
+const Uuid Uuid::kEmpty = Uuid::From128BitBE(UUID128Bit{{0x00}});
+
+namespace {
+constexpr Uuid kBase = Uuid::From128BitBE(
+    UUID128Bit{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+                0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}});
+}  // namespace
+
+size_t Uuid::GetShortestRepresentationSize() const {
+  if (memcmp(uu.data() + kNumBytes32, kBase.uu.data() + kNumBytes32,
+             kNumBytes128 - kNumBytes32) != 0) {
+    return kNumBytes128;
+  }
+
+  if (uu[0] == 0 && uu[1] == 0) return kNumBytes16;
+
+  return kNumBytes32;
+}
+
+bool Uuid::Is16Bit() const {
+  return GetShortestRepresentationSize() == kNumBytes16;
+}
+
+uint16_t Uuid::As16Bit() const { return (((uint16_t)uu[2]) << 8) + uu[3]; }
+
+uint32_t Uuid::As32Bit() const {
+  return (((uint32_t)uu[0]) << 24) + (((uint32_t)uu[1]) << 16) +
+         (((uint32_t)uu[2]) << 8) + uu[3];
+}
+
+Uuid Uuid::FromString(const std::string& uuid, bool* is_valid) {
+  if (is_valid) *is_valid = false;
+  Uuid ret = kBase;
+
+  if (uuid.empty()) return ret;
+
+  uint8_t* p = ret.uu.data();
+  if (uuid.size() == kString128BitLen) {
+    if (uuid[8] != '-' || uuid[13] != '-' || uuid[18] != '-' ||
+        uuid[23] != '-') {
+      return ret;
+    }
+
+    int c;
+    int rc =
+        sscanf(uuid.c_str(),
+               "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx"
+               "-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%n",
+               &p[0], &p[1], &p[2], &p[3], &p[4], &p[5], &p[6], &p[7], &p[8],
+               &p[9], &p[10], &p[11], &p[12], &p[13], &p[14], &p[15], &c);
+    if (rc != 16) return ret;
+    if (c != kString128BitLen) return ret;
+
+    if (is_valid) *is_valid = true;
+  } else if (uuid.size() == 8) {
+    int c;
+    int rc = sscanf(uuid.c_str(), "%02hhx%02hhx%02hhx%02hhx%n", &p[0], &p[1],
+                    &p[2], &p[3], &c);
+    if (rc != 4) return ret;
+    if (c != 8) return ret;
+
+    if (is_valid) *is_valid = true;
+  } else if (uuid.size() == 4) {
+    int c;
+    int rc = sscanf(uuid.c_str(), "%02hhx%02hhx%n", &p[2], &p[3], &c);
+    if (rc != 2) return ret;
+    if (c != 4) return ret;
+
+    if (is_valid) *is_valid = true;
+  }
+
+  return ret;
+}
+
+Uuid Uuid::From16Bit(uint16_t uuid16) {
+  Uuid u = kBase;
+
+  u.uu[2] = (uint8_t)((0xFF00 & uuid16) >> 8);
+  u.uu[3] = (uint8_t)(0x00FF & uuid16);
+  return u;
+}
+
+Uuid Uuid::From32Bit(uint32_t uuid32) {
+  Uuid u = kBase;
+
+  u.uu[0] = (uint8_t)((0xFF000000 & uuid32) >> 24);
+  u.uu[1] = (uint8_t)((0x00FF0000 & uuid32) >> 16);
+  u.uu[2] = (uint8_t)((0x0000FF00 & uuid32) >> 8);
+  u.uu[3] = (uint8_t)(0x000000FF & uuid32);
+  return u;
+}
+
+Uuid Uuid::From128BitBE(const uint8_t* uuid) {
+  UUID128Bit tmp;
+  memcpy(tmp.data(), uuid, kNumBytes128);
+  return From128BitBE(tmp);
+}
+
+Uuid Uuid::From128BitLE(const UUID128Bit& uuid) {
+  Uuid u;
+  std::reverse_copy(uuid.data(), uuid.data() + kNumBytes128, u.uu.begin());
+  return u;
+}
+
+Uuid Uuid::From128BitLE(const uint8_t* uuid) {
+  UUID128Bit tmp;
+  memcpy(tmp.data(), uuid, kNumBytes128);
+  return From128BitLE(tmp);
+}
+
+const UUID128Bit Uuid::To128BitLE() const {
+  UUID128Bit le;
+  std::reverse_copy(uu.data(), uu.data() + kNumBytes128, le.begin());
+  return le;
+}
+
+const UUID128Bit& Uuid::To128BitBE() const { return uu; }
+
+Uuid Uuid::GetRandom() {
+  Uuid uuid;
+  base::RandBytes(uuid.uu.data(), uuid.uu.size());
+  return uuid;
+}
+
+bool Uuid::IsEmpty() const { return *this == kEmpty; }
+
+void Uuid::UpdateUuid(const Uuid& uuid) {
+  uu = uuid.uu;
+}
+
+bool Uuid::operator<(const Uuid& rhs) const {
+  return std::lexicographical_compare(uu.begin(), uu.end(), rhs.uu.begin(),
+                                      rhs.uu.end());
+}
+
+bool Uuid::operator==(const Uuid& rhs) const { return uu == rhs.uu; }
+
+bool Uuid::operator!=(const Uuid& rhs) const { return uu != rhs.uu; }
+
+std::string Uuid::ToString() const {
+  return base::StringPrintf(
+      "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+      uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7], uu[8], uu[9],
+      uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]);
+}
+}  // namespace bluetooth
diff --git a/le_audio/certification_tool/types/bluetooth/uuid.h b/le_audio/certification_tool/types/bluetooth/uuid.h
new file mode 100644
index 0000000..0fe5b5b
--- /dev/null
+++ b/le_audio/certification_tool/types/bluetooth/uuid.h
@@ -0,0 +1,143 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 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.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+#include <array>
+#include <string>
+
+namespace bluetooth {
+
+// This class is representing Bluetooth UUIDs across whole stack.
+// Here are some general endianness rules:
+// 1. UUID is internally kept as as Big Endian.
+// 2. Bytes representing UUID coming from upper layers, Java or Binder, are Big
+//    Endian.
+// 3. Bytes representing UUID coming from lower layer, HCI packets, are Little
+//    Endian.
+// 4. UUID in storage is always string.
+class Uuid final {
+ public:
+  static constexpr size_t kNumBytes128 = 16;
+  static constexpr size_t kNumBytes32 = 4;
+  static constexpr size_t kNumBytes16 = 2;
+
+  static constexpr size_t kString128BitLen = 36;
+
+  static const Uuid kEmpty;  // 00000000-0000-0000-0000-000000000000
+
+  using UUID128Bit = std::array<uint8_t, kNumBytes128>;
+
+  Uuid() = default;
+
+  // Creates and returns a random 128-bit UUID.
+  static Uuid GetRandom();
+
+  // Returns the shortest possible representation of this UUID in bytes. Either
+  // kNumBytes16, kNumBytes32, or kNumBytes128
+  size_t GetShortestRepresentationSize() const;
+
+  // Returns true if this UUID can be represented as 16 bit.
+  bool Is16Bit() const;
+
+  // Returns 16 bit Little Endian representation of this UUID. Use
+  // GetShortestRepresentationSize() or Is16Bit() before using this method.
+  uint16_t As16Bit() const;
+
+  // Returns 32 bit Little Endian representation of this UUID. Use
+  // GetShortestRepresentationSize() before using this method.
+  uint32_t As32Bit() const;
+
+  // Converts string representing 128, 32, or 16 bit UUID in
+  // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, xxxxxxxx, or xxxx format to UUID. If
+  // set, optional is_valid parameter will be set to true if conversion is
+  // successfull, false otherwise.
+  static Uuid FromString(const std::string& uuid, bool* is_valid = nullptr);
+
+  // Converts 16bit Little Endian representation of UUID to UUID
+  static Uuid From16Bit(uint16_t uuid16bit);
+
+  // Converts 32bit Little Endian representation of UUID to UUID
+  static Uuid From32Bit(uint32_t uuid32bit);
+
+  // Converts 128 bit Big Endian array representing UUID to UUID.
+  static constexpr Uuid From128BitBE(const UUID128Bit& uuid) {
+    Uuid u(uuid);
+    return u;
+  }
+
+  // Converts 128 bit Big Endian array representing UUID to UUID. |uuid| points
+  // to beginning of array.
+  static Uuid From128BitBE(const uint8_t* uuid);
+
+  // Converts 128 bit Little Endian array representing UUID to UUID.
+  static Uuid From128BitLE(const UUID128Bit& uuid);
+
+  // Converts 128 bit Little Endian array representing UUID to UUID. |uuid|
+  // points to beginning of array.
+  static Uuid From128BitLE(const uint8_t* uuid);
+
+  // Returns 128 bit Little Endian representation of this UUID
+  const UUID128Bit To128BitLE() const;
+
+  // Returns 128 bit Big Endian representation of this UUID
+  const UUID128Bit& To128BitBE() const;
+
+  // Returns string representing this UUID in
+  // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format, lowercase.
+  std::string ToString() const;
+
+  // Returns true if this UUID is equal to kEmpty
+  bool IsEmpty() const;
+
+  // Update UUID with new value
+  void UpdateUuid(const Uuid& uuid);
+
+  bool operator<(const Uuid& rhs) const;
+  bool operator==(const Uuid& rhs) const;
+  bool operator!=(const Uuid& rhs) const;
+
+ private:
+  constexpr Uuid(const UUID128Bit& val) : uu{val} {};
+
+  // Network-byte-ordered ID (Big Endian).
+  UUID128Bit uu;
+};
+}  // namespace bluetooth
+
+inline std::ostream& operator<<(std::ostream& os, const bluetooth::Uuid& a) {
+  os << a.ToString();
+  return os;
+}
+
+// Custom std::hash specialization so that bluetooth::UUID can be used as a key
+// in std::unordered_map.
+namespace std {
+
+template <>
+struct hash<bluetooth::Uuid> {
+  std::size_t operator()(const bluetooth::Uuid& key) const {
+    const auto& uuid_bytes = key.To128BitBE();
+    std::hash<std::string> hash_fn;
+    return hash_fn(std::string(reinterpret_cast<const char*>(uuid_bytes.data()),
+                               uuid_bytes.size()));
+  }
+};
+
+}  // namespace std
diff --git a/le_audio/certification_tool/types/class_of_device.cc b/le_audio/certification_tool/types/class_of_device.cc
new file mode 100644
index 0000000..775a412
--- /dev/null
+++ b/le_audio/certification_tool/types/class_of_device.cc
@@ -0,0 +1,78 @@
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#include "class_of_device.h"
+
+#include <base/strings/string_split.h>
+#include <base/strings/stringprintf.h>
+#include <stdint.h>
+#include <algorithm>
+#include <vector>
+
+static_assert(sizeof(ClassOfDevice) == ClassOfDevice::kLength,
+              "ClassOfDevice must be 3 bytes long!");
+
+ClassOfDevice::ClassOfDevice(const uint8_t (&class_of_device)[kLength]) {
+  std::copy(class_of_device, class_of_device + kLength, cod);
+};
+
+std::string ClassOfDevice::ToString() const {
+  return base::StringPrintf("%03x-%01x-%02x",
+                            (static_cast<uint16_t>(cod[2]) << 4) | cod[1] >> 4,
+                            cod[1] & 0x0f, cod[0]);
+}
+
+bool ClassOfDevice::FromString(const std::string& from, ClassOfDevice& to) {
+  ClassOfDevice new_cod;
+  if (from.length() != 8) return false;
+
+  std::vector<std::string> byte_tokens =
+      base::SplitString(from, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+  if (byte_tokens.size() != 3) return false;
+  if (byte_tokens[0].length() != 3) return false;
+  if (byte_tokens[1].length() != 1) return false;
+  if (byte_tokens[2].length() != 2) return false;
+
+  uint16_t values[3];
+
+  for (size_t i = 0; i < kLength; i++) {
+    const auto& token = byte_tokens[i];
+
+    char* temp = nullptr;
+    values[i] = strtol(token.c_str(), &temp, 16);
+    if (*temp != '\0') return false;
+  }
+
+  new_cod.cod[0] = values[2];
+  new_cod.cod[1] = values[1] | ((values[0] & 0xf) << 4);
+  new_cod.cod[2] = values[0] >> 4;
+
+  to = new_cod;
+  return true;
+}
+
+size_t ClassOfDevice::FromOctets(const uint8_t* from) {
+  std::copy(from, from + kLength, cod);
+  return kLength;
+};
+
+bool ClassOfDevice::IsValid(const std::string& cod) {
+  ClassOfDevice tmp;
+  return ClassOfDevice::FromString(cod, tmp);
+}
diff --git a/le_audio/certification_tool/types/class_of_device.h b/le_audio/certification_tool/types/class_of_device.h
new file mode 100644
index 0000000..b0fffc8
--- /dev/null
+++ b/le_audio/certification_tool/types/class_of_device.h
@@ -0,0 +1,63 @@
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <cstring>
+#include <string>
+
+namespace bluetooth {
+namespace types {
+
+/** Bluetooth Class of Device */
+class ClassOfDevice final {
+ public:
+  static constexpr unsigned int kLength = 3;
+
+  uint8_t cod[kLength];
+
+  ClassOfDevice() = default;
+  ClassOfDevice(const uint8_t (&class_of_device)[kLength]);
+
+  bool operator==(const ClassOfDevice& rhs) const {
+    return (std::memcmp(cod, rhs.cod, sizeof(cod)) == 0);
+  }
+
+  std::string ToString() const;
+
+  // Converts |string| to ClassOfDevice and places it in |to|. If |from| does
+  // not represent a Class of Device, |to| is not modified and this function
+  // returns false. Otherwise, it returns true.
+  static bool FromString(const std::string& from, ClassOfDevice& to);
+
+  // Copies |from| raw Class of Device octets to the local object.
+  // Returns the number of copied octets (always ClassOfDevice::kLength)
+  size_t FromOctets(const uint8_t* from);
+
+  static bool IsValid(const std::string& class_of_device);
+};
+
+inline std::ostream& operator<<(std::ostream& os, const ClassOfDevice& c) {
+  os << c.ToString();
+  return os;
+}
+
+}  // namespace types
+}  // namespace bluetooth
+
+using ::bluetooth::types::ClassOfDevice;  // TODO, remove
diff --git a/le_audio/certification_tool/types/raw_address.cc b/le_audio/certification_tool/types/raw_address.cc
new file mode 100644
index 0000000..8369f5f
--- /dev/null
+++ b/le_audio/certification_tool/types/raw_address.cc
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ *
+ *  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 "raw_address.h"
+
+#include <base/strings/string_split.h>
+#include <base/strings/stringprintf.h>
+#include <stdint.h>
+#include <algorithm>
+#include <vector>
+
+static_assert(sizeof(RawAddress) == 6, "RawAddress must be 6 bytes long!");
+
+const RawAddress RawAddress::kAny{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
+const RawAddress RawAddress::kEmpty{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+
+RawAddress::RawAddress(const uint8_t (&addr)[6]) {
+  std::copy(addr, addr + kLength, address);
+}
+
+std::string RawAddress::ToString() const {
+  return base::StringPrintf("%02x:%02x:%02x:%02x:%02x:%02x", address[0],
+                            address[1], address[2], address[3], address[4],
+                            address[5]);
+}
+
+bool RawAddress::FromString(const std::string& from, RawAddress& to) {
+  RawAddress new_addr;
+  if (from.length() != 17) return false;
+
+  std::vector<std::string> byte_tokens =
+      base::SplitString(from, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+  if (byte_tokens.size() != 6) return false;
+
+  for (int i = 0; i < 6; i++) {
+    const auto& token = byte_tokens[i];
+
+    if (token.length() != 2) return false;
+
+    char* temp = nullptr;
+    new_addr.address[i] = strtol(token.c_str(), &temp, 16);
+    if (*temp != '\0') return false;
+  }
+
+  to = new_addr;
+  return true;
+}
+
+size_t RawAddress::FromOctets(const uint8_t* from) {
+  std::copy(from, from + kLength, address);
+  return kLength;
+};
+
+bool RawAddress::IsValidAddress(const std::string& address) {
+  RawAddress tmp;
+  return RawAddress::FromString(address, tmp);
+}
diff --git a/le_audio/certification_tool/types/raw_address.h b/le_audio/certification_tool/types/raw_address.h
new file mode 100644
index 0000000..ca75018
--- /dev/null
+++ b/le_audio/certification_tool/types/raw_address.h
@@ -0,0 +1,79 @@
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <array>
+#include <cstring>
+#include <string>
+
+/** Bluetooth Address */
+class RawAddress final {
+ public:
+  static constexpr unsigned int kLength = 6;
+
+  uint8_t address[kLength];
+
+  RawAddress() = default;
+  RawAddress(const uint8_t (&addr)[kLength]);
+
+  bool operator<(const RawAddress& rhs) const {
+    return (std::memcmp(address, rhs.address, sizeof(address)) < 0);
+  }
+  bool operator==(const RawAddress& rhs) const {
+    return (std::memcmp(address, rhs.address, sizeof(address)) == 0);
+  }
+  bool operator>(const RawAddress& rhs) const { return (rhs < *this); }
+  bool operator<=(const RawAddress& rhs) const { return !(*this > rhs); }
+  bool operator>=(const RawAddress& rhs) const { return !(*this < rhs); }
+  bool operator!=(const RawAddress& rhs) const { return !(*this == rhs); }
+
+  bool IsEmpty() const { return *this == kEmpty; }
+
+  std::string ToString() const;
+
+  // Converts |string| to RawAddress and places it in |to|. If |from| does
+  // not represent a Bluetooth address, |to| is not modified and this function
+  // returns false. Otherwise, it returns true.
+  static bool FromString(const std::string& from, RawAddress& to);
+
+  // Copies |from| raw Bluetooth address octets to the local object.
+  // Returns the number of copied octets - should be always RawAddress::kLength
+  size_t FromOctets(const uint8_t* from);
+
+  static bool IsValidAddress(const std::string& address);
+
+  static const RawAddress kEmpty;  // 00:00:00:00:00:00
+  static const RawAddress kAny;    // FF:FF:FF:FF:FF:FF
+};
+
+inline std::ostream& operator<<(std::ostream& os, const RawAddress& a) {
+  os << a.ToString();
+  return os;
+}
+
+template <>
+struct std::hash<RawAddress> {
+  std::size_t operator()(const RawAddress& val) const {
+    static_assert(sizeof(uint64_t) >= RawAddress::kLength);
+    uint64_t int_addr = 0;
+    memcpy(reinterpret_cast<uint8_t*>(&int_addr), val.address,
+           RawAddress::kLength);
+    return std::hash<uint64_t>{}(int_addr);
+  }
+};
diff --git a/le_audio/frameworks/base/Android.bp b/le_audio/frameworks/base/Android.bp
new file mode 100644
index 0000000..1d9cf9f
--- /dev/null
+++ b/le_audio/frameworks/base/Android.bp
@@ -0,0 +1,22 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+filegroup {
+    name: "framework-bluetooth-adva-srcs",
+    srcs: ["core/**/*.java",],
+}
diff --git a/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastAudioScanAssistCallback.java b/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastAudioScanAssistCallback.java
new file mode 100644
index 0000000..bbb5a30
--- /dev/null
+++ b/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastAudioScanAssistCallback.java
@@ -0,0 +1,178 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package android.bluetooth;
+
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Retention;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.ScanResult;
+import android.annotation.IntDef;
+import java.util.Map;
+import java.lang.String;
+import java.lang.Integer;
+import java.util.List;
+
+
+
+/**
+ * Bluetooth LE Broadcast Scan Assistance related callbacks, used to deliver result of
+ * Broadcast Assist operations performed using {@link BleBroadcastAudioScanAssistManager}
+ *
+ * @hide
+ * @see BleBroadcastAudioScanAssistManager
+ */
+public abstract class BleBroadcastAudioScanAssistCallback {
+
+    /** @hide */
+    @IntDef(prefix = "BASS_STATUS_", value = {
+              BASS_STATUS_SUCCESS,
+              BASS_STATUS_FAILURE,
+              BASS_STATUS_FATAL,
+              BASS_STATUS_TXN_TIMEOUT,
+              BASS_STATUS_INVALID_SOURCE_ID,
+              BASS_STATUS_COLOCATED_SRC_UNAVAILABLE,
+              BASS_STATUS_INVALID_SOURCE_SELECTED,
+              BASS_STATUS_SOURCE_UNAVAILABLE,
+              BASS_STATUS_DUPLICATE_ADDITION,
+    })
+
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Bass_Status {}
+
+    public static final int BASS_STATUS_SUCCESS = 0x00;
+    public static final int BASS_STATUS_FAILURE = 0x01;
+    public static final int BASS_STATUS_FATAL = 0x02;
+    public static final int BASS_STATUS_TXN_TIMEOUT = 0x03;
+
+    public static final int BASS_STATUS_INVALID_SOURCE_ID = 0x04;
+    public static final int BASS_STATUS_COLOCATED_SRC_UNAVAILABLE = 0x05;
+    public static final int BASS_STATUS_INVALID_SOURCE_SELECTED = 0x06;
+    public static final int BASS_STATUS_SOURCE_UNAVAILABLE = 0x07;
+    public static final int BASS_STATUS_DUPLICATE_ADDITION = 0x08;
+    public static final int BASS_STATUS_NO_EMPTY_SLOT = 0x09;
+    public static final int BASS_STATUS_INVALID_GROUP_OP = 0x10;
+
+    /**
+     * Callback when BLE broadcast audio source found.
+     * result of {@link BleBroadcastAudioScanAssistManager#searchforLeAudioBroadcasters} will be
+     * delivered through this callback
+     *
+     * @param scanres {@link ScanResult} object of the scanned result
+     */
+    public void onBleBroadcastSourceFound(ScanResult scanres) {
+    };
+
+
+    /**
+     * Callback when BLE broadcast audio source found.
+     * result of {@link BleBroadcastAudioScanAssistManager#searchforLeAudioBroadcasters} will be
+     * delivered through this callback
+     *
+     * @param status Status of the Broadcast source selection.
+     * @param broadcastSourceChannels {@link BleBroadcastSourceChannel} List
+     * containing avaiable broadcast source channels that are being broadcasted from the selected
+     * broadcast source
+     *
+     */
+    public void onBleBroadcastSourceSelected(BluetoothDevice device,
+                                                         @Bass_Status int status,
+                                List<BleBroadcastSourceChannel> broadcastSourceChannels) {
+    };
+
+    /**
+     * Callback when BLE broadcast audio source is been successfully added to the remote Scan delegator.
+     * result of {@link BleBroadcastAudioScanAssistManager#addBroadcastSource} will be
+     * delivered through this callback
+     *
+     * This callback is an acknowledgement confirming the source information added
+     * to the Scan delegator. Actual updated source Information values of resulting Broadcast Source Information
+     * will be notified using {@link BleBroadcastAudioScanAssistManager#ACTION_BROADCAST_RECEIVER_STATE} intent
+     *
+     * @param device remote scan delegator for which Source is been added.
+     * @param srcId source Id of the Broadcast source information added
+     * @param status true on succesful addition of source Information, false otherwise.
+     *
+     */
+   public void onBleBroadcastAudioSourceAdded(BluetoothDevice device,
+                                             byte srcId,
+                                             @Bass_Status int status) {
+    };
+
+    /**
+     * Callback when BLE broadcast audio source Information is been updated to the remote Scan delegator.
+     * result of {@link BleBroadcastAudioScanAssistManager#updateBroadcastSource} will be
+     * delivered through this callback
+     *
+     * This callback is an acknowledgement confirming the source information update request is succesfully
+     * written on the Scan delegator. Actual updated source Information values of resulting Broadcast Source Information
+     * will be notified using {@link BleBroadcastAudioScanAssistManager#ACTION_BROADCAST_RECEIVER_STATE} intent
+     *
+     * @param device remote scan delegator for which Source is been updated.
+     * @param srcId source Id of the Broadcast source information updated.
+     * @param status true on succesful updating of source Information, false otherwise.
+     *
+     */
+    public void onBleBroadcastAudioSourceUpdated(BluetoothDevice device,
+                                             byte srcId,
+                                             @Bass_Status int status) {
+    };
+
+    /**
+     * Callback when BLE broadcast audio source Information is updated with broadcast PIN code to the remote Scan delegator.
+     * result of {@link BleBroadcastAudioScanAssistManager#setBroadcastCode} will be
+     * delivered through this callback
+     *
+     * This callback is an acknowledgement confirming the Broadcast PIN update request is succesfully
+     * written to the Scan delegator. Actual updated source Information values of resulting Broadcast Source Information
+     * will be notified using {@link BleBroadcastAudioScanAssistManager#ACTION_BROADCAST_RECEIVER_STATE} intent.
+     * Encryption status from the {@link BleBroadcastAudioScanAssistManager#ACTION_BROADCAST_RECEIVER_STATE} will
+     * confirm the succesfull Broadcast PIN code and resulting decryption of the Broadcast data at the reciver side.
+     *
+     * @param device remote scan delegator for which Source is been updated.
+     * @param srcId source Id of the Broadcast PIN updated.
+     * @param status true on succesful updating of source Information, false otherwise.
+     *
+     */
+    public void onBleBroadcastPinUpdated(BluetoothDevice rcvr,
+                                                byte srcId,
+                                                @Bass_Status int status) {
+    };
+
+    /**
+     * Callback when BLE broadcast audio source Information is removed from the remote Scan delegator.
+     * result of {@link BleBroadcastAudioScanAssistManager#removeBroadcastSource} will be
+     * delivered through this callback
+     *
+     * This callback is an acknowledgement confirming the Broadcast source infor removal request is succesfully
+     * written to the Scan delegator. Actual removal of source Information values of resulting Broadcast Source Information
+     * will be notified using {@link BleBroadcastAudioScanAssistManager#ACTION_BROADCAST_RECEIVER_STATE} intent.
+     * Deletion of source Information will result is setting all the source information attributes to ZERO other than
+     * source Id
+     *
+     * @param device remote scan delegator for which Source is removed.
+     * @param srcId source Id of the Broadcast source information removed.
+     * @param status true on succesful updating of source Information, false otherwise.
+     *
+     */
+    public void onBleBroadcastAudioSourceRemoved(BluetoothDevice rcvr,
+                                             byte srcId,
+                                             @Bass_Status int status) {
+    };
+}
diff --git a/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastAudioScanAssistManager.java b/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastAudioScanAssistManager.java
new file mode 100644
index 0000000..c16bcb6
--- /dev/null
+++ b/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastAudioScanAssistManager.java
@@ -0,0 +1,635 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package android.bluetooth;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.IntDef;
+import android.annotation.SdkConstant.SdkConstantType;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Retention;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothAdapter.LeScanCallback;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import java.io.InvalidClassException;
+import android.os.DeadObjectException;
+import android.util.Log;
+import android.content.Context;
+import java.util.UUID;
+import android.os.ParcelUuid;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Objects;
+
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+
+import android.os.SystemProperties;
+
+/**
+ * This class provides methods to perform Broadcast Assistance related
+ * operations.
+ * <p>
+ * Use {@link BleBroadcastAudioScanAssistManager()} to get an
+ * instance of {@link BleBroadcastAudioScanAssistManager}.
+ * <p>
+ * <b>Note:</b> Most of the methods here require
+ * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * @hide
+ */
+public final class BleBroadcastAudioScanAssistManager {
+
+    private static final String TAG = "BleBroadcastAudioScanAssistManager";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = true;
+
+    /** @hide */
+    @IntDef(prefix = "SYNC_", value = {
+           SYNC_METADATA,
+           SYNC_AUDIO,
+           SYNC_METADATA_AUDIO
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BroadcastAssistSyncState {}
+    /**
+     * Input to {@link BleBroadcastAudioScanAssistManager#addBroadcastSource} method
+     * where Application wants to synchronize only to Metadata (i.e. Only Periodic advs) and not to
+     *  Broadcsat audio stream (i.e. BIS )from broadcast source
+     */
+    public static final int SYNC_METADATA = 0;
+    /**
+     * Input to {@link BleBroadcastAudioScanAssistManager#addBroadcastSource} method
+     * where Application wants to synchronize only to Broadcast Audio stream (i.e. BIS) and not to
+       Metadata (i.e. Periodic advs )from broadcast source
+     */
+    public static final int SYNC_AUDIO = 1;
+    /**
+     * Input to {@link BleBroadcastAudioScanAssistManager#addBroadcastSource} method
+     * where Application wants to synchronize to both  Broadcast Audio stream (i.e. BIS) and also to
+     * Metadata (i.e. Periodic advs )from broadcast source
+     */
+    public static final int SYNC_METADATA_AUDIO = 2;
+
+    private BluetoothAdapter mBluetoothAdapter;
+    BleBroadcastAudioScanAssistCallback mAppCallback;
+    BluetoothDevice mBluetoothDevice;
+    int mSyncState = SYNC_METADATA;
+    BluetoothSyncHelper mBluetoothSyncHelper = null;
+    BleBroadcastSourceInfo mBroadcastAudioSourceInfo = null;
+    private byte INVALID_SOURCE_ID = -1;
+
+    /**
+     * Intent used to broadcast the "Broadcast receiver State" information of a Scan delegator device.
+     * Whenever there is a change in Broadcast source Information stored at Scan delegator device
+     * this Itent will be delivered to Application layer
+     *
+     * {@link #BluetoothSyncHelper} profile need to be connected to the Scan delegator device
+     * to get these notifications
+     *
+     * <p>This intent will have two extra:
+     * <ul>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device for which broadcast reciver
+     * state information is broadcasted. It can
+     * be null if no device is active. </li>
+     * </ul>
+     * <ul>
+     * <li> {@link BleBroadcastSourceInfo#EXTRA_SOURCE_INFO} - The BleBroadcastSourceInfo Object
+     * having information Broadcast receiver state </li>
+     * </ul>
+     * <ul>
+     * <li> {@link BleBroadcastSourceInfo#EXTRA_SOURCE_INFO_INDEX} - Index of the BleBroadcastSourceInfo
+     * object broadcasted </li>
+     * </ul>
+     * <ul>
+     * <li> {@link BleBroadcastSourceInfo#EXTRA_MAX_NUM_SOURCE_INFOS} - Maximum number of source Informations
+     * that this Broadcast receiver can hold </li>
+     * </ul>
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_BROADCAST_SOURCE_INFO =
+            "android.bluetooth.BroadcastAudioSAManager.action.BROADCAST_SOURCE_INFO";
+
+
+    // These callbacks run on the main thread.
+    private final class BassclientServiceListener
+            implements BluetoothProfile.ServiceListener {
+
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            log(TAG, "BassService connected");
+            onBluetoothSyncHelperStateChanged(true, proxy);
+
+        }
+
+        public void onServiceDisconnected(int profile) {
+            log(TAG, "BassService disconnected");
+            onBluetoothSyncHelperStateChanged(false, null);
+        }
+    }
+
+    private void onBluetoothSyncHelperStateChanged(boolean on, BluetoothProfile proxy) {
+        if (on) {
+            mBluetoothSyncHelper = (BluetoothSyncHelper) proxy;
+            mBluetoothSyncHelper.registerAppCallback(mBluetoothDevice, mAppCallback);
+            this.notifyAll();
+        } else {
+            mBluetoothSyncHelper = null;
+        }
+    }
+
+    /*package*/BleBroadcastAudioScanAssistManager(BluetoothSyncHelper scanOffloader, BluetoothDevice device,
+                                                  BleBroadcastAudioScanAssistCallback callback
+                                                 ) {
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        mAppCallback = callback;
+        mBluetoothDevice = device;
+        mBluetoothSyncHelper = scanOffloader;
+    }
+
+
+    /*finalize method to cleanup*/
+    protected void finalize() {
+        log(TAG, "finalize()");
+        if (mBluetoothSyncHelper != null) {
+            mBluetoothSyncHelper.unregisterAppCallback(mBluetoothDevice, mAppCallback);
+        }
+    }
+
+    /**
+     * Search for Le Audio Broadcasters on behalf of the Scan delegator with which this
+     * {@ BleBroadcastAudioScanAssistManager} is instantiated
+     *
+     *  search results will be delivered to application using
+     * {@link BleBroadcastAudioScanAssistCallback#onBleBroadcastSourceFound}
+     *
+     * @return returns true if It is successfully initiated the Search for Audio broadcasters,
+     *         false otherwise
+     * @hide
+     */
+    public boolean searchforLeAudioBroadcasters () {
+        log(TAG, "searchforLeAudioBroadcasters: ");
+        if (mBluetoothSyncHelper != null) {
+            return mBluetoothSyncHelper.searchforLeAudioBroadcasters(mBluetoothDevice);
+        } else {
+            Log.e(TAG, "searchforLeAudioBroadcasters: mBluetoothSyncHelper is null");
+        }
+        return false;
+      }
+    /**
+     * Stops an ongoing Bluetooth LE Search for Audio Broadcasters.
+     *
+     * @return returns true if It is successfully initiated the Stopped the Search for Audio broadcasters
+     *         false otherwise
+     *
+     *@hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public boolean stopSearchforLeAudioBroadcasters() {
+        log(TAG, "stopSearchforLeAudioBroadcasters()");
+        if (mBluetoothSyncHelper != null) {
+            return mBluetoothSyncHelper.stopSearchforLeAudioBroadcasters(mBluetoothDevice);
+        } else {
+            Log.e(TAG, "stopSearchforLeAudioBroadcasters: mBluetoothSyncHelper is null");
+        }
+        return false;
+
+    }
+
+    /* Internal helper function to convert user input sync state to required internal
+     * format
+     */
+    private int convertMetadataSyncState(int syncState) {
+        if (syncState == SYNC_METADATA_AUDIO || syncState == SYNC_METADATA) {
+            return BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC;
+        }
+        return BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IDLE;
+    }
+
+    /* Internal helper function to convert user input sync state to required internal
+     * format
+     */
+    private int convertAudioDataSyncState(int syncState) {
+        if (syncState == SYNC_METADATA_AUDIO || syncState == SYNC_AUDIO) {
+            return BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED;
+        } else {
+            Log.e(TAG, "searchforLeAudioBroadcasters: mBluetoothSyncHelper is null");
+        }
+        return BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED;
+    }
+
+    /**
+     * Selects broadcast source for the Scan delegator. This internally performs Periodic
+     * synchronization to the given Broadcast source device, upon acquision of Synchronization information,
+     * It will be notified with avaiable Broadcast source channels that can be synchronized in the remote
+     * device.
+     * Application should select set of Broadcast channels that need to be synchronized and follow up
+     * with a call to {@link #addBroadcastSource} operation
+     *
+     * Result of selction of Broadcast source  will be delivered through
+     * {@link BleBroadcastAudioScanAssistCallback#OnBroadcastAudioSourceSelected}
+     *
+     * If this operation need to be performed over all the members of coordinated set members, isGroupOp
+     * will be set to true. Select broadcast source operation will be performed on behalf of
+     * all the Coordinated set devices
+     *
+     *
+     *  @param ScanResult {@link #ScanResult} of the Broadcasting source,
+     *  this is the result obtained from the {@link BleBroadcastAudioScanAssistCallback#onBleBroadcastSourceFound}
+     *  @param isGroupOp set to true If Application wants to perform this operation for the whole
+     *  coordinated set members
+     *
+     * @return returns true if It is successfully initiated select Broadcast source operation
+     *         false otherwise
+     * @hide
+     */
+    public boolean selectBroadcastSource(ScanResult scanRes, boolean isGroupOp) {
+        if (scanRes == null) {
+            Log.e(TAG, "selectBroadcastSource: Invalid scan res");
+            return false;
+        }
+        log(TAG, "selectBroadcastSource: " + scanRes);
+        if (mBluetoothSyncHelper != null) {
+            return mBluetoothSyncHelper.selectBroadcastSource(mBluetoothDevice, scanRes, isGroupOp);
+        } else {
+            Log.e(TAG, "selectBroadcastSource: mBluetoothSyncHelper is null");
+        }
+        return false;
+    }
+
+
+    private boolean isValidBroadcastSourceInfo(BleBroadcastSourceInfo srcInfo) {
+        boolean ret = true;
+        List<BleBroadcastSourceInfo> currentSourceInfos =
+            mBluetoothSyncHelper.getAllBroadcastSourceInformation(mBluetoothDevice);
+        if (currentSourceInfos == null) {
+            Log.e(TAG, "no source info details for remote");
+            ret = false;
+        } else {
+            for (int i=0; i<currentSourceInfos.size(); i++) {
+                if (srcInfo.matches(currentSourceInfos.get(i))) {
+                    ret = false;
+                    break;
+                }
+            }
+        }
+
+        log(TAG, "isValidBroadcastSourceInfo returns: " + ret);
+        return ret;
+    }
+
+    private boolean isValidSourceId (byte sourceId) {
+        boolean retVal = false;
+        List<BleBroadcastSourceInfo> currentSourceInfos =
+        mBluetoothSyncHelper.getAllBroadcastSourceInformation(mBluetoothDevice);
+        if (currentSourceInfos == null) {
+            retVal = false;
+        } else {
+            for (int i=0; i<currentSourceInfos.size(); i++) {
+                if (currentSourceInfos.get(i).getSourceId() == sourceId) {
+                    retVal = true;
+                    break;
+                }
+            }
+        }
+        log(TAG, "isValidSourceId returns: " + retVal);
+        return retVal;
+    }
+
+    private void printSelectedIndicies(List<BleBroadcastSourceChannel> selectedBISIndicies) {
+        if (selectedBISIndicies == null) {
+            log(TAG, "printSelectedIndicies : no selected indicies");
+            return;
+        }
+        for (int i=0; i<selectedBISIndicies.size(); i++) {
+            log(TAG, selectedBISIndicies.get(i).getDescription() + ": " + selectedBISIndicies.get(i).getStatus());
+        }
+    }
+    /**
+     * Adds a broadcast source information to the Scan delegator. This internally performs Periodic
+     * synchronization to the given Broadcast source device, upon acquision of Synchronization information,
+     * It will be written on to the "Scan delegators" Characteristics
+     *
+     * Result of addition of Broadcast source to the scan delegator will be delivered through
+     * {@link BleBroadcastAudioScanAssistCallback#onBleBroadcastAudioSourceAdded}
+     *
+     * Successful addition Broadcast source will be indicated through Broadcast reciver state information
+     * update intent through {@link #ACTION_BROADCAST_RECEIVER_STATE} intent
+     *
+     *
+     * If this operation need to be performed over all the members of coordinated set members, isGroupOp
+     * will be set to true. add broadcast source operation will be performed on behalf of
+     * all the Coordinated set devices
+     *
+     * Same Broadcast source Information will be written on to all the members of Coordinated set and
+     * PAST will be performed based on the request.
+     *
+     * In case of Group Operation, If there is any matching entry already present in any of coordinated set members,
+     * Add Broadcast source opeation will be failed and result will notified through
+     * {@link BleBroadcastAudioScanAssistCallback#onBleBroadcastAudioSourceAdded}
+     *
+     *  @param audioSource {@link #BluetoothDevice} object selected as Source which need to be synchronized with
+     *  @param ScanResult {@link #ScanResult} result obtained from the {@link BleBroadcastAudioScanAssistCallback#onBleBroadcastSourceFound}
+     *  @param syncState  can be one of {@link #SYNC_METADATA},
+     *  {@link #SYNC_METADATA_AUDIO}
+     *  @param selectedBroadcastChannels is a List of Broadcast channels that need to be synchronized with the given broadcast audio source
+     *  from Avaialble Broadcast indicies.
+     *  Avaiable broadcast indicies are notified application using {@link BleBroadcastAudioScanAssistCallback#onBleBroadcastSourceSelected}
+     *  BroadcastSourceChannel.mStatus set to be TRUE or FALSE based on the need of synchronization.
+     *
+     *
+     *  null value of selectedBroadcastChannels resulting in syncing to all avaialble Broadcast channels.
+     *  check {@link BleBroadcastSourceChannel} for more information
+     *  @param isGroupOp set to true If Application wants to perform this operation for the whole
+     *  coordinated set members, False otherwise
+     *
+     * @return returns true if It is successfully initiated add Broadcast source operation
+     *         false otherwise
+     * @hide
+     */
+     public boolean addBroadcastSource (BluetoothDevice audioSource,
+                        @BroadcastAssistSyncState int syncState,
+                        List<BleBroadcastSourceChannel> selectedBroadcastChannels,
+                        boolean isGroupOp) {
+        if (mBluetoothSyncHelper == null) {
+            log(TAG, "addBroadcastSource: no BluetoothSyncHelper handle");
+            return false;
+        }
+
+         if (syncState != SYNC_METADATA &&
+              syncState != SYNC_METADATA_AUDIO) {
+              log(TAG, "addBroadcastSource: Invalid syncState" + syncState);
+             return false;
+         }
+         printSelectedIndicies(selectedBroadcastChannels);
+         int metadataSyncState = -1;
+         int audioSyncState = -1;
+         mSyncState = syncState;
+         metadataSyncState = convertMetadataSyncState (mSyncState);
+         audioSyncState = convertAudioDataSyncState(mSyncState);
+         if (mBroadcastAudioSourceInfo ==  null) {
+             //all of these will be overriden at service layer later
+            mBroadcastAudioSourceInfo = new BleBroadcastSourceInfo(
+                                                            audioSource,
+                                                            (byte)0xBB, /*advSid*/
+                                                            BleBroadcastSourceInfo.BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC,
+                                                            metadataSyncState,
+                                                            audioSyncState,
+                                                            selectedBroadcastChannels);
+             if (mBroadcastAudioSourceInfo == null) {
+                 Log.e(TAG, "addBroadcastSource: mBroadcastAudioSourceInfo instantiated failure");
+                 return false;
+             }
+         }
+         if(isValidBroadcastSourceInfo(mBroadcastAudioSourceInfo)) {
+             mBluetoothSyncHelper.addBroadcastSource(mBluetoothDevice,
+                                        mBroadcastAudioSourceInfo,
+                                        isGroupOp
+                                        );
+         } else {
+             log(TAG, "Similar source information already exists");
+             return false;
+         }
+         return true;
+    }
+    /**
+     * Updates a broadcast source information in the Scan delegator.
+     * It will be written on to the Scan delegator's Characteristics
+     *
+     * Result of updating of Broadcast source to the scan delegator will be delivered through
+     * {@link BleBroadcastAudioScanAssistCallback#onBleBroadcastAudioSourceUpdated}
+     *
+     *  However Successful updating of Broadcast source information will be indicated through Broadcast reciver state information
+     *  update intent through {@link #ACTION_BROADCAST_RECEIVER_STATE} intent
+     *
+     * If this operation need to be performed over all the members of coordinated set members, isGroupOp
+     * will be set to true. Update broadcast source operation will be performed on behalf of
+     * all the Coordinated set devices
+     *
+     * Same Broadcast source Information change will be written on to all the members of Coordinated set and
+     * PAST will be performed based on the request from remote.
+     *
+     * In case of Group Operation, If there are no matching source Information present in any of coordinated set members,
+     * Update Broadcast source opeation will be failed and result will notified through
+     * {@link BleBroadcastAudioScanAssistCallback#onBleBroadcastAudioSourceUpdated}
+     *
+     *  @param sourceId sourceId of the Broadcast Source information which need to be updated
+     *  @param syncState  can be one of {@link #SYNC_METADATA},
+     *  {@link #SYNC_AUDIO}, {@link #SYNC_METADATA_AUDIO}
+     *
+     *  @param selectedBroadcastChannels is a List of Broadcast channels that need to be synchronized with the given broadcast audio source
+     *  from Avaialble Broadcast indicies.
+     *  Avaiable broadcast indicies are notified application using {@link BleBroadcastAudioScanAssistCallback#onBleBroadcastSourceSelected}
+     *  BroadcastSourceChannel.mStatus set to be TRUE or FALSE based on the need of synchronization.
+     *
+     *  null value of selectedBroadcastChannels resulting in syncing to all avaialble Broadcast channels.
+     *  check {@link BleurceChannel} for more information
+     *  @param isGroupOp set to true If Application wants to perform this operation for the whole
+     *  coordinated set members, False otherwise
+     *
+     * @return returns true if It is successfully initiated update Broadcast source information
+     *  operation
+     *         false otherwise
+     * @hide
+     */
+     public boolean updateBroadcastSource (byte sourceId, int syncState,
+                                            List<BleBroadcastSourceChannel> selectedBroadcastChannels,
+                                            boolean isGroupOp) {
+           if (mBluetoothSyncHelper == null) {
+               log(TAG, "updateBroadcastSource: no BluetoothSyncHelper handle");
+               return false;
+           }
+           if (isValidSourceId(sourceId) == false) {
+              log(TAG, "updateBroadcastSource: Invalid source Id");
+              return false;
+           }
+           int audioSyncState = -1;
+           int metadataSyncState = -1;
+           log(TAG, "updateBroadcastSource: sourceId" + sourceId + ", syncState:" + syncState);
+
+           mSyncState = syncState;
+           metadataSyncState = convertMetadataSyncState (mSyncState);
+           audioSyncState = convertAudioDataSyncState(mSyncState);
+
+           printSelectedIndicies(selectedBroadcastChannels);
+
+           log(TAG, "updateBroadcastSource: audioSyncState:" + audioSyncState);
+           log(TAG, "updateBroadcastSource: metadataSyncState:" + metadataSyncState);
+
+           BleBroadcastSourceInfo sourceInfo = new BleBroadcastSourceInfo(sourceId);
+           if (sourceInfo != null) {
+              sourceInfo.setMetadataSyncState(metadataSyncState);
+              sourceInfo.setAudioSyncState(audioSyncState);
+              sourceInfo.setSourceId(sourceId);
+              sourceInfo.setBroadcastChannelsSyncStatus(selectedBroadcastChannels);
+           } else {
+              Log.e(TAG, "updateBroadcastSource: sourceInfo not created");
+              return false;
+           }
+           return mBluetoothSyncHelper.updateBroadcastSource(mBluetoothDevice,
+                                                  sourceInfo,
+                                                  isGroupOp);
+    }
+    /**
+      * Sets the Broadcast pin code to the Scan delegator so that It can decrypt
+      * the synchronized audio at the reciver side
+      *
+      * It will be written on to the Scan delegator's Characteristics.
+      * Result of Setting  of Broadcast PIN code to the scan delegator will be delivered through
+      * {@link BleBroadcastAudioScanAssistCallback#onBroadcastPinUpdated}
+      *
+      * If this operation need to be performed over all the members of coordinated set members, isGroupOp
+      * will be set to true. set Broadcast PIN operation will be performed on all the Coordinated set devices
+      *
+      * Same Broadcast PIN code will be written on to all the members of Coordinated set and
+      * on the request from remote.
+      *
+      * In case of Group Operation, If there are no matching source Information(BD address, adv instance)
+      * present in any of coordinated set members,
+      * Set Broadcast PIN opeation will be failed and result will notified through
+      * {@link BleBroadcastAudioScanAssistCallback#onBroadcastPinUpdated}
+      *
+      *
+      * However, Successful updating of Broadcast PIN code will be indicated through Broadcast reciver state information
+      * update intent through {@link #ACTION_BROADCAST_RECEIVER_STATE} intent.
+      *
+      *  @param sourceId sourceId of the Broadcast Source information which need to be updated
+      *  @param broadcastCode is the String of maximum 16 characters in length
+      *  @param isGroupOp set to true If Application wants to perform this operation for the whole
+      *  coordinated set members, False otherwise
+      *
+      * @return returns true if It is successfully initiated set Broadcast code operation
+      *         false otherwise
+      * @hide
+      */
+    public boolean setBroadcastCode (byte sourceId, String broadcastCode, boolean isGroupOp) {
+           if (mBluetoothSyncHelper == null) {
+               log(TAG, "setBroadcastCode: no BluetoothSyncHelper handle");
+               return false;
+           }
+           if (isValidSourceId(sourceId) == false) {
+              log(TAG, "setBroadcastCode: Invalid source Id");
+              return false;
+           }
+
+           log(TAG, "setBroadcastCode: " + "sourceId:"
+                            + sourceId + "BroadcastCode:" + broadcastCode);
+           BleBroadcastSourceInfo sourceInfo = new BleBroadcastSourceInfo(sourceId);
+           if (sourceInfo != null) {
+                sourceInfo.setSourceId(sourceId);
+                sourceInfo.setBroadcastCode(broadcastCode);
+           } else {
+               Log.e(TAG, "setBroadcastCode: sourceInfo not created");
+               return false;
+           }
+           return mBluetoothSyncHelper.setBroadcastCode(mBluetoothDevice,
+                                                   sourceInfo,
+                                                   isGroupOp);
+    }
+     /**
+     * Removes the Broadcast Source Information from the Scan delegator
+     * It will be written on to the "Scan delegators" Characteristics
+     *
+     * Result of removal of Broadcast source to the scan delegator will be delivered through
+     * {@link BleBroadcastAudioScanAssistCallback#OnBroadcastAudioSourRemoved}
+     *
+     * If this operation need to be performed over all the members of coordinated set members, isGroupOp
+     * will be set to true. remove broadcast operation will be performed on all the Coordinated set devices
+     *
+     * Remove Broadcast will be performed on to all the members of Coordinated set
+     *
+     * In case of Group Operation, If there are no matching source Information(BD address, adv instance)
+     * present in any of coordinated set members.
+     *
+     * Set Broadcast PIN opeation will be failed and result will notified through
+     * {@link BleBroadcastAudioScanAssistCallback#onBroadcastPinUpdated}
+     * Successful removal of Brocast source information will be indicated through
+     * Broadcast receiver state Information through
+     * {@link #ACTION_BROADCAST_RECEIVER_STATE} intent
+     *
+     * @param sourceId sourceId of the Broadcast Source information which need to be updated
+     *  @param isGroupOp set to true If Application wants to perform this operation for the whole
+     *  coordinated set members, False otherwise
+     *
+     * @return returns true if It is successfully initiated remove broadcast source operation
+     *         false otherwise
+      * @hide
+     */
+    public boolean removeBroadcastSource (byte sourceId, boolean isGroupOp) {
+           if (mBluetoothSyncHelper == null) {
+               log(TAG, "removeBroadcastSource: no BluetoothSyncHelper handle");
+               return false;
+           }
+           if (isValidSourceId(sourceId) == false) {
+              log(TAG, "removeBroadcastSource: Invalid source Id");
+              return false;
+           }
+           log(TAG, "removeBroadcastSource: sourceId" + sourceId);
+
+           return mBluetoothSyncHelper.removeBroadcastSource(mBluetoothDevice,
+                                                        sourceId,
+                                                        isGroupOp);
+    }
+     /**
+     * Get all the Broadcast Source Information stored in remote Scan delegators
+     *
+     * @return returns the List of Broadcast Source Information {@link #BleBroadcastSourceInfo} stored in
+     * remote and its corresponding state or null in case if there are nothing
+     *
+     * @hide
+     */
+    public List<BleBroadcastSourceInfo> getAllBroadcastSourceInformation () {
+            if (mBluetoothSyncHelper == null) {
+                log(TAG, "GetNumberOfAcceptableBroadcastSources: no BluetoothSyncHelper handle");
+                return null;
+            }
+            return mBluetoothSyncHelper.getAllBroadcastSourceInformation(mBluetoothDevice);
+    }
+
+    private static void log(String TAG, String msg) {
+        BleBroadcastSourceInfo.BASS_Debug(TAG, msg);
+    }
+}
diff --git a/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastSourceChannel.java b/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastSourceChannel.java
new file mode 100644
index 0000000..d8f3d61
--- /dev/null
+++ b/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastSourceChannel.java
@@ -0,0 +1,231 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothManager;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Retention;
+import android.annotation.IntDef;
+import android.compat.annotation.UnsupportedAppUsage;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import java.util.Objects;
+import android.util.Log;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * This class provides Interface to select the Broadcast source channels
+ * to be synchronized from the Broadcast source. these Broadcast source channels
+ * are mapped to the BIS indicies that given Broadcast source is broadcasting with.
+ *
+ * <p>This also acts as general data structure for updating the Broadcast
+ * source channel information
+ *
+ * This class is used to input the User provided data for below operations
+ * {@link BleBroadcastAudioScanAssistManager#addBroadcastSource},
+ * {@link BleBroadcastAudioScanAssistManager#updateBroadcastSource} and
+ *
+ * mIndex : index is the Identifier for Broadcast channel
+ * mDescription: Description describing the type of Broadcast data being broadcasted
+ * mStatus: TRUE means broadcast source channel need to be synchronized
+ *            FALSE means broadcast source channel need NOT be synchronized
+ *
+ * @hide
+ */
+public final class BleBroadcastSourceChannel implements Parcelable {
+
+    private static final String TAG = "BleBroadcastSourceChannel";
+    private int mIndex;
+    private String mDescription;
+    private boolean mStatus;
+    private int mSubGroupId;
+    private byte[] mMetadata;
+
+    public BleBroadcastSourceChannel (int index,
+                            String description,
+                            boolean st,
+                            int aSubGroupId,
+                            byte[] aMetadata) {
+            mIndex = index;
+            mDescription = description;
+            mStatus = st;
+            mSubGroupId = aSubGroupId;
+            if (aMetadata != null && aMetadata.length != 0) {
+                mMetadata = new byte[aMetadata.length];
+                System.arraycopy(aMetadata, 0, mMetadata, 0, aMetadata.length);
+            }
+    }
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BleBroadcastSourceChannel) {
+            BleBroadcastSourceChannel other = (BleBroadcastSourceChannel) o;
+            return (other.mIndex == mIndex
+                    && other.mDescription == mDescription
+                    && other.mStatus == mStatus
+                    );
+        }
+        return false;
+    }
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIndex, mDescription, mStatus);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return mDescription;
+    }
+
+    /**
+     * Gets the Source Id of the BleBroadcastSourceChannel Object
+     *
+     * @return byte representing the Source Id of the Broadcast Source Info Object
+     *          {@link #BROADCAST_ASSIST_INVALID_SOURCE_ID} in case if this field is not valid
+     * @hide
+     */
+    public int getIndex () {
+        return mIndex;
+    }
+
+    /**
+     * Gets the Broadcast source Device object from the BleBroadcastSourceChannel Object
+     *
+     * @return BluetoothDevice object for Broadcast source device
+     * @hide
+     */
+    public String getDescription () {
+        return mDescription;
+    }
+
+     /**
+     * Gets the status of given BleBroadcastSourceChannel
+     *
+     * @return true if selected, false otherwise
+     * @hide
+     *
+     * @deprecated
+     */
+
+    public boolean getStatus () {
+        return mStatus;
+    }
+
+     /**
+     * Gets the address type of the Broadcast source advertisement for the BleBroadcastSourceChannel Object
+     *
+     * @return byte addressType, this can be one of {@link #BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC} OR {@link #BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC}
+     * @hide
+     *
+     * @deprecated
+     */
+
+    public byte[] getMetadata () {
+        return mMetadata;
+    }
+
+     /**
+     * Gets the subgroup Id that broadcast Channel belongs
+     * Internal helper function
+     *
+     * @hide
+     * @deprecated
+     */
+    public int getSubGroupId () {
+         return mSubGroupId;
+    }
+
+     /**
+     * Sets the status of given BleBroadcastSourceChannel
+     *
+     * @return true if selected, false otherwise
+     * @hide
+     *
+     * @deprecated
+     */
+    public void setStatus (boolean status) {
+        mStatus = status;
+    }
+
+
+    public static final @android.annotation.NonNull Parcelable.Creator<BleBroadcastSourceChannel> CREATOR =
+            new Parcelable.Creator<BleBroadcastSourceChannel>() {
+                public BleBroadcastSourceChannel createFromParcel(Parcel in) {
+
+                    log(TAG, "createFromParcel>");
+                    final int index = in.readInt();
+                    final String desc = in.readString();
+                    final boolean status = in.readBoolean();
+                    final int subGroupId = in.readInt();
+
+                    final int metadataLength = in.readInt();
+                    byte[] metadata = null;
+                    if (metadataLength > 0) {
+                        metadata = new byte[metadataLength];
+                        in.readByteArray(metadata);
+                    }
+
+                    BleBroadcastSourceChannel srcChannel =
+                        new BleBroadcastSourceChannel(index, desc, status, subGroupId, metadata);
+                    log(TAG, "createFromParcel:" + srcChannel);
+                    return srcChannel;
+                }
+
+                public BleBroadcastSourceChannel[] newArray(int size) {
+                    return new BleBroadcastSourceChannel[size];
+                }
+            };
+
+
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        log(TAG, "writeToParcel>");
+        out.writeInt(mIndex);
+        out.writeString(mDescription);
+        out.writeBoolean(mStatus);
+        out.writeInt(mSubGroupId);
+        if (mMetadata != null) {
+            out.writeInt(mMetadata.length);
+            out.writeByteArray(mMetadata);
+        } else {
+            out.writeInt(0);
+        }
+        log(TAG, "writeToParcel:" + toString());
+    }
+    private static void log(String TAG, String msg) {
+        BleBroadcastSourceInfo.BASS_Debug(TAG, msg);
+    }
+};
+
diff --git a/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastSourceInfo.java b/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastSourceInfo.java
new file mode 100644
index 0000000..1e15901
--- /dev/null
+++ b/le_audio/frameworks/base/core/java/android/bluetooth/BleBroadcastSourceInfo.java
@@ -0,0 +1,1052 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothManager;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Retention;
+import android.annotation.IntDef;
+import android.compat.annotation.UnsupportedAppUsage;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import java.util.Objects;
+import android.util.Log;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * This class provides methods to get various information of Broadcast
+ * source information stored in remote. Users can call get/set methods
+ * enquire the required information
+ *
+ * <p>This also acts as general data structure for updating the Broadcast
+ * source information
+ * This class is used to input the User provided data for below operations
+ * {@link BleBroadcastAudioScanAssistManager#addBroadcastSource},
+ * {@link BleBroadcastAudioScanAssistManager#updateBroadcastSource} and
+ * {@link BleBroadcastAudioScanAssistManager#setBroadcastCode}
+ *
+ * <p>This is also used to pack all Broadcast source information as part of {@link #ACTION_BROADCAST_RECEIVER_STATE}
+ * Intent. User can retrive the {@link BleBroadcastSourceInfo} using {@link BleBroadcastSourceInfo#EXTRA_RECEIVER_STATE}
+ * extra field
+ * @hide
+ */
+public final class BleBroadcastSourceInfo implements Parcelable {
+
+    private static final String TAG = "BleBroadcastSourceInfo";
+    private static final boolean BASS_DBG = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /** @hide
+     * @deprecated
+     */
+    @Deprecated
+    @IntDef(prefix = "BROADCAST_ASSIST_ADDRESS_TYPE_", value = {
+           BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC,
+           BROADCAST_ASSIST_ADDRESS_TYPE_RANDOM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BroadcastAssistAddressType {}
+
+    /**
+     * Address Type of the LE Broadcast Audio Source Device
+     * Specifies whether LE Broadcast Audio Source device using public OR
+     * random address for the LE Audio broadcasts
+     *
+     * @deprecated
+     */
+    @Deprecated
+    public static final int BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC = 0;
+    /**
+     * Address Type of the LE Broadcast Audio Source Device
+     * Specifies whether LE Broadcast Audio Source device using public OR
+     * random address for the LE Audio broadcasts
+     *
+     * @deprecated
+     */
+    @Deprecated
+    public static final int BROADCAST_ASSIST_ADDRESS_TYPE_RANDOM = 1;
+     /**
+     * Address Type of the LE Broadcast Audio Source Device
+     * Specifies whether LE Broadcast Audio Source device using public PR
+     * random address for the LE Audio broadcasts
+     *
+     * @deprecated
+     */
+    @Deprecated
+    public static final int BROADCAST_ASSIST_ADDRESS_TYPE_INVALID = 0xFFFF;
+
+    /** @hide */
+    @IntDef(prefix = "BROADCAST_ASSIST_PA_SYNC_STATE_", value = {
+           BROADCAST_ASSIST_PA_SYNC_STATE_IDLE,
+           BROADCAST_ASSIST_PA_SYNC_STATE_SYNCINFO_REQ,
+           BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC,
+           BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_FAIL,
+           BROADCAST_ASSIST_PA_SYNC_STATE_NO_PAST
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BroadcastAssistMetadataSyncState {}
+
+    /**
+     * Meta data Sync State
+     * Broadcast receiver sync state w.r.t PA. State IDLE specifies that broadcast
+     * receiver is not able to sync the Metada/PA
+     */
+    public static final int BROADCAST_ASSIST_PA_SYNC_STATE_IDLE = 0;
+    /**
+     * Meta data Sync State
+     * Broadcast receiver sync state w.r.t PA. State SYNCINFO REQ specifies that broadcast
+     * receiver requesting for SYNCINFO from the Scan Offloader to synchronie
+     * to Metadata/PA
+     */
+    public static final int BROADCAST_ASSIST_PA_SYNC_STATE_SYNCINFO_REQ = 1;
+    /**
+     * Meta data Sync State
+     * Broadcast receiver sync state w.r.t PA. State INSYNC specifies that broadcast
+     * receiver in sync with to Metadata/PA.
+     */
+    public static final int BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC = 2;
+    /**
+     * Meta data Sync State
+     * Broadcast receiver sync state w.r.t PA. State INSYNC specifies that broadcast
+     * receiver is failed to sync with Metadata/PA.
+     */
+    public static final int BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_FAIL = 3;
+    /**
+     * Meta data Sync State
+     * Broadcast receiver sync state w.r.t PA. State SYNC NOPAST denotes that broadcast
+     * receiver needs PAST procedure to sync with Metadata.
+     */
+    public static final int BROADCAST_ASSIST_PA_SYNC_STATE_NO_PAST = 4;
+    /**
+     * Meta data Sync State
+     * Broadcast receiver sync state w.r.t PA. State SYNC NOPAST denotes that broadcast
+     * receiver needs PAST procedure to sync with Metadata.
+     */
+    public static final int BROADCAST_ASSIST_PA_SYNC_STATE_INVALID = 0xFFFF;
+
+    /** @hide */
+    @IntDef(prefix = "BROADCAST_ASSIST_AUDIO_SYNC_STATE_", value = {
+           BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED,
+           BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BroadcastAssistAudioSyncState {}
+
+    /**
+     * Broadcast Audio stream Sync State
+     * Broadcast receiver sync state w.r.t Broadcast Audio stream BIS. denotes
+     * receiver is not synchronized to LE Audio BIS
+     */
+    public static final int BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED = 0;
+    /**
+     * Broadcast Audio stream Sync State
+     * Broadcast receiver sync state w.r.t Broadcast Audio stream BIS. denotes
+     * receiver is not synchronized to LE Audio BIS
+     */
+    public static final int BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED = 1;
+    /**
+     * Broadcast Audio stream Sync State
+     * Broadcast receiver sync state w.r.t Broadcast Audio stream BIS. denotes
+     * receiver is not synchronized to LE Audio BIS
+     */
+    public static final int BROADCAST_ASSIST_AUDIO_SYNC_STATE_INVALID = 0xFFFF;
+
+
+    /** @hide */
+    @IntDef(prefix = "BROADCAST_ASSIST_ENC_STATE_", value = {
+           BROADCAST_ASSIST_ENC_STATE_UNENCRYPTED,
+           BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED,
+           BROADCAST_ASSIST_ENC_STATE_DECRYPTING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BroadcastAssistEncryptionState {}
+   /**
+    * Encryption Status at the LE Audio broadcast receiver side
+    * UNENCRYPTED denoted that broadcast receiver is in sync with an uncrypted
+    * broadcasted audio
+    */
+    public static final int BROADCAST_ASSIST_ENC_STATE_UNENCRYPTED = 0;
+   /**
+    * Encryption Status at the LE Audio broadcast receiver side
+    * PIN_NEEDED denote that the Broadcast receiver needs broadcast PIN
+    * to sync and listen to Broadcasted Audio
+    */
+    public static final int BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED = 1;
+   /**
+     * Encryption Status at the LE Audio broadcast receiver side
+     * state DECRYPTING denote that the Broadcast receiver is able to decrypt
+     * and listen to the Broadcasted Audio
+     */
+    public static final int BROADCAST_ASSIST_ENC_STATE_DECRYPTING = 2;
+   /**
+     * Encryption Status at the LE Audio broadcast receiver side
+     * state BADCODE denote that the Broadcast receiver has got bad code
+     * and not able decrypt
+     * Incorrect code that Scan delegator tried to decrypt can be retrieved from
+     *
+     */
+    public static final int BROADCAST_ASSIST_ENC_STATE_BADCODE = 3;
+   /**
+     * Encryption Status at the LE Audio broadcast receiver side
+     * state DECRYPTING denote that the Broadcast receiver is able to decrypt
+     * and listen to the Broadcasted Audio
+     */
+    public static final int BROADCAST_ASSIST_ENC_STATE_INVALID = 0xFFFF;
+
+    /*
+     * Invalid Broadcast source Information Id
+     */
+    public static final byte BROADCAST_ASSIST_INVALID_SOURCE_ID = (byte)0x00;
+    /*
+     * Invalid Broadcaster Identifier of the given Broadcast Source
+     */
+    public static final int BROADCASTER_ID_INVALID = 0xFFFF;
+    /**
+     * Used as an int extra field in {@link BleBroadcastAudioScanAssistManager#ACTION_BROADCAST_RECEIVER_STATE}
+     * intent notifys the Broadcast Source Information to Application layer
+     *
+     * <p> Source Info object can be extracted using this extra field at Application layer
+     *
+     * This is used to read the {@link BleBroadcastSourceInfo } parcelable object
+     * @hide
+     */
+    public static final String EXTRA_SOURCE_INFO = "android.bluetooth.device.extra.SOURCE_INFO";
+    /**
+     * Used as an int extra field in {@link BleBroadcastAudioScanAssistManager#ACTION_BROADCAST_RECEIVER_STATE}
+     * intent Broadcast Source Information to Application layer
+     *
+     * <p> Index of the Source Info object can be extracted using this extra field at Application layer
+     *
+     * This is used to read the {@link BleBroadcastSourceInfo } parcelable object
+     * @hide
+     */
+    public static final String EXTRA_SOURCE_INFO_INDEX = "android.bluetooth.device.extra.SOURCE_INFO_INDEX";
+    /**
+     * Used as an int extra field in {@link BleBroadcastAudioScanAssistManager#ACTION_BROADCAST_RECEIVER_STATE}
+     * intent notifys the Broadcast Source Information to Application layer
+     *
+     * <p> Maximm number of the Broadcast Source Information that given broadcast receiver can hold, can be extracted using
+     * this extra field at Application layer
+     *
+     * @hide
+     */
+    public static final String EXTRA_MAX_NUM_SOURCE_INFOS = "android.bluetooth.device.extra.MAX_NUM_SOURCE_INFOS";
+    private byte mSourceId;
+    private @BroadcastAssistAddressType int mSourceAddressType;
+    private BluetoothDevice mSourceDevice;
+    private byte mSourceAdvSid;
+    private int mBroadcasterId;
+    private @BroadcastAssistMetadataSyncState int mMetaDataSyncState;
+    private @BroadcastAssistAudioSyncState int mAudioSyncState;
+    private Map<Integer, Integer> mAudioBisIndexList = new HashMap <Integer, Integer>();
+    private @BroadcastAssistEncryptionState int mEncyptionStatus;
+    private Map<Integer, byte[]> mMetadataList = new HashMap<Integer, byte[]>();
+    private String mBroadcastCode;
+    private byte[] mBadBroadcastCode;
+    private byte mNumSubGroups;
+    private static final int BIS_NO_PREF = 0xFFFFFFFF;
+    private static final int BROADCAST_CODE_SIZE = 16;
+
+    /**
+     * Constructor to create an Empty object of {@link BleBroadcastSourceInfo } with given source Id,
+     * which contains, Broadcast reciever state information for Broadcast Assistant Usecases.
+     *
+     * This is mainly used to represent the Empty Broadcast source entries
+     *
+     *  @param sourceId Source Id for this broadcast source info object
+     *
+     *  @deprecated
+     *  @hide
+     */
+    @Deprecated
+    public BleBroadcastSourceInfo (byte sourceId) {
+       mSourceId = sourceId;
+       mMetaDataSyncState = BROADCAST_ASSIST_PA_SYNC_STATE_INVALID;
+       mAudioSyncState = BROADCAST_ASSIST_AUDIO_SYNC_STATE_INVALID;
+       mSourceAddressType = BROADCAST_ASSIST_ADDRESS_TYPE_INVALID;
+       mSourceDevice = null;
+       mSourceAdvSid = (byte)0x00;
+       mEncyptionStatus = BROADCAST_ASSIST_ENC_STATE_INVALID;
+       mBroadcastCode = null;
+       mBadBroadcastCode = null;
+       mNumSubGroups = 0;
+       mBroadcasterId = BROADCASTER_ID_INVALID;
+    }
+    /**
+     * Constructor to create an object of {@link BleBroadcastSourceInfo } which contains
+     * Broadcast reciever state information for Broadcast Assistant Usecases.
+     * This is  mainly used for input purpose of {@link BleBroadcastAudioScanAssistManager#addBroadcastSource}
+     * operation
+     *
+     *  @param audioSource BluetoothDevice object whcih is selected as Broadcast source
+     *  @param advSid advertising Sid of the Broadcast source device for which reciever synchronized with.
+     *  @param addressType type of address. This can be be one of {@link #BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC} or
+     *                                              {@link #BROADCAST_ASSIST_ADDRESS_TYPE_RANDOM}
+     *  @param metadataSyncState sync status of metadata at the receiver side from this Broadcast source. This can
+     *                           be one of {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IDLE}, {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNCINFO_REQ},
+     *                           {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC},
+     *                           {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_FAIL} OR  {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_NO_PAST}
+     *  @param audioSyncState Audio sync status of metadata at the receiver side from this broadcast source. This can be
+     *                        one of {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED} OR
+     *                        {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED}
+     *  @param audioBisIndex Audio BIS index for what Broadcast reciever synchronized with
+     *  @param metadataLength Length of the  metadata field
+     *  @prama metadata metadata information about the type Broadcast information being synchronized at receiver side
+     *
+     *
+     *  @hide
+     */
+    /*package*/ BleBroadcastSourceInfo (BluetoothDevice audioSource,
+                            byte advSid,
+                            @BroadcastAssistAddressType int addressType,
+                            @BroadcastAssistMetadataSyncState int metadataSyncstate,
+                            @BroadcastAssistAudioSyncState int audioSyncstate,
+                            List<BleBroadcastSourceChannel> selectedBISIndicies
+                            ) {
+       mMetaDataSyncState =  metadataSyncstate;
+       mAudioSyncState = audioSyncstate;
+       mSourceAddressType = addressType;
+       mSourceDevice = audioSource;
+       mSourceAdvSid = advSid;
+       mBroadcasterId = BROADCASTER_ID_INVALID;
+       if (selectedBISIndicies == null) {
+           BASS_Debug(TAG, "selectedBISIndiciesList is null");
+       } else {
+           for (int i=0; i<selectedBISIndicies.size(); i++) {
+             if (selectedBISIndicies.get(i).getStatus() == true) {
+                  Integer audioBisIndex = 0;
+                 int subGroupId = selectedBISIndicies.get(i).getSubGroupId();
+                 if (mAudioBisIndexList.containsKey(subGroupId)) {
+                     audioBisIndex = mAudioBisIndexList.get(subGroupId);
+                 }
+                 audioBisIndex = audioBisIndex | (1<<selectedBISIndicies.get(i).getIndex());
+                 BASS_Debug(TAG, "index" + selectedBISIndicies.get(i).getIndex() + "is set");
+                 mAudioBisIndexList.put(subGroupId, audioBisIndex);
+             }
+           }
+       }
+
+       //not valid info
+       mSourceId = BROADCAST_ASSIST_INVALID_SOURCE_ID;
+       mEncyptionStatus = BROADCAST_ASSIST_ENC_STATE_INVALID;
+       mBroadcastCode = null;
+       mBadBroadcastCode = null;
+       mNumSubGroups = 0;
+    }
+
+    /**
+     * Constructor override  to create an object of {@link BleBroadcastSourceInfo } which contains
+     * Broadcast reciever state information for Broadcast Assistant Usecases.
+     *
+     * This is mainly used for output purpose to create an object from the receiver state information
+     * read from the remote BASS server. This will be packed and broadcasted as an Intent using
+     * {@link #ACTION_BROADCAST_RECEIVER_STATE}
+     *
+     *  @param audioSource BluetoothDevice object whcih is selected as Broadcast source
+     *  @param sourceId Source Id for this broadcast source info object
+     *  @param advSid advertising Sid of the Broadcast source device for which reciever synchronized with
+     *  @param addressType type of address. This can be be one of {@link #BLE_ASSIST_ADDRESS_TYPE_PUBLIC} or
+     *                                              {@link #BLE_ASSIST_ADDRESS_TYPE_RANDOM}
+     *  @param metadataSyncState sync status of metadata at the receiver side from this Broadcast source. This can
+     *                           be one of {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IDLE}, {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNCINFO_REQ},
+     *                           {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC},
+     *                           {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_FAIL} OR  {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_NO_PAST}
+     *  @param audioSyncState Audio sync status of metadata at the receiver side from this broadcast source. This can be
+     *                        one of {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED} OR
+     *                        {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED}
+     *  @param encryptionStatus Encryotion state at Broadcast receiver. This can be one of {@link #BROADCAST_ASSIST_ENC_STATE_UNENCRYPTED},
+                                {@link #BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED} OR {@link #BROADCAST_ASSIST_ENC_STATE_DECRYPTING}
+     *  @param audioBisIndex Audio BIS index for what Broadcast reciever synchronized with
+     *  @param metadataLength Length of the  metadata field
+     *  @prama metadata metadata information about the type Broadcast information being synchronized at receiver side
+     *
+     *
+     *  @deprecated
+     *  @hide
+     */
+    @Deprecated
+    public BleBroadcastSourceInfo (BluetoothDevice audioSource,
+                            byte sourceId,
+                            byte advSid,
+                            int broadcasterId,
+                            @BroadcastAssistAddressType int addressType,
+                            @BroadcastAssistMetadataSyncState int metadataSyncstate,
+                            @BroadcastAssistEncryptionState int encryptionStatus,
+                            byte[] badCode,
+                            byte numSubGroups,
+                            @BroadcastAssistAudioSyncState int audioSyncstate,
+                            Map<Integer, List<BleBroadcastSourceChannel>> selectedBISIndiciesList,
+                            Map<Integer, byte[]> metadataList
+                            ) {
+       mSourceId = sourceId;
+       mSourceAddressType = addressType;
+       mSourceDevice = audioSource;
+       mSourceAdvSid = advSid;
+       mBroadcasterId = broadcasterId;
+       mMetaDataSyncState =  metadataSyncstate;
+       mAudioSyncState = audioSyncstate;
+       mEncyptionStatus = encryptionStatus;
+       if (badCode != null) {
+           mBadBroadcastCode = new byte[BROADCAST_CODE_SIZE];
+           System.arraycopy(badCode, 0, mBadBroadcastCode, 0, mBadBroadcastCode.length);
+       }
+       mNumSubGroups = numSubGroups;
+       int audioBisIndex = 0;
+       if (selectedBISIndiciesList != null) {
+           for (Map.Entry<Integer, List<BleBroadcastSourceChannel>> entry : selectedBISIndiciesList.entrySet()) {
+               List<BleBroadcastSourceChannel> selectedBISIndicies = entry.getValue();
+                  if (selectedBISIndicies == null) {
+                   //do nothing
+                   BASS_Debug(TAG, "selectedBISIndiciesList is null");
+                  } else {
+                      for (int i=0; i<selectedBISIndicies.size(); i++) {
+                          if (selectedBISIndicies.get(i).getStatus() == true) {
+                           audioBisIndex = audioBisIndex | (1<<selectedBISIndicies.get(i).getIndex());
+                              BASS_Debug(TAG, "index" + selectedBISIndicies.get(i).getIndex() + "is set");
+                          }
+                      }
+                  }
+               BASS_Debug(TAG, "subGroupId:" + entry.getKey() + "audioBisIndex" + audioBisIndex);
+               mAudioBisIndexList.put(entry.getKey(), audioBisIndex);
+           }
+       }
+       if (metadataList != null) {
+           for (Map.Entry<Integer, byte[]> entry : metadataList.entrySet()) {
+               byte[] metadata = entry.getValue();
+               if (metadata != null && metadata.length != 0) {
+                   byte[] mD = new byte[metadata.length];
+                   System.arraycopy(metadata, 0, mD, 0, metadata.length);
+               }
+               mMetadataList.put(entry.getKey(), metadata);
+               }
+           }
+    }
+
+    /**
+     * Constructor override  to create an object of {@link BleBroadcastSourceInfo } which contains
+     * Broadcast reciever state information for Broadcast Assistant Usecases.
+     *
+     * This is mainly used for output purpose to create an object from the receiver state information
+     * read from the remote BASS server. This will be packed and broadcasted as an Intent using
+     * {@link #ACTION_BROADCAST_RECEIVER_STATE}
+     *
+     *  @param audioSource BluetoothDevice object whcih is selected as Broadcast source
+     *  @param advSid advertising Sid of the Broadcast source device for which reciever synchronized with
+     *  @param addressType type of address. This can be be one of {@link #BLE_ASSIST_ADDRESS_TYPE_PUBLIC} or
+     *                                              {@link #BLE_ASSIST_ADDRESS_TYPE_RANDOM}
+     *  @param metadataSyncState sync status of metadata at the receiver side from this Broadcast source. This can
+     *                           be one of {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IDLE}, {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNCINFO_REQ},
+     *                           {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC},
+     *                           {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_FAIL} OR  {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_NO_PAST}
+     *  @param audioSyncState Audio sync status of metadata at the receiver side from this broadcast source. This can be
+     *                        one of {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED} OR
+     *                        {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED}
+     *  @param encryptionStatus Encryotion state at Broadcast receiver. This can be one of {@link #BROADCAST_ASSIST_ENC_STATE_UNENCRYPTED},
+     *                          {@link #BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED} OR {@link #BROADCAST_ASSIST_ENC_STATE_DECRYPTING}
+     *  @param audioBisIndex Audio BIS index for what Broadcast reciever synchronized with
+     *  @param metadataLength Length of the  metadata field
+     *  @prama metadata metadata information about the type Broadcast information being synchronized at receiver side
+     *  @param broadcastCode Numeric Character String maximum of 16 characters in length, which serves as broadcast PIN code
+     *
+     *  @hide
+     */
+    /*package*/ BleBroadcastSourceInfo (BluetoothDevice device,
+                            byte sourceId,
+                            byte advSid,
+                            @BroadcastAssistAddressType int addressType,
+                            @BroadcastAssistMetadataSyncState int metadataSyncstate,
+                            @BroadcastAssistAudioSyncState int audioSyncstate,
+                            List<BleBroadcastSourceChannel> selectedBISIndicies,
+                            @BroadcastAssistEncryptionState int encryptionStatus,
+                            String broadcastCode) {
+       mSourceId = sourceId;
+       mMetaDataSyncState =  metadataSyncstate;
+       mAudioSyncState = audioSyncstate;
+       mEncyptionStatus = encryptionStatus;
+       mSourceAddressType = addressType;
+       mSourceDevice = device;
+       mSourceAdvSid = advSid;
+       mBroadcasterId = BROADCASTER_ID_INVALID;
+       if (selectedBISIndicies == null) {
+           BASS_Debug(TAG, "selectedBISIndiciesList is null");
+       } else {
+           for (int i=0; i<selectedBISIndicies.size(); i++) {
+             if (selectedBISIndicies.get(i).getStatus() == true) {
+                  Integer audioBisIndex = 0;
+                 int subGroupId = selectedBISIndicies.get(i).getSubGroupId();
+                 if (mAudioBisIndexList.containsKey(subGroupId)) {
+                     audioBisIndex = mAudioBisIndexList.get(subGroupId);
+                 }
+                 audioBisIndex = audioBisIndex | (1<<selectedBISIndicies.get(i).getIndex());
+                 BASS_Debug(TAG, "index" + selectedBISIndicies.get(i).getIndex() + "is set");
+                 BASS_Debug(TAG, "audioBisIndex" + audioBisIndex);
+                 mAudioBisIndexList.put(subGroupId, audioBisIndex);
+             }
+           }
+
+       }
+       /*if (metadata != null && metadata.length != 0) {
+           mMetadata = new byte[metadata.length];
+           System.arraycopy(metadata, 0, mMetadata, 0, metadata.length);
+       }*/
+       mBroadcastCode = broadcastCode;
+       mBadBroadcastCode = null;
+       mNumSubGroups = 0;
+    }
+
+    /*package*/ BleBroadcastSourceInfo (BluetoothDevice device,
+                                byte sourceId,
+                                byte advSid,
+                                int broadcasterId,
+                                @BroadcastAssistAddressType int addressType,
+                                @BroadcastAssistMetadataSyncState int metadataSyncstate,
+                                @BroadcastAssistAudioSyncState int audioSyncstate,
+                                @BroadcastAssistEncryptionState int encryptionStatus,
+                                String broadcastCode,
+                                byte[] badCode,
+                                byte numSubGroups,
+                                Map<Integer, Integer> bisIndiciesList,
+                                Map<Integer, byte[]> metadataList
+                                ) {
+           mSourceId = sourceId;
+           mMetaDataSyncState =  metadataSyncstate;
+           mAudioSyncState = audioSyncstate;
+           mEncyptionStatus = encryptionStatus;
+           mSourceAddressType = addressType;
+           mSourceDevice = device;
+           mSourceAdvSid = advSid;
+           mBroadcasterId = broadcasterId;
+           mBroadcastCode = broadcastCode;
+           if (badCode != null && badCode.length != 0) {
+               mBadBroadcastCode= new byte[badCode.length];
+               System.arraycopy(badCode, 0, mBadBroadcastCode, 0, badCode.length);
+           }
+           mNumSubGroups = numSubGroups;
+           mAudioBisIndexList = new HashMap<Integer, Integer> (bisIndiciesList);
+           mMetadataList = new HashMap<Integer, byte[]> (metadataList);
+        }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BleBroadcastSourceInfo) {
+            BleBroadcastSourceInfo other = (BleBroadcastSourceInfo) o;
+            BASS_Debug(TAG, "other>>  " + o.toString());
+            BASS_Debug(TAG, "local>>  " + toString());
+            return (other.mSourceId == mSourceId
+                    && other.mMetaDataSyncState == mMetaDataSyncState
+                    && other.mAudioSyncState == mAudioSyncState
+                    && other.mSourceAddressType == mSourceAddressType
+                    && other.mSourceDevice == mSourceDevice
+                    && other.mSourceAdvSid == mSourceAdvSid
+                    && other.mEncyptionStatus == mEncyptionStatus
+                    && other.mBroadcastCode == mBroadcastCode
+                    && other.mBroadcasterId == mBroadcasterId
+                    );
+        }
+        return false;
+    }
+
+    public boolean isEmptyEntry()  {
+        boolean ret = false;
+        if (mMetaDataSyncState == (int)BROADCAST_ASSIST_PA_SYNC_STATE_INVALID &&
+            mAudioSyncState == (int)BROADCAST_ASSIST_AUDIO_SYNC_STATE_INVALID &&
+            mSourceAddressType == (int)BROADCAST_ASSIST_ADDRESS_TYPE_INVALID &&
+            mSourceDevice == null &&
+            mSourceAdvSid == (byte)0 &&
+            mEncyptionStatus == (int)BROADCAST_ASSIST_ENC_STATE_INVALID
+            ) {
+                ret = true;
+            }
+        BASS_Debug(TAG, "isEmptyEntry returns: " + ret);
+        return ret;
+    }
+
+    public boolean matches(BleBroadcastSourceInfo srcInfo) {
+        boolean ret = false;
+        if (srcInfo == null) {
+            ret = false;
+        } else {
+            if (mSourceDevice == null) {
+                if (mSourceAdvSid == srcInfo.getAdvertisingSid() &&
+                   mSourceAddressType == srcInfo.getAdvAddressType()) {
+                      ret = true;
+                }
+            } else {
+                if (mSourceDevice.equals(srcInfo.getSourceDevice()) &&
+                   mSourceAdvSid == srcInfo.getAdvertisingSid() &&
+                   mSourceAddressType == srcInfo.getAdvAddressType() &&
+                   mBroadcasterId == srcInfo.getBroadcasterId()) {
+                      ret = true;
+                }
+            }
+        }
+        BASS_Debug(TAG, "matches returns: " + ret);
+        return ret;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSourceId, mMetaDataSyncState, mAudioSyncState,
+                mSourceAddressType, mSourceDevice, mSourceAdvSid,
+                mEncyptionStatus, mBroadcastCode);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+    @Override
+    public String toString() {
+        return "{BleBroadcastSourceInfo : mSourceId" + mSourceId
+               + " sourceDevice: " + mSourceDevice
+               + " addressType: " + mSourceAddressType
+               + " mSourceAdvSid:" + mSourceAdvSid
+               + " mMetaDataSyncState:" + mMetaDataSyncState
+               + " mAudioSyncState" + mAudioSyncState
+               + " mEncyptionStatus" + mEncyptionStatus
+               + " mBadBroadcastCode" + mBadBroadcastCode
+               + " mNumSubGroups" + mNumSubGroups
+               + " mBroadcastCode" + mBroadcastCode
+               + " mAudioBisIndexList" + mAudioBisIndexList
+               + " mMetadataList" + mMetadataList
+               + " mBroadcasterId" + mBroadcasterId
+               + "}";
+    }
+
+    /**
+     * Gets the Source Id of the BleBroadcastSourceInfo Object
+     *
+     * @return byte representing the Source Id of the Broadcast Source Info Object
+     *          {@link #BROADCAST_ASSIST_INVALID_SOURCE_ID} in case if this field is not valid
+     * @hide
+     */
+    public byte getSourceId () {
+        return mSourceId;
+    }
+
+    /**
+     * Sets the Source Id of the BleBroadcastSourceInfo Object
+     *
+     * @param byte source Id for the BleBroadcastSourceInfo Object
+     *
+     * @hide
+     */
+    public void setSourceId (byte sourceId) {
+        mSourceId = sourceId;
+    }
+
+    /**
+     * Sets the Broadcast source device for the BleBroadcastSourceInfo Object
+     *
+     * @param BluetoothDevice which need to be set as Broadcast source device
+     * @hide
+     */
+    public void setSourceDevice(BluetoothDevice sourceDevice) {
+        mSourceDevice = sourceDevice;
+    }
+
+    /**
+     * Gets the Broadcast source Device object from the BleBroadcastSourceInfo Object
+     *
+     * @return BluetoothDevice object for Broadcast source device
+     * @hide
+     */
+    public BluetoothDevice getSourceDevice () {
+        return mSourceDevice;
+    }
+
+    /**
+     * Sets the address type of the Broadcast source advertisement for the BleBroadcastSourceInfo Object
+     *
+     * @param byte addressType, this can be one of {@link #BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC} OR {@link #BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC}
+     * @hide
+     */
+     public void setAdvAddressType(int addressType) {
+        mSourceAddressType = addressType;
+    }
+
+    /**
+     * Gets the address type of the Broadcast source advertisement for the BleBroadcastSourceInfo Object
+     *
+     * @return byte addressType, this can be one of {@link #BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC} OR {@link #BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC}
+     * @hide
+     *
+     * @deprecated
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public int getAdvAddressType () {
+        return mSourceAddressType;
+    }
+
+    /**
+     * Sets the advertising Sid of the Broadcast source advertisement for the BleBroadcastSourceInfo Object
+     *
+     * @param byte advertising Sid value
+     * @hide
+     */
+    public void setAdvertisingSid(byte advSid) {
+        mSourceAdvSid = advSid;
+    }
+
+    /**
+     * Gets the advertising Sid of the Broadcast source advertisement for the BleBroadcastSourceInfo Object
+     *
+     * @return byte advertising Sid value
+     * @hide
+     */
+    public byte getAdvertisingSid () {
+        return mSourceAdvSid;
+    }
+
+    /**
+     * Gets the Broadcast Id of the Broadcast source of the BleBroadcastSourceInfo Object
+     *
+     * @return int broadcast source Identifier
+     * @hide
+     */
+    public int getBroadcasterId () {
+        return mBroadcasterId;
+    }
+
+    /**
+     * Sets the Metadata sync status at the Broadcast receiver side for the BleBroadcastSourceInfo Object
+     *
+     * @param BroadcastAssistMetadataSyncState representing the state of Meta data sync status. this can be one of
+     *                       {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IDLE}, {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNCINFO_REQ},
+     *                       {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC},
+     *                       {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_FAIL} OR  {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_NO_PAST}
+     *
+     * @hide
+     */
+    /*package*/ void setMetadataSyncState(@BroadcastAssistMetadataSyncState int metadataSyncState) {
+        mMetaDataSyncState = metadataSyncState;
+    }
+
+    /**
+     * Gets the Metadata sync status at the Broadcast receiver side from the BleBroadcastSourceInfo Object
+     *
+     * @return BroadcastAssistMetadataSyncState representing the state of Meta data sync status. this can be one of
+     *                       {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IDLE}, {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNCINFO_REQ},
+     *                       {@link #BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC},
+     *                       {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_FAIL} OR  {@link #BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_NO_PAST}
+     *
+     * @hide
+     */
+    public  @BroadcastAssistMetadataSyncState int getMetadataSyncState () {
+        return mMetaDataSyncState;
+    }
+
+    /**
+     * Sets the Audio sync status at the Broadcast receiver side for the BleBroadcastSourceInfo Object
+     *
+     * @param BroadcastAssistAudioSyncState representing the state of Meta data sync status. this can be one of
+     *                         {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED} OR
+     *                        {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED}
+     *
+     * @hide
+     */
+    /*package*/ void setAudioSyncState(@BroadcastAssistAudioSyncState int audioSyncState) {
+        mAudioSyncState = audioSyncState;
+    }
+
+    /**
+     * Gets the Audio sync status at the Broadcast receiver side from the BleBroadcastSourceInfo Object
+     *
+     * @return BroadcastAssistAudioSyncState representing the state of Meta data sync status. this can be one of
+     *                           {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED} OR
+     *                           {@link #BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED}   *
+     * @hide
+     */
+    public @BroadcastAssistAudioSyncState int getAudioSyncState () {
+        return mAudioSyncState;
+    }
+
+    /**
+     * Sets the Encryption status at the Broadcast receiver side for the BleBroadcastSourceInfo Object
+     *
+     * @param BroadcastAssistEncryptionState  representing the state of Meta data sync status. This can be one of
+     *                           {@link #BROADCAST_ASSIST_ENC_STATE_UNENCRYPTED},
+     *                           {@link #BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED}, {@link #BROADCAST_ASSIST_ENC_STATE_DECRYPTING}
+     *                           Or {@link #BROADCAST_ASSIST_ENC_STATE_BADCODE}
+     * @hide
+     */
+    /*package*/ void setEncryptionStatus(@BroadcastAssistEncryptionState int encryptionStatus) {
+        mEncyptionStatus = encryptionStatus;
+    }
+
+    /**
+     * Gets the Audio sync status at the Broadcast receiver side from the BleBroadcastSourceInfo Object
+     *
+     * @return BroadcastAssistEncryptionState  representing the state of Meta data sync status. This can be one of
+     *                           {@link #BROADCAST_ASSIST_ENC_STATE_UNENCRYPTED},
+     *                           {@link #BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED} ,{@link #BROADCAST_ASSIST_ENC_STATE_DECRYPTING}
+     *                           Or {@link #BROADCAST_ASSIST_ENC_STATE_BADCODE}
+     * @hide
+     */
+    public @BroadcastAssistEncryptionState int getEncryptionStatus () {
+        return mEncyptionStatus;
+    }
+
+    /**
+     * Gets the Incorrect Broadcast code with which Scan delegator try
+     * decrypt the Broadcast audio and failed
+     *
+     * This code is valid only if {@link #getEncryptionStatus} returns
+     * {@link #BROADCAST_ASSIST_ENC_STATE_BADCODE}
+     *
+     * @param byte[] byte array containing bad broadcast value
+     *               null if the current Encryptetion status is
+     *                     not {@link #BROADCAST_ASSIST_ENC_STATE_BADCODE}
+     *
+     * @hide
+     */
+    public byte[] getBadBroadcastCode () {
+        return mBadBroadcastCode;
+    }
+
+    /**
+     * Gets the number of subgroups of the BleBroadcastSourceInfo Object
+     *
+     * @return byte number of subgroups
+     * @hide
+     *
+     * @deprecated
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public byte getNumberOfSubGroups () {
+        return mNumSubGroups;
+    }
+
+    /**
+     * Sets the Audio Broadcast channels to which receiver need to be synchronized with,
+     * for BleBroadcastSourceInfo Object
+     *
+     *
+     * @param int audioBis Index to which reciever need to be synchronized with
+     * @hide
+     */
+    /*package*/ void setBroadcastChannelsSyncStatus(List<BleBroadcastSourceChannel> selectedBISIndicies) {
+        if (selectedBISIndicies == null) {
+            //set No preference
+            BASS_Debug(TAG, "selectedBISIndiciesList is null");
+            return;
+        }
+        for (int i=0; i<selectedBISIndicies.size(); i++) {
+          if (selectedBISIndicies.get(i).getStatus() == true) {
+              Integer audioBisIndex = 0;
+              int subGroupId = selectedBISIndicies.get(i).getSubGroupId();
+              if (mAudioBisIndexList.containsKey(subGroupId)) {
+                 audioBisIndex = mAudioBisIndexList.get(subGroupId);
+              }
+              audioBisIndex = audioBisIndex | (1<<selectedBISIndicies.get(i).getIndex());
+              BASS_Debug(TAG, "index" + selectedBISIndicies.get(i).getIndex() + "is set");
+              mAudioBisIndexList.put(subGroupId, audioBisIndex);
+          }
+        }
+
+    }
+    /**
+     * Gets the Broadcast channels index and the sync status from BleBroadcastSourceInfo Object
+     * This maps the various broadcast source indicies and sync status of them
+     *
+     * @param int audio BIS index from the BleBroadcastSourceInfo object
+     * @hide
+     */
+    public List<BleBroadcastSourceChannel> getBroadcastChannelsSyncStatus () {
+        List<BleBroadcastSourceChannel> bcastIndicies = new ArrayList<BleBroadcastSourceChannel>();
+        for (int i=0; i<mNumSubGroups; i++) {
+            int bisIndexValue = mAudioBisIndexList.get(i);
+            int index =0;
+            while (bisIndexValue != 0) {
+                if ((bisIndexValue&0x01) == 0x01) {
+                    BleBroadcastSourceChannel bI =
+                        new BleBroadcastSourceChannel(index, String.valueOf(index), true, i, mMetadataList.get(i));
+                    bcastIndicies.add(bI);
+                }
+                bisIndexValue = bisIndexValue>>1;
+                index++;
+            }
+        }
+
+        BASS_Debug(TAG, "returning Bisindicies:" + bcastIndicies);
+        return bcastIndicies;
+    }
+
+    @UnsupportedAppUsage
+    @Deprecated
+    public Map<Integer, Integer> getBisIndexList() {
+        return mAudioBisIndexList;
+    }
+
+    /*package*/ void setBroadcastCode(String broadcastCode) {
+        mBroadcastCode = broadcastCode;
+    }
+
+    @UnsupportedAppUsage
+    @Deprecated
+    public void setBroadcasterId(int broadcasterId) {
+        mBroadcasterId = broadcasterId;
+    }
+
+    /**
+     * Gets the broadcastCode value from BleBroadcastSourceInfo Object
+     *
+     * @param String broadcast code from the BleBroadcastSourceInfo object
+     * @hide
+     *
+     * @deprecated
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public String getBroadcastCode () {
+        return mBroadcastCode;
+    }
+
+    private void writeMapToParcel(Parcel dest, Map<Integer, Integer> bisIndexList) {
+        dest.writeInt(bisIndexList.size());
+        for (Map.Entry<Integer, Integer> entry : bisIndexList.entrySet()) {
+            dest.writeInt(entry.getKey());
+            dest.writeInt(entry.getValue());
+        }
+    }
+
+    private static void readMapFromParcel(Parcel in, Map<Integer, Integer> bisIndexList) {
+        int size = in.readInt();
+
+        for (int i = 0; i < size; i++) {
+            Integer key = in.readInt();
+            Integer value = in.readInt();
+            bisIndexList.put(key, value);
+        }
+    }
+
+    private void writeMetadataListToParcel(Parcel dest, Map<Integer, byte[]> metadataList) {
+        dest.writeInt(metadataList.size());
+        for (Map.Entry<Integer, byte[]> entry : metadataList.entrySet()) {
+            dest.writeInt(entry.getKey());
+            byte[] metadata = entry.getValue();
+            if (metadata != null) {
+                dest.writeInt(metadata.length);
+                dest.writeByteArray(metadata);
+            }
+        }
+    }
+
+    private static void readMetadataListFromParcel(Parcel in, Map<Integer, byte[]> metadataList) {
+        int size = in.readInt();
+
+        for (int i = 0; i < size; i++) {
+            Integer key = in.readInt();
+            Integer metaDataLen = in.readInt();
+            byte[] metadata = null;
+            if (metaDataLen != 0) {
+                metadata = new byte[metaDataLen];
+                in.readByteArray(metadata);
+            }
+            metadataList.put(key, metadata);
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<BleBroadcastSourceInfo> CREATOR =
+            new Parcelable.Creator<BleBroadcastSourceInfo>() {
+                public BleBroadcastSourceInfo createFromParcel(Parcel in) {
+
+                    BASS_Debug(TAG, "createFromParcel>");
+                    final byte sourceId = in.readByte();
+                    final int sourceAddressType = in.readInt();
+                    final BluetoothDevice sourceDevice = in.readTypedObject(
+                             BluetoothDevice.CREATOR);
+                    final byte sourceAdvSid = in.readByte();
+                    final int broadcastId = in.readInt();
+                    BASS_Debug(TAG, "broadcastId" + broadcastId);
+                    final int metaDataSyncState = in.readInt();
+                    final int audioSyncState = in.readInt();
+                    BASS_Debug(TAG, "audioSyncState" + audioSyncState);
+                    final int encyptionStatus = in.readInt();
+                    final int badBroadcastLen = in.readInt();
+                    byte[] badBroadcastCode = null;
+                    if (badBroadcastLen > 0) {
+                        badBroadcastCode = new byte[badBroadcastLen];
+                        in.readByteArray(badBroadcastCode);
+                    }
+                    final byte numSubGroups = in.readByte();
+                    final String broadcastCode = in.readString();
+                    Map<Integer,Integer> bisIndexList = new HashMap <Integer, Integer>();
+                    readMapFromParcel(in, bisIndexList);
+                    Map<Integer,byte[]> metadataList = new HashMap <Integer, byte[]>();
+                    readMetadataListFromParcel(in, metadataList);
+
+                    BleBroadcastSourceInfo srcInfo = new BleBroadcastSourceInfo(sourceDevice, sourceId, sourceAdvSid, broadcastId,
+                            sourceAddressType, metaDataSyncState,audioSyncState,
+                            encyptionStatus, broadcastCode, badBroadcastCode, numSubGroups, bisIndexList, metadataList);
+                    BASS_Debug(TAG, "createFromParcel:" + srcInfo);
+                    return srcInfo;
+                }
+
+                public BleBroadcastSourceInfo[] newArray(int size) {
+                    return new BleBroadcastSourceInfo[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        BASS_Debug(TAG, "writeToParcel>");
+        out.writeByte(mSourceId);
+        out.writeInt(mSourceAddressType);
+        out.writeTypedObject(mSourceDevice, 0);
+        out.writeByte(mSourceAdvSid);
+        out.writeInt(mBroadcasterId);
+        out.writeInt(mMetaDataSyncState);
+        out.writeInt(mAudioSyncState);
+        out.writeInt(mEncyptionStatus);
+        if (mBadBroadcastCode != null) {
+            out.writeInt(mBadBroadcastCode.length);
+            out.writeByteArray(mBadBroadcastCode);
+        } else {
+            //write ZERO to parcel to say no badBroadcastcode
+            out.writeInt(0);
+        }
+        out.writeByte(mNumSubGroups);
+        out.writeString(mBroadcastCode);
+        writeMapToParcel(out, mAudioBisIndexList);
+        writeMetadataListToParcel(out, mMetadataList);
+        BASS_Debug(TAG, "writeToParcel:" + toString());
+    }
+
+    static void BASS_Debug(String TAG, String msg) {
+        if (BASS_DBG) {
+           Log.d(TAG, msg);
+        }
+    }
+
+};
+
diff --git a/le_audio/frameworks/base/core/java/android/bluetooth/BluetoothBroadcast.java b/le_audio/frameworks/base/core/java/android/bluetooth/BluetoothBroadcast.java
new file mode 100644
index 0000000..b709078
--- /dev/null
+++ b/le_audio/frameworks/base/core/java/android/bluetooth/BluetoothBroadcast.java
@@ -0,0 +1,280 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package android.bluetooth;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+import android.app.ActivityThread;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Broadcast
+ * profile.
+ *
+ * <p>BluetoothBroadcast is a proxy object for controlling the Bluetooth
+ * Broadcast Service via IPC. Use {@link BluetoothAdapter#getProfileProxy}
+ * to get the BluetoothBroadcast proxy object.
+ *
+ * @hide
+ */
+
+public final class BluetoothBroadcast implements BluetoothProfile{
+    private static final String TAG = "BluetoothBroadcast";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    /**
+     * Intent used to broadcast the change in broadcast state.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_Disabled}, {@link #Enabling},
+     * {@link #STATE_ENABLED}, {@link #STATE_DISABLING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_BROADCAST_STATE_CHANGED =
+            "android.bluetooth.broadcast.profile.action.BROADCAST_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the change in broadcast audio state.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     * <li> {@link #EXTRA_STATE} - The current audio state . </li>
+     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous audio state.</li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_BROADCAST_AUDIO_STATE_CHANGED =
+            "android.bluetooth.broadcast.profile.action.BROADCAST_AUDIO_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast encryption key generation status.
+     *
+     * <p>This intent will have 2 extras:
+     * <ul>
+     * <li> {@link #EXTRA_STATE} - The current audio state . </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} can be any of
+     * {@link #TRUE}, {@link #FALSE},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_BROADCAST_ENCRYPTION_KEY_GENERATED =
+            "android.bluetooth.broadcast.profile.action.BROADCAST_ENCRYPTION_KEY_GENERATED";
+
+    public static final int STATE_DISABLED = 10;
+    public static final int STATE_ENABLING = 11;
+    public static final int STATE_ENABLED = 12;
+    public static final int STATE_DISABLING = 13;
+    public static final int STATE_STREAMING = 14;
+    public static final int STATE_PLAYING = 10;
+    public static final int STATE_NOT_PLAYING = 11;
+
+    private BluetoothAdapter mAdapter;
+    private final BluetoothProfileConnector<IBluetoothBroadcast> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.BROADCAST, "BluetoothBroadcast",
+                    IBluetoothBroadcast.class.getName()) {
+                @Override
+                public IBluetoothBroadcast getServiceInterface(IBinder service) {
+                    return IBluetoothBroadcast.Stub.asInterface(Binder.allowBlocking(service));
+                }
+    };
+    /**
+     * Create a BluetoothBroadcast proxy object for interacting with the local
+     * Bluetooth Broadcast service.
+     * @hide
+     */
+    /*package*/ BluetoothBroadcast(Context context, ServiceListener listener) {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mProfileConnector.connect(context, listener);
+    }
+
+    /*
+     * @hide
+    */
+    //@UnsupportedAppUsage
+    /*package*/ void close() {
+        mProfileConnector.disconnect();
+    }
+
+    /*
+     * @hide
+    */
+    private IBluetoothBroadcast getService() {
+        return mProfileConnector.getService();
+    }
+
+    @Override
+    public void finalize() {
+        // The empty finalize needs to be kept or the
+        // cts signature tests would fail.
+    }
+    /*
+     * @hide
+    */
+    //@UnsupportedAppUsage
+    public boolean SetBroadcastMode(boolean enable) {
+        if (DBG) log("EnableBroadcast");
+        String packageName = ActivityThread.currentPackageName();
+        try {
+            final IBluetoothBroadcast service = getService();
+            if (service != null && isEnabled()) {
+                return service.SetBroadcast(enable, packageName);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        }
+    }
+    /**
+     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+     * with {@link BluetoothProfile#GATT} as argument
+     *
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public int getConnectionState(BluetoothDevice device) {
+        throw new UnsupportedOperationException(
+                   "Use BluetoothManager#getConnectedDevices instead.");
+    }
+    /**
+     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+     * with {@link BluetoothProfile#GATT} as argument
+     *
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        throw new UnsupportedOperationException(
+                   "Use BluetoothManager#getConnectedDevices instead.");
+    }
+    /**
+     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+     * with {@link BluetoothProfile#GATT} as argument
+     *
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public List<BluetoothDevice> getConnectedDevices() {
+        throw new UnsupportedOperationException(
+                   "Use BluetoothManager#getConnectedDevices instead.");
+    }
+
+    /*
+     * @hide
+    */
+    public boolean SetEncryption(boolean enable, int enc_len/*4bytes,16bytes*/, boolean use_existing) {
+        if (DBG) log("SetEncryption");
+        String packageName = ActivityThread.currentPackageName();
+        try {
+            final IBluetoothBroadcast service = getService();
+            if (service != null && isEnabled()) {
+                return service.SetEncryption(enable, enc_len, use_existing, packageName);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        }
+    }
+    /*
+     * @hide
+    */
+    public byte[] GetEncryptionKey() {
+        if (DBG) log("GetBroadcastEncryptionKey");
+        String packageName = ActivityThread.currentPackageName();
+        try {
+            final IBluetoothBroadcast service = getService();
+            if (service != null && isEnabled()) {
+                return service.GetEncryptionKey(packageName);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return null;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return null;
+        }
+    }
+    /*
+     * @hide
+    */
+    public int GetBroadcastStatus() {
+        if (DBG) log("GetBroadcastStatus");
+        String packageName = ActivityThread.currentPackageName();
+        try {
+            final IBluetoothBroadcast service = getService();
+            if (service != null && isEnabled()) {
+                return service.GetBroadcastStatus(packageName);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return STATE_DISABLED;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return STATE_DISABLED;
+        }
+    }
+    //@UnsupportedAppUsage
+//    public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {return null;}
+
+    private boolean isEnabled() {
+        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+        return false;
+    }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+}
+
+
diff --git a/le_audio/frameworks/base/core/java/android/bluetooth/BluetoothSyncHelper.java b/le_audio/frameworks/base/core/java/android/bluetooth/BluetoothSyncHelper.java
new file mode 100644
index 0000000..7072891
--- /dev/null
+++ b/le_audio/frameworks/base/core/java/android/bluetooth/BluetoothSyncHelper.java
@@ -0,0 +1,736 @@
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+
+ * Copyright (C) 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.
+ */
+
+package android.bluetooth;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothManager;
+import android.bluetooth.IBleBroadcastAudioScanAssistCallback;
+import android.bluetooth.IBluetoothSyncHelper;
+import android.bluetooth.BluetoothAdapter.LeScanCallback;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.content.Context;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.PeriodicAdvertisingCallback;
+import android.bluetooth.le.PeriodicAdvertisingManager;
+import android.bluetooth.le.PeriodicAdvertisingReport;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.os.SystemProperties;
+import java.util.IdentityHashMap;
+
+/**
+ * This class provides methods to perform Broadcast Scan Assistance client Profile related
+ * operations.
+ * It uses Bluetooth GATT APIs to achieve Braodcast Scan assistance client operations. Application should ensure
+ * BASS profile is connected with the given remote device before performing the operations using
+ * {@link BleBroadcastAudioScanAssistManager} interface operations
+ *
+ * <p>BluetoothSyncHelper is a proxy object for controlling the Bluetooth Scan Offloader (BASS client)
+ * Service via IPC.
+ *
+ * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothScanOfflaoder proxy object. Use
+ * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
+ *
+ * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothSyncHelper proxy object. Use
+ * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
+ *
+ * <b>Note:</b> Most of the methods here require
+ * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * @hide
+ */
+public final class BluetoothSyncHelper implements BluetoothProfile {
+
+    private static final String TAG = "BluetoothSyncHelper";
+    private static final boolean DBG = true;
+
+    private BluetoothAdapter mBluetoothAdapter;
+    /* maps callback, to callback wrapper and sync handle */
+    private Map<BleBroadcastAudioScanAssistCallback,
+             IBleBroadcastAudioScanAssistCallback /* callbackWrapper */> mAppCallbackWrappers;
+
+    private Map<BluetoothDevice,
+                 BleBroadcastAudioScanAssistManager> sBleAssistManagers = null;
+    private Context mContext = null;
+
+   /**
+     * Intent used to broadcast the change in connection state of the Bass client
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+            "android.bluetooth.bc.profile.action.CONNECTION_STATE_CHANGED";
+
+
+    private final BluetoothProfileConnector<IBluetoothSyncHelper> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.BC_PROFILE,
+                    "BluetoothSyncHelper", IBluetoothSyncHelper.class.getName()) {
+                @Override
+                public IBluetoothSyncHelper getServiceInterface(IBinder service) {
+                    return IBluetoothSyncHelper.Stub.asInterface(Binder.allowBlocking(service));
+                }
+    };
+
+    /*package*/ void close() {
+        mProfileConnector.disconnect();
+        mAppCallbackWrappers.clear();
+    }
+
+    /*package*/ IBluetoothSyncHelper getService() {
+        return mProfileConnector.getService();
+    }
+
+    static boolean isSupported() {
+        boolean isSupported = SystemProperties.getBoolean("persist.vendor.service.bt.bc", true);
+        log("BluetoothSyncHelper: isSupported returns " + isSupported);
+        return isSupported;
+    }
+    /**
+     * Create a BluetoothHeadset proxy object.
+     */
+    /*package*/ BluetoothSyncHelper(Context context, ServiceListener listener) {
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mProfileConnector.connect(context, listener);
+        BluetoothManager bluetoothManager = context.getSystemService(
+                 BluetoothManager.class);
+        mAppCallbackWrappers = new IdentityHashMap<BleBroadcastAudioScanAssistCallback, IBleBroadcastAudioScanAssistCallback>();
+        sBleAssistManagers = new IdentityHashMap<BluetoothDevice, BleBroadcastAudioScanAssistManager>();
+        mContext = context;
+    }
+
+    /**
+     * Interface to get Broadcast Audio Scan assistance for LE Audio usecases.This is instantiated per BluetoothDevice
+     * which is Scan delegator
+     * Application will get an Instance of the  {@link BleBroadcastAudioScanAssistManager} for the given
+     * scan delegator device
+     *
+     * @param BluetoothDevice Scan Delegator device for which BLE Broadcast SCAN Assistance operations will
+     * be performed
+     * @param {@link #BleBroadcastAudioScanAssistCallback} where callbacks related to BLE Broadcast Scan
+     * assistance will be deliverd
+     * @hide
+     */
+    public BleBroadcastAudioScanAssistManager getBleBroadcastAudioScanAssistManager(
+                                                  BluetoothDevice device,
+                                                  BleBroadcastAudioScanAssistCallback callback) {
+        if (isSupported() == false) {
+            Log.e(TAG, "Broadcast scan assistance not supported");
+            return null;
+        }
+
+        BleBroadcastAudioScanAssistManager assistMgr = null;
+        if (sBleAssistManagers != null) {
+            assistMgr = sBleAssistManagers.get(device);
+        }
+        if (assistMgr == null) {
+            assistMgr = new BleBroadcastAudioScanAssistManager(this, device,
+                                                  callback);
+        } else {
+            //object already exists, just registers the callback and retrun the same object
+            log("calling registerAppCb only");
+        }
+        registerAppCallback(device, callback);
+        return assistMgr;
+    }
+
+
+   /**
+     * Initiate connection to a BASS server profile of the remote bluetooth device.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is already connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that
+     * connection state intent for the profile will be broadcasted with
+     * the state. Users can get the connection state of the profile
+     * from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean connect(BluetoothDevice device) {
+        log("connect(" + device + ")");
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.connect(device);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        }
+    }
+
+    /**
+     * Initiate disconnection from a profile
+     *
+     * <p> This API will return false in scenarios like the profile on the
+     * Bluetooth device is not in connected state etc. When this API returns,
+     * true, it is guaranteed that the connection state change
+     * intent will be broadcasted with the state. Users can get the
+     * disconnection state of the profile from this intent.
+     *
+     * <p> If the disconnection is initiated by a remote device, the state
+     * will transition from {@link #STATE_CONNECTED} to
+     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
+     * host (local) device the state will transition from
+     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
+     * state {@link #STATE_DISCONNECTED}. The transition to
+     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
+     * two scenarios.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.disconnect(device);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public @NonNull List<BluetoothDevice> getConnectedDevices() {
+        log("getConnectedDevices()");
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()) {
+                return service.getConnectedDevices();
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return new ArrayList<BluetoothDevice>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new ArrayList<BluetoothDevice>();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+    @NonNull int[] states) {
+        log("getDevicesMatchingStates()");
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()) {
+                return service.getDevicesMatchingConnectionStates(states);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return new ArrayList<BluetoothDevice>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new ArrayList<BluetoothDevice>();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public @BluetoothProfile.BtProfileState int getConnectionState(
+    @NonNull BluetoothDevice device) {
+        log("getState(" + device + ")");
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                return service.getConnectionState(device);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return BluetoothProfile.STATE_DISCONNECTED;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+    }
+    /**
+     * Get the connection policy of the profile.
+     *
+     * <p> The connection policy can be any of:
+     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
+     * {@link #CONNECTION_POLICY_UNKNOWN}
+     *
+     * @param device Bluetooth device
+     * @return connection policy of the device
+     * @hide
+     */
+    //@SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getConnectionPolicy(@NonNull BluetoothDevice device) {
+        log("getConnectionPolicy(" + device + ")");
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                return service.getConnectionPolicy(device);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+        }
+    }
+
+    /**
+     * Set connection policy of the profile
+     *
+     * <p> The device should already be paired.
+     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+     *
+     * @param device Paired bluetooth device
+     * @param connectionPolicy is the connection policy to set to for this profile
+     * @return true if connectionPolicy is set, false on error
+     * @hide
+     */
+    //@SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+            int connectionPolicy) {
+        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+                        && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                    return false;
+                }
+                return service.setConnectionPolicy(device, connectionPolicy);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        }
+    }
+
+    private IBleBroadcastAudioScanAssistCallback wrap(BleBroadcastAudioScanAssistCallback callback,
+                 Handler handler) {
+             return new IBleBroadcastAudioScanAssistCallback.Stub() {
+                 public void onBleBroadcastSourceFound(ScanResult scanres) {
+                     handler.post(new Runnable() {
+                         @Override
+                         public void run() {
+                             log("calling onBleBroadcastSourceFound for " +
+                                                 "scanres:" + scanres);
+                             callback.onBleBroadcastSourceFound(
+                                                      scanres);
+                         }
+                      });
+                 }
+
+                 public void onBleBroadcastAudioSourceSelected(BluetoothDevice device, int status,
+                         List<BleBroadcastSourceChannel> broadcastSourceChannels) {
+                     handler.post(new Runnable() {
+                       @Override
+                       public void run() {
+                           log("calling onBleBroadcastSourceSelected for " +
+                                                 "status:" + status);
+                           callback.onBleBroadcastSourceSelected(device,
+                                           status, broadcastSourceChannels);
+                       }
+                    });
+                 }
+                 public void onBleBroadcastAudioSourceAdded(BluetoothDevice rcvr,
+                                              byte srcId,
+                                             int status) {
+                     handler.post(new Runnable() {
+                         @Override
+                         public void run() {
+                             log("calling onBleBroadcastAudioSourceAdded for " + rcvr +
+                                 "srcId:" + srcId + "status:" + status);
+                             callback.onBleBroadcastAudioSourceAdded(rcvr, srcId,
+                                     status);
+                         }
+                     });
+                 }
+                 public void onBleBroadcastAudioSourceUpdated(BluetoothDevice rcvr,
+                                             byte srcId,
+                                             int status) {
+                     handler.post(new Runnable() {
+                         @Override
+                         public void run() {
+                             log("calling onBleBroadcastAudioSourceUpdated for " + rcvr +
+                                 "srcId:" + srcId + "status:" + status);
+                             callback.onBleBroadcastAudioSourceUpdated(rcvr, srcId,
+                                     status);
+                         }
+                     });
+                 }
+                 public void onBleBroadcastPinUpdated(BluetoothDevice rcvr,
+                                                byte srcId,
+                                                int status) {
+                     handler.post(new Runnable() {
+                         @Override
+                         public void run() {
+                             log("calling onBleBroadcastPinUpdated for " + rcvr +
+                                 "srcId:" + srcId + "status:" + status);
+                             callback.onBleBroadcastPinUpdated(rcvr, srcId,
+                                     status);
+                             // App can still unregister the sync until notified it's lost.
+                             // Remove callback after app was notifed.
+                             //mCallbackWrappers.remove(callback);
+                         }
+                     });
+                 }
+
+                 public void onBleBroadcastAudioSourceRemoved(BluetoothDevice rcvr,
+                                             byte srcId,
+                                             int status) {
+                     handler.post(new Runnable() {
+                         @Override
+                         public void run() {
+                             log("calling onBleBroadcastAudioSourceRemoved for " + rcvr +
+                                 "srcId:" + srcId + "status:" + status);
+                             callback.onBleBroadcastAudioSourceRemoved(rcvr, srcId,
+                                     status);
+
+                         }
+                     });
+                 }
+             };
+         }
+
+
+     boolean startScanOffload (BluetoothDevice device, boolean isGroupOp) {
+        log("startScanOffload(" + device + ", isGroupOp: " + isGroupOp + ")");
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                return service.startScanOffload(device, isGroupOp);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        }
+     }
+
+     boolean stopScanOffload (BluetoothDevice device, boolean isGroupOp) {
+        log("stopScanOffload(" + device + ", isGroupOp: " + isGroupOp + ")" );
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                return service.stopScanOffload(device, isGroupOp);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        }
+     }
+
+     boolean searchforLeAudioBroadcasters (BluetoothDevice device) {
+         log("searchforLeAudioBroadcasters(" + device + ")");
+         final IBluetoothSyncHelper service = getService();
+         try {
+             if (service != null && isEnabled()
+                     && isValidDevice(device)) {
+                 return service.searchforLeAudioBroadcasters(device);
+             }
+             if (service == null) Log.w(TAG, "Proxy not attached to service");
+             return false;
+         } catch (RemoteException e) {
+             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+             return false;
+         }
+     }
+
+     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+     boolean stopSearchforLeAudioBroadcasters(BluetoothDevice device) {
+         log("stopSearchforLeAudioBroadcasters(" + device + ")");
+         final IBluetoothSyncHelper service = getService();
+         try {
+             if (service != null && isEnabled()
+                     && isValidDevice(device)) {
+                 return service.stopSearchforLeAudioBroadcasters(device);
+             }
+             if (service == null) Log.w(TAG, "Proxy not attached to service");
+             return false;
+         } catch (RemoteException e) {
+             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+             return false;
+         }
+     }
+
+     boolean selectBroadcastSource (BluetoothDevice device, ScanResult scanRes, boolean isGroupOp) {
+        log("selectBroadcastSource(" + device + ": groupop" + isGroupOp +")");
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                return service.selectBroadcastSource(device, scanRes, isGroupOp);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        }
+     }
+
+     void registerAppCallback(BluetoothDevice device, BleBroadcastAudioScanAssistCallback appCallback) {
+          log("registerAppCallback device :" + device + "appCB: " + appCallback);
+          Handler handler = new Handler(Looper.getMainLooper());
+
+          IBleBroadcastAudioScanAssistCallback wrapped = wrap(appCallback, handler);
+          final IBluetoothSyncHelper service = getService();
+          try {
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+               service.registerAppCallback(device, wrapped);
+               if (mAppCallbackWrappers != null) {
+                   mAppCallbackWrappers.put(appCallback, wrapped);
+               }
+            }
+            if (service == null) {
+                Log.w(TAG, "Proxy not attached to service");
+                return;
+            }
+          } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return;
+          }
+     }
+
+     void unregisterAppCallback(BluetoothDevice device, BleBroadcastAudioScanAssistCallback appCallback) {
+         log("unregisterAppCallback: device" + device + "appCB:" + appCallback);
+         // Remove callback after app was notifed.
+
+         final IBluetoothSyncHelper service = getService();
+         IBleBroadcastAudioScanAssistCallback cb = mAppCallbackWrappers.get(device);
+         try {
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                service.unregisterAppCallback(device, cb);
+                mAppCallbackWrappers.remove(appCallback);
+                return;
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return;
+          } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return;
+          }
+     }
+
+
+    boolean addBroadcastSource (BluetoothDevice sinkDevice,
+                                          BleBroadcastSourceInfo srcInfo,
+                                          boolean isGroupOp) {
+        log("addBroadcastSource  for :" + sinkDevice
+            + "SourceInfo: " + srcInfo+ "isGroupOp: " + isGroupOp);
+        boolean ret = false;
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()
+                    && isValidDevice(sinkDevice)) {
+
+                return service.addBroadcastSource(sinkDevice, srcInfo, isGroupOp);
+            }
+            if (service == null)
+            {
+                Log.w(TAG, "Proxy not attached to service");
+                ret = false;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            ret = false;
+        }
+        return ret;
+    }
+    boolean updateBroadcastSource (BluetoothDevice device,
+                                      BleBroadcastSourceInfo srcInfo,
+                                      boolean isGroupOp) {
+            //Same device can have more than one SourceId
+            log("updateBroadcastSource for :" + device +
+                "SourceInfo: " + srcInfo+ "isGroupOp: " + isGroupOp);
+            boolean ret = false;
+            final IBluetoothSyncHelper service = getService();
+            try {
+                if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                  return service.updateBroadcastSource(device,
+                                            srcInfo, isGroupOp);
+                }
+                if (service == null)
+                {
+                    Log.w(TAG, "Proxy not attached to service");
+                    ret = false;
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                ret = false;
+            }
+            return ret;
+    }
+
+     boolean setBroadcastCode (BluetoothDevice device,
+                                BleBroadcastSourceInfo srcInfo,
+                                boolean isGroupOp) {
+            //Same device can have more than one SourceId
+            log("setBroadcastCode for :" + device);
+            log("SourceInfo: " + srcInfo+ "isGroupOp: " + isGroupOp);
+            boolean ret = false;
+            final IBluetoothSyncHelper service = getService();
+            try {
+                if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                   return service.setBroadcastCode(device,
+                                                 srcInfo, isGroupOp);
+                }
+                if (service == null)
+                {
+                    Log.w(TAG, "Proxy not attached to service");
+                    ret = false;
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                ret = false;
+            }
+            return ret;
+    }
+    boolean removeBroadcastSource (BluetoothDevice device,
+                                      byte sourceId,
+                                      boolean isGroupOp
+                                      ) {
+            log("removeBroadcastSource for :" + device +
+                "SourceId: " + sourceId + "isGroupOp: " + isGroupOp);
+            final IBluetoothSyncHelper service = getService();
+            boolean ret = false;
+            try {
+                if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                   return service.removeBroadcastSource(device, sourceId
+                                                          , isGroupOp);
+                }
+                if (service == null)
+                {
+                    Log.w(TAG, "Proxy not attached to service");
+                    ret = false;
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                ret = false;
+            }
+            return ret;
+    }
+
+    List<BleBroadcastSourceInfo> getAllBroadcastSourceInformation (BluetoothDevice device) {
+        log("GetAllBroadcastReceiverStates for :" + device);
+        final IBluetoothSyncHelper service = getService();
+        try {
+            if (service != null && isEnabled()
+                && isValidDevice(device)) {
+                return service.getAllBroadcastSourceInformation(device);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+                return null;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return null;
+        }
+     }
+    private boolean isEnabled() {
+        if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+        return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+        if (device == null) return false;
+
+        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+        return false;
+    }
+
+    private static void log(String msg) {
+        BleBroadcastSourceInfo.BASS_Debug(TAG, msg);
+    }
+
+}
diff --git a/le_audio/frameworks/base/packages/SettingsLib/Android.bp b/le_audio/frameworks/base/packages/SettingsLib/Android.bp
new file mode 100644
index 0000000..d7a2bb1
--- /dev/null
+++ b/le_audio/frameworks/base/packages/SettingsLib/Android.bp
@@ -0,0 +1,22 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+filegroup {
+    name: "framework-settingslib-adva-srcs",
+    srcs: ["src/**/*.java"],
+}
diff --git a/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BCProfile.java b/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BCProfile.java
new file mode 100644
index 0000000..677186e
--- /dev/null
+++ b/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BCProfile.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+
+ * Copyright (C) 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
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSyncHelper;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.util.Log;
+import android.os.ParcelUuid;
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+import android.bluetooth.BleBroadcastAudioScanAssistCallback;
+import android.content.Intent;
+import android.bluetooth.BleBroadcastSourceInfo;
+
+import com.android.settingslib.R;
+import android.os.SystemProperties;
+import android.os.Handler;
+
+import androidx.annotation.Keep;
+import java.util.ArrayList;
+import java.util.List;
+
+@Keep
+public class BCProfile implements LocalBluetoothProfile {
+    private static final String TAG = "BCProfile";
+    private static boolean V = true;
+
+    private Context mContext;
+
+    private BluetoothSyncHelper mService;
+    private boolean mIsProfileReady;
+
+    private final CachedBluetoothDeviceManager mDeviceManager;
+
+    static final String NAME = "BCProfile";
+    private final LocalBluetoothProfileManager mProfileManager;
+
+    // Order of this profile in device profiles list
+    private static final int ORDINAL = 1;
+
+    // These callbacks run on the main thread.
+    private final class BassclientServiceListener
+            implements BluetoothProfile.ServiceListener {
+
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            Log.d(TAG, "BassclientService connected");
+            mService = (BluetoothSyncHelper) proxy;
+            // We just bound to the service, so refresh the UI for any connected Bassclient devices.
+            //List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+            mIsProfileReady=true;//BassService connected
+            mProfileManager.callServiceConnectedListeners();
+        }
+
+        public void onServiceDisconnected(int profile) {
+            Log.d(TAG, "BassclientService disconnected");
+            mIsProfileReady=false;
+        }
+    }
+
+    public boolean isProfileReady() {
+        return mIsProfileReady;
+    }
+
+    @Override
+    public int getProfileId() {
+        return BluetoothProfile.BC_PROFILE;
+    }
+
+    @Override
+    public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+        boolean isEnabled = false;
+        if (mService == null) {
+            return false;
+        }
+        if (enabled) {
+            Log.d(TAG, "BCProfile: " + device + ":" + enabled);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                isEnabled = mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+            }
+        } else {
+            isEnabled = mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+        }
+
+        return isEnabled;
+    }
+
+    @Override
+    public boolean isEnabled(BluetoothDevice device) {
+        if (mService == null) {
+            return false;
+        }
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
+    }
+
+   @Override
+    public int getConnectionPolicy(BluetoothDevice device) {
+        return BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+    }
+
+    BCProfile(Context context, CachedBluetoothDeviceManager deviceManager,
+            LocalBluetoothProfileManager profileManager) {
+        mContext = context;
+        mDeviceManager = deviceManager;
+        mProfileManager = profileManager;
+        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
+                new BassclientServiceListener(), BluetoothProfile.BC_PROFILE);
+    }
+
+    public boolean accessProfileEnabled() {
+        //return true for BASS always so that
+        //It shows the profile preference in device details
+        return true;
+    }
+
+    public boolean isAutoConnectable() {
+        if (mService == null) return false;
+        Log.d(TAG, "isAutoConnectable return false");
+        return false;
+    }
+
+    /**
+     * Get Scan delegator devices matching connection states{
+     * @code BluetoothProfile.STATE_CONNECTED,
+     * @code BluetoothProfile.STATE_CONNECTING,
+     * @code BluetoothProfile.STATE_DISCONNECTING}
+     *
+     * @return Matching device list
+     */
+    public List<BluetoothDevice> getConnectedDevices() {
+        return getDevicesByStates(new int[] {
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_CONNECTING,
+                BluetoothProfile.STATE_DISCONNECTING});
+    }
+
+    /**
+     * Get Scan delegator  devices matching connection states{
+     * @code BluetoothProfile.STATE_DISCONNECTED,
+     * @code BluetoothProfile.STATE_CONNECTED,
+     * @code BluetoothProfile.STATE_CONNECTING,
+     * @code BluetoothProfile.STATE_DISCONNECTING}
+     *
+     * @return Matching device list
+     */
+    public List<BluetoothDevice> getConnectableDevices() {
+        return getDevicesByStates(new int[] {
+                BluetoothProfile.STATE_DISCONNECTED,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_CONNECTING,
+                BluetoothProfile.STATE_DISCONNECTING});
+    }
+
+    private List<BluetoothDevice> getDevicesByStates(int[] states) {
+        if (mService == null) {
+            return new ArrayList<BluetoothDevice>(0);
+        }
+        return mService.getDevicesMatchingConnectionStates(states);
+    }
+
+    public boolean connect(BluetoothDevice device) {
+        Log.d(TAG, "BCProfile Connect to  device: " + device);
+        if (mService == null) return false;
+        return mService.connect(device);
+    }
+
+    public boolean disconnect(BluetoothDevice device) {
+        Log.d(TAG, "BCProfile disonnect to  device: " + device);
+        if (mService == null) return false;
+        // Downgrade priority as user is disconnecting the Bassclient.
+        if (mService.getConnectionPolicy(device) > BluetoothProfile.PRIORITY_ON){
+            mService.setConnectionPolicy(device, BluetoothProfile.PRIORITY_ON);
+        }
+        return mService.disconnect(device);
+    }
+
+    public int getConnectionStatus(BluetoothDevice device) {
+        if (mService == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+        return mService.getConnectionState(device);
+    }
+
+    public int getPreferred(BluetoothDevice device) {
+        if (mService == null) return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+        return mService.getConnectionPolicy(device);
+    }
+
+    public void setPreferred(BluetoothDevice device, boolean preferred) {
+        if (mService == null) return;
+        if (preferred) {
+            if (mService.getConnectionPolicy(device) !=
+                       BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device,
+                       BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+            }
+        } else {
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+        }
+    }
+
+    public String toString() {
+        return NAME;
+    }
+
+    public int getOrdinal() {
+        return ORDINAL;
+    }
+
+    public int getNameResource(BluetoothDevice device) {
+        return R.string.bluetooth_profile_bc;
+    }
+
+    public BleBroadcastAudioScanAssistManager getBSAManager(BluetoothDevice device,
+                                                  BleBroadcastAudioScanAssistCallback callback) {
+        if (mService == null) {
+            Log.d(TAG, "getBroadcastAudioScanAssistManager: service is null");
+            return null;
+        }
+        return mService.getBleBroadcastAudioScanAssistManager(device, callback);
+    }
+
+    public int getSummaryResourceForDevice(BluetoothDevice device) {
+        int state = getConnectionStatus(device);
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return R.string.bluetooth_bc_profile_summary_use_for;
+
+            case BluetoothProfile.STATE_CONNECTED:
+                return R.string.bluetooth_bc_profile_summary_connected;
+
+            default:
+                return BluetoothUtils.getConnectionStateSummary(state);
+        }
+    }
+
+    public int getDrawableResource(BluetoothClass btClass) {
+        return com.android.internal.R.drawable.ic_bt_hearing_aid;
+    }
+
+    protected void finalize() {
+        Log.d(TAG, "finalize()");
+        if (mService != null) {
+            try {
+                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.BC_PROFILE,
+                                                                       mService);
+                mService = null;
+            }catch (Throwable t) {
+                Log.w(TAG, "Error cleaning up BAss client proxy", t);
+            }
+        }
+    }
+
+    static boolean isBCSupported() {
+        boolean isBCSupported = SystemProperties.getBoolean("persist.vendor.service.bt.bc", true);
+        Log.d(TAG, "BassClientProfile: isBCSupported returns " + isBCSupported);
+        return isBCSupported;
+    }
+
+    static public boolean isBASeeker(BluetoothDevice device) {
+       //always send true
+       boolean isSeeker = SystemProperties.getBoolean("persist.vendor.service.bt.baseeker", false);
+       ParcelUuid[] uuids = null;
+       if (device != null) {
+          uuids = device.getUuids();
+       }
+       ParcelUuid sd = ParcelUuid.fromString("0000184F-0000-1000-8000-00805F9B34FB");
+       if (isBCSupported()) {
+           if (uuids != null) {
+               for (ParcelUuid uid : uuids) {
+                  if (uid.equals(sd)) {
+                        Log.d(TAG, "SD uuid present");
+                      isSeeker = true;
+                  }
+               }
+           }
+       }
+       Log.d(TAG,"isBASeeker returns:" + isSeeker);
+       return isSeeker;
+    }
+
+}
diff --git a/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastProfile.java b/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastProfile.java
new file mode 100644
index 0000000..d1d9c01
--- /dev/null
+++ b/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastProfile.java
@@ -0,0 +1,180 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothBroadcast;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.util.Log;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import com.android.settingslib.R;
+import androidx.annotation.Keep;
+
+/**
+ * BroadcastProfile handles Bluetooth Broadcast profile.
+ */
+@Keep
+public final class BroadcastProfile implements LocalBluetoothProfile {
+    private static final String TAG = "BroadcastProfile";
+    private static boolean V = true;
+
+    private BluetoothBroadcast mService;
+    private boolean mIsProfileReady = false;
+
+    static final String NAME = "Broadcast";
+
+    // Order of this profile in device profiles list
+    private static final int ORDINAL = 0;
+
+    // These callbacks run on the main thread.
+    private final class BroadcastListener
+            implements BluetoothProfile.ServiceListener {
+
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (profile == BluetoothProfile.BROADCAST) {
+              if (V) Log.d(TAG,"Bluetooth Broadcast service connected");
+              mService = (BluetoothBroadcast) proxy;
+              mIsProfileReady = true;
+            }
+        }
+
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.BROADCAST) {
+              if (V) Log.d(TAG,"Bluetooth Broadcast service disconnected");
+              mIsProfileReady = false;
+            }
+        }
+    }
+
+    public boolean isProfileReady() {
+        Log.d(TAG,"isProfileReady = " + mIsProfileReady);
+        return mIsProfileReady;
+    }
+
+    @Override
+    public int getProfileId() {
+        Log.d(TAG,"getProfileId");
+        return BluetoothProfile.BROADCAST;
+    }
+
+    BroadcastProfile(Context context) {
+        Log.d(TAG,"BroadcastProfile constructor");
+        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
+                new BroadcastListener(), BluetoothProfile.BROADCAST);
+    }
+
+    public boolean accessProfileEnabled() {
+        Log.d(TAG,"accessProfileEnabled");
+        return false;
+    }
+
+    public boolean isAutoConnectable() {
+        return false;
+    }
+
+    public boolean connect(BluetoothDevice device) {
+        return false;
+    }
+
+    public boolean disconnect(BluetoothDevice device) {
+        return false;
+    }
+
+    public int getConnectionStatus(BluetoothDevice device) {
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    public boolean isEnabled(BluetoothDevice device) {
+        return false;
+    }
+
+    public int getConnectionPolicy(BluetoothDevice device) {
+        return CONNECTION_POLICY_FORBIDDEN;     
+    }
+
+    public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+        return false;//CONNECTION_POLICY_ALLOWED;
+    }
+     
+    public void setPreferred(BluetoothDevice device, boolean preferred) {
+    }
+    public int getPreferred(BluetoothDevice device) {
+        return BluetoothProfile.PRIORITY_OFF;
+    }
+    public boolean isPreferred(BluetoothDevice device) {
+        return false;
+    }
+    public String toString() {
+        return NAME;
+    }
+
+    public int getOrdinal() {
+        return ORDINAL;
+    }
+
+    public int getNameResource(BluetoothDevice device) {
+        return R.string.bluetooth_profile_broadcast;
+    }
+
+    public int getSummaryResourceForDevice(BluetoothDevice device) {
+        return 0;
+    }
+
+    public int getDrawableResource(BluetoothClass btClass) {
+        return 0;
+    }
+
+    public boolean setEncryption(boolean enable, int enc_len, boolean use_existing) {
+      Log.d(TAG,"setEncryption");
+      return mService.SetEncryption(enable, enc_len, use_existing);
+    }
+
+    public byte[] getEncryptionKey() {
+      Log.d(TAG,"getEncryptionKey");
+      return mService.GetEncryptionKey();
+    }
+
+    public int getBroadcastStatus() {
+      Log.d(TAG,"getBroadcastStatus");
+      return mService.GetBroadcastStatus();
+    }
+
+    public boolean setBroadcastMode(boolean enable) {
+      Log.d(TAG,"setBroadcastMode");
+      return mService.SetBroadcastMode(enable);
+    }
+
+    protected void finalize() {
+        if (V) Log.d(TAG, "finalize()");
+        if (mService != null) {
+            try {
+                BluetoothAdapter.getDefaultAdapter().closeProfileProxy
+                                    (BluetoothProfile.BROADCAST, mService);
+                mService = null;
+            } catch (Throwable t) {
+                Log.w(TAG, "Error cleaning up Broadcast proxy", t);
+            }
+        }
+    }
+}
diff --git a/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastSourceInfoHandler.java b/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastSourceInfoHandler.java
new file mode 100644
index 0000000..054623e
--- /dev/null
+++ b/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BroadcastSourceInfoHandler.java
@@ -0,0 +1,79 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.Log;
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import android.content.Intent;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.os.Handler;
+
+public class BroadcastSourceInfoHandler implements BluetoothEventManager.Handler {
+        private static final String TAG = "BroadcastSourceInfoHandler";
+        private static final boolean V = Log.isLoggable(TAG, Log.VERBOSE);
+        private final CachedBluetoothDeviceManager mDeviceManager;
+        BroadcastSourceInfoHandler(CachedBluetoothDeviceManager deviceManager
+            ) {
+            mDeviceManager = deviceManager;
+        }
+        @Override
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            if (device == null) {
+                Log.w(TAG, "BroadcastSourceInfoHandler: device is null");
+                return;
+            }
+
+            final String action = intent.getAction();
+            if (action == null) {
+                Log.w(TAG, "BroadcastSourceInfoHandler: action is null");
+                return;
+            }
+            BleBroadcastSourceInfo sourceInfo = intent.getParcelableExtra(
+                              BleBroadcastSourceInfo.EXTRA_SOURCE_INFO);
+
+            int sourceInfoIdx = intent.getIntExtra(
+                              BleBroadcastSourceInfo.EXTRA_SOURCE_INFO_INDEX,
+                              BluetoothAdapter.ERROR);
+
+            int maxNumOfsrcInfo = intent.getIntExtra(
+                              BleBroadcastSourceInfo.EXTRA_MAX_NUM_SOURCE_INFOS,
+                              BluetoothAdapter.ERROR);
+            if (V) {
+                Log.d(TAG, "Rcved :BCAST_RECEIVER_STATE Intent for : " + device);
+                Log.d(TAG, "Rcvd BroadcastSourceInfo index=" + sourceInfoIdx);
+                Log.d(TAG, "Rcvd max num of source Info=" + maxNumOfsrcInfo);
+                Log.d(TAG, "Rcvd BroadcastSourceInfo=" + sourceInfo);
+            }
+            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+            VendorCachedBluetoothDevice vDevice =
+            VendorCachedBluetoothDevice.getVendorCachedBluetoothDevice(cachedDevice, null);
+            if (vDevice != null) {
+                vDevice.onBroadcastReceiverStateChanged(sourceInfo,
+                                     sourceInfoIdx, maxNumOfsrcInfo);
+                cachedDevice.dispatchAttributesChanged();
+            } else {
+                Log.e(TAG, "No vCachedDevice created for this Device");
+            }
+        }
+};
diff --git a/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/VendorCachedBluetoothDevice.java b/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/VendorCachedBluetoothDevice.java
new file mode 100644
index 0000000..f1d0afd
--- /dev/null
+++ b/le_audio/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/VendorCachedBluetoothDevice.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BleBroadcastSourceChannel;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+import android.bluetooth.BleBroadcastAudioScanAssistCallback;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.BluetoothUuid;
+import android.os.ParcelUuid;
+import android.content.Context;
+import android.content.SharedPreferences;
+import java.util.IdentityHashMap;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.SystemClock;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+import java.lang.Integer;
+
+import android.os.SystemProperties;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.R;
+import com.android.settingslib.Utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * VendorCachedBluetoothDevice represents a remote Bluetooth device. It contains
+ * attributes of the device (such as the address, name, RSSI, etc.) and
+ * functionality that can be performed on the device (connect, pair, disconnect,
+ * etc.).
+ */
+public class VendorCachedBluetoothDevice extends CachedBluetoothDevice {
+    private static final String TAG = "VendorCachedBluetoothDevice";
+    private static final boolean V = Log.isLoggable(TAG, Log.VERBOSE);
+    private ScanResult mScanRes = null;
+    private BleBroadcastAudioScanAssistManager mScanAssistManager;
+    static private Map<Integer, BleBroadcastSourceInfo> mBleBroadcastReceiverStates
+        = new HashMap<Integer, BleBroadcastSourceInfo>();
+    private LocalBluetoothProfileManager mProfileManager = null;
+    static private Map<CachedBluetoothDevice,
+                 VendorCachedBluetoothDevice> mVcbdEntries = new IdentityHashMap<CachedBluetoothDevice, VendorCachedBluetoothDevice>();
+
+    public static VendorCachedBluetoothDevice getVendorCachedBluetoothDevice(
+                     CachedBluetoothDevice cachedDevice,
+                     LocalBluetoothProfileManager profileManager) {
+        VendorCachedBluetoothDevice vCbd = null;
+        if (mVcbdEntries != null) {
+            vCbd = mVcbdEntries.get(cachedDevice);
+        }
+        //dont create new instance if profileMgr is null
+        if (vCbd == null && profileManager != null) {
+            vCbd = new VendorCachedBluetoothDevice(cachedDevice,
+                                                  profileManager);
+            Log.d(TAG, "getVendorCachedBluetoothDevice: created new Instance");
+            mVcbdEntries.put(cachedDevice, vCbd);
+        }
+        return vCbd;
+    }
+
+    VendorCachedBluetoothDevice(CachedBluetoothDevice cachedDevice,LocalBluetoothProfileManager profileManager) {
+        super(cachedDevice);
+        mProfileManager = profileManager;
+        mBleBroadcastReceiverStates = new HashMap<Integer, BleBroadcastSourceInfo>();
+        InitializeSAManager();
+    }
+
+    VendorCachedBluetoothDevice(Context context, LocalBluetoothProfileManager profileManager,
+            BluetoothDevice device) {
+         super(context, profileManager, device);
+        mProfileManager = profileManager;
+        mBleBroadcastReceiverStates = new HashMap<Integer, BleBroadcastSourceInfo>();
+        InitializeSAManager();
+    }
+
+    /**
+     * Describes the current device and profile for logging.
+     *
+     * @param profile Profile to describe
+     * @return Description of the device and profile
+     */
+    private String describe(LocalBluetoothProfile profile) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Address:").append(mDevice);
+        if (profile != null) {
+            sb.append(" Profile:").append(profile);
+        }
+
+        return sb.toString();
+    }
+
+    void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
+        if (V) {
+            Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device=" + mDevice
+                    + ", newProfileState " + newProfileState);
+        }
+        if (profile instanceof BCProfile
+                 && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+           cleanUpSAMananger();
+           super.dispatchAttributesChanged();
+       }
+    }
+
+    private BleBroadcastAudioScanAssistCallback mScanAssistCallback = new BleBroadcastAudioScanAssistCallback() {
+        public void onBleBroadcastSourceFound(ScanResult res) {
+            if (V) {
+                Log.d(TAG, "onBleBroadcastSourceFound" + res.getDevice());
+            }
+            setScanResult(res);
+        };
+
+        public void onBleBroadcastAudioSourceAdded(BluetoothDevice rcvr,
+                                                byte srcId,
+                                                int status) {
+        };
+
+        public void onBleBroadcastSourceSelected( int status,
+            List<BleBroadcastSourceChannel> broadcastSourceIndicies) {
+        };
+
+        public void onBleBroadcastAudioSourceUpdated(BluetoothDevice rcvr,
+                                             byte srcId,
+                                             int status) {
+        };
+
+        public void onBleBroadcastPinUpdated(BluetoothDevice rcvr,
+                                                byte srcId,
+                                                int status) {
+        };
+        public void onBleBroadcastAudioSourceRemoved(BluetoothDevice rcvr,
+                                             byte srcId,
+                                             int status) {
+        };
+    };
+
+    public BleBroadcastAudioScanAssistManager getScanAssistManager()
+    {   InitializeSAManager();
+        return mScanAssistManager;
+    }
+
+    void InitializeSAManager() {
+        BCProfile bcProfile = (BCProfile)mProfileManager.getBCProfile();
+        mScanAssistManager = bcProfile.getBSAManager(
+                                  mDevice, mScanAssistCallback);
+    }
+
+    void cleanUpSAMananger() {
+        mScanAssistManager = null;
+        if (mBleBroadcastReceiverStates != null) {
+            mBleBroadcastReceiverStates.clear();
+        }
+    }
+
+    void updateBroadcastreceiverStates(BleBroadcastSourceInfo srcInfo, int index,
+                                       int maxSourceInfosNum) {
+        BleBroadcastSourceInfo entry = mBleBroadcastReceiverStates.get(index);
+        if (entry != null) {
+            Log.d(TAG, "updateBroadcastreceiverStates: Replacing receiver State Information");
+            mBleBroadcastReceiverStates.replace(index, srcInfo);
+        } else {
+            mBleBroadcastReceiverStates.put(index, srcInfo);
+        }
+        super.dispatchAttributesChanged();
+    }
+
+    public int getNumberOfBleBroadcastReceiverStates() {
+        int ret = 0;
+        if (mScanAssistManager == null) {
+            InitializeSAManager();
+            if (mScanAssistManager == null) {
+                return ret;
+            }
+        }
+        List<BleBroadcastSourceInfo> srcInfo = mScanAssistManager.getAllBroadcastSourceInformation();
+        if (srcInfo != null) {
+            ret = srcInfo.size();
+        }
+        if (V) {
+            Log.d(TAG, "getNumberOfBleBroadcastReceiverStates:"+ ret);
+        }
+        return ret;
+    }
+
+    public Map<Integer, BleBroadcastSourceInfo> getAllBleBroadcastreceiverStates() {
+        if (mScanAssistManager == null) {
+            InitializeSAManager();
+            if (mScanAssistManager == null) {
+                Log.e(TAG, "SA Manager cant be initialized");
+                return null;
+            }
+        }
+        List<BleBroadcastSourceInfo> srcInfos = mScanAssistManager.getAllBroadcastSourceInformation();
+        if (srcInfos == null) {
+             Log.e(TAG, "getAllBleBroadcastreceiverStates: no src Info");
+             return null;
+        }
+        for (int i=0; i<srcInfos.size(); i++) {
+            BleBroadcastSourceInfo sI = srcInfos.get(i);
+            mBleBroadcastReceiverStates.put((int)sI.getSourceId(), sI);
+        }
+        return  mBleBroadcastReceiverStates;
+    }
+
+    void onBroadcastReceiverStateChanged (BleBroadcastSourceInfo srcInfo, int index,
+                                          int maxSourceInfoNum) {
+       updateBroadcastreceiverStates(srcInfo, index, maxSourceInfoNum);
+    }
+
+    public void setScanResult(ScanResult res) {
+        mScanRes = res;
+    }
+
+    public ScanResult getScanResult() {
+        return mScanRes;
+    }
+
+    @androidx.annotation.Keep
+    public boolean isBroadcastAudioSynced() {
+        if (mScanAssistManager == null) {
+            InitializeSAManager();
+            if (mScanAssistManager == null) {
+                Log.e(TAG, "SA Manager cant be initialized");
+                return false;
+            }
+        }
+        List<BleBroadcastSourceInfo> srcInfos = mScanAssistManager.getAllBroadcastSourceInformation();
+        if (srcInfos == null) {
+             Log.e(TAG, "isBroadcastAudioSynced: no src Info");
+             return false;
+        }
+        for (int i=0; i<srcInfos.size(); i++) {
+            BleBroadcastSourceInfo sI = srcInfos.get(i);
+            if (sI.getAudioSyncState() ==
+                    BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED) {
+                return true;
+            }
+        }
+        Log.d(TAG,"isAudioSynced: false");
+        return false;
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/Android.bp b/le_audio/packages/apps/Bluetooth/Android.bp
new file mode 100644
index 0000000..76e8499
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/Android.bp
@@ -0,0 +1,30 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+filegroup {
+    name: "bluetooth-apps-adva-srcs",
+    srcs: ["src/com/android/bluetooth/groupclient/*.java",
+           "src/com/android/bluetooth/bassclient/*.java",
+           "src/com/android/bluetooth/broadcast/*.java",
+           "src/com/android/bluetooth/acm/*.java",
+           "src/com/android/bluetooth/pacsclient/*.java",
+           "src/com/android/bluetooth/apm/*.java",
+           "src/com/android/bluetooth/vcp/*.java",
+           "src/com/android/bluetooth/mcp/*.java",
+           "src/com/android/bluetooth/cc/*.java"],
+}
diff --git a/le_audio/packages/apps/Bluetooth/jni/Android.bp b/le_audio/packages/apps/Bluetooth/jni/Android.bp
new file mode 100644
index 0000000..1c50616
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/Android.bp
@@ -0,0 +1,52 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+cc_defaults {
+    name: "libbluetoothqti_jni_adva_default",
+    header_libs: ["libbluetooth_headers", "libnativehelper_header_only"],
+    include_dirs: [
+        "system/bt/types",
+        "vendor/qcom/opensource/commonsys/packages/apps/Bluetooth/jni",
+        "vendor/qcom/opensource/commonsys/bluetooth_ext/vhal/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_lea/vhal/include",
+        "vendor/qcom/opensource/commonsys-intf/bluetooth/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_lea/vhal/include",
+    ],
+    shared_libs: [
+        "libandroid_runtime",
+    ],
+}
+
+// BT LE Audio static library for target
+// ========================================================
+cc_library_static {
+    name: "libbluetoothqti_jni_adva",
+    defaults: ["libbluetoothqti_jni_adva_default"],
+    enabled: false,
+    srcs: [
+        "com_android_bluetooth_btservice_AdapterServiceExt.cpp",
+        "com_android_bluetooth_pacs_client.cpp",
+        "com_android_bluetooth_broadcast.cpp",
+        "com_android_bluetooth_csip_client.cpp",
+        "com_android_bluetooth_acm.cpp",
+        "com_android_bluetooth_apm.cpp",
+        "com_android_bluetooth_vcp_controller.cpp",
+        "com_android_bluetooth_mcp.cpp",
+        "com_android_bluetooth_cc.cpp",
+    ],
+}
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_acm.cpp b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_acm.cpp
new file mode 100644
index 0000000..900af0e
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_acm.cpp
@@ -0,0 +1,557 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+#define LOG_TAG "BluetoothAcmServiceJni"
+
+#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "com_android_bluetooth.h"
+#include "hardware/bt_acm.h"
+#include "utils/Log.h"
+
+#include <string.h>
+#include <shared_mutex>
+
+using bluetooth::bap::pacs::CodecIndex;
+using bluetooth::bap::pacs::CodecPriority;
+using bluetooth::bap::pacs::CodecSampleRate;
+using bluetooth::bap::pacs::CodecBPS;
+using bluetooth::bap::pacs::CodecChannelMode;
+
+namespace android {
+static jmethodID method_onConnectionStateChanged;
+static jmethodID method_onAudioStateChanged;
+static jmethodID method_onCodecConfigChanged;
+
+static struct {
+  jclass clazz;
+  jmethodID constructor;
+  jmethodID getCodecType;
+  jmethodID getCodecPriority;
+  jmethodID getSampleRate;
+  jmethodID getBitsPerSample;
+  jmethodID getChannelMode;
+  jmethodID getCodecSpecific1;
+  jmethodID getCodecSpecific2;
+  jmethodID getCodecSpecific3;
+  jmethodID getCodecSpecific4;
+} android_bluetooth_BluetoothCodecConfig;
+
+static const btacm_initiator_interface_t* sBluetoothAcmInterface = nullptr;
+static std::shared_timed_mutex interface_mutex;
+
+static jobject mCallbacksObj = nullptr;
+static std::shared_timed_mutex callbacks_mutex;
+
+static void btacm_connection_state_callback(const RawAddress& bd_addr,
+                                            btacm_connection_state_t state, uint16_t contextType) {
+  ALOGI("%s", __func__);
+
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+  ScopedLocalRef<jbyteArray> addr(
+      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+  if (!addr.get()) {
+    ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
+    return;
+  }
+
+  sCallbackEnv->SetByteArrayRegion(
+      addr.get(), 0, sizeof(RawAddress),
+      reinterpret_cast<const jbyte*>(bd_addr.address));
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
+                               addr.get(), (jint)state, (jint)contextType);
+}
+
+static void btacm_audio_state_callback(const RawAddress& bd_addr,
+                                       btacm_audio_state_t state, uint16_t contextType) {
+  ALOGI("%s", __func__);
+
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+  ScopedLocalRef<jbyteArray> addr(
+      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+  if (!addr.get()) {
+    ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
+    return;
+  }
+
+  sCallbackEnv->SetByteArrayRegion(
+      addr.get(), 0, sizeof(RawAddress),
+      reinterpret_cast<const jbyte*>(bd_addr.address));
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
+                               addr.get(), (jint)state, (jint)contextType);
+}
+
+static void btacm_audio_config_callback(
+    const RawAddress& bd_addr, CodecConfig codec_config,
+    std::vector<CodecConfig> codecs_local_capabilities,
+    std::vector<CodecConfig> codecs_selectable_capabilities, uint16_t contextType) {
+  ALOGI("%s", __func__);
+
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+  jobject codecConfigObj = sCallbackEnv->NewObject(
+      android_bluetooth_BluetoothCodecConfig.clazz,
+      android_bluetooth_BluetoothCodecConfig.constructor,
+      (jint)codec_config.codec_type, (jint)codec_config.codec_priority,
+      (jint)codec_config.sample_rate, (jint)codec_config.bits_per_sample,
+      (jint)codec_config.channel_mode, (jlong)codec_config.codec_specific_1,
+      (jlong)codec_config.codec_specific_2,
+      (jlong)codec_config.codec_specific_3,
+      (jlong)codec_config.codec_specific_4);
+
+  jsize i = 0;
+  jobjectArray local_capabilities_array = sCallbackEnv->NewObjectArray(
+      (jsize)codecs_local_capabilities.size(),
+      android_bluetooth_BluetoothCodecConfig.clazz, nullptr);
+  for (auto const& cap : codecs_local_capabilities) {
+    jobject capObj = sCallbackEnv->NewObject(
+        android_bluetooth_BluetoothCodecConfig.clazz,
+        android_bluetooth_BluetoothCodecConfig.constructor,
+        (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
+        (jint)cap.bits_per_sample, (jint)cap.channel_mode,
+        (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
+        (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
+    sCallbackEnv->SetObjectArrayElement(local_capabilities_array, i++, capObj);
+    sCallbackEnv->DeleteLocalRef(capObj);
+  }
+
+  i = 0;
+  jobjectArray selectable_capabilities_array = sCallbackEnv->NewObjectArray(
+      (jsize)codecs_selectable_capabilities.size(),
+      android_bluetooth_BluetoothCodecConfig.clazz, nullptr);
+  for (auto const& cap : codecs_selectable_capabilities) {
+    jobject capObj = sCallbackEnv->NewObject(
+        android_bluetooth_BluetoothCodecConfig.clazz,
+        android_bluetooth_BluetoothCodecConfig.constructor,
+        (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
+        (jint)cap.bits_per_sample, (jint)cap.channel_mode,
+        (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
+        (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
+    sCallbackEnv->SetObjectArrayElement(selectable_capabilities_array, i++,
+                                        capObj);
+    sCallbackEnv->DeleteLocalRef(capObj);
+  }
+
+  ScopedLocalRef<jbyteArray> addr(
+      sCallbackEnv.get(), sCallbackEnv->NewByteArray(RawAddress::kLength));
+  if (!addr.get()) {
+    ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
+    return;
+  }
+  sCallbackEnv->SetByteArrayRegion(
+      addr.get(), 0, RawAddress::kLength,
+      reinterpret_cast<const jbyte*>(bd_addr.address));
+
+  sCallbackEnv->CallVoidMethod(
+      mCallbacksObj, method_onCodecConfigChanged, addr.get(), codecConfigObj,
+      local_capabilities_array, selectable_capabilities_array, (jint)contextType);
+}
+
+static btacm_initiator_callbacks_t sBluetoothAcmCallbacks = {
+    sizeof(sBluetoothAcmCallbacks),
+    btacm_connection_state_callback,
+    btacm_audio_state_callback,
+    btacm_audio_config_callback
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+  jclass jniBluetoothCodecConfigClass =
+      env->FindClass("android/bluetooth/BluetoothCodecConfig");
+  android_bluetooth_BluetoothCodecConfig.constructor =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "<init>", "(IIIIIJJJJ)V");
+  android_bluetooth_BluetoothCodecConfig.getCodecType =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecType", "()I");
+  android_bluetooth_BluetoothCodecConfig.getCodecPriority =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecPriority", "()I");
+  android_bluetooth_BluetoothCodecConfig.getSampleRate =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getSampleRate", "()I");
+  android_bluetooth_BluetoothCodecConfig.getBitsPerSample =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getBitsPerSample", "()I");
+  android_bluetooth_BluetoothCodecConfig.getChannelMode =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getChannelMode", "()I");
+  android_bluetooth_BluetoothCodecConfig.getCodecSpecific1 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific1", "()J");
+  android_bluetooth_BluetoothCodecConfig.getCodecSpecific2 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific2", "()J");
+  android_bluetooth_BluetoothCodecConfig.getCodecSpecific3 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific3", "()J");
+  android_bluetooth_BluetoothCodecConfig.getCodecSpecific4 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J");
+
+  method_onConnectionStateChanged =
+      env->GetMethodID(clazz, "onConnectionStateChanged", "([BII)V");
+
+  method_onAudioStateChanged =
+      env->GetMethodID(clazz, "onAudioStateChanged", "([BII)V");
+
+  method_onCodecConfigChanged =
+      env->GetMethodID(clazz, "onCodecConfigChanged",
+                       "([BLandroid/bluetooth/BluetoothCodecConfig;"
+                       "[Landroid/bluetooth/BluetoothCodecConfig;"
+                       "[Landroid/bluetooth/BluetoothCodecConfig;I)V");
+
+  ALOGI("%s: succeeds", __func__);
+}
+
+static std::vector<CodecConfig> prepareCodecPreferences(
+    JNIEnv* env, jobject object, jobjectArray codecConfigArray) {
+  std::vector<CodecConfig> codec_preferences;
+
+  int numConfigs = env->GetArrayLength(codecConfigArray);
+  for (int i = 0; i < numConfigs; i++) {
+    jobject jcodecConfig = env->GetObjectArrayElement(codecConfigArray, i);
+    if (jcodecConfig == nullptr) continue;
+    if (!env->IsInstanceOf(jcodecConfig,
+                           android_bluetooth_BluetoothCodecConfig.clazz)) {
+      ALOGE("%s: Invalid BluetoothCodecConfig instance", __func__);
+      continue;
+    }
+    jint codecType = env->CallIntMethod(
+        jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecType);
+    jint codecPriority = env->CallIntMethod(
+        jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecPriority);
+    jint sampleRate = env->CallIntMethod(
+        jcodecConfig, android_bluetooth_BluetoothCodecConfig.getSampleRate);
+    jint bitsPerSample = env->CallIntMethod(
+        jcodecConfig, android_bluetooth_BluetoothCodecConfig.getBitsPerSample);
+    jint channelMode = env->CallIntMethod(
+        jcodecConfig, android_bluetooth_BluetoothCodecConfig.getChannelMode);
+    jlong codecSpecific1 = env->CallLongMethod(
+        jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific1);
+    jlong codecSpecific2 = env->CallLongMethod(
+        jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific2);
+    jlong codecSpecific3 = env->CallLongMethod(
+        jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific3);
+    jlong codecSpecific4 = env->CallLongMethod(
+        jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific4);
+
+    CodecConfig codec_config = {
+        .codec_type = static_cast<CodecIndex>(codecType),
+        .codec_priority =
+            static_cast<CodecPriority>(codecPriority),
+        .sample_rate = static_cast<CodecSampleRate>(sampleRate),
+        .bits_per_sample =
+            static_cast<CodecBPS>(bitsPerSample),
+        .channel_mode =
+            static_cast<CodecChannelMode>(channelMode),
+        .codec_specific_1 = codecSpecific1,
+        .codec_specific_2 = codecSpecific2,
+        .codec_specific_3 = codecSpecific3,
+        .codec_specific_4 = codecSpecific4};
+
+    codec_preferences.push_back(codec_config);
+  }
+  return codec_preferences;
+}
+
+static void initNative(JNIEnv* env, jobject object,
+                            jint maxConnectedAudioDevices,
+                            jobjectArray codecConfigArray) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    ALOGE("%s: Bluetooth module is not loaded", __func__);
+    return;
+  }
+
+  if (sBluetoothAcmInterface != nullptr) {
+    ALOGW("%s: Cleaning up ACM Interface before initializing...", __func__);
+    sBluetoothAcmInterface->cleanup();
+    sBluetoothAcmInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    ALOGW("%s: Cleaning up ACM callback object", __func__);
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+
+  if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
+    ALOGE("%s: Failed to allocate Global Ref for ACM Callbacks", __func__);
+    return;
+  }
+
+  android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef(
+      env->FindClass("android/bluetooth/BluetoothCodecConfig"));
+  if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) {
+    ALOGE("%s: Failed to allocate Global Ref for BluetoothCodecConfig class",
+          __func__);
+    return;
+  }
+
+  sBluetoothAcmInterface =
+      (btacm_initiator_interface_t*)btInf->get_profile_interface(
+          BT_PROFILE_ACM_ID);
+  if (sBluetoothAcmInterface == nullptr) {
+    ALOGE("%s: Failed to get Bluetooth ACM Interface", __func__);
+    return;
+  }
+
+  std::vector<CodecConfig> codec_priorities =
+      prepareCodecPreferences(env, object, codecConfigArray);
+
+  bt_status_t status = sBluetoothAcmInterface->init(
+      &sBluetoothAcmCallbacks, maxConnectedAudioDevices, codec_priorities);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed to initialize Bluetooth ACM, status: %d", __func__,
+          status);
+    sBluetoothAcmInterface = nullptr;
+    return;
+  }
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    ALOGE("%s: Bluetooth module is not loaded", __func__);
+    return;
+  }
+
+  if (sBluetoothAcmInterface != nullptr) {
+    sBluetoothAcmInterface->cleanup();
+    sBluetoothAcmInterface = nullptr;
+  }
+
+  env->DeleteGlobalRef(android_bluetooth_BluetoothCodecConfig.clazz);
+  android_bluetooth_BluetoothCodecConfig.clazz = nullptr;
+
+  if (mCallbacksObj != nullptr) {
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+}
+
+static jboolean connectAcmNative(JNIEnv* env, jobject object,
+                                         jbyteArray address, jint contextType,
+                                         jint profileType, jint preferredContext) {
+  ALOGI("%s: sBluetoothAcmInterface: %p", __func__, sBluetoothAcmInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothAcmInterface) {
+    ALOGE("%s: Failed to get the Bluetooth ACM Interface", __func__);
+    return JNI_FALSE;
+  }
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress bd_addr;
+  bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  bt_status_t status = sBluetoothAcmInterface->connect(bd_addr, contextType,
+                                                       profileType, preferredContext);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed ACM connection, status: %d", __func__, status);
+  }
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean disconnectAcmNative(JNIEnv* env, jobject object,
+                                             jbyteArray address, jint contextType) {
+  ALOGI("%s: sBluetoothAcmInterface: %p", __func__, sBluetoothAcmInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothAcmInterface) {
+    ALOGE("%s: Failed to get the Bluetooth ACM Interface", __func__);
+    return JNI_FALSE;
+  }
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress bd_addr;
+  bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  bt_status_t status = sBluetoothAcmInterface->disconnect(bd_addr, contextType);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed ACM disconnection, status: %d", __func__, status);
+  }
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean startStreamNative(JNIEnv* env, jobject object,
+                                          jbyteArray address, jint contextType) {
+  ALOGI("%s: sBluetoothAcmInterface: %p", __func__, sBluetoothAcmInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothAcmInterface) {
+    ALOGE("%s: Failed to get the Bluetooth ACM Interface", __func__);
+    return JNI_FALSE;
+  }
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+
+  RawAddress bd_addr = RawAddress::kEmpty;
+  if (addr) {
+    bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  }
+  bt_status_t status = sBluetoothAcmInterface->start_stream(bd_addr, contextType);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed ACM set_active_device, status: %d", __func__, status);
+  }
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean stopStreamNative(JNIEnv* env, jobject object,
+                                         jbyteArray address, jint contextType) {
+  ALOGI("%s: sBluetoothAcmInterface: %p", __func__, sBluetoothAcmInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothAcmInterface) {
+    ALOGE("%s: Failed to get the Bluetooth ACM Interface", __func__);
+    return JNI_FALSE;
+  }
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+
+  RawAddress bd_addr = RawAddress::kEmpty;
+  if (addr) {
+    bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  }
+  bt_status_t status = sBluetoothAcmInterface->stop_stream(bd_addr, contextType);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed ACM set_active_device, status: %d", __func__, status);
+  }
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setActiveDeviceNative(JNIEnv* env, jobject object,
+                                      jbyteArray address, jint contextType) {
+  ALOGI("%s: sBluetoothAcmInterface: %p", __func__, sBluetoothAcmInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothAcmInterface) {
+    ALOGE("%s: Failed to get the Bluetooth ACM Interface", __func__);
+    return JNI_FALSE;
+  }
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+
+  RawAddress bd_addr = RawAddress::kEmpty;
+  if (addr) {
+    bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  }
+  bt_status_t status = sBluetoothAcmInterface->set_active_device(bd_addr, contextType);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed ACM set_active_device, status: %d", __func__, status);
+  }
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object,
+                                                              jbyteArray address,
+                                                              jobjectArray codecConfigArray,
+                                                              jint contextType, jint preferredContext) {
+  ALOGI("%s: sBluetoothAcmInterface: %p", __func__, sBluetoothAcmInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothAcmInterface) {
+    ALOGE("%s: Failed to get the Bluetooth ACM Interface", __func__);
+    return JNI_FALSE;
+  }
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress bd_addr;
+  bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  std::vector<CodecConfig> codec_preferences =
+      prepareCodecPreferences(env, object, codecConfigArray);
+
+  bt_status_t status =
+      sBluetoothAcmInterface->config_codec(bd_addr, codec_preferences, contextType, preferredContext);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed codec configuration, status: %d", __func__, status);
+  }
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean ChangeCodecConfigPreferenceNative(JNIEnv* env, jobject object,
+                                                  jbyteArray address,
+                                                  jstring message) {
+  ALOGI("%s: sBluetoothAcmInterface: %p", __func__, sBluetoothAcmInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothAcmInterface) {
+    ALOGE("%s: Failed to get the Bluetooth ACM Interface", __func__);
+    return JNI_FALSE;
+  }
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress bd_addr;
+  bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  const char* c_msg = env->GetStringUTFChars(message, NULL);
+  bt_status_t status =
+      sBluetoothAcmInterface->change_config_codec(bd_addr, (char*)c_msg);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed codec configuration, status: %d", __func__, status);
+  }
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static JNINativeMethod sMethods[] = {
+    {"classInitNative", "()V", (void*)classInitNative},
+    {"initNative", "(I[Landroid/bluetooth/BluetoothCodecConfig;)V",
+     (void*)initNative},
+    {"cleanupNative", "()V", (void*)cleanupNative},
+    {"connectAcmNative", "([BIII)Z", (void*)connectAcmNative},
+    {"disconnectAcmNative", "([BI)Z", (void*)disconnectAcmNative},
+    {"startStreamNative", "([BI)Z", (void*)startStreamNative},
+    {"stopStreamNative", "([BI)Z", (void*)stopStreamNative},
+    {"setActiveDeviceNative", "([BI)Z", (void*)setActiveDeviceNative},
+    {"setCodecConfigPreferenceNative",
+     "([B[Landroid/bluetooth/BluetoothCodecConfig;II)Z",
+     (void*)setCodecConfigPreferenceNative},
+    {"ChangeCodecConfigPreferenceNative",
+     "([BLjava/lang/String;)Z",
+     (void*)ChangeCodecConfigPreferenceNative},
+};
+
+int register_com_android_bluetooth_acm(JNIEnv* env) {
+  return jniRegisterNativeMethods(
+      env, "com/android/bluetooth/acm/AcmNativeInterface", sMethods,
+      NELEM(sMethods));
+}
+}
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_apm.cpp b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_apm.cpp
new file mode 100644
index 0000000..6ffa2da
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_apm.cpp
@@ -0,0 +1,194 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+#define LOG_TAG "BluetoothAPM_Jni"
+
+#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "com_android_bluetooth.h"
+#include "hardware/bt_apm.h"
+#include "utils/Log.h"
+
+#include <string.h>
+#include <shared_mutex>
+
+namespace android {
+static jmethodID method_onGetActiveprofileCallback;
+
+static const bt_apm_interface_t* sBluetoothApmInterface = nullptr;
+static std::shared_timed_mutex interface_mutex;
+
+static jobject mCallbacksObj = nullptr;
+static std::shared_timed_mutex callbacks_mutex;
+
+
+static int btapm_active_profile_callback(const RawAddress& bd_addr, uint16_t audio_type)
+{
+  ALOGI("%s", __func__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return -1;
+
+  ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+  if (!addr.get()) {
+  ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
+  return -1;
+  }
+
+  sCallbackEnv->SetByteArrayRegion(
+          addr.get(), 0, sizeof(RawAddress),
+          reinterpret_cast<const jbyte*>(bd_addr.address));
+  return sCallbackEnv->CallIntMethod(mCallbacksObj, method_onGetActiveprofileCallback,
+                                                            addr.get(), (jint)audio_type);
+}
+
+
+static btapm_initiator_callbacks_t sBluetoothApmCallbacks = {
+        sizeof(sBluetoothApmCallbacks),
+        btapm_active_profile_callback
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+
+  ALOGI("%s: succeeds", __func__);
+  method_onGetActiveprofileCallback =
+     env->GetMethodID(clazz, "getActiveProfile", "([BI)I");
+}
+
+static bool initNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    ALOGE("%s: Bluetooth module is not loaded", __func__);
+    return JNI_FALSE;
+  }
+
+  if (sBluetoothApmInterface != nullptr) {
+    ALOGW("%s: Cleaning up APM Interface before initializing...", __func__);
+    sBluetoothApmInterface->cleanup();
+    sBluetoothApmInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    ALOGW("%s: Cleaning up APM callback object", __func__);
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+
+  if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
+    ALOGE("%s: Failed to allocate Global Ref for APM Callbacks", __func__);
+    return JNI_FALSE;
+  }
+
+  sBluetoothApmInterface =
+      (bt_apm_interface_t*)btInf->get_profile_interface(
+          BT_APM_MODULE_ID);
+  if (sBluetoothApmInterface == nullptr) {
+    ALOGE("%s: Failed to get Bluetooth APM Interface", __func__);
+    return JNI_FALSE;
+  }
+  bt_status_t status = sBluetoothApmInterface->init(&sBluetoothApmCallbacks);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed to initialize Bluetooth APM, status: %d", __func__,
+          status);
+    sBluetoothApmInterface = nullptr;
+    return JNI_FALSE;
+  }
+  return JNI_TRUE;
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    ALOGE("%s: Bluetooth module is not loaded", __func__);
+    return;
+  }
+
+  if (sBluetoothApmInterface != nullptr) {
+    sBluetoothApmInterface->cleanup();
+    sBluetoothApmInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+}
+
+static jboolean activeDeviceUpdateNative(JNIEnv* env, jobject object,
+                            jbyteArray address, jint profile, jint audio_type) {
+  ALOGI("%s: sBluetoothApmInterface: %p", __func__, sBluetoothApmInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothApmInterface) {
+    ALOGE("%s: Failed to get the Bluetooth APM Interface", __func__);
+    return JNI_FALSE;
+  }
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+
+  RawAddress bd_addr = RawAddress::kEmpty;
+  if (addr) {
+    bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  }
+  bt_status_t status = sBluetoothApmInterface->active_device_change(bd_addr, profile, audio_type);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed APM active_device_change, status: %d", __func__, status);
+  }
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setContentControlNative(JNIEnv* env, jobject object,
+                                      jint content_control_id, jint profile) {
+  ALOGI("%s: sBluetoothApmInterface: %p", __func__, sBluetoothApmInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothApmInterface) {
+    ALOGE("%s: Failed to get the Bluetooth APM Interface", __func__);
+    return JNI_FALSE;
+  }
+
+  bt_status_t status = sBluetoothApmInterface->set_content_control_id(content_control_id, profile);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed APM content control update, status: %d", __func__, status);
+  }
+
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static JNINativeMethod sMethods[] = {
+    {"classInitNative", "()V", (void*)classInitNative},
+    {"initNative", "()V", (void*)initNative},
+    {"cleanupNative", "()V", (void*)cleanupNative},
+    {"activeDeviceUpdateNative", "([BII)Z", (void*)activeDeviceUpdateNative},
+    {"setContentControlNative", "(II)Z", (void*)setContentControlNative},
+};
+
+int register_com_android_bluetooth_apm(JNIEnv* env) {
+  return jniRegisterNativeMethods(
+      env, "com/android/bluetooth/apm/ApmNativeInterface", sMethods,
+      NELEM(sMethods));
+}
+}
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_broadcast.cpp b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_broadcast.cpp
new file mode 100644
index 0000000..75ea085
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_broadcast.cpp
@@ -0,0 +1,470 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "BluetoothBapBroadcastServiceJni"
+
+#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "com_android_bluetooth.h"
+#include "hardware/bt_av.h"
+#include "hardware/bt_bap_ba.h"
+#include "utils/Log.h"
+
+#include <string.h>
+#include <shared_mutex>
+
+namespace android {
+static jmethodID method_onBroadcastStateChanged;
+static jmethodID method_onAudioStateChanged;
+static jmethodID method_onCodecConfigChanged;
+//static jmethodID method_onIsoDataPathChanged;
+static jmethodID method_onEncryptionKeyGenerated;
+static jmethodID method_onSetupBIG;
+static jmethodID method_onBroadcastIdGenerated;
+static struct {
+  jclass clazz;
+  jmethodID constructor;
+  jmethodID getCodecType;
+  jmethodID getCodecPriority;
+  jmethodID getSampleRate;
+  jmethodID getBitsPerSample;
+  jmethodID getChannelMode;
+  jmethodID getCodecSpecific1;
+  jmethodID getCodecSpecific2;
+  jmethodID getCodecSpecific3;
+  jmethodID getCodecSpecific4;
+} android_bluetooth_BluetoothCodecConfig;
+
+static const btbap_broadcast_interface_t* sBluetoothBapBroadcastInterface = nullptr;
+static std::shared_timed_mutex interface_mutex;
+
+static jobject mCallbacksObj = nullptr;
+static std::shared_timed_mutex callbacks_mutex;
+
+static void btbap_broadcast_state_callback(jint adv_id, btbap_broadcast_state_t state) {
+  ALOGI("%s", __func__);
+
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  ALOGI("%s: lock acquired", __func__);
+  CallbackEnv sCallbackEnv(__func__);
+  ALOGI("%s:got callback env", __func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
+    ALOGI("%s:either callback is not valid or callbackobj is null", __func__);
+    return;
+  }
+  ALOGI("%s: calling method to native interface", __func__);
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onBroadcastStateChanged, adv_id, (jint)state);
+}
+
+static void btbap_broadcast_audio_state_callback(jint big_handle, btbap_broadcast_audio_state_t state) {
+  ALOGI("%s", __func__);
+
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, big_handle, (jint)state);
+}
+
+static void btbap_broadcast_audio_config_callback(jint adv_id, btav_a2dp_codec_config_t codec_config,
+                                                             std::vector<btav_a2dp_codec_config_t> codec_capabilities) {
+  ALOGI("%s", __func__);
+
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+  jobject codecConfigObj = sCallbackEnv->NewObject(
+      android_bluetooth_BluetoothCodecConfig.clazz,
+      android_bluetooth_BluetoothCodecConfig.constructor,
+      (jint)codec_config.codec_type, (jint)codec_config.codec_priority,
+      (jint)codec_config.sample_rate, (jint)codec_config.bits_per_sample,
+      (jint)codec_config.channel_mode, (jlong)codec_config.codec_specific_1,
+      (jlong)codec_config.codec_specific_2,
+      (jlong)codec_config.codec_specific_3,
+      (jlong)codec_config.codec_specific_4);
+
+  jsize i = 0;
+  jobjectArray local_capabilities_array = sCallbackEnv->NewObjectArray(
+      (jsize)codec_capabilities.size(),
+      android_bluetooth_BluetoothCodecConfig.clazz, nullptr);
+  for (auto const& cap : codec_capabilities) {
+    jobject capObj = sCallbackEnv->NewObject(
+        android_bluetooth_BluetoothCodecConfig.clazz,
+        android_bluetooth_BluetoothCodecConfig.constructor,
+        (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
+        (jint)cap.bits_per_sample, (jint)cap.channel_mode,
+        (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
+        (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
+    sCallbackEnv->SetObjectArrayElement(local_capabilities_array, i++, capObj);
+    sCallbackEnv->DeleteLocalRef(capObj);
+  }
+
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCodecConfigChanged,
+                              adv_id, codecConfigObj, local_capabilities_array);
+}
+
+/*static void btbap_broadcast_iso_datapath_callback(jint big_handle, jint state) {
+  ALOGI("%s", __func__);
+
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onIsoDataPathChanged, big_handle, state);
+}*/
+
+static void btbap_broadcast_enckey_callback(std::string pin) {
+  ALOGI("%s", __func__);
+
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+  /*ScopedLocalRef<jbyteArray> pinkey(
+      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(pin)));
+  if (!addr.get()) {
+    ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
+    return;
+  }
+
+  sCallbackEnv->SetByteArrayRegion(
+      pinkey.get(), 0, sizeof(pin),
+      reinterpret_cast<const jbyte*>(pin));*/
+
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onEncryptionKeyGenerated,
+                                     sCallbackEnv->NewStringUTF(pin.c_str()));
+}
+
+static void btbap_broadcast_setup_big_callback(jint setup, jint adv_id, jint big_handle,
+                                       jint num_bises, std::vector<uint16_t> bis_handles) {
+  ALOGI("%s", __func__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+  ScopedLocalRef<jcharArray> jc(sCallbackEnv.get(), sCallbackEnv->NewCharArray(bis_handles.size()));
+  sCallbackEnv->SetCharArrayRegion(jc.get(), 0, bis_handles.size(), (jchar*) bis_handles.data());
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetupBIG, setup, adv_id, big_handle, num_bises, jc.get());
+}
+
+static void btbap_broadcast_bid_callback(std::vector<uint8_t> broadcast_id) {
+  ALOGI("%s", __func__);
+
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+  ALOGI("%s: broadcast_id size = %d",__func__,broadcast_id.size());
+  ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), sCallbackEnv->NewByteArray(broadcast_id.size()));
+  if (!jb.get()) {
+      ALOGI("%s:Failed to allocate byte array");
+      return;
+  }
+  sCallbackEnv->SetByteArrayRegion(jb.get(), 0, broadcast_id.size(), (jbyte*) broadcast_id.data());
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onBroadcastIdGenerated, jb.get());
+}
+
+static btbap_broadcast_callbacks_t sBluetoothBapBroadcastCallbacks = {
+    sizeof(sBluetoothBapBroadcastCallbacks),
+    btbap_broadcast_state_callback,
+    btbap_broadcast_audio_state_callback,
+    btbap_broadcast_audio_config_callback,
+    //btbap_broadcast_iso_datapath_callback,
+    btbap_broadcast_enckey_callback,
+    btbap_broadcast_setup_big_callback,
+    btbap_broadcast_bid_callback,
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+  jclass jniBluetoothCodecConfigClass =
+      env->FindClass("android/bluetooth/BluetoothCodecConfig");
+  android_bluetooth_BluetoothCodecConfig.constructor =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "<init>", "(IIIIIJJJJ)V");
+  android_bluetooth_BluetoothCodecConfig.getCodecType =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecType", "()I");
+  android_bluetooth_BluetoothCodecConfig.getCodecPriority =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecPriority", "()I");
+  android_bluetooth_BluetoothCodecConfig.getSampleRate =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getSampleRate", "()I");
+  android_bluetooth_BluetoothCodecConfig.getBitsPerSample =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getBitsPerSample", "()I");
+  android_bluetooth_BluetoothCodecConfig.getChannelMode =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getChannelMode", "()I");
+  android_bluetooth_BluetoothCodecConfig.getCodecSpecific1 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific1", "()J");
+  android_bluetooth_BluetoothCodecConfig.getCodecSpecific2 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific2", "()J");
+  android_bluetooth_BluetoothCodecConfig.getCodecSpecific3 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific3", "()J");
+  android_bluetooth_BluetoothCodecConfig.getCodecSpecific4 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J");
+
+  method_onBroadcastStateChanged =
+      env->GetMethodID(clazz, "onBroadcastStateChanged", "(II)V");
+
+  method_onAudioStateChanged =
+      env->GetMethodID(clazz, "onAudioStateChanged", "(II)V");
+
+  method_onCodecConfigChanged =
+      env->GetMethodID(clazz, "onCodecConfigChanged",
+                       "(ILandroid/bluetooth/BluetoothCodecConfig;"
+                       "[Landroid/bluetooth/BluetoothCodecConfig;)V");
+
+//  method_onIsoDataPathChanged =
+//      env->GetMethodID(clazz, "onIsoDataPathChanged","(II)V");
+  method_onEncryptionKeyGenerated =
+      env->GetMethodID(clazz, "onEncryptionKeyGenerated", "(Ljava/lang/String;)V");
+
+  method_onSetupBIG = env->GetMethodID(clazz, "onSetupBIG", "(IIII[C)V");
+  method_onBroadcastIdGenerated = env->GetMethodID(clazz, "onBroadcastIdGenerated", "([B)V");
+
+  ALOGI("%s: succeeds", __func__);
+}
+static btav_a2dp_codec_config_t prepare_codec_config(
+              JNIEnv* env, jobject object,jobject jcodecConfig) {
+
+  /*if (!env->IsInstanceOf(jcodecConfig,
+                           android_bluetooth_BluetoothCodecConfig.clazz)) {
+      ALOGE("%s: Invalid BluetoothCodecConfig instance", __func__);
+      return ((btav_a2dp_codec_config_t)NULL);
+  }*/
+  jint codecType = env->CallIntMethod(
+    jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecType);
+  jint codecPriority = env->CallIntMethod(
+    jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecPriority);
+  jint sampleRate = env->CallIntMethod(
+    jcodecConfig, android_bluetooth_BluetoothCodecConfig.getSampleRate);
+  jint bitsPerSample = env->CallIntMethod(
+    jcodecConfig, android_bluetooth_BluetoothCodecConfig.getBitsPerSample);
+  jint channelMode = env->CallIntMethod(
+    jcodecConfig, android_bluetooth_BluetoothCodecConfig.getChannelMode);
+  jlong codecSpecific1 = env->CallLongMethod(
+    jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific1);
+  jlong codecSpecific2 = env->CallLongMethod(
+    jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific2);
+  jlong codecSpecific3 = env->CallLongMethod(
+    jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific3);
+  jlong codecSpecific4 = env->CallLongMethod(
+    jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific4);
+
+  btav_a2dp_codec_config_t codec_config = {
+         .codec_type = static_cast<btav_a2dp_codec_index_t>(codecType),
+         .codec_priority =
+             static_cast<btav_a2dp_codec_priority_t>(codecPriority),
+         .sample_rate = static_cast<btav_a2dp_codec_sample_rate_t>(sampleRate),
+         .bits_per_sample =
+             static_cast<btav_a2dp_codec_bits_per_sample_t>(bitsPerSample),
+         .channel_mode =
+             static_cast<btav_a2dp_codec_channel_mode_t>(channelMode),
+         .codec_specific_1 = codecSpecific1,
+         .codec_specific_2 = codecSpecific2,
+         .codec_specific_3 = codecSpecific3,
+         .codec_specific_4 = codecSpecific4};
+  return codec_config;
+}
+
+static void initNative(JNIEnv* env, jobject object,
+                       jint maxBroadcast, jobject codecConfig, jint mode) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    ALOGE("%s: Bluetooth module is not loaded", __func__);
+    return;
+  }
+
+  if (sBluetoothBapBroadcastInterface != nullptr) {
+    ALOGW("%s: Cleaning up BapBroadcast Interface before initializing...", __func__);
+    sBluetoothBapBroadcastInterface->cleanup();
+    sBluetoothBapBroadcastInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    ALOGW("%s: Cleaning up BapBroadcast callback object", __func__);
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+
+  if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
+    ALOGE("%s: Failed to allocate Global Ref for bap broadcast Callbacks", __func__);
+    return;
+  }
+
+  android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef(
+      env->FindClass("android/bluetooth/BluetoothCodecConfig"));
+  if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) {
+    ALOGE("%s: Failed to allocate Global Ref for BluetoothCodecConfig class",
+          __func__);
+    return;
+  }
+
+  sBluetoothBapBroadcastInterface =
+      (btbap_broadcast_interface_t*)btInf->get_profile_interface(
+          BT_PROFILE_BAP_BROADCAST_ID);
+  if (sBluetoothBapBroadcastInterface == nullptr) {
+    ALOGE("%s: Failed to get Bluetooth BapBroadcast Interface", __func__);
+    return;
+  }
+  btav_a2dp_codec_config_t codec_config =
+                 prepare_codec_config(env, object, codecConfig);
+  /*if (codec_config == NULL) {
+    ALOGE("%s:Invalid codec config",__func__);
+    return;
+  }*/
+  bt_status_t status = sBluetoothBapBroadcastInterface->init(
+      &sBluetoothBapBroadcastCallbacks, maxBroadcast, codec_config, mode);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed to initialize Bluetooth A2DP, status: %d", __func__,
+          status);
+    sBluetoothBapBroadcastInterface = nullptr;
+    return;
+  }
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    ALOGE("%s: Bluetooth module is not loaded", __func__);
+    return;
+  }
+
+  if (sBluetoothBapBroadcastInterface != nullptr) {
+    sBluetoothBapBroadcastInterface->cleanup();
+    sBluetoothBapBroadcastInterface = nullptr;
+  }
+
+  env->DeleteGlobalRef(android_bluetooth_BluetoothCodecConfig.clazz);
+  android_bluetooth_BluetoothCodecConfig.clazz = nullptr;
+
+  if (mCallbacksObj != nullptr) {
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+}
+
+static jboolean setActiveDeviceNative(JNIEnv* env, jobject object,
+                                         jboolean enable, jint adv_id) {
+  ALOGI("%s: sBluetoothBapBroadcastInterface: %p", __func__, sBluetoothBapBroadcastInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothBapBroadcastInterface) {
+    ALOGE("%s: Failed to get the BapBroadcast Interface", __func__);
+    return JNI_FALSE;
+  }
+  bt_status_t status = sBluetoothBapBroadcastInterface->set_broadcast_active(enable, adv_id);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean enableBroadcastNative(JNIEnv* env, jobject object, jobject codecConfig) {
+  ALOGI("%s: sBluetoothBapBroadcastInterface: %p", __func__, sBluetoothBapBroadcastInterface);
+  btav_a2dp_codec_config_t codec_config =
+               prepare_codec_config(env, object, codecConfig);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothBapBroadcastInterface) {
+    ALOGE("%s: Failed to get the BapBroadcast Interface", __func__);
+    return JNI_FALSE;
+  }
+  bt_status_t status = sBluetoothBapBroadcastInterface->enable_broadcast(codec_config);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean disableBroadcastNative(JNIEnv* env, jobject object, jint adv_id) {
+  ALOGI("%s: sBluetoothBapBroadcastInterface: %p", __func__, sBluetoothBapBroadcastInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothBapBroadcastInterface) {
+    ALOGE("%s: Failed to get the BapBroadcast Interface", __func__);
+    return JNI_FALSE;
+  }
+  bt_status_t status = sBluetoothBapBroadcastInterface->disable_broadcast(adv_id);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setupAudioPathNative(JNIEnv* env, jobject object, jboolean enable,jint adv_id,
+                                               jint big_handle, jint num_bises, jintArray bises) {
+  ALOGI("%s: sBluetoothBapBroadcastInterface: %p", __func__, sBluetoothBapBroadcastInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothBapBroadcastInterface) {
+    ALOGE("%s: Failed to get the BapBroadcast Interface", __func__);
+    return JNI_FALSE;
+  }
+  jint* bis_handles = env->GetIntArrayElements(bises, NULL);
+  bt_status_t status = sBluetoothBapBroadcastInterface->setup_audiopath(enable, adv_id, big_handle, num_bises, bis_handles);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jstring getEncryptionKeyNative(JNIEnv* env, jobject object) {
+  ALOGI("%s: sBluetoothBapBroadcastInterface: %p", __func__, sBluetoothBapBroadcastInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothBapBroadcastInterface) {
+    ALOGE("%s: Failed to get the BapBroadcast Interface", __func__);
+    return JNI_FALSE;
+  }
+  std::string stdstr = sBluetoothBapBroadcastInterface->get_encryption_key();
+  return env->NewStringUTF(stdstr.c_str());
+}
+
+static jboolean setEncryptionKeyNative(JNIEnv* env, jobject object, jboolean enabled, jint length) {
+  ALOGI("%s: sBluetoothBapBroadcastInterface: %p", __func__, sBluetoothBapBroadcastInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothBapBroadcastInterface) {
+    ALOGE("%s: Failed to get the BapBroadcast Interface", __func__);
+    return JNI_FALSE;
+  }
+  bt_status_t status = sBluetoothBapBroadcastInterface->set_encryption(enabled, length);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object,
+                                                       jint adv_handle, jobject codecConfig) {
+  ALOGI("%s: sBluetoothBapBroadcastInterface: %p", __func__, sBluetoothBapBroadcastInterface);
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sBluetoothBapBroadcastInterface) {
+    ALOGE("%s: Failed to get the BapBroadcast Interface", __func__);
+    return JNI_FALSE;
+  }
+  btav_a2dp_codec_config_t codec_config =
+               prepare_codec_config(env, object, codecConfig);
+  bt_status_t status = sBluetoothBapBroadcastInterface->codec_config_change(adv_handle, codec_config);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static JNINativeMethod sMethods[] = {
+    {"classInitNative", "()V", (void*)classInitNative},
+    {"initNative", "(ILandroid/bluetooth/BluetoothCodecConfig;I)V",
+     (void*)initNative},
+    {"cleanupNative", "()V", (void*)cleanupNative},
+    {"setActiveDeviceNative", "(ZI)Z", (void*)setActiveDeviceNative},
+    {"enableBroadcastNative", "(Landroid/bluetooth/BluetoothCodecConfig;)Z", (void*)enableBroadcastNative},
+    {"disableBroadcastNative", "(I)Z", (void*)disableBroadcastNative},
+    {"getEncryptionKeyNative", "()Ljava/lang/String;", (void*)getEncryptionKeyNative},
+    {"setEncryptionKeyNative", "(ZI)Z", (void*)setEncryptionKeyNative},
+    {"setupAudioPathNative", "(ZIII[I)Z", (void*)setupAudioPathNative},
+    {"setCodecConfigPreferenceNative",
+     "(ILandroid/bluetooth/BluetoothCodecConfig;)Z",
+     (void*)setCodecConfigPreferenceNative},
+};
+
+int register_com_android_bluetooth_bap_broadcast(JNIEnv* env) {
+  return jniRegisterNativeMethods(
+      env, "com/android/bluetooth/broadcast/BroadcastNativeInterface", sMethods,
+      NELEM(sMethods));
+}
+
+}
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterServiceExt.cpp b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterServiceExt.cpp
new file mode 100644
index 0000000..f763310
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterServiceExt.cpp
@@ -0,0 +1,76 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 "android_runtime/AndroidRuntime.h"
+#include "com_android_bluetooth_ext.h"
+
+namespace android {
+
+  int register_com_android_bluetooth_adv_audio_profiles(JNIEnv* env) {
+    ALOGE("%s", __func__);
+
+    int status = android::register_com_android_bluetooth_csip_client(env);
+    if (status < 0) {
+      ALOGE("jni csip registration failure: %d", status);
+      return JNI_ERR;
+    }
+
+    status = android::register_com_android_bluetooth_acm(env);
+    if (status < 0) {
+      ALOGE("jni acm registration failure: %d", status);
+      return JNI_ERR;
+    }
+
+    status = android::register_com_android_bluetooth_apm(env);
+    if (status < 0) {
+      ALOGE("jni APM registration failure: %d", status);
+      return JNI_ERR;
+    }
+
+    status = android::register_com_android_bluetooth_bap_broadcast(env);
+    if (status < 0) {
+      ALOGE("jni bap broadcast registration failure: %d", status);
+      return JNI_ERR;
+    }
+
+    status = android::register_com_android_bluetooth_vcp_controller(env);
+    if (status < 0) {
+      ALOGE("jni vcp controller registration failure: %d", status);
+      return JNI_ERR;
+    }
+
+    status = android::register_com_android_bluetooth_pacs_client(env);
+    if (status < 0) {
+      ALOGE("jni pacs client registration failure: %d", status);
+      return JNI_ERR;
+    }
+    status = android::register_com_android_bluetooth_call_controller(env);
+    if (status < 0) {
+      ALOGE("jni CC registration failure: %d", status);
+      return JNI_ERR;
+    }
+
+    status = android::register_com_android_bluetooth_mcp(env);
+    if (status < 0) {
+      ALOGE("jni mcp registration failure: %d", status);
+      return JNI_ERR;
+    }
+    return JNI_VERSION_1_6;
+  }
+}
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_cc.cpp b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_cc.cpp
new file mode 100644
index 0000000..e8203ea
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_cc.cpp
@@ -0,0 +1,402 @@
+/*
+ *Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BluetoothCCServiceJni"
+
+#define LOG_NDEBUG 0
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <map>
+#include <mutex>
+#include <shared_mutex>
+#include <vector>
+
+#include "android_runtime/AndroidRuntime.h"
+#include "base/logging.h"
+#include "com_android_bluetooth.h"
+#include "hardware/bluetooth.h"
+#include "hardware/bluetooth_callcontrol_callbacks.h"
+#include "hardware/bluetooth_callcontrol_interface.h"
+
+using bluetooth::call_control::CallControllerCallbacks;
+using bluetooth::call_control::CallControllerInterface;
+using bluetooth::Uuid;
+static CallControllerInterface* sCallControllerInterface = nullptr;
+
+namespace android {
+static jmethodID method_CallControlInitializedCallback;
+static jmethodID method_OnConnectionStateChanged;
+static jmethodID method_CallControlPointChangedRequest;
+static std::shared_timed_mutex interface_mutex;
+static jobject mCallbacksObj = nullptr;
+static std::shared_timed_mutex callbacks_mutex;
+
+class CallControllerCallbacksImpl : public CallControllerCallbacks {
+   public:
+  ~CallControllerCallbacksImpl() = default;
+  void CallControlInitializedCallback(uint8_t state) override {
+    LOG(INFO) << __func__;
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_CallControlInitializedCallback,
+                                 (jint)state);
+  }
+  void ConnectionStateCallback(uint8_t state, const RawAddress& bd_addr) override {
+    LOG(INFO) << __func__;
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
+      return;
+    }
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&bd_addr);
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_OnConnectionStateChanged,
+                                 (jint)state, addr.get());
+  }
+
+  void CallControlCallback(uint8_t op, std::vector<int32_t> p_indices, int count, std::vector<uint8_t> uri_data, const RawAddress& bd_addr) override {
+    LOG(INFO) << __func__;
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
+      return;
+    }
+    ScopedLocalRef<jintArray> indices(sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(count));
+    ScopedLocalRef<jbyteArray> originate_uri(
+    sCallbackEnv.get(), sCallbackEnv->NewByteArray(uri_data.size()));
+    if (!originate_uri.get()) {
+        ALOGE("Error while allocation byte array for uri data in %s", __func__);
+        return;
+    }
+    sCallbackEnv->SetByteArrayRegion(originate_uri.get(), 0, uri_data.size(),
+           (jbyte*)uri_data.data());
+
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&bd_addr);
+    sCallbackEnv->SetIntArrayRegion(indices.get(), 0, count,(jint*)p_indices.data());
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_CallControlPointChangedRequest,
+                                  (jint)op, indices.get(), (jint)count, originate_uri.get(), addr.get());
+  }
+};
+
+static CallControllerCallbacksImpl sCallControllerCallbacks;
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+  method_CallControlInitializedCallback =
+      env->GetMethodID(clazz, "callControlInitializedCallback", "(I)V");
+  method_OnConnectionStateChanged =
+      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
+  method_CallControlPointChangedRequest =
+      env->GetMethodID(clazz, "callControlPointChangedRequest", "(I[II[B[B)V");
+
+  LOG(INFO) << __func__ << " : succeeds";
+}
+
+static void initializeNative(JNIEnv* env, jobject object, jstring uuid,
+                      jint max_ccs_clients, jboolean inband_ringing_enabled) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (!btInf) {
+    ALOGE("%s: Bluetooth module is not loaded", __func__);
+    jniThrowIOException(env, EINVAL);
+    return;
+  }
+
+  if (sCallControllerInterface) {
+    ALOGI("%s: Cleaning up Bluetooth CallControl Interface before initializing",
+          __func__);
+    sCallControllerInterface->Cleanup();
+    sCallControllerInterface = nullptr;
+  }
+
+  if (mCallbacksObj) {
+    ALOGI("%s: Cleaning up Bluetooth CallControl callback object", __func__);
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+
+  const char* _uuid = env->GetStringUTFChars(uuid, nullptr);
+
+  sCallControllerInterface =
+      (CallControllerInterface*)btInf->get_profile_interface(
+          BT_PROFILE_CC_ID);
+  if (!sCallControllerInterface) {
+    ALOGW("%s: Failed to get Bluetooth CallControl Interface", __func__);
+    jniThrowIOException(env, EINVAL);
+    return;
+  }
+  bt_status_t status =
+      sCallControllerInterface->Init(&sCallControllerCallbacks,
+                                   bluetooth::Uuid::FromString(_uuid), max_ccs_clients, inband_ringing_enabled);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("%s: Failed to initialize LE audio Call control Interface, status: %d",
+          __func__, status);
+    sCallControllerInterface = nullptr;
+    return;
+  }
+
+  env->ReleaseStringUTFChars(uuid, _uuid);
+  mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    LOG(ERROR) << "Bluetooth module is not loaded";
+    return;
+  }
+
+  if (sCallControllerInterface != nullptr) {
+    sCallControllerInterface->Cleanup();
+    sCallControllerInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+}
+
+static jboolean updateBearerNameNative(JNIEnv* env, jobject object,
+                                   jstring operator_str) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) {
+    ALOGW("%s: sCallControllerInterface is null", __func__);
+    return JNI_FALSE;
+  }
+  const char* operator_name = env->GetStringUTFChars(operator_str, nullptr);
+  bt_status_t status =
+      sCallControllerInterface->UpdateBearerName((uint8_t*)operator_name);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("Failed updateBearerNameNative, status: %d", status);
+  }
+  env->ReleaseStringUTFChars(operator_str, operator_name);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean updateBearerTechnologyNative(JNIEnv* env, jobject object,
+                                 jint bearer_tech) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) {
+    ALOGW("%s: sCallControllerInterface is null", __func__);
+    return JNI_FALSE;
+  }
+  bt_status_t status = sCallControllerInterface->UpdateBearerTechnology(bearer_tech);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("Failed updateBearerTechnologyNative, status: %d", status);
+  }
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean updateSupportedBearerListNative(JNIEnv* env, jobject object,
+                                 jstring bearer_list) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) {
+    ALOGW("%s: sCallControllerInterface is null", __func__);
+    return JNI_FALSE;
+  }
+  const char* list_bearer_string = env->GetStringUTFChars(bearer_list, nullptr);
+  bt_status_t status = sCallControllerInterface->UpdateSupportedBearerList((uint8_t*)list_bearer_string);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("Failed updateSupportedBearerListNative, status: %d", status);
+  }
+  env->ReleaseStringUTFChars(bearer_list, list_bearer_string);
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+
+static jboolean callControlPointOpcodeSupportedNative(JNIEnv* env, jobject object,
+                                                 jint feature) {
+   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+   if (!sCallControllerInterface) {
+     ALOGW("%s: sCallControllerInterface is null", __func__);
+     return JNI_FALSE;
+   }
+   bt_status_t status = sCallControllerInterface->CallControlOptionalOpSupported(feature);
+   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean updateStatusFlagsNative(JNIEnv* env, jobject object,
+                                         jint flags) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) {
+    ALOGW("%s: sCallControllerInterface is null", __func__);
+    return JNI_FALSE;
+  }
+  bt_status_t status = sCallControllerInterface->UpdateStatusFlags(flags);
+
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean updateSignalStatusNative(JNIEnv* env, jobject object,
+                                          jint signal) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) {
+    ALOGW("%s: sCallControllerInterface is null", __func__);
+    return JNI_FALSE;
+  }
+  bt_status_t status = sCallControllerInterface->UpdateSignalStatus(signal);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("FAILED updateSignalStatusNative, status: %d", status);
+  }
+  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+static jboolean updateIncomingCallNative(JNIEnv* env, jobject object,
+                                          jint index, jstring uri_str) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) {
+    ALOGW("%s: sCallControllerInterface is null", __func__);
+    return JNI_FALSE;
+  }
+  const char* uri = env->GetStringUTFChars(uri_str, nullptr);
+  sCallControllerInterface->UpdateIncomingCall(index, (uint8_t*)uri);
+  env->ReleaseStringUTFChars(uri_str, uri);
+  return JNI_TRUE;
+}
+
+static jboolean callControlResponseNative(JNIEnv* env, jobject object,
+                          jint op, jint index, jint status, jbyteArray address) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) {
+    ALOGW("%s: sCallControllerInterface is null", __func__);
+    return JNI_FALSE;
+  }
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+   jniThrowIOException(env, EINVAL);
+   return JNI_FALSE;
+  }
+  RawAddress* tmpraw = (RawAddress*)addr;
+  bt_status_t ret_status =
+      sCallControllerInterface->CallControlResponse(op, index, status, *tmpraw);
+  if (ret_status != BT_STATUS_SUCCESS) {
+    ALOGE("Failed to send callControlResponseNative, status: %d", ret_status);
+  }
+  return (ret_status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setActiveDeviceNative(JNIEnv* env, jobject object,
+                                          jint set_id, jbyteArray address) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) return JNI_FALSE;
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+   jniThrowIOException(env, EINVAL);
+   return JNI_FALSE;
+  }
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sCallControllerInterface->SetActiveDevice(*tmpraw, set_id);
+  env->ReleaseByteArrayElements(address, addr, 0);
+
+  return JNI_TRUE;
+}
+
+static jboolean callStateNative(JNIEnv* env, jobject object, jint len,
+                   jbyteArray callList) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+
+  jbyte* cList = env->GetByteArrayElements(callList, NULL);
+  if (!cList) {
+   jniThrowIOException(env, EINVAL);
+   return JNI_FALSE;
+  }
+  uint16_t array_len = (uint16_t)env->GetArrayLength(callList);
+
+  std::vector<uint8_t> vect_val(cList, cList + array_len);
+
+  if (!sCallControllerInterface) {
+    ALOGW("%s: sCallControllerInterface is null", __func__);
+    return JNI_FALSE;
+  }
+  sCallControllerInterface->CallState(len, std::move(vect_val));
+  env->ReleaseByteArrayElements(callList, cList, 0);
+  return JNI_TRUE;
+}
+
+static jboolean contentControlIdNative(JNIEnv* env, jobject object,
+                                           jint ccid) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) return JNI_FALSE;
+
+  sCallControllerInterface->ContentControlId(ccid);
+  return JNI_TRUE;
+}
+
+static jboolean disconnectNative(JNIEnv* env, jobject object,
+                                           jbyteArray address) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sCallControllerInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+   jniThrowIOException(env, EINVAL);
+   return JNI_FALSE;
+  }
+  RawAddress* tmpraw = (RawAddress*)addr;
+
+  sCallControllerInterface->Disconnect(*tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static JNINativeMethod sMethods[] = {
+    {"classInitNative", "()V", (void*)classInitNative},
+    {"initializeNative", "(Ljava/lang/String;IZ)V", (void*)initializeNative},
+    {"cleanupNative", "()V", (void*)cleanupNative},
+    {"updateBearerNameNative", "(Ljava/lang/String;)Z", (void*)updateBearerNameNative},
+    {"updateBearerTechnologyNative", "(I)Z", (void*)updateBearerTechnologyNative},
+    {"updateSupportedBearerListNative", "(Ljava/lang/String;)Z", (void*)updateSupportedBearerListNative},
+    {"updateSignalStatusNative", "(I)Z", (void*)updateSignalStatusNative},
+    {"updateStatusFlagsNative", "(I)Z", (void*)updateStatusFlagsNative},
+    {"updateIncomingCallNative", "(ILjava/lang/String;)Z", (void*)updateIncomingCallNative},
+    {"callControlResponseNative", "(III[B)Z", (void*)callControlResponseNative},
+    {"callStateNative", "(I[B)Z", (void*)callStateNative},
+    {"callControlPointOpcodeSupportedNative", "(I)Z", (void*)callControlPointOpcodeSupportedNative},
+    {"setActiveDeviceNative", "(I[B)Z", (void*)setActiveDeviceNative},
+    {"contentControlIdNative", "(I)Z", (void*)contentControlIdNative},
+    {"disconnectNative", "([B)Z", (void*)disconnectNative},
+};
+
+int register_com_android_bluetooth_call_controller(JNIEnv* env) {
+  return jniRegisterNativeMethods(
+      env, "com/android/bluetooth/cc/CCNativeInterface",
+      sMethods, NELEM(sMethods));
+}
+}  // namespace android
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_csip_client.cpp b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_csip_client.cpp
new file mode 100644
index 0000000..c3a8f6d
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_csip_client.cpp
@@ -0,0 +1,409 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "BluetoothCsipClientJni"
+
+#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "com_android_bluetooth.h"
+#include "hardware/bt_csip.h"
+#include "utils/Log.h"
+
+#include <shared_mutex>
+#include <string.h>
+
+using bluetooth::Uuid;
+
+#define UUID_PARAMS(uuid) uuid_lsb(uuid), uuid_msb(uuid)
+
+static uint64_t uuid_lsb(const Uuid& uuid) {
+  uint64_t lsb = 0;
+
+  auto uu = uuid.To128BitBE();
+  for (int i = 8; i <= 15; i++) {
+    lsb <<= 8;
+    lsb |= uu[i];
+  }
+
+  return lsb;
+}
+
+static uint64_t uuid_msb(const Uuid& uuid) {
+  uint64_t msb = 0;
+
+  auto uu = uuid.To128BitBE();
+  for (int i = 0; i <= 7; i++) {
+    msb <<= 8;
+    msb |= uu[i];
+  }
+
+  return msb;
+}
+
+static Uuid from_java_uuid(jlong uuid_msb, jlong uuid_lsb) {
+  std::array<uint8_t, Uuid::kNumBytes128> uu{};
+  for (int i = 0; i < 8; i++) {
+    uu[7 - i] = (uuid_msb >> (8 * i)) & 0xFF;
+    uu[15 - i] = (uuid_lsb >> (8 * i)) & 0xFF;
+  }
+  return Uuid::From128BitBE(uu);
+}
+
+static RawAddress str2addr(JNIEnv* env, jstring address) {
+  RawAddress bd_addr;
+  const char* c_address = env->GetStringUTFChars(address, NULL);
+  if (!c_address) return bd_addr;
+
+  RawAddress::FromString(std::string(c_address), bd_addr);
+  env->ReleaseStringUTFChars(address, c_address);
+
+  return bd_addr;
+}
+
+namespace android {
+static jmethodID method_onCsipAppRegistered;
+static jmethodID method_onConnectionStateChanged;
+static jmethodID method_onNewSetFound;
+static jmethodID method_onNewSetMemberFound;
+static jmethodID method_onLockStatusChanged;
+static jmethodID method_onLockAvailable;
+static jmethodID method_onSetSirkChanged;
+static jmethodID method_onSetSizeChanged;
+
+static const btcsip_interface_t* sBluetoothCsipInterface = NULL;
+static jobject mCallbacksObj = NULL;
+static std::shared_timed_mutex mCallbacks_mutex;
+
+static jstring bdaddr2newjstr(JNIEnv* env, const RawAddress* bda) {
+  char c_address[32];
+  snprintf(c_address, sizeof(c_address), "%02X:%02X:%02X:%02X:%02X:%02X",
+           bda->address[0], bda->address[1], bda->address[2], bda->address[3],
+           bda->address[4], bda->address[5]);
+
+  return env->NewStringUTF(c_address);
+}
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+  method_onCsipAppRegistered =
+      env->GetMethodID(clazz, "onCsipAppRegistered", "(IIJJ)V");
+
+  method_onConnectionStateChanged =
+      env->GetMethodID(clazz, "onConnectionStateChanged", "(ILjava/lang/String;II)V");
+
+  method_onNewSetFound =
+      env->GetMethodID(clazz, "onNewSetFound", "(ILjava/lang/String;I[BJJZ)V");
+
+  method_onNewSetMemberFound =
+      env->GetMethodID(clazz, "onNewSetMemberFound", "(ILjava/lang/String;)V");
+
+  method_onLockStatusChanged =
+      env->GetMethodID(clazz, "onLockStatusChanged", "(IIII[Ljava/lang/String;)V");
+
+  method_onLockAvailable =
+      env->GetMethodID(clazz, "onLockAvailable", "(IILjava/lang/String;)V");
+
+  method_onSetSirkChanged =
+      env->GetMethodID(clazz, "onSetSirkChanged", "(I[BLjava/lang/String;)V");
+
+  method_onSetSizeChanged =
+      env->GetMethodID(clazz, "onSetSizeChanged", "(IILjava/lang/String;)V");
+
+  ALOGI("%s: succeeds", __func__);
+}
+
+static void csip_app_registered_callback(uint8_t status, uint8_t app_id,
+                                         const bluetooth::Uuid& uuid){
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid()) return;
+
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCsipAppRegistered,
+                               status, app_id, UUID_PARAMS(uuid));
+}
+
+static void connection_state_changed_callback(uint8_t app_id, RawAddress& addr,
+                                              uint8_t state, uint8_t status){
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid()) return;
+
+  // address
+  ScopedLocalRef<jstring> address(sCallbackEnv.get(),
+                                  bdaddr2newjstr(sCallbackEnv.get(), &addr));
+
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
+                               app_id, address.get(), state, status);
+}
+
+static void new_set_found_callback(uint8_t set_id, RawAddress& bd_addr, uint8_t size,
+                                   uint8_t* sirk, const bluetooth::Uuid& p_srvc_uuid,
+                                   bool lock_support) {
+  ALOGI("%s: ", __func__);
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid()) return;
+
+  // address
+  ScopedLocalRef<jstring> address(sCallbackEnv.get(),
+                                  bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
+
+  ALOGI("%s: new_set_found_callback: process sirk", __func__);
+  ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), NULL);
+  jb.reset(sCallbackEnv->NewByteArray(16));
+  sCallbackEnv->SetByteArrayRegion(jb.get(), 0, 16, (jbyte*)sirk);
+
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNewSetFound, set_id, address.get(),
+                               size, jb.get(), UUID_PARAMS(p_srvc_uuid), lock_support);
+}
+
+static void new_set_member_found_cb(uint8_t set_id, RawAddress& bd_addr) {
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid()) return;
+
+  // address
+  ScopedLocalRef<jstring> address(sCallbackEnv.get(),
+                                  bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNewSetMemberFound, set_id, address.get());
+}
+
+/** Callback for lock status changed event from stack
+ */
+static void lock_state_changed_callback(uint8_t app_id, uint8_t set_id,
+                                        uint8_t value, uint8_t status,
+                                        std::vector<RawAddress> addr_list) {
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid()) return;
+  if (!mCallbacksObj) {
+    ALOGE("mCallbacksObj is NULL. Return.");
+    return;
+  }
+
+  int i;
+  jstring bd_addr;
+  jobjectArray device_list;
+  jsize len = addr_list.size();
+  device_list = sCallbackEnv->NewObjectArray(
+                    len, sCallbackEnv->FindClass("java/lang/String"), 0);
+
+  for(i = 0; i < len; i++)
+  {
+    bd_addr = sCallbackEnv->NewStringUTF(addr_list[i].ToString().c_str());
+    sCallbackEnv->SetObjectArrayElement(device_list, i, bd_addr);
+  }
+
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onLockStatusChanged,
+                               app_id, set_id, value, status, device_list);
+}
+
+/** Callback when lock is available on earlier denying set member
+ */
+static void lock_available_callback(uint8_t app_id, uint8_t set_id,
+                                    RawAddress& bd_addr) {
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid()) return;
+  if (!mCallbacksObj) {
+    ALOGE("mCallbacksObj is NULL. Return.");
+    return;
+  }
+
+// address
+  ScopedLocalRef<jstring> address(sCallbackEnv.get(),
+                                  bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
+
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onLockAvailable, app_id, set_id, address.get());
+}
+
+/** Callback when size of coordinated set has been changed
+ */
+static void set_size_changed_callback(uint8_t set_id, uint8_t size,
+                                      RawAddress& bd_addr) {
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid()) return;
+  if (!mCallbacksObj) {
+    ALOGE("mCallbacksObj is NULL. Return.");
+    return;
+  }
+
+  // address
+  ScopedLocalRef<jstring> address(sCallbackEnv.get(),
+                                  bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetSizeChanged, set_id, size, address.get());
+}
+
+/** Callback when SIRK of coordinated set has been changed
+ */
+static void set_sirk_changed_callback(uint8_t set_id, uint8_t* sirk,
+                                      RawAddress& bd_addr) {
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid()) return;
+  if (!mCallbacksObj) {
+    ALOGE("mCallbacksObj is NULL. Return.");
+    return;
+  }
+
+  ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), NULL);
+  jb.reset(sCallbackEnv->NewByteArray(24));
+  sCallbackEnv->SetByteArrayRegion(jb.get(), 0, 24, (jbyte*)sirk);
+
+  // address
+  ScopedLocalRef<jstring> address(sCallbackEnv.get(),
+                                  bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
+  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetSirkChanged, set_id, jb.get(), address.get());
+}
+
+static btcsip_callbacks_t sBluetoothCsipCallbacks = {
+  sizeof(sBluetoothCsipCallbacks),
+  csip_app_registered_callback,
+  connection_state_changed_callback,
+  new_set_found_callback,
+  new_set_member_found_cb,
+  lock_state_changed_callback,
+  lock_available_callback,
+  set_size_changed_callback,
+  set_sirk_changed_callback,
+};
+
+static void initNative(JNIEnv* env, jobject object) {
+  ALOGI("%s: initNative()", __func__);
+
+  std::unique_lock<std::shared_timed_mutex> lock(mCallbacks_mutex);
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == NULL) {
+    ALOGE("Bluetooth module is not loaded");
+    return;
+  }
+
+  if (sBluetoothCsipInterface != NULL) {
+    ALOGW("Cleaning up Bluetooth CSIP CLIENT Interface before initializing...");
+    sBluetoothCsipInterface->cleanup();
+    sBluetoothCsipInterface = NULL;
+  }
+
+  if (mCallbacksObj != NULL) {
+    ALOGW("Cleaning up Bluetooth CSIP callback object");
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = NULL;
+  }
+
+  sBluetoothCsipInterface =
+      (btcsip_interface_t*)btInf->get_profile_interface(BT_PROFILE_CSIP_CLIENT_ID);
+  if (sBluetoothCsipInterface == NULL) {
+    ALOGE("Failed to get Bluetooth CSIPInterface");
+    return;
+  }
+
+  bt_status_t status = sBluetoothCsipInterface->init(&sBluetoothCsipCallbacks);
+  if (status != BT_STATUS_SUCCESS) {
+    ALOGE("Failed to initialize Bluetooth CSIP Client, status: %d", status);
+    sBluetoothCsipInterface = NULL;
+    return;
+  }
+
+  mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+  ALOGI("%s: cleanupNative()", __func__);
+  if (!sBluetoothCsipInterface) return;
+
+  sBluetoothCsipInterface->cleanup();
+}
+
+static jboolean connectSetDeviceNative(JNIEnv* env, jobject object,
+                                       jint app_id, jbyteArray address) {
+  if (!sBluetoothCsipInterface) return JNI_FALSE;
+
+  ALOGI("%s: connectSetDeviceNative()", __func__);
+  jboolean ret = JNI_TRUE;
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress bd_addr;
+  bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  sBluetoothCsipInterface->connect(app_id, &bd_addr);
+  return ret;
+}
+
+static jboolean disconnectSetDeviceNative(JNIEnv* env, jobject object,
+                                            jint app_id, jbyteArray address) {
+  if (!sBluetoothCsipInterface) return JNI_FALSE;
+
+  jboolean ret = JNI_TRUE;
+  ALOGI("%s: disconnectSetDeviceNative()", __func__);
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress bd_addr;
+  bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  sBluetoothCsipInterface->disconnect(app_id, &bd_addr);
+  return ret;
+}
+
+static void registerCsipAppNative(JNIEnv* env, jobject object,
+                                  jlong app_uuid_lsb, jlong app_uuid_msb) {
+  if (!sBluetoothCsipInterface) return;
+
+  Uuid uuid = from_java_uuid(app_uuid_msb, app_uuid_lsb);
+  sBluetoothCsipInterface->register_csip_app(uuid);
+}
+
+static void unregisterCsipAppNative(JNIEnv* env, jobject object, jint app_id) {
+  if (!sBluetoothCsipInterface) return;
+
+  sBluetoothCsipInterface->unregister_csip_app(app_id);
+}
+
+static void setLockValueNative(JNIEnv* env, jobject object, jint app_id,
+                               jint set_id, jint value, jobjectArray devicesList) {
+  if (!sBluetoothCsipInterface) return;
+
+  std::vector<RawAddress> lock_list;
+  int listCount = env->GetArrayLength(devicesList);
+  for (int i=0; i < listCount; i++) {
+    jstring address = (jstring) (env->GetObjectArrayElement(devicesList, i));
+    RawAddress bd_addr = str2addr(env, address);
+    lock_list.push_back(bd_addr);
+    env->DeleteLocalRef(address);
+  }
+
+  sBluetoothCsipInterface->set_lock_value(app_id, set_id, value, lock_list);
+}
+
+static JNINativeMethod sMethods[] = {
+    {"classInitNative", "()V", (void*)classInitNative},
+    {"initNative", "()V", (void*)initNative},
+    {"cleanupNative", "()V", (void*)cleanupNative},
+    {"connectSetDeviceNative", "(I[B)Z", (void*)connectSetDeviceNative},
+    {"disconnectSetDeviceNative", "(I[B)Z", (void*)disconnectSetDeviceNative},
+    {"registerCsipAppNative", "(JJ)V", (void*)registerCsipAppNative},
+    {"unregisterCsipAppNative", "(I)V", (void*)unregisterCsipAppNative},
+    {"setLockValueNative", "(III[Ljava/lang/String;)V", (void*)setLockValueNative},
+};
+
+int register_com_android_bluetooth_csip_client(JNIEnv* env) {
+  ALOGE("%s", __func__);
+  return jniRegisterNativeMethods(
+      env, "com/android/bluetooth/groupclient/GroupClientNativeInterface",
+      sMethods, NELEM(sMethods));
+}
+}
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_ext.h b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_ext.h
new file mode 100644
index 0000000..c82146d
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_ext.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 COM_ANDROID_BLUETOOTH_EXT
+#define COM_ANDROID_BLUETOOTH_EXT
+
+namespace android {
+
+int register_com_android_bluetooth_bap_broadcast(JNIEnv* env);
+
+int register_com_android_bluetooth_acm(JNIEnv* env);
+
+int register_com_android_bluetooth_apm(JNIEnv* env);
+
+int register_com_android_bluetooth_csip_client(JNIEnv* env);
+
+int register_com_android_bluetooth_adv_audio_profiles(JNIEnv* env);
+
+int register_com_android_bluetooth_vcp_controller(JNIEnv* env);
+
+int register_com_android_bluetooth_pacs_client(JNIEnv* env);
+
+int register_com_android_bluetooth_mcp(JNIEnv* env);
+
+int register_com_android_bluetooth_call_controller(JNIEnv* env);
+}
+
+#endif
+
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_mcp.cpp b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_mcp.cpp
new file mode 100644
index 0000000..5dc2122
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_mcp.cpp
@@ -0,0 +1,431 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+#define LOG_TAG "BluetoothMCPService_jni"
+
+#define LOG_NDEBUG 0
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <map>
+#include <mutex>
+#include <shared_mutex>
+#include <vector>
+
+
+#include "android_runtime/AndroidRuntime.h"
+#include "base/logging.h"
+#include "com_android_bluetooth.h"
+#include "hardware/bt_mcp.h"
+#include "hardware/bluetooth.h"
+
+
+
+using bluetooth::mcp_server::McpServerCallbacks;
+using bluetooth::mcp_server::McpServerInterface;
+using bluetooth::Uuid;
+static McpServerInterface* sMcpServerInterface = nullptr;
+
+
+namespace android {
+static jmethodID method_OnConnectionStateChanged;
+static jmethodID method_MediaControlPointChangedRequest;
+static jmethodID method_TrackPositionChangedRequest;
+static jmethodID method_PlayingOrderChangedRequest;
+
+
+static std::shared_timed_mutex interface_mutex;
+
+static jobject mCallbacksObj = nullptr;
+static std::shared_timed_mutex callbacks_mutex;
+
+class McpServerCallbacksImpl : public McpServerCallbacks {
+   public:
+  ~McpServerCallbacksImpl() = default;
+
+  void OnConnectionStateChange(int state, const RawAddress& bd_addr) override {
+    LOG(INFO) << __func__;
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
+      return;
+    }
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&bd_addr);
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_OnConnectionStateChanged,
+                                 (jint)state, addr.get());
+  }
+
+
+  void MediaControlPointChangeReq(uint8_t state, const RawAddress& bd_addr) override {
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
+      return;
+    }
+
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&bd_addr);
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_MediaControlPointChangedRequest,
+                                 (jint)state, addr.get());
+  }
+
+  void TrackPositionChangeReq(int32_t position) override {
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_TrackPositionChangedRequest,
+                                 (jint)position);
+  }
+
+  void PlayingOrderChangeReq(uint32_t order) override {
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_PlayingOrderChangedRequest,
+                                 (jint)order);
+  }
+};
+
+
+static McpServerCallbacksImpl sMcpServerCallbacks;
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+  LOG(INFO) << __func__ << ": class init native";
+  method_OnConnectionStateChanged =
+      env->GetMethodID(clazz, "OnConnectionStateChanged", "(I[B)V");
+  LOG(INFO) << __func__ << ": class init native 1";
+  method_MediaControlPointChangedRequest =
+      env->GetMethodID(clazz, "MediaControlPointChangedRequest", "(I[B)V");
+  LOG(INFO) << __func__ << ": class init native 2";
+  method_TrackPositionChangedRequest =
+      env->GetMethodID(clazz, "TrackPositionChangedRequest", "(I)V");
+  method_PlayingOrderChangedRequest =
+      env->GetMethodID(clazz, "PlayingOrderChangedRequest", "(I)V");
+
+  LOG(INFO) << __func__ << ": succeeds";
+}
+
+//<TBD> uuid not fixed
+Uuid uuid = Uuid::FromString("00008fd1-0000-1000-8000-00805F9B34FB");
+
+static void initNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    LOG(ERROR) << "Bluetooth module is not loaded";
+    return;
+  }
+
+  if (sMcpServerInterface != nullptr) {
+    LOG(INFO) << "Cleaning up McpServer Interface before initializing...";
+    sMcpServerInterface->Cleanup();
+    sMcpServerInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    LOG(INFO) << "Cleaning up McpServer callback object";
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+
+  if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
+    LOG(ERROR) << "Failed to allocate Global Ref for Mcp Controller Callbacks";
+    return;
+  }
+  LOG(INFO) << "mcs callback initialized";
+  sMcpServerInterface = (McpServerInterface* )btInf->get_profile_interface(
+      BT_PROFILE_MCP_ID);
+  if (sMcpServerInterface == nullptr) {
+    LOG(ERROR) << "Failed to get Bluetooth Hearing Aid Interface";
+    return;
+  }
+
+  sMcpServerInterface->Init(&sMcpServerCallbacks, uuid);
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    LOG(ERROR) << "Bluetooth module is not loaded";
+    return;
+  }
+
+  if (sMcpServerInterface != nullptr) {
+    sMcpServerInterface->Cleanup();
+    sMcpServerInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+}
+
+
+
+static jboolean mediaControlPointOpcodeSupportedNative(JNIEnv* env, jobject object,
+                                           jint feature) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  sMcpServerInterface->MediaControlPointOpcodeSupported(feature);
+  return JNI_TRUE;
+}
+
+static jboolean mediaControlPointNative(JNIEnv* env, jobject object,
+                                           jint value) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  sMcpServerInterface->MediaControlPoint(value);
+  return JNI_TRUE;
+}
+
+static jboolean mediaStateNative(JNIEnv* env, jobject object,
+                                           jint state) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  sMcpServerInterface->MediaState(state);
+  return JNI_TRUE;
+}
+
+static jboolean mediaPlayerNameNative(JNIEnv* env, jobject object,
+                                           jstring playerName) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+
+  const char *nativeString = env->GetStringUTFChars(playerName, nullptr);
+  if (!nativeString) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+  sMcpServerInterface->MediaPlayerName((uint8_t*)nativeString);
+  env->ReleaseStringUTFChars(playerName, nativeString);
+  return JNI_TRUE;
+}
+
+static jboolean trackChangedNative(JNIEnv* env, jobject object,
+                                           jint status) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  sMcpServerInterface->TrackChanged((bool)status);
+  return JNI_TRUE;
+}
+
+static jboolean trackPositionNative(JNIEnv* env, jobject object,
+                                           jint playPosition) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+
+  sMcpServerInterface->TrackPosition(playPosition);
+  return JNI_TRUE;
+}
+
+static jboolean trackDurationNative(JNIEnv* env, jobject object,
+                                           jint duration) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  sMcpServerInterface->TrackDuration(duration);
+
+  return JNI_TRUE;
+}
+
+
+
+static jboolean trackTitleNative(JNIEnv* env, jobject object,
+                                           jstring title) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  const char *nativeString = env->GetStringUTFChars(title, nullptr);
+  if (!nativeString) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+  sMcpServerInterface->TrackTitle((uint8_t*)nativeString);
+  env->ReleaseStringUTFChars(title, nativeString);
+  return JNI_TRUE;
+}
+
+static jboolean setActiveDeviceNative(JNIEnv* env, jobject object,
+                                         jint profile, jint set_id,
+                                         jbyteArray address) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  RawAddress bd_addr = RawAddress::kEmpty;
+  if (addr) {
+    bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
+  }
+  if (bd_addr == RawAddress::kEmpty) {
+    LOG(INFO) << __func__ << " active device is null";
+  }
+
+  sMcpServerInterface->SetActiveDevice(bd_addr, set_id, profile);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static jboolean bondStateChangeNative(JNIEnv* env, jobject object,
+                                          jint state, jbyteArray address) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+  RawAddress* tmpraw = (RawAddress*)addr;
+
+  sMcpServerInterface->BondStateChange(*tmpraw, state);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static jboolean playingOrderSupportedNative(JNIEnv* env, jobject object,
+                                           jint order) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  sMcpServerInterface->PlayingOrderSupported(order);
+
+  return JNI_TRUE;
+}
+
+static jboolean playingOrderNative(JNIEnv* env, jobject object,
+                                           jint order) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  sMcpServerInterface->PlayingOrder(order);
+
+  return JNI_TRUE;
+}
+
+static jboolean contentControlIdNative(JNIEnv* env, jobject object,
+                                           jint ccid) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  sMcpServerInterface->ContentControlId(ccid);
+  return JNI_TRUE;
+}
+
+static jboolean disconnectMcpNative(JNIEnv* env, jobject object,
+                                           jbyteArray address) {
+
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sMcpServerInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+  RawAddress* tmpraw = (RawAddress*)addr;
+
+  sMcpServerInterface->DisconnectMcp(*tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static JNINativeMethod sMethods[] = {
+    {"classInitNative", "()V", (void*)classInitNative},
+    {"initNative", "()V", (void*)initNative},
+    {"cleanupNative", "()V", (void*)cleanupNative},
+    {"mediaStateNative", "(I)Z", (void*)mediaStateNative},
+    {"mediaPlayerNameNative", "(Ljava/lang/String;)Z", (void*)mediaPlayerNameNative},
+    {"mediaControlPointOpcodeSupportedNative", "(I)Z", (void*)mediaControlPointOpcodeSupportedNative},
+    {"mediaControlPointNative", "(I)Z", (void*)mediaControlPointNative},
+    {"trackChangedNative", "(I)Z", (void*)trackChangedNative},
+    {"trackTitleNative", "(Ljava/lang/String;)Z", (void*)trackTitleNative},
+    {"trackPositionNative", "(I)Z", (void*)trackPositionNative},
+    {"trackDurationNative", "(I)Z", (void*)trackDurationNative},
+    {"playingOrderSupportedNative", "(I)Z", (void*)playingOrderSupportedNative},
+    {"playingOrderNative", "(I)Z", (void*)playingOrderNative},
+    {"setActiveDeviceNative", "(II[B)Z", (void*)setActiveDeviceNative},
+    {"contentControlIdNative", "(I)Z", (void*)contentControlIdNative},
+    {"disconnectMcpNative", "([B)Z", (void*)disconnectMcpNative},
+    {"bondStateChangeNative", "(I[B)Z", (void*)bondStateChangeNative},
+};
+
+
+
+int register_com_android_bluetooth_mcp(JNIEnv* env) {
+  return jniRegisterNativeMethods(
+      env, "com/android/bluetooth/mcp/McpNativeInterface",
+      sMethods, NELEM(sMethods));
+}
+}  // namespace android
+
+
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_pacs_client.cpp b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_pacs_client.cpp
new file mode 100644
index 0000000..c65a5d0
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_pacs_client.cpp
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+
+ * 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.
+ */
+
+#define LOG_TAG "BluetoothPacsClienServiceJni"
+
+#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "base/logging.h"
+#include "com_android_bluetooth.h"
+#include "hardware/bt_pacs_client.h"
+
+#include <string.h>
+#include <shared_mutex>
+
+using bluetooth::bap::pacs::ConnectionState;
+using bluetooth::bap::pacs::PacsClientInterface;
+using bluetooth::bap::pacs::PacsClientCallbacks;
+
+namespace android {
+
+static jmethodID method_OnInitialized;
+static jmethodID method_onConnectionStateChanged;
+static jmethodID method_OnAudioContextAvailable;
+static jmethodID method_onServiceDiscovery;
+
+static struct {
+   jclass clazz;
+   jmethodID constructor;
+   jmethodID getCodecType;
+   jmethodID getCodecPriority;
+   jmethodID getSampleRate;
+   jmethodID getBitsPerSample;
+   jmethodID getChannelMode;
+   jmethodID getCodecSpecific1;
+   jmethodID getCodecSpecific2;
+   jmethodID getCodecSpecific3;
+   jmethodID getCodecSpecific4;
+} android_bluetooth_pacs_record;
+
+static PacsClientInterface* sPacsClientInterface = nullptr;
+static std::shared_timed_mutex interface_mutex;
+
+static jobject mCallbacksObj = nullptr;
+static std::shared_timed_mutex callbacks_mutex;
+
+class PacsClientCallbacksImpl : public PacsClientCallbacks {
+ public:
+  ~PacsClientCallbacksImpl() = default;
+  void OnInitialized(int status,
+                     int client_id) override {
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_OnInitialized,
+                                 (jint)status, (jint)client_id);
+  }
+
+  void OnConnectionState(const RawAddress& bd_addr,
+                         ConnectionState state) override {
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
+      return;
+    }
+
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&bd_addr);
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
+                                 addr.get(), (jint)state);
+  }
+
+  void OnAudioContextAvailable(const RawAddress& bd_addr,
+                        uint32_t available_contexts) override {
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr for available audio context";
+      return;
+    }
+
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&bd_addr);
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_OnAudioContextAvailable,
+                                 addr.get(), (jint)available_contexts);
+  }
+
+   void OnSearchComplete(int status, const RawAddress& address,
+                         std::vector<bluetooth::bap::pacs::CodecConfig> sink_pac_records,
+                         std::vector<bluetooth::bap::pacs::CodecConfig> src_pac_records,
+                         uint32_t sink_locations,
+                         uint32_t src_locations,
+                         uint32_t available_contexts,
+                         uint32_t supported_contexts) override {
+
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+
+    jsize i = 0;
+    jobjectArray sink_pac_records_array = sCallbackEnv->NewObjectArray(
+        (jsize)sink_pac_records.size(),
+        android_bluetooth_pacs_record.clazz, nullptr);
+    for (auto const& cap : sink_pac_records) {
+      jobject capObj = sCallbackEnv->NewObject(
+          android_bluetooth_pacs_record.clazz,
+          android_bluetooth_pacs_record.constructor,
+          (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
+          (jint)cap.bits_per_sample, (jint)cap.channel_mode,
+          (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
+          (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
+      sCallbackEnv->SetObjectArrayElement(sink_pac_records_array, i++, capObj);
+      sCallbackEnv->DeleteLocalRef(capObj);
+    }
+
+    i = 0;
+    jobjectArray src_pac_records_array = sCallbackEnv->NewObjectArray(
+        (jsize)src_pac_records.size(),
+        android_bluetooth_pacs_record.clazz, nullptr);
+    for (auto const& cap : src_pac_records) {
+      jobject capObj = sCallbackEnv->NewObject(
+          android_bluetooth_pacs_record.clazz,
+          android_bluetooth_pacs_record.constructor,
+          (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
+          (jint)cap.bits_per_sample, (jint)cap.channel_mode,
+          (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
+          (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
+      sCallbackEnv->SetObjectArrayElement(src_pac_records_array, i++,
+                                          capObj);
+      sCallbackEnv->DeleteLocalRef(capObj);
+    }
+
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr";
+      return;
+    }
+
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&address);
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceDiscovery,
+                                 sink_pac_records_array, src_pac_records_array, (jint)sink_locations,
+                                 (jint)src_locations, (jint)available_contexts, (jint)supported_contexts,
+                                 (jint)status, addr.get());
+  }
+};
+
+static PacsClientCallbacksImpl sPacsClientCallbacks;
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+
+  jclass jniBluetoothCodecConfigClass =
+      env->FindClass("android/bluetooth/BluetoothCodecConfig");
+  android_bluetooth_pacs_record.constructor =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "<init>", "(IIIIIJJJJ)V");
+  android_bluetooth_pacs_record.getCodecType =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecType", "()I");
+  android_bluetooth_pacs_record.getCodecPriority =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecPriority", "()I");
+  android_bluetooth_pacs_record.getSampleRate =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getSampleRate", "()I");
+  android_bluetooth_pacs_record.getBitsPerSample =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getBitsPerSample", "()I");
+  android_bluetooth_pacs_record.getChannelMode =
+      env->GetMethodID(jniBluetoothCodecConfigClass, "getChannelMode", "()I");
+  android_bluetooth_pacs_record.getCodecSpecific1 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific1", "()J");
+  android_bluetooth_pacs_record.getCodecSpecific2 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific2", "()J");
+  android_bluetooth_pacs_record.getCodecSpecific3 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific3", "()J");
+  android_bluetooth_pacs_record.getCodecSpecific4 = env->GetMethodID(
+      jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J");
+
+  method_OnInitialized =
+      env->GetMethodID(clazz, "OnInitialized", "(II)V");
+
+  method_onConnectionStateChanged =
+      env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V");
+
+  method_OnAudioContextAvailable =
+      env->GetMethodID(clazz, "OnAudioContextAvailable", "([BI)V");
+
+  method_onServiceDiscovery =
+      env->GetMethodID(clazz, "onServiceDiscovery", "([Landroid/bluetooth/BluetoothCodecConfig;"
+                                                    "[Landroid/bluetooth/BluetoothCodecConfig;"
+                                                    "IIIII[B)V");
+
+  LOG(INFO) << __func__ << ": succeeds";
+}
+
+static void initNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    LOG(ERROR) << "Bluetooth module is not loaded";
+    return;
+  }
+
+  if (sPacsClientInterface != nullptr) {
+    LOG(INFO) << "Cleaning up PacsClient Interface before initializing...";
+    sPacsClientInterface->Cleanup(0);
+    sPacsClientInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    LOG(INFO) << "Cleaning up PacsClient callback object";
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+
+  if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
+    LOG(ERROR) << "Failed to allocate Global Ref for pacs client Callbacks";
+    return;
+  }
+
+  android_bluetooth_pacs_record.clazz = (jclass)env->NewGlobalRef(
+      env->FindClass("android/bluetooth/BluetoothCodecConfig"));
+  if (android_bluetooth_pacs_record.clazz == nullptr) {
+    ALOGE("%s: Failed to allocate Global Ref for BluetoothCodecConfig class",
+          __func__);
+    return;
+  }
+
+  sPacsClientInterface = (PacsClientInterface*)btInf->get_profile_interface(
+      BT_PROFILE_PACS_CLIENT_ID);
+  if (sPacsClientInterface == nullptr) {
+    LOG(ERROR) << "Failed to get Bluetooth pacs client Interface";
+    return;
+  }
+
+  sPacsClientInterface->Init(&sPacsClientCallbacks);
+}
+
+static void cleanupNative(JNIEnv* env, jobject object, jint client_id) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    LOG(ERROR) << "Bluetooth module is not loaded";
+    return;
+  }
+
+  if (sPacsClientInterface != nullptr) {
+    sPacsClientInterface->Cleanup(client_id);
+    sPacsClientInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+  env->DeleteGlobalRef(android_bluetooth_pacs_record.clazz);
+  android_bluetooth_pacs_record.clazz = nullptr;
+}
+
+static jboolean connectPacsClientNative(JNIEnv* env, jobject object,
+                                        jint client_id, jbyteArray address) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sPacsClientInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sPacsClientInterface->Connect(client_id, *tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static jboolean disconnectPacsClientNative(JNIEnv* env, jobject object,
+                                           jint client_id, jbyteArray address) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sPacsClientInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sPacsClientInterface->Disconnect(client_id, *tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static jboolean startDiscoveryNative(JNIEnv* env, jobject object,
+                                     jint client_id, jbyteArray address) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sPacsClientInterface) return JNI_FALSE;
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sPacsClientInterface->StartDiscovery(client_id, *tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static void GetAvailableAudioContextsNative(JNIEnv* env, jobject object,
+                                      jint client_id, jbyteArray address) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sPacsClientInterface) return;
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return;
+  }
+
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sPacsClientInterface->GetAvailableAudioContexts(client_id, *tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+
+static JNINativeMethod sMethods[] = {
+    {"classInitNative", "()V", (void*)classInitNative},
+    {"initNative", "()V", (void*)initNative},
+    {"cleanupNative", "(I)V", (void*)cleanupNative},
+    {"connectPacsClientNative", "(I[B)Z", (void*)connectPacsClientNative},
+    {"disconnectPacsClientNative", "(I[B)Z", (void*)disconnectPacsClientNative},
+    {"startDiscoveryNative", "(I[B)Z", (void*)startDiscoveryNative},
+    {"GetAvailableAudioContextsNative", "(I[B)Z", (void*)GetAvailableAudioContextsNative},
+};
+
+int register_com_android_bluetooth_pacs_client(JNIEnv* env) {
+  return jniRegisterNativeMethods(
+      env, "com/android/bluetooth/pc/PacsClientNativeInterface",
+      sMethods, NELEM(sMethods));
+}
+}  // namespace android
diff --git a/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_vcp_controller.cpp b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_vcp_controller.cpp
new file mode 100644
index 0000000..552fd8a
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/jni/com_android_bluetooth_vcp_controller.cpp
@@ -0,0 +1,295 @@
+/*
+ *Copyright (c) 2020, The Linux Foundation. All rights reserved.
+
+ * 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.
+ */
+
+#define LOG_TAG "BluetoothVCPControllerJni"
+
+#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "base/logging.h"
+#include "com_android_bluetooth.h"
+#include "hardware/bt_vcp_controller.h"
+
+#include <string.h>
+#include <shared_mutex>
+
+using bluetooth::vcp_controller::ConnectionState;
+using bluetooth::vcp_controller::VcpControllerCallbacks;
+using bluetooth::vcp_controller::VcpControllerInterface;
+
+namespace android {
+static jmethodID method_onConnectionStateChanged;
+static jmethodID method_onVolumeStateChange;
+static jmethodID method_onVolumeFlagsChange;
+
+static VcpControllerInterface* sVcpControllerInterface = nullptr;
+static std::shared_timed_mutex interface_mutex;
+
+static jobject mCallbacksObj = nullptr;
+static std::shared_timed_mutex callbacks_mutex;
+
+class VcpControllerCallbacksImpl : public VcpControllerCallbacks {
+ public:
+  ~VcpControllerCallbacksImpl() = default;
+
+  void OnConnectionState(ConnectionState state,
+                         const RawAddress& bd_addr) override {
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
+      return;
+    }
+
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&bd_addr);
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
+                                 (jint)state, addr.get());
+  }
+
+
+  void OnVolumeStateChange(uint8_t volume, uint8_t mute,
+                         const RawAddress& bd_addr) override {
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
+      return;
+    }
+
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&bd_addr);
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeStateChange,
+                                 (jint)volume, (jboolean)mute, addr.get());
+  }
+
+  void OnVolumeFlagsChange(uint8_t flags,
+                         const RawAddress& bd_addr) override {
+    LOG(INFO) << __func__;
+
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+    ScopedLocalRef<jbyteArray> addr(
+        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+    if (!addr.get()) {
+      LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
+      return;
+    }
+
+    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+                                     (jbyte*)&bd_addr);
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeFlagsChange,
+                                 (jint)flags, addr.get());
+  }
+};
+
+static VcpControllerCallbacksImpl sVcpControllerCallbacks;
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+  method_onConnectionStateChanged =
+      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
+
+  method_onVolumeStateChange =
+      env->GetMethodID(clazz, "OnVolumeStateChange", "(II[B)V");
+
+  method_onVolumeFlagsChange =
+      env->GetMethodID(clazz, "OnVolumeFlagsChange", "(I[B)V");
+
+  LOG(INFO) << __func__ << ": succeeds";
+}
+
+static void initNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    LOG(ERROR) << "Bluetooth module is not loaded";
+    return;
+  }
+
+  if (sVcpControllerInterface != nullptr) {
+    LOG(INFO) << "Cleaning up VcpController Interface before initializing...";
+    sVcpControllerInterface->Cleanup();
+    sVcpControllerInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    LOG(INFO) << "Cleaning up VcpController callback object";
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+
+  if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
+    LOG(ERROR) << "Failed to allocate Global Ref for Vcp Controller Callbacks";
+    return;
+  }
+
+  sVcpControllerInterface = (VcpControllerInterface*)btInf->get_profile_interface(
+      BT_PROFILE_VOLUME_CONTROL_ID);
+  if (sVcpControllerInterface == nullptr) {
+    LOG(ERROR) << "Failed to get Bluetooth Hearing Aid Interface";
+    return;
+  }
+
+  sVcpControllerInterface->Init(&sVcpControllerCallbacks);
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
+  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
+
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == nullptr) {
+    LOG(ERROR) << "Bluetooth module is not loaded";
+    return;
+  }
+
+  if (sVcpControllerInterface != nullptr) {
+    sVcpControllerInterface->Cleanup();
+    sVcpControllerInterface = nullptr;
+  }
+
+  if (mCallbacksObj != nullptr) {
+    env->DeleteGlobalRef(mCallbacksObj);
+    mCallbacksObj = nullptr;
+  }
+}
+
+static jboolean connectVcpNative(JNIEnv* env, jobject object,
+                                        jbyteArray address, jboolean isDirect) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sVcpControllerInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sVcpControllerInterface->Connect(*tmpraw, isDirect);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static jboolean disconnectVcpNative(JNIEnv* env, jobject object,
+                                           jbyteArray address) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sVcpControllerInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sVcpControllerInterface->Disconnect(*tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static jboolean setAbsVolumeNative(JNIEnv* env, jobject object,
+                                           jint volume, jbyteArray address) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sVcpControllerInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sVcpControllerInterface->SetAbsVolume(volume, *tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static jboolean muteNative(JNIEnv* env, jobject object,
+                                           jbyteArray address) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sVcpControllerInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sVcpControllerInterface->Mute(*tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static jboolean unmuteNative(JNIEnv* env, jobject object,
+                                           jbyteArray address) {
+  LOG(INFO) << __func__;
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sVcpControllerInterface) return JNI_FALSE;
+
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (!addr) {
+    jniThrowIOException(env, EINVAL);
+    return JNI_FALSE;
+  }
+
+  RawAddress* tmpraw = (RawAddress*)addr;
+  sVcpControllerInterface->Unmute(*tmpraw);
+  env->ReleaseByteArrayElements(address, addr, 0);
+  return JNI_TRUE;
+}
+
+static JNINativeMethod sMethods[] = {
+    {"classInitNative", "()V", (void*)classInitNative},
+    {"initNative", "()V", (void*)initNative},
+    {"cleanupNative", "()V", (void*)cleanupNative},
+    {"connectVcpNative", "([BZ)Z", (void*)connectVcpNative},
+    {"disconnectVcpNative", "([B)Z", (void*)disconnectVcpNative},
+    {"setAbsVolumeNative", "(I[B)Z", (void*)setAbsVolumeNative},
+    {"muteNative", "([B)Z", (void*)muteNative},
+    {"unmuteNative", "([B)Z", (void*)unmuteNative},
+};
+
+int register_com_android_bluetooth_vcp_controller(JNIEnv* env) {
+  return jniRegisterNativeMethods(
+      env, "com/android/bluetooth/vcp/VcpControllerNativeInterface",
+      sMethods, NELEM(sMethods));
+}
+}  // namespace android
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmCodecConfig.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmCodecConfig.java
new file mode 100644
index 0000000..6c79c66
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmCodecConfig.java
@@ -0,0 +1,141 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.acm;
+
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.os.SystemProperties;
+import android.util.Log;
+import com.android.bluetooth.R;
+import com.android.bluetooth.btservice.AdapterService;
+
+import java.util.Arrays;
+import java.util.Objects;
+/*
+ * ACM Codec Configuration setup.
+ */
+class AcmCodecConfig {
+    private static final boolean DBG = true;
+    private static final String TAG = "AcmCodecConfig";
+    static final int CONTEXT_TYPE_UNKNOWN = 0;
+    static final int CONTEXT_TYPE_MUSIC = 1;
+    static final int CONTEXT_TYPE_VOICE = 2;
+    static final int CONTEXT_TYPE_MUSIC_VOICE = 3;
+
+    private Context mContext;
+    private AcmNativeInterface mAcmNativeInterface;
+
+    private BluetoothCodecConfig[] mCodecConfigPriorities;
+    private int mAcmSourceCodecPriorityLC3 = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
+
+    private int assigned_codec_length = 0;
+    AcmCodecConfig(Context context, AcmNativeInterface acmNativeInterface) {
+        mContext = context;
+        mAcmNativeInterface = acmNativeInterface;
+        mCodecConfigPriorities = assignCodecConfigPriorities();
+    }
+
+    BluetoothCodecConfig[] codecConfigPriorities() {
+        return mCodecConfigPriorities;
+    }
+
+    void setCodecConfigPreference(BluetoothDevice device,
+                                  BluetoothCodecConfig newCodecConfig,
+                                  int contextType) {
+        //Objects.requireNonNull(codecStatus);
+
+        /*// Check whether the codecConfig is selectable for this Bluetooth device.
+        BluetoothCodecConfig[] selectableCodecs = codecStatus.getCodecsSelectableCapabilities();
+        if (!Arrays.asList(selectableCodecs).stream().anyMatch(codec ->
+                codec.isMandatoryCodec())) {
+            // Do not set codec preference to native if the selectableCodecs not contain mandatory
+            // codec. The reason could be remote codec negotiation is not completed yet.
+            Log.w(TAG, "setCodecConfigPreference: must have mandatory codec before changing.");
+            return;
+        }
+        if (!codecStatus.isCodecConfigSelectable(newCodecConfig)) {
+            Log.w(TAG, "setCodecConfigPreference: invalid codec "
+                    + Objects.toString(newCodecConfig));
+            return;
+        }
+
+        // Check whether the codecConfig would change current codec config.
+        int prioritizedCodecType = getPrioitizedCodecType(newCodecConfig, selectableCodecs);
+        BluetoothCodecConfig currentCodecConfig = codecStatus.getCodecConfig();
+        if (prioritizedCodecType == currentCodecConfig.getCodecType()
+                && (prioritizedCodecType != newCodecConfig.getCodecType()
+                || (currentCodecConfig.similarCodecFeedingParameters(newCodecConfig)
+                && currentCodecConfig.sameCodecSpecificParameters(newCodecConfig)))) {
+            // Same codec with same parameters, no need to send this request to native.
+            Log.w(TAG, "setCodecConfigPreference: codec not changed.");
+            return;
+        }*/
+
+        BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1];
+        codecConfigArray[0] = newCodecConfig;
+        mAcmNativeInterface.setCodecConfigPreference(device, codecConfigArray, contextType, contextType);
+    }
+
+    // Get the codec type of the highest priority of selectableCodecs and codecConfig.
+    private int getPrioitizedCodecType(BluetoothCodecConfig codecConfig,
+            BluetoothCodecConfig[] selectableCodecs) {
+        BluetoothCodecConfig prioritizedCodecConfig = codecConfig;
+        for (BluetoothCodecConfig config : selectableCodecs) {
+            if (prioritizedCodecConfig == null) {
+                prioritizedCodecConfig = config;
+            }
+            if (config.getCodecPriority() > prioritizedCodecConfig.getCodecPriority()) {
+                prioritizedCodecConfig = config;
+            }
+        }
+        return prioritizedCodecConfig.getCodecType();
+    }
+
+    // Assign the ACM Source codec config priorities
+    private BluetoothCodecConfig[] assignCodecConfigPriorities() {
+        Resources resources = mContext.getResources();
+        if (resources == null) {
+            return null;
+        }
+
+        int value;
+        mAcmSourceCodecPriorityLC3 = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
+
+        BluetoothCodecConfig codecConfig;
+        BluetoothCodecConfig[] codecConfigArray;
+        int codecCount = 0;
+        codecConfigArray =
+                new BluetoothCodecConfig[BluetoothCodecConfig.SOURCE_QVA_CODEC_TYPE_MAX];
+
+        codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+                mAcmSourceCodecPriorityLC3, BluetoothCodecConfig.SAMPLE_RATE_NONE,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig
+                .CHANNEL_MODE_NONE, 0 /* codecSpecific1 */,
+                0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 0 /* codecSpecific4 */);
+        codecConfigArray[codecCount++] = codecConfig;
+        assigned_codec_length = codecCount;
+        return codecConfigArray;
+    }
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmNativeInterface.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmNativeInterface.java
new file mode 100644
index 0000000..a7f24dc
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmNativeInterface.java
@@ -0,0 +1,238 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.acm;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.util.Log;
+import java.util.List;
+
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * ACM Native Interface to/from JNI.
+ */
+public class AcmNativeInterface {
+    private static final String TAG = "AcmNativeInterface";
+    private static final boolean DBG = true;
+    private BluetoothAdapter mAdapter;
+    static final int CONTEXT_TYPE_UNKNOWN = 0;
+    static final int CONTEXT_TYPE_MUSIC = 1;
+    static final int CONTEXT_TYPE_VOICE = 2;
+    static final int CONTEXT_TYPE_MUSIC_VOICE = 3;
+    @GuardedBy("INSTANCE_LOCK")
+    private static AcmNativeInterface sInstance;
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static {
+        classInitNative();
+    }
+
+    @VisibleForTesting
+    private AcmNativeInterface() {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mAdapter == null) {
+            Log.wtf(TAG, "No Bluetooth Adapter Available");
+        }
+    }
+
+    /**
+     * Get singleton instance.
+     */
+    public static AcmNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new AcmNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Initializes the native interface.
+     *
+     * @param maxConnectedAudioDevices maximum number of A2DP Sink devices that can be connected
+     * simultaneously
+     * @param codecConfigPriorities an array with the codec configuration
+     * priorities to configure.
+     */
+    public void init(int maxConnectedAudioDevices, BluetoothCodecConfig[] codecConfigPriorities) {
+        initNative(maxConnectedAudioDevices, codecConfigPriorities);
+    }
+
+
+    /**
+     * Initiates ACM connection to a remote device.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    public boolean connectAcm(BluetoothDevice device, int contextType, int profileType, int preferredContext) {
+        return connectAcmNative(getByteAddress(device), contextType, profileType, preferredContext);
+    }
+
+    /**
+     * Disconnects ACM from a remote device.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    public boolean disconnectAcm(BluetoothDevice device, int contextType) {
+        return disconnectAcmNative(getByteAddress(device), contextType);
+    }
+
+    /**
+     * Sets a connected ACM group/remote as active.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    public boolean setActiveDevice(BluetoothDevice device, int contextType) {
+        return setActiveDeviceNative(getByteAddress(device), contextType);
+    }
+
+    /**
+     * Sends Start stream to remote group/remote for voice call.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    public boolean startStream(BluetoothDevice device, int contextType) {
+        return startStreamNative(getByteAddress(device), contextType);
+    }
+
+    /**
+     * Sends Stop stream to remote group/remote for voice call.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    public boolean stopStream(BluetoothDevice device, int contextType) {
+        return stopStreamNative(getByteAddress(device), contextType);
+    }
+
+    /**
+     * Sets the codec configuration preferences.
+     *
+     * @param device the remote Bluetooth device
+     * @param codecConfigArray an array with the codec configurations to
+     * configure.
+     * @return true on success, otherwise false.
+     */
+    public boolean setCodecConfigPreference(BluetoothDevice device, BluetoothCodecConfig[] codecConfigArray,
+                                                     int contextType, int preferredContext) {
+        return setCodecConfigPreferenceNative(getByteAddress(device), codecConfigArray,
+                                              contextType, preferredContext);
+    }
+
+    public boolean ChangeCodecConfigPreference(BluetoothDevice device,
+                                               String message) {
+        return ChangeCodecConfigPreferenceNative(getByteAddress(device), message);
+    }
+    /**
+     * Cleanup the native interface.
+     */
+    public void cleanup() {
+        cleanupNative();
+    }
+
+    private BluetoothDevice getDevice(byte[] address) {
+        return mAdapter.getRemoteDevice(address);
+    }
+
+    private byte[] getByteAddress(BluetoothDevice device) {
+        if (device == null) {
+            return Utils.getBytesFromAddress("00:00:00:00:00:00");
+        }
+        return Utils.getBytesFromAddress(device.getAddress());
+    }
+
+    private void sendMessageToService(AcmStackEvent event) {
+        AcmService service = AcmService.getAcmService();
+        if (service != null) {
+            service.messageFromNative(event);
+        } else {
+            Log.w(TAG, "Event ignored, service not available: " + event);
+        }
+    }
+
+    // Callbacks from the native stack back into the Java framework.
+    // All callbacks are routed via the Service which will disambiguate which
+    // state machine the message should be routed to.
+
+    private void onConnectionStateChanged(byte[] address, int state, int contextType) {
+        AcmStackEvent event =
+                new AcmStackEvent(AcmStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = state;
+        event.valueInt2 = contextType;
+
+        if (DBG) {
+            Log.d(TAG, "onConnectionStateChanged: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void onAudioStateChanged(byte[] address, int state, int contextType) {
+        AcmStackEvent event = new AcmStackEvent(AcmStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = state;
+        event.valueInt2 = contextType;
+
+        if (DBG) {
+            Log.d(TAG, "onAudioStateChanged: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void onCodecConfigChanged(byte[] address,
+            BluetoothCodecConfig newCodecConfig,
+            BluetoothCodecConfig[] codecsLocalCapabilities,
+            BluetoothCodecConfig[] codecsSelectableCapabilities, int contextType) {
+        AcmStackEvent event = new AcmStackEvent(AcmStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED);
+        event.device = getDevice(address);
+        event.codecStatus = new BluetoothCodecStatus(newCodecConfig,
+                                                     codecsLocalCapabilities,
+                                                     codecsSelectableCapabilities);
+        event.valueInt2 = contextType;
+        if (DBG) {
+            Log.d(TAG, "onCodecConfigChanged: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    // Native methods that call into the JNI interface
+    private static native void classInitNative();
+    private native void initNative(int maxConnectedAudioDevices,
+                                   BluetoothCodecConfig[] codecConfigPriorities);
+    private native boolean connectAcmNative(byte[] address, int contextType, int profileType, int preferredContext);
+    private native boolean disconnectAcmNative(byte[] address, int contextType);
+    private native boolean setActiveDeviceNative(byte[] address, int contextType);
+    private native boolean startStreamNative(byte[] address, int contextType);
+    private native boolean stopStreamNative(byte[] address, int contextType);
+    private native boolean setCodecConfigPreferenceNative(byte[] address,
+                BluetoothCodecConfig[] codecConfigArray, int contextType, int preferredContext);
+    private native boolean ChangeCodecConfigPreferenceNative(byte[] address, String Id);
+    private native void cleanupNative();
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmService.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmService.java
new file mode 100644
index 0000000..ef264a1
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmService.java
@@ -0,0 +1,1866 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.acm;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothA2dp.OptionalCodecsPreferenceStatus;
+import android.bluetooth.BluetoothA2dp.OptionalCodecsSupportStatus;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.os.HandlerThread;
+import android.os.Handler;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.util.Log;
+import android.os.Message;
+import android.bluetooth.BluetoothGroupCallback;
+import com.android.bluetooth.groupclient.GroupService;
+import android.bluetooth.DeviceGroup;
+import android.bluetooth.BluetoothDeviceGroup;
+import com.android.bluetooth.apm.ActiveDeviceManagerService;
+import com.android.bluetooth.apm.VolumeManager;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import android.os.SystemProperties;
+import com.android.bluetooth.BluetoothMetricsProto;
+import com.android.bluetooth.BluetoothStatsLog;
+import com.android.bluetooth.Utils;
+import android.bluetooth.BluetoothAdapter;
+import com.android.bluetooth.apm.ApmConst;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.MetricsLogger;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import com.android.bluetooth.vcp.VcpController;
+/**
+ * Provides Bluetooth ACM profile, as a service in the Bluetooth application.
+ * @hide
+ */
+public class AcmService extends ProfileService {
+    private static final boolean DBG = true;
+    private static final String TAG = "AcmService";
+    private String mAcmName;
+    public static final int ACM_AUDIO_UNICAST = 25;
+    public static final int INVALID_SET_ID = 0x10;
+    private static AcmService sAcmService;
+    private BluetoothAdapter mAdapter;
+    private AdapterService mAdapterService;
+    private HandlerThread mStateMachinesThread;
+    private static final int LOCK_RELEASED = 0;                    // (LOCK Released successfully)
+    private static final int LOCK_RELEASED_TIMEOUT = 1;          // (LOCK Released by timeout)
+    private static final int ALL_LOCKS_ACQUIRED = 2;              // (LOCK Acquired for all requested set members)
+    private static final int SOME_LOCKS_ACQUIRED_REASON_TIMEOUT = 3; // (Request timeout for some set members)
+    private static final int SOME_LOCKS_ACQUIRED_REASON_DISC = 4;   // (Some of the set members were disconnected)
+    private static final int LOCK_DENIED = 5;                     // (Denied by one of the set members)
+    private static final int INVALID_REQUEST_PARAMS = 6;         // (Upper layer provided invalid parameters)
+    private static final int LOCK_RELEASE_NOT_ALLOWED = 7;           // (Response from remote (PTS))
+    private static final int INVALID_VALUE = 8;
+    @VisibleForTesting
+    AcmNativeInterface mAcmNativeInterface;
+    @VisibleForTesting
+    ServiceFactory mFactory = new ServiceFactory();
+
+    static final int CONTEXT_TYPE_UNKNOWN = 0;
+    static final int CONTEXT_TYPE_MUSIC = 1;
+    static final int CONTEXT_TYPE_VOICE = 2;
+    static final int CONTEXT_TYPE_MUSIC_VOICE = 3;
+    static final int CONTEXT_TYPE_BROADCAST_AUDIO = 6;
+
+    private AcmCodecConfig mAcmCodecConfig;
+    private final Object mAudioManagerLock = new Object();
+    private final Object mBtLeaLock = new Object();
+    private final Object mBtAcmLock = new Object();
+    private String mLeaChannelMode = "stereo";
+    private AudioManager mAudioManager;
+    @GuardedBy("mStateMachines")
+    private BluetoothDevice mGroupBdAddress = null;
+    private BluetoothDevice mActiveDevice = null;
+    private BluetoothDevice mActiveDeviceVoice = null;
+    private int mActiveDeviceProfile = 0;
+    private int mActiveDeviceVoiceProfile = 0;
+    private final ConcurrentMap<BluetoothDevice, AcmStateMachine> mStateMachines =
+            new ConcurrentHashMap<>();
+    private HashMap<BluetoothDevice, BluetoothAcmDevice> mAcmDevices =
+                     new HashMap<BluetoothDevice, BluetoothAcmDevice>();
+
+    // Upper limit of all ACM devices: Bonded or Connected
+    private static final int MAX_ACM_STATE_MACHINES = 50;
+    // Upper limit of all ACM devices that are Connected or Connecting
+    private int mMaxConnectedAudioDevices = 1;
+    CsipManager mCsipManager = null;
+    boolean mIsCsipRegistered = false;
+    boolean mShoPend = false;
+    boolean mVoiceShoPend = false;
+    //volume
+    private int mAudioStreamMax;
+    private int mActiveDeviceLocalMediaVol;
+    private int mActiveDeviceLocalVoiceVol;
+    private boolean mActiveDeviceIsMuted;
+    private static final int VCP_MAX_VOL = 255;
+    private VcpController mVcpController;
+
+    private BroadcastReceiver mBondStateChangedReceiver;
+    private final ReentrantReadWriteLock mAcmNativeInterfaceLock = new ReentrantReadWriteLock();
+    public int mCsipAppId = -1;
+
+    private static final int SET_EBMONO_CFG = 1;
+    private static final int SET_EBSTEREO_CFG = 2;
+    private static final int MonoCfg_Timeout = 3000;
+    private static final int StereoCfg_Timeout = 3000;
+    private Handler mHandler = new Handler() {
+       @Override
+       public void handleMessage(Message msg)
+       {
+         synchronized(mBtLeaLock) {
+           switch (msg.what) {
+               case SET_EBMONO_CFG:
+                   Log.d(TAG, "setparameters to Mono");
+                   synchronized (mAudioManagerLock) {
+                        if(mAudioManager != null)
+                           mAudioManager.setParameters("LEAMono=true");
+                   }
+                   mLeaChannelMode = "mono";
+                   break;
+               case SET_EBSTEREO_CFG:
+                   Log.d(TAG, "setparameters to stereo");
+                   synchronized (mAudioManagerLock) {
+                       if(mAudioManager != null)
+                           mAudioManager.setParameters("LEAMono=false");
+                   }
+                   mLeaChannelMode = "stereo";
+                   break;
+              default:
+                   break;
+           }
+         }
+       }
+    };
+
+    @Override
+    protected void create() {
+        Log.i(TAG, "create()");
+    }
+
+    @Override
+    protected boolean start() {
+        Log.i(TAG, "start()");
+        String propValue;
+
+        if (sAcmService != null) {
+            Log.w(TAG, "AcmService is already running");
+            return true;
+        }
+
+        // Step 1: Get AdapterService, AcmNativeInterface.
+        // None of them can be null.
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+                "AdapterService cannot be null when AcmService starts");
+        mAcmNativeInterface = Objects.requireNonNull(AcmNativeInterface.getInstance(),
+                "AcmNativeInterface cannot be null when AcmService starts");
+
+        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+                "AdapterService cannot be null when StreamAudioService starts");
+
+        Log.i(TAG, "mAdapterService.isHostAdvAudioUnicastFeatureSupported() returned "
+                  + mAdapterService.isHostAdvAudioUnicastFeatureSupported());
+        Log.i(TAG, "mAdapterService.isHostAdvAudioStereoRecordingFeatureSupported() returned "
+                  + mAdapterService.isHostAdvAudioStereoRecordingFeatureSupported());
+        Log.i(TAG, "mAdapterService.isAdvUnicastAudioFeatEnabled() returned "
+                  + mAdapterService.isAdvUnicastAudioFeatEnabled());
+
+        // SOC supports unicast, host supports unicast and stereo recording
+        if (mAdapterService.isHostAdvAudioUnicastFeatureSupported() &&
+            mAdapterService.isHostAdvAudioStereoRecordingFeatureSupported() &&
+            mAdapterService.isAdvUnicastAudioFeatEnabled()) {
+
+            Log.i(TAG, "SOC supports unicast, host supports unicast, stereo recording");
+            // set properties only if they are not set to allow user enable/disable
+            // the features explicitly
+            propValue = SystemProperties.get("persist.vendor.service.bt.bap.enable_ucast");
+
+            if (propValue == null || propValue.length() == 0 || !propValue.equals("false")) {
+                SystemProperties.set("persist.vendor.service.bt.bap.enable_ucast", "true");
+            } else {
+                Log.i(TAG, "persist.vendor.service.bt.bap.enable_ucast is already set to "
+                        + propValue);
+            }
+
+            propValue = SystemProperties.get("persist.vendor.service.bt.recording_supported");
+            if (propValue == null || propValue.length() == 0 || !propValue.equals("false")) {
+                SystemProperties.set("persist.vendor.service.bt.recording_supported", "true");
+                 Log.i(TAG, "persist.vendor.service.bt.recording_supported set to true");
+            } else {
+                Log.i(TAG, "persist.vendor.service.bt.recording_supported is already set to "
+                        + propValue);
+            }
+        }
+
+        Log.i(TAG, "mAdapterService.isHostQHSFeatureSupported() returned "
+                  + mAdapterService.isHostQHSFeatureSupported());
+
+        // SOC supports unicast, host supports unicast and QHS
+        if (mAdapterService.isHostAdvAudioUnicastFeatureSupported() &&
+            mAdapterService.isHostQHSFeatureSupported() &&
+            mAdapterService.isAdvUnicastAudioFeatEnabled()) {
+
+            Log.i(TAG, "SOC supports unicast, host supports unicast, QHS");
+            // set properties only if they are not set to allow user enable/disable
+            // the features explicitly
+            propValue = SystemProperties.get("persist.vendor.btstack.qhs_enable");
+
+            if (propValue == null || propValue.length() == 0 || !propValue.equals("false")) {
+                SystemProperties.set("persist.vendor.btstack.qhs_enable", "true");
+            } else {
+                Log.i(TAG, "persist.vendor.service.bt.bap.enable_ucast is already set to "
+                        + propValue);
+            }
+        }
+
+        Log.i(TAG, "isHostAdvAudioLC3QFeatureSupported(): "
+                         + mAdapterService.isHostAdvAudioLC3QFeatureSupported());
+
+        // SOC supports unicast, host supports unicast and LC3Q
+        if (mAdapterService.isHostAdvAudioUnicastFeatureSupported() &&
+            mAdapterService.isHostAdvAudioLC3QFeatureSupported() &&
+            mAdapterService.isAdvUnicastAudioFeatEnabled()) {
+
+            Log.i(TAG, "host supports LC3Q");
+            // set properties only if they are not set to allow user enable/disable
+            // the features explicitly
+            propValue = SystemProperties.get("persist.vendor.service.bt.is_lc3q_supported");
+
+            if (propValue == null || propValue.length() == 0 || !propValue.equals("false")) {
+                SystemProperties.set("persist.vendor.service.bt.is_lc3q_supported", "true");
+            } else {
+                Log.i(TAG, "persist.vendor.service.bt.is_lc3q_supported is already set to "
+                        + propValue);
+            }
+        }
+
+        // Step 2: Get maximum number of connected audio devices
+        mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
+        Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices);
+
+
+        String LeaChannelMode = SystemProperties.get("persist.vendor.btstack.Lea.defaultchannelmode");
+        if (!LeaChannelMode.isEmpty() && "mono".equals(LeaChannelMode)) {
+            mLeaChannelMode = "mono";
+        }
+        Log.d(TAG, "Default LEA ChannelMode: " + LeaChannelMode);
+        // Step 3: Start handler thread for state machines
+        mStateMachines.clear();
+        mStateMachinesThread = new HandlerThread("AcmService.StateMachines");
+        mStateMachinesThread.start();
+
+        // Step 4: Setup codec config
+        mAcmCodecConfig = new AcmCodecConfig(this, mAcmNativeInterface);
+
+        if (mAdapterService.isAdvUnicastAudioFeatEnabled()) {
+          Log.d(TAG, "Initialize AcmNativeInterface");
+          // Step 5: Initialize native interface
+          mAcmNativeInterface.init(mMaxConnectedAudioDevices,
+                                   mAcmCodecConfig.codecConfigPriorities());
+        }
+
+        // Step 6: Setup broadcast receivers
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mBondStateChangedReceiver = new BondStateChangedReceiver();
+        registerReceiver(mBondStateChangedReceiver, filter);
+        synchronized (mAudioManagerLock) {
+             mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+             Objects.requireNonNull(mAudioManager,
+                                "AudioManager cannot be null when AcmService starts");
+             mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        }
+        // Step 7: Mark service as started
+        setAcmService(this);
+
+        //step 8: Register CSIP module
+        mCsipManager = new CsipManager();
+
+        //step 9: Get Vcp Controller
+        mVcpController = VcpController.make(this);
+        Objects.requireNonNull(mVcpController, "mVcpController cannot be null when AcmService starts");
+        return true;
+    }
+
+    @Override
+    protected boolean stop() {
+        Log.i(TAG, "stop()");
+        if (sAcmService == null) {
+            Log.w(TAG, "stop() called before start()");
+            return true;
+        }
+
+        // Step 9: do quit Vcp Controller
+        if (mVcpController != null) {
+            mVcpController.doQuit();
+        }
+
+        // Step 8: Mark service as stopped
+        setAcmService(null);
+
+        unregisterReceiver(mBondStateChangedReceiver);
+        mBondStateChangedReceiver = null;
+        // Step 6: Cleanup native interface
+        mAcmNativeInterface.cleanup();
+        mAcmNativeInterface = null;
+
+        // Step 5: Clear codec config
+        mAcmCodecConfig = null;
+
+        // Step 4: Destroy state machines and stop handler thread
+        synchronized (mStateMachines) {
+            for (AcmStateMachine sm : mStateMachines.values()) {
+                sm.doQuit();
+                sm.cleanup();
+            }
+            mStateMachines.clear();
+        }
+        mStateMachinesThread.quitSafely();
+        mStateMachinesThread = null;
+
+        // Step 2: Reset maximum number of connected audio devices
+        mMaxConnectedAudioDevices = 1;
+
+        // Step 1: Clear AdapterService, AcmNativeInterface, AudioManager
+        mAcmNativeInterface = null;
+        mAdapterService = null;
+        if (mAcmDevices != null)
+            mAcmDevices.clear();
+
+        mCsipManager.unregisterCsip();
+        mCsipManager = null;
+        return true;
+    }
+
+    @Override
+    protected void cleanup() {
+        Log.i(TAG, "cleanup()");
+    }
+
+    @Override
+    protected IProfileServiceBinder initBinder() {
+        return new AcmBinder(this);
+    }
+
+    private class BluetoothAcmDevice {
+        private BluetoothDevice mGrpDevice; // group bd address
+        private int mState;
+        private int msetID;
+
+        BluetoothAcmDevice(BluetoothDevice device, int state, int setID) {
+            mGrpDevice = device;
+            mState = state;
+            msetID = setID;
+        }
+    }
+
+    private BluetoothDevice getAddressFromString(String address) {
+        return mAdapter.getRemoteDevice(address);
+    }
+
+    public BluetoothDevice makeGroupBdAddress(BluetoothDevice device, int state, int setid) {
+        Log.i(TAG, " Set id : " + setid + " Num of connected acm devices: " + mAcmDevices.size());
+        boolean setIdMatched = false;
+        if (setid == INVALID_SET_ID) {
+            Log.d(TAG, "Device is not part of any group");
+            BluetoothAcmDevice acmDevice = new BluetoothAcmDevice(device, state, setid);
+            mAcmDevices.put(device, acmDevice);
+            mGroupBdAddress = acmDevice.mGrpDevice;
+            return mGroupBdAddress;
+        }
+       // BluetoothDevice bdaddr = null;
+        if (mAcmDevices == null) {
+            Log.d(TAG, "Hash Map is NULL");
+            return mGroupBdAddress;
+        }
+        if (mAcmDevices.containsKey(device)) {
+            Log.d(TAG, "Device is available in Hash Map");
+            BluetoothAcmDevice acmDevice = mAcmDevices.get(device);
+            mGroupBdAddress = acmDevice.mGrpDevice;
+            return mGroupBdAddress;
+        }
+        if (mAcmDevices.size() != 0) {
+            for (BluetoothDevice dm : mAcmDevices.keySet()) {
+                BluetoothAcmDevice d = mAcmDevices.get(dm);
+                if (d.msetID == setid) {
+                    setIdMatched = true;
+                    Log.d(TAG, "Device is part of same set ID");
+                    BluetoothAcmDevice acmDevice = new BluetoothAcmDevice(d.mGrpDevice, state, setid);
+                    mAcmDevices.put(device, acmDevice);
+                    mGroupBdAddress = acmDevice.mGrpDevice;
+                    break;
+                }
+            }
+        }
+        if (!setIdMatched) {
+            Log.d(TAG, "create new group or device is not part of existing set ID");
+            String address = "9E:8B:00:00:00:0";
+            BluetoothDevice bdaddr = getAddressFromString(address + setid);
+            BluetoothAcmDevice acmDevice = new BluetoothAcmDevice(bdaddr, state, setid);
+            mAcmDevices.put(device, acmDevice);
+            mGroupBdAddress = bdaddr;
+        }
+        return mGroupBdAddress;
+    }
+
+    public void handleAcmDeviceStateChange(BluetoothDevice device, int state, int setid) {
+        Log.d(TAG, "handleAcmDeviceStateChange: device: " + device + ", state: " + state
+                + " Set id : " + setid);
+        Log.i(TAG, " Num of connected ACM devices: " + mAcmDevices.size());
+        boolean update = false;
+        if (device == null || mAcmDevices.size() == 0)
+            return;
+        BluetoothAcmDevice acmDevice = mAcmDevices.get(device);
+        //check if current active group address is same as this device group address
+        if (acmDevice != null && mGroupBdAddress != acmDevice.mGrpDevice) {
+            Log.d(TAG, "Inactive device is disconnected");
+            update = true;
+        }
+        if (state == BluetoothProfile.STATE_DISCONNECTED) {
+            Log.d(TAG, "Remove Device from hash map");
+            mAcmDevices.remove(device);
+        } else {
+            acmDevice.mState = state;
+            Log.d(TAG, "Update state");
+        }
+        for (BluetoothDevice dm : mAcmDevices.keySet()) {
+            BluetoothAcmDevice d = mAcmDevices.get(dm);
+            if (d.msetID == setid) {
+                if (d.mState == BluetoothProfile.STATE_CONNECTED) {
+                    update = true;
+                    Log.d(TAG, "Atleast one member is connected");
+                    break;
+                }
+            }
+        }
+        if (!update) {
+            /*if (!mAcmNativeInterface.setActiveDevice(null, 0)) {//send unknown context type
+                Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native layer");
+            }*/
+        }
+    }
+
+    public static synchronized AcmService getAcmService() {
+        if (sAcmService == null) {
+            Log.w(TAG, "getAcmService(): service is null");
+            return null;
+        }
+        if (!sAcmService.isAvailable()) {
+            Log.w(TAG, "getAcmService(): service is not available");
+            return null;
+        }
+        return sAcmService;
+    }
+
+    private static synchronized void setAcmService(AcmService instance) {
+        if (DBG) {
+            Log.d(TAG, "setAcmService(): set to: " + instance);
+        }
+        sAcmService = instance;
+    }
+
+    public boolean connect(BluetoothDevice device, int contextType,
+                           int profileType, int preferredContext) {
+
+        if (DBG) {
+            Log.d(TAG, "connect(): " + device + " contextType: " + contextType
+                    + " profileType: " + profileType + " preferredContext: " + preferredContext);
+        }
+        if (device.getAddress().contains("9E:8B:00:00:00")) {
+            Log.d(TAG, "Connect request for group");
+            byte[] addrByte = Utils.getByteAddress(device);
+            int set_id = addrByte[5];
+            List<BluetoothDevice> d = mCsipManager.getSetMembers(set_id);
+            if (d == null) {
+                Log.d(TAG, "No set member found");
+                return false;
+            }
+            Iterator<BluetoothDevice> members = d.iterator();
+            if (members != null) {
+                while (members.hasNext()) {
+                    BluetoothDevice addr = members.next();
+                    Log.d(TAG, "connect member: " + addr);
+                    synchronized (mStateMachines) {
+                        if (!connectionAllowedCheckMaxDevices(addr)) {
+                            // when mMaxConnectedAudioDevices is one, disconnect current device first.
+                            if (mMaxConnectedAudioDevices == 1) {
+                                List<BluetoothDevice> sinks = getDevicesMatchingConnectionStates(
+                                        new int[] {BluetoothProfile.STATE_CONNECTED,
+                                                BluetoothProfile.STATE_CONNECTING,
+                                                BluetoothProfile.STATE_DISCONNECTING});
+                                for (BluetoothDevice sink : sinks) {
+                                    if (sink.equals(addr)) {
+                                        Log.w(TAG, "Connecting to device " + addr + " : disconnect skipped");
+                                        continue;
+                                    }
+                                    disconnect(sink, contextType);
+                                }
+                            } else {
+                                Log.e(TAG, "Cannot connect to " + addr + " : too many connected devices");
+                                return false;
+                            }
+                        }
+                        AcmStateMachine smConnect = getOrCreateStateMachine(addr);
+                        if (smConnect == null) {
+                            Log.e(TAG, "Cannot connect to " + addr + " : no state machine");
+                            return false;
+                        }
+                        Message msg = smConnect.obtainMessage(AcmStateMachine.CONNECT);
+                        msg.obj = preferredContext;
+                        msg.arg1 = contextType;
+                        msg.arg2 = profileType;
+                        smConnect.sendMessage(msg);
+                    }
+                }
+            }
+            return true;
+        }
+        synchronized (mStateMachines) {
+            if (!connectionAllowedCheckMaxDevices(device)) {
+                // when mMaxConnectedAudioDevices is one, disconnect current device first.
+                if (mMaxConnectedAudioDevices == 1) {
+                    List<BluetoothDevice> sinks = getDevicesMatchingConnectionStates(
+                            new int[] {BluetoothProfile.STATE_CONNECTED,
+                                    BluetoothProfile.STATE_CONNECTING,
+                                    BluetoothProfile.STATE_DISCONNECTING});
+                    for (BluetoothDevice sink : sinks) {
+                        if (sink.equals(device)) {
+                            Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
+                            continue;
+                        }
+                        disconnect(sink, contextType);
+                    }
+                } else {
+                    Log.e(TAG, "Cannot connect to " + device + " : too many connected devices");
+                    return false;
+                }
+            }
+            AcmStateMachine smConnect = getOrCreateStateMachine(device);
+            if (smConnect == null) {
+                Log.e(TAG, "Cannot connect to " + device + " : no state machine");
+                return false;
+            }
+            Message msg = smConnect.obtainMessage(AcmStateMachine.CONNECT);
+            msg.obj = preferredContext;
+            msg.arg1 = contextType;
+            msg.arg2 = profileType;
+            smConnect.sendMessage(msg);
+            return true;
+        }
+    }
+
+    /**
+     * Disconnects Acm for the remote bluetooth device
+     *
+     * @param device is the device with which we would like to disconnect acm
+     * @return true if profile disconnected, false if device not connected over acm
+     */
+    public boolean disconnect(BluetoothDevice device, int contextType) {
+
+        if (DBG) {
+            Log.d(TAG, "disconnect(): " + device);
+        }
+
+        if (device.getAddress().contains("9E:8B:00:00:00")) {
+            Log.d(TAG, "Disonnect request for group");
+            byte[] addrByte = Utils.getByteAddress(device);
+            int set_id = addrByte[5];
+            List<BluetoothDevice> d = mCsipManager.getSetMembers(set_id);
+            if (d == null) {
+                Log.d(TAG, "No set member found");
+                return false;
+            }
+            Iterator<BluetoothDevice> members = d.iterator();
+            if (members != null) {
+                while (members.hasNext()) {
+                    BluetoothDevice addr = members.next();
+                    Log.d(TAG, "disconnect member: " + device);
+                    synchronized (mStateMachines) {
+                        AcmStateMachine sm = mStateMachines.get(addr);
+                        if (sm == null) {
+                            Log.e(TAG, "Ignored disconnect request for " + addr + " : no state machine");
+                            return false;
+                        }
+                        Message msg = sm.obtainMessage(AcmStateMachine.DISCONNECT);
+                        msg.obj = contextType;
+                        sm.sendMessage(msg);
+                    }
+                }
+                return true;
+            }
+        }
+        synchronized (mStateMachines) {
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine");
+                return false;
+            }
+            Message msg = sm.obtainMessage(AcmStateMachine.DISCONNECT);
+            msg.obj = contextType;
+            sm.sendMessage(msg);
+            return true;
+        }
+    }
+
+    public List<BluetoothDevice> getConnectedDevices() {
+
+        synchronized (mStateMachines) {
+            List<BluetoothDevice> devices = new ArrayList<>();
+            for (AcmStateMachine sm : mStateMachines.values()) {
+                if (sm.isConnected()) {
+                    devices.add(sm.getDevice());
+                }
+            }
+            return devices;
+        }
+    }
+
+    //check if it can be a list ?
+    public BluetoothDevice getCsipLockRequestedDevice() {
+
+        synchronized (mStateMachines) {
+            BluetoothDevice device = null;
+            for (AcmStateMachine sm : mStateMachines.values()) {
+                if (sm.isCsipLockRequested()) {
+                    device = sm.getDevice();
+                }
+            }
+            return device;
+        }
+    }
+
+    public boolean IsLockSupportAvailable(BluetoothDevice device) {
+        boolean isLockSupported = false;
+        /*int setId = mSetCoordinator.getRemoteSetId(device, ACM_UUID);
+        DeviceGroup set = mSetCoordinator.getDeviceGroup(setId);
+        isLockSupported = set.mLockSupport;*/
+        //isLockSupported = mAdapterService.isCsipLockSupport(device);
+        Log.d(TAG, "Exclusive Access SupportAvaible for:" + device + "returns " + isLockSupported);
+        return isLockSupported;
+    }
+
+    /**
+     * Check whether can connect to a peer device.
+     * The check considers the maximum number of connected peers.
+     *
+     * @param device the peer device to connect to
+     * @return true if connection is allowed, otherwise false
+     */
+    private boolean connectionAllowedCheckMaxDevices(BluetoothDevice device) {
+        int connected = 0;
+        // Count devices that are in the process of connecting or already connected
+        synchronized (mStateMachines) {
+            for (AcmStateMachine sm : mStateMachines.values()) {
+                switch (sm.getConnectionState()) {
+                    case BluetoothProfile.STATE_CONNECTING:
+                    case BluetoothProfile.STATE_CONNECTED:
+                        if (Objects.equals(device, sm.getDevice())) {
+                            return true;    // Already connected or accounted for
+                        }
+                        connected++;
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+        return (connected < mMaxConnectedAudioDevices);
+    }
+
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+
+        List<BluetoothDevice> devices = new ArrayList<>();
+        if (states == null) {
+            return devices;
+        }
+        final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
+        if (bondedDevices == null) {
+            return devices;
+        }
+        synchronized (mStateMachines) {
+            for (BluetoothDevice device : bondedDevices) {
+                /*if (!ArrayUtils.contains(mAdapterService.getRemoteUuids(device),
+                                                 BluetoothUuid.ACM_SINK)) {
+                    continue;
+                }*/
+                int connectionState = BluetoothProfile.STATE_DISCONNECTED;
+                AcmStateMachine sm = mStateMachines.get(device);
+                if (sm != null) {
+                    connectionState = sm.getConnectionState();
+                }
+                for (int state : states) {
+                    if (connectionState == state) {
+                        devices.add(device);
+                        break;
+                    }
+                }
+            }
+            return devices;
+        }
+    }
+
+    /**
+     * Get the list of devices that have state machines.
+     *
+     * @return the list of devices that have state machines
+     */
+    @VisibleForTesting
+    List<BluetoothDevice> getDevices() {
+        List<BluetoothDevice> devices = new ArrayList<>();
+        synchronized (mStateMachines) {
+            for (AcmStateMachine sm : mStateMachines.values()) {
+                devices.add(sm.getDevice());
+            }
+            return devices;
+        }
+    }
+
+    public int getConnectionState(BluetoothDevice device) {
+
+        synchronized (mStateMachines) {
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+            return sm.getConnectionState();
+        }
+    }
+
+    public int getCsipConnectionState(BluetoothDevice device) {
+
+        synchronized (mStateMachines) {
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+            return sm.getCsipConnectionState();
+        }
+    }
+
+    // Handle messages from native (JNI) to Java
+    void messageFromNative(AcmStackEvent stackEvent) {
+        Objects.requireNonNull(stackEvent.device,
+                               "Device should never be null, event: " + stackEvent);
+        synchronized (mStateMachines) {
+            BluetoothDevice device = stackEvent.device;
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                if (stackEvent.type == AcmStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
+                    switch (stackEvent.valueInt1) {
+                        case AcmStackEvent.CONNECTION_STATE_CONNECTED:
+                        case AcmStackEvent.CONNECTION_STATE_CONNECTING:
+                            // Create a new state machine only when connecting to a device
+                            if (!connectionAllowedCheckMaxDevices(device)) {
+                                Log.e(TAG, "Cannot connect to " + device
+                                        + " : too many connected devices");
+                                return;
+                            }
+                            sm = getOrCreateStateMachine(device);
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+            if (sm == null) {
+                Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent);
+                return;
+            }
+            sm.sendMessage(AcmStateMachine.STACK_EVENT, stackEvent);
+        }
+    }
+
+    private AcmStateMachine getOrCreateStateMachine(BluetoothDevice device) {
+        if (device == null) {
+            Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
+            return null;
+        }
+        synchronized (mStateMachines) {
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm != null) {
+                return sm;
+            }
+            // Limit the maximum number of state machines to avoid DoS attack
+            if (mStateMachines.size() >= MAX_ACM_STATE_MACHINES) {
+                Log.e(TAG, "Maximum number of ACM state machines reached: "
+                        + MAX_ACM_STATE_MACHINES);
+                return null;
+            }
+            if (DBG) {
+                Log.d(TAG, "Creating a new state machine for " + device);
+            }
+            sm = AcmStateMachine.make(device, this, mAcmNativeInterface,
+                                      mStateMachinesThread.getLooper());
+            mStateMachines.put(device, sm);
+            return sm;
+        }
+    }
+
+    private class BondStateChangedReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
+                return;
+            }
+            int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                                           BluetoothDevice.ERROR);
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+            Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
+            bondStateChanged(device, state);
+        }
+    }
+
+    /**
+     * Process a change in the bonding state for a device.
+     *
+     * @param device the device whose bonding state has changed
+     * @param bondState the new bond state for the device. Possible values are:
+     * {@link BluetoothDevice#BOND_NONE},
+     * {@link BluetoothDevice#BOND_BONDING},
+     * {@link BluetoothDevice#BOND_BONDED}.
+     */
+    @VisibleForTesting
+    void bondStateChanged(BluetoothDevice device, int bondState) {
+        if (DBG) {
+            Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState);
+        }
+        // Remove state machine if the bonding for a device is removed
+        if (bondState != BluetoothDevice.BOND_NONE) {
+            return;
+        }
+        synchronized (mStateMachines) {
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                return;
+            }
+            if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
+                return;
+            }
+        }
+        removeStateMachine(device);
+    }
+
+    private void removeStateMachine(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                Log.w(TAG, "removeStateMachine: device " + device
+                        + " does not have a state machine");
+                return;
+            }
+            Log.i(TAG, "removeStateMachine: removing state machine for device: " + device);
+            sm.doQuit();
+            sm.cleanup();
+            mStateMachines.remove(device);
+            mAcmDevices.remove(device);
+        }
+    }
+
+    void updateLeaChannelMode(int state, BluetoothDevice device) {
+        BluetoothDevice peerLeaDevice = null;
+        peerLeaDevice = getLeaPeerDevice(device);
+        if (peerLeaDevice == null) {
+            Log.d(TAG, "updateLeaChannelMode: peer device is NULL");
+            return;
+        }
+        Log.d(TAG, "LeaChannelMode: " + mLeaChannelMode + "state: " + state);
+        synchronized(mBtLeaLock) {
+            if ("mono".equals(mLeaChannelMode)) {
+                if ((state == BluetoothA2dp.STATE_PLAYING) && (peerLeaDevice!= null)
+                     && peerLeaDevice.isConnected() && isAcmPlayingMusic(peerLeaDevice)) {
+                    Log.d(TAG, "updateLeaChannelMode: send delay message to set stereo ");
+                    Message msg = mHandler.obtainMessage(SET_EBSTEREO_CFG);
+                    mHandler.sendMessageDelayed(msg, StereoCfg_Timeout);
+                } else if (state == BluetoothA2dp.STATE_PLAYING) {
+                    Log.d(TAG, "updateLeaChannelMode: setparameters to Mono");
+                    synchronized (mAudioManagerLock) {
+                        if (mAudioManager != null) {
+                            Log.d(TAG, "updateLeaChannelMode: Acquired mVariableLock");
+                            mAudioManager.setParameters("LeaChannelConfig=mono");
+                        }
+                    }
+                    Log.d(TAG, "updateLeaChannelMode: Released mVariableLock");
+                }
+                if ((state == BluetoothA2dp.STATE_NOT_PLAYING) &&
+                       isAcmPlayingMusic(peerLeaDevice)) {
+                    if (mHandler.hasMessages(StereoCfg_Timeout)) {
+                        Log.d(TAG, "updateLeaChannelMode:remove delay message for stereo");
+                        mHandler.removeMessages(StereoCfg_Timeout);
+                    }
+                }
+            } else if ("stereo".equals(mLeaChannelMode)) {
+                if ((state == BluetoothA2dp.STATE_PLAYING) &&
+                     (getConnectionState(peerLeaDevice) != BluetoothProfile.STATE_CONNECTED
+                     || !isAcmPlayingMusic(peerLeaDevice))) {
+                    Log.d(TAG, "updateLeaChannelMode: send delay message to set mono");
+                    Message msg = mHandler.obtainMessage(SET_EBMONO_CFG);
+                    mHandler.sendMessageDelayed(msg, MonoCfg_Timeout);
+                }
+                if ((state == BluetoothA2dp.STATE_PLAYING) && isAcmPlayingMusic(peerLeaDevice)) {
+                    if (mHandler.hasMessages(SET_EBMONO_CFG)) {
+                        Log.d(TAG, "updateLeaChannelMode: remove delay message to set mono");
+                        mHandler.removeMessages(SET_EBMONO_CFG);
+                    }
+                }
+                if ((state == BluetoothA2dp.STATE_NOT_PLAYING) && isAcmPlayingMusic(peerLeaDevice)) {
+                    Log.d(TAG, "setparameters to Mono");
+                    synchronized (mAudioManagerLock) {
+                        if (mAudioManager != null)
+                            mAudioManager.setParameters("LeaChannelConfig=mono");
+                    }
+                    mLeaChannelMode = "mono";
+                }
+            }
+        }
+    }
+
+    private BluetoothDevice getLeaPeerDevice(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                return null;
+            }
+            return sm.getPeerDevice();
+        }
+    }
+
+    public boolean isPeerDeviceConnected(BluetoothDevice device, int setid) {
+        boolean isConnected = false;
+        if (mAcmDevices.size() != 0) {
+            for (BluetoothDevice dm : mAcmDevices.keySet()) {
+                BluetoothAcmDevice d = mAcmDevices.get(dm);
+                if ((d.msetID == setid) && !Objects.equals(dm, device)) {
+                    if (d.mState == BluetoothProfile.STATE_CONNECTED) {
+                        isConnected = true;
+                        Log.d(TAG, "At least one member is in connected state");
+                        break;
+                    }
+                }
+            }
+        }
+        return isConnected;
+    }
+
+    public boolean isPeerDeviceStreamingMusic(BluetoothDevice device, int setid) {
+        boolean isStreaming = false;
+        if (mAcmDevices.size() != 0) {
+            for (BluetoothDevice dm : mAcmDevices.keySet()) {
+                BluetoothAcmDevice d = mAcmDevices.get(dm);
+                if ((d.msetID == setid) && !Objects.equals(dm, device)) {
+                    if (d.mState == BluetoothProfile.STATE_CONNECTED) {
+                        synchronized (mBtAcmLock) {
+                            AcmStateMachine sm = mStateMachines.get(dm);
+                            if (sm == null) {
+                                return false;
+                            }
+                            if (sm.isMusicPlaying()) {
+                                isStreaming = true;
+                                Log.d(TAG, "At least one member is streaming for music");
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return isStreaming;
+    }
+
+    public boolean isShoPendingStop() {
+        Log.d(TAG, "isShoPendingStop " + mShoPend);
+        return mShoPend;
+    }
+
+    public void resetShoPendingStop() {
+        mShoPend = false;
+    }
+
+    public boolean isVoiceShoPendingStop() {
+        Log.d(TAG, "isVoiceShoPendingStop " + mVoiceShoPend);
+        return mVoiceShoPend;
+    }
+
+    public void resetVoiceShoPendingStop() {
+        mVoiceShoPend = false;
+    }
+
+    public BluetoothDevice getVoiceActiveDevice() {
+        return mActiveDeviceVoice;
+    }
+
+    public void removePeersFromBgWl(BluetoothDevice device, int setid) {
+        synchronized (mStateMachines) {
+            BluetoothDevice d = null;
+            List<BluetoothDevice> members = mCsipManager.getSetMembers(setid);
+            if (members == null) {
+                Log.d(TAG, "No set member found");
+                return;
+            }
+            Iterator<BluetoothDevice> i = members.iterator();
+            if (i != null) {
+                while (i.hasNext()) {
+                    d = i.next();
+                    if (!(Objects.equals(d, device))) {
+                        Log.d(TAG, "Device: " + d);
+                        AcmStateMachine sm = mStateMachines.get(d);
+                        if (sm == null) {
+                            return;
+                        }
+                        sm.removeDevicefromBgWL();
+                    }
+                }
+            }
+        }
+    }
+
+    public boolean isAcmPlayingMusic(BluetoothDevice device) {
+        if (DBG) {
+            Log.d(TAG, "isAcmPlayingMusic(" + device + ")");
+        }
+        synchronized (mBtAcmLock) {
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                return false;
+            }
+            return sm.isMusicPlaying();
+        }
+    }
+
+    public boolean isAcmPlayingVoice(BluetoothDevice device) {
+        if (DBG) {
+            Log.d(TAG, "isAcmPlayingVoice(" + device + ")");
+        }
+        synchronized (mBtAcmLock) {
+            AcmStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                return false;
+            }
+            return sm.isVoicePlaying();
+        }
+    }
+
+    public BluetoothDevice getGroup(BluetoothDevice device) {
+        Log.d(TAG, "Get group address for (" + device + ")");
+        if (device.getAddress().contains("9E:8B:00:00:00")) {
+            Log.d(TAG, "Called for group address");
+            return device;
+        }
+        BluetoothDevice dm = null;
+
+        if (mAcmDevices != null) {
+            Log.d(TAG, "Hash Map is not NULL");
+            BluetoothAcmDevice d = mAcmDevices.get(device);
+            if (d != null)
+                dm = d.mGrpDevice;
+        }
+        if (dm == null) {
+            Log.d(TAG, "Group address is NULL, make New");
+            int Id = mCsipManager.getCsipSetId(device, null /*ACM_UUID*/); //TODO: UUID what to set ?
+            dm = makeGroupBdAddress(device, BluetoothProfile.STATE_DISCONNECTED, Id);
+        }
+        return dm;
+    }
+
+    public int setActiveDevice(BluetoothDevice device, int contextType, int profileType, boolean playReq) {
+
+        Log.d(TAG, "setActiveDevice: " + device + " contextType: " + contextType + " profileType: " + profileType +
+                " play req: " + playReq + " mActiveDeviceProfile: " + mActiveDeviceProfile+ " mActiveDeviceVoiceProfile: " + mActiveDeviceVoiceProfile);
+
+        if (Objects.equals(device, mActiveDevice) && contextType == CONTEXT_TYPE_MUSIC && (mActiveDeviceProfile == profileType)) {
+            Log.e(TAG, "setActiveDevice(" + device + "): already set to active for media and profileType same as active profile");
+            return ActiveDeviceManagerService.ALREADY_ACTIVE;
+        }
+        if (Objects.equals(device, mActiveDeviceVoice) && contextType == CONTEXT_TYPE_VOICE && (mActiveDeviceVoiceProfile == profileType)) {
+            Log.e(TAG, "setActiveDevice(" + device + "): already set to active for voice and profileType same as active profile");
+            return ActiveDeviceManagerService.ALREADY_ACTIVE;
+        }
+        if (contextType == CONTEXT_TYPE_MUSIC) {
+            mShoPend = false;
+            if ((device == null) && (mActiveDevice != null)) {
+                if (mActiveDevice.getAddress().contains("9E:8B:00:00:00")) {
+                    byte[] addrByte = Utils.getByteAddress(mActiveDevice);
+                    int set_id = addrByte[5];
+                    List<BluetoothDevice> members = mCsipManager.getSetMembers(set_id);
+                    if (members == null) {
+                        Log.d(TAG, "No set member found");
+                    }
+                    Iterator<BluetoothDevice> i = members.iterator();
+                    if (i != null) {
+                        while (i.hasNext()) {
+                            BluetoothDevice addr = i.next();
+                            Log.d(TAG, "isAcmPlayingMusic(addr) " + isAcmPlayingMusic(addr));
+                            if (isAcmPlayingMusic(addr)) {
+                                mShoPend = true;
+                                break;
+                            }
+                        }
+                    }
+                } else {
+                    Log.d(TAG, "TWM active device");
+                    mShoPend = isAcmPlayingMusic(mActiveDevice);
+                }
+            }
+            Log.d(TAG, "mShoPend " + mShoPend);
+        } else if (contextType == CONTEXT_TYPE_VOICE) {
+            mVoiceShoPend = false;
+            if (mActiveDeviceVoice != null) {
+                if (mActiveDeviceVoice.getAddress().contains("9E:8B:00:00:00")) {
+                    byte[] addrByte = Utils.getByteAddress(mActiveDeviceVoice);
+                    int set_id = addrByte[5];
+                    List<BluetoothDevice> members = mCsipManager.getSetMembers(set_id);
+                    if (members == null) {
+                        Log.d(TAG, "No set member found");
+                    }
+                    Iterator<BluetoothDevice> i = members.iterator();
+                    if (i != null) {
+                        while (i.hasNext()) {
+                            BluetoothDevice addr = i.next();
+                            Log.d(TAG, "isAcmPlayingVoice(addr) " + isAcmPlayingVoice(addr));
+                            if (isAcmPlayingVoice(addr)) {
+                                mVoiceShoPend = true;
+                                break;
+                            }
+                        }
+                    }
+                } else {
+                    Log.d(TAG, "TWM active device");
+                    mVoiceShoPend = isAcmPlayingVoice(mActiveDeviceVoice);
+                }
+            }
+            Log.d(TAG, "mVoiceShoPend " + mVoiceShoPend);
+        }
+        Log.d(TAG, "old mActiveDevice: " + mActiveDevice + " & old mActiveDeviceVoice: " + mActiveDeviceVoice);
+
+        if (setActiveDeviceAcm(device, contextType, profileType)) {
+            if (contextType == CONTEXT_TYPE_MUSIC) {
+              mActiveDevice = device;
+              mActiveDeviceProfile = profileType;
+            } else if (contextType == CONTEXT_TYPE_VOICE) {
+              mActiveDeviceVoice = device;
+              mActiveDeviceVoiceProfile = profileType;
+            }
+            Log.d(TAG, "new mActiveDevice: " + mActiveDevice + " & new mActiveDeviceVoice: " + mActiveDeviceVoice);
+            if(!playReq) {
+               if (mShoPend || mVoiceShoPend) {
+                   Log.d(TAG, "setActiveDevice(" + device + "): returns with status " + ActiveDeviceManagerService.SHO_PENDING);
+                   return ActiveDeviceManagerService.SHO_PENDING;
+               } else {
+                   Log.d(TAG, "setActiveDevice(" + device + "): returns with status " + ActiveDeviceManagerService.SHO_SUCCESS);
+                   return ActiveDeviceManagerService.SHO_SUCCESS;
+               }
+            } else {
+                Log.d(TAG, "setActiveDevice(" + device + "): returns with status " + ActiveDeviceManagerService.SHO_PENDING);
+                return ActiveDeviceManagerService.SHO_PENDING;
+            }
+        }
+        Log.d(TAG, "setActiveDevice(" + device + "): returns with status " + ActiveDeviceManagerService.SHO_FAILED);
+        mShoPend = false;
+        mVoiceShoPend = false;
+        return ActiveDeviceManagerService.SHO_FAILED;
+    }
+
+    private boolean setActiveDeviceAcm(BluetoothDevice device, int contextType, int profileType) {
+        Log.d(TAG, "setActiveDeviceAcm: " + device);
+        try {
+            mAcmNativeInterfaceLock.readLock().lock();
+            if (mAcmNativeInterface != null && !mAcmNativeInterface.setActiveDevice(device, profileType)) {
+              Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer");
+              return false;
+            }
+        } finally {
+            mAcmNativeInterfaceLock.readLock().unlock();
+        }
+        Log.d(TAG, "setActiveDeviceAcm(" + device + "): returns true");
+        return true;
+    }
+
+    public void setCodecConfigPreference(BluetoothDevice device,
+                                          BluetoothCodecConfig codecConfig, int contextType) {
+
+        if (DBG) {
+            Log.d(TAG, "setCodecConfigPreference(" + device + "): "
+                    + Objects.toString(codecConfig));
+        }
+        mAcmCodecConfig.setCodecConfigPreference(device, codecConfig, contextType);
+    }
+
+    public void ChangeCodecConfigPreference(BluetoothDevice device,
+                                            String mesg) {
+
+        if (DBG) {
+            Log.d(TAG, "ChangeCodecConfigPreference " + device + "string: " + mesg);
+        }
+        if (device == null)
+            return;
+        mAcmName = mesg;
+        synchronized (mStateMachines) {
+            if (mAcmDevices.size() != 0) {
+                for (BluetoothDevice dm : mAcmDevices.keySet()) {
+                    BluetoothAcmDevice d = mAcmDevices.get(dm);
+                    if (Objects.equals(device, d.mGrpDevice)) {
+                        if (d.mState == BluetoothProfile.STATE_CONNECTED) {
+                            AcmStateMachine sm = getOrCreateStateMachine(dm);
+                            if (sm == null) {
+                                //TODO:handle this case
+                                continue;
+                            }
+                            Message msg = sm.obtainMessage(AcmStateMachine.CODEC_CONFIG_CHANGED);
+                            msg.obj = d.msetID;
+                            sm.sendMessage(msg);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public boolean StartStream(BluetoothDevice device, int contextType) {
+        if (DBG) {
+            Log.d(TAG, "startStream for " + device+ " context type: " + contextType);
+        }
+        if (device == null)
+            return false;
+        synchronized (mStateMachines) {
+            if (mAcmDevices.size() != 0) {
+                for (BluetoothDevice dm : mAcmDevices.keySet()) {
+                    BluetoothAcmDevice d = mAcmDevices.get(dm);
+                    if (Objects.equals(device, d.mGrpDevice)) {
+                        if (d.mState == BluetoothProfile.STATE_CONNECTED) {
+                            AcmStateMachine sm = getOrCreateStateMachine(dm);
+                            if (sm == null) {
+                                //TODO:handle this case
+                                continue;
+                            }
+                            Message msg = sm.obtainMessage(AcmStateMachine.START_STREAM);
+                            msg.obj = contextType;
+                            sm.sendMessage(msg);
+                        }
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    public boolean StopStream(BluetoothDevice device, int contextType) {
+        if (DBG) {
+            Log.d(TAG, "stopStream for " + device+ " context type: " + contextType);
+        }
+        if (device == null)
+            return false;
+        synchronized (mStateMachines) {
+            if (mAcmDevices.size() != 0) {
+                for (BluetoothDevice dm : mAcmDevices.keySet()) {
+                    BluetoothAcmDevice d = mAcmDevices.get(dm);
+                    if (Objects.equals(device, d.mGrpDevice)) {
+                        if (d.mState == BluetoothProfile.STATE_CONNECTED) {
+                            AcmStateMachine sm = getOrCreateStateMachine(dm);
+                            if (sm == null) {
+                                //TODO:handle this case
+                                continue;
+                            }
+                            Message msg = sm.obtainMessage(AcmStateMachine.STOP_STREAM);
+                            msg.obj = contextType;
+                            sm.sendMessage(msg);
+                        }
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    public void setAbsoluteVolume(BluetoothDevice grpAddr, int volumeLevel, int audioType) {
+        //Convert volume level to VCP level before sending.
+        int vcpVolume = convertToVcpVolume(volumeLevel);
+        BluetoothDevice activeDevice = null;
+        Log.d(TAG, "AudioVolumeLevel " + volumeLevel + " vcpVolume "  + vcpVolume);
+
+        if (audioType == ApmConst.AudioFeatures.MEDIA_AUDIO ||
+                audioType == ApmConst.AudioFeatures.CALL_AUDIO) {
+            ActiveDeviceManagerService activeDeviceManager =
+                    ActiveDeviceManagerService.get(this);
+            activeDevice = activeDeviceManager.getActiveDevice(audioType);
+            Log.d(TAG, "activeDevice " + activeDevice + " grpAddr "  + grpAddr
+                    + " audioType " + audioType);
+            if (!grpAddr.equals(activeDevice)) {
+                Log.e(TAG, "Ignore setAbsoluteVolume for inactive device");
+                return;
+            }
+        } else if (audioType == ApmConst.AudioFeatures.BROADCAST_AUDIO) {
+            Log.d(TAG, "No active device, grpAddr " + grpAddr + " is broadcast device or group");
+        } else {
+            Log.e(TAG, "Invalid audio type for set volume, ignore this request");
+            return;
+        }
+
+        if (audioType == ApmConst.AudioFeatures.MEDIA_AUDIO) {
+            mActiveDeviceLocalMediaVol = volumeLevel;
+        } else if (audioType == ApmConst.AudioFeatures.CALL_AUDIO) {
+            mActiveDeviceLocalVoiceVol = volumeLevel;
+        }
+
+        if (grpAddr.getAddress().contains("9E:8B:00:00:00")) {
+            Log.d(TAG, "setAbsoluteVolume for group addr, send abs vol to all members");
+            byte[] addrByte = Utils.getByteAddress(grpAddr);
+            int set_id = addrByte[5];
+            List<BluetoothDevice> members = mCsipManager.getSetMembers(set_id);
+            if (members == null) {
+                Log.d(TAG, "No set member found");
+                return;
+            }
+            Iterator<BluetoothDevice> i = members.iterator();
+            if (i != null) {
+              while (i.hasNext()) {
+                BluetoothDevice addr = i.next();
+                Log.d(TAG, "send vol to member: " + addr);
+                mVcpController.setAbsoluteVolume(addr, vcpVolume, audioType);
+              }
+            }
+        } else {
+            Log.d(TAG, "setAbsoluteVolume for single addr, send abs vol to " + grpAddr);
+            mVcpController.setAbsoluteVolume(grpAddr, vcpVolume, audioType);
+        }
+    }
+
+    public void setMute(BluetoothDevice grpAddr, boolean enableMute) {
+        if (mActiveDevice == null) {
+            Log.d(TAG, "No active device set, this grpAddr " + grpAddr + " is broadcast group");
+        } else {
+            Log.d(TAG, "mActiveDevice " + mActiveDevice + " grpAddr "  + grpAddr);
+        }
+
+        if (grpAddr.getAddress().contains("9E:8B:00:00:00")) {
+            Log.d(TAG, "setMute for group addr, send mute/unmute to all members");
+            byte[] addrByte = Utils.getByteAddress(grpAddr);
+            int set_id = addrByte[5];
+            List<BluetoothDevice> members = mCsipManager.getSetMembers(set_id);
+            if (members == null) {
+                Log.d(TAG, "No set member found");
+                return;
+            }
+            Iterator<BluetoothDevice> i = members.iterator();
+            if (i != null) {
+              while (i.hasNext()) {
+                BluetoothDevice addr = i.next();
+                Log.d(TAG, "send setMute to member: " + addr);
+                mVcpController.setMute(addr, enableMute);
+              }
+            }
+        } else {
+            Log.d(TAG, "setMute for single addr, send mute/unmute to " + grpAddr);
+            mVcpController.setMute(grpAddr, enableMute);
+        }
+    }
+
+    public void onVolumeStateChanged(BluetoothDevice device, int vol, int audioType) {
+        VolumeManager service = VolumeManager.get();
+        //Get or Make group address
+        BluetoothDevice grpDev = getGroup(device);
+
+        if (audioType == ApmConst.AudioFeatures.BROADCAST_AUDIO) {
+            Log.d(TAG, "volume notification for Broadcast audio");
+        } else if (audioType == ApmConst.AudioFeatures.MEDIA_AUDIO) {
+            Log.d(TAG, "volume notification for Media audio");
+        } else if (audioType == ApmConst.AudioFeatures.CALL_AUDIO) {
+            Log.d(TAG, "volume notification for Call audio");
+        } else {
+            // remote volume change case
+            Log.d(TAG, "Volume change from remote: " + device +  " vcp vol " + vol);
+            audioType = service.getActiveAudioType(device);
+        }
+
+        //Convert vol
+        int audioVolume = convertToAudioStreamVolume(vol);
+        Log.d(TAG, "vcp vol " + vol + " audioVolume " + audioVolume);
+
+        if (audioType == ApmConst.AudioFeatures.BROADCAST_AUDIO) {
+            Log.d(TAG, "update volume to APM for Broadcast audio");
+            service.onVolumeChange(grpDev, audioVolume, audioType);
+        } else if (audioType == ApmConst.AudioFeatures.MEDIA_AUDIO) {
+            //Check if new vol is diff than active group vol
+            Log.d(TAG, "new vol: " + audioVolume + " mActiveDeviceLocalMediaVol: " + mActiveDeviceLocalMediaVol);
+            if (mActiveDeviceLocalMediaVol != audioVolume) {
+                Log.d(TAG, "new vol is different than mActiveDeviceLocalMediaVol update APM");
+                service.onVolumeChange(grpDev, audioVolume, audioType);
+                mActiveDeviceLocalMediaVol = audioVolume;
+            } else {
+                Log.d(TAG, "local active media vol same as device new vol, ignore sending to APM");
+            }
+        } else if (audioType == ApmConst.AudioFeatures.CALL_AUDIO) {
+            Log.d(TAG, "new vol: " + audioVolume + " mActiveDeviceLocalVoiceVol: " + mActiveDeviceLocalVoiceVol);
+            if (mActiveDeviceLocalVoiceVol != audioVolume) {
+                Log.d(TAG, "new vol is different than mActiveDeviceLocalVoiceVol update APM");
+                service.onVolumeChange(grpDev, audioVolume, audioType);
+                mActiveDeviceLocalVoiceVol = audioVolume;
+            } else {
+                Log.d(TAG, "local active call vol same as device new vol, ignore sending to APM");
+            }
+        } else {
+            Log.d(TAG, "No audio is streaming and is inactive device, ignore sending to APM");
+        }
+
+        //Check if this device is group device, then take lock (ignored for now) and update vol to other members as well.
+        applyVolToOtherMembers(device, mCsipManager.getCsipSetId(device, null /*ACM_UUID*/), vol, audioType);
+    }
+
+    public void onMuteStatusChanged(BluetoothDevice device, boolean isMuted) {
+        VolumeManager service = VolumeManager.get();
+        //Get or Make group address
+        BluetoothDevice grpDev = getGroup(device);
+        //default context type
+        int c_type = CONTEXT_TYPE_UNKNOWN;
+
+        Log.d(TAG, "isMuted " + isMuted + " mActiveDeviceIsMuted " + mActiveDeviceIsMuted);
+
+        if (!mVcpController.isBroadcastDevice(device) && ((mActiveDevice == null) || (mActiveDevice != grpDev))) {
+            Log.d(TAG, "unicast-mode, either Active device null or muteStatus changed for inactive device -- return");
+            return;
+        }
+
+        if ((mActiveDevice == null) || (mActiveDevice != grpDev)) {
+            Log.d(TAG, "No Active device or device is not active, seems in Broadcast mode");
+            c_type = CONTEXT_TYPE_BROADCAST_AUDIO;
+        } else {
+            Log.d(TAG, "mActiveDevice " + mActiveDevice + " grpDev "  + grpDev + " device " + device);
+            c_type = CONTEXT_TYPE_MUSIC;
+        }
+
+        //Check if new mute state is diff than active group mute state
+        if (mActiveDeviceIsMuted != isMuted) {
+            Log.d(TAG, "new mute state is different than mActiveDeviceIsMuted update APM");
+            service.onMuteStatusChange(grpDev, isMuted, CONTEXT_TYPE_MUSIC);
+            mActiveDeviceIsMuted = isMuted;
+        } else {
+            Log.d(TAG, "local active device mute state same as device new mute state, ignore sending to APM, but send to other members");
+        }
+
+        //Check if this device is group device, then take lock(ignored for now) and update mute state to other members as well.
+        applyMuteStateToOtherMembers(device, mCsipManager.getCsipSetId(device, null /*ACM_UUID*/), isMuted);
+    }
+
+    public void setAbsVolSupport(BluetoothDevice device, boolean isAbsVolSupported, int initialVol) {
+        VolumeManager service = VolumeManager.get();
+        //Get or Make group address
+        BluetoothDevice grpDev = getGroup(device);
+        if (grpDev == null) {
+            Log.d(TAG, "Group not created, return");
+            return;
+        }
+        if ((mVcpController.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED) && isAbsVolSupported) {
+            if (!isVcpPeerDeviceConnected(device, mCsipManager.getCsipSetId(device, null /*ACM_UUID*/))) {
+                Log.d(TAG, "VCP Peer device not connected, this is 1st member, update support to APM ");
+                //get current vol from VCP and send to APM during connection.
+                Log.d(TAG, "VCP initialVol " + initialVol + " when connected device " + device);
+                Log.d(TAG, "Update APM with connection vol " + convertToAudioStreamVolume(initialVol));
+                service.setAbsoluteVolumeSupport(grpDev, isAbsVolSupported, convertToAudioStreamVolume(initialVol), ApmConst.AudioProfiles.VCP);
+
+                //get current mute state from VCP and sent to APM during connection.
+                Log.d(TAG, "VCP mute state " + mVcpController.isMute(device) + " when connected device " + device);
+                service.onMuteStatusChange(grpDev, mVcpController.isMute(device), CONTEXT_TYPE_MUSIC);
+            } else {
+                Log.d(TAG, "VCP Peer device connected, this is not 1st member, skip update to APM");
+            }
+        } else if ((mVcpController.getConnectionState(device) == BluetoothProfile.STATE_DISCONNECTED) && !isAbsVolSupported) {
+            if (isVcpPeerDeviceConnected(device, mCsipManager.getCsipSetId(device, null /*ACM_UUID*/))) {
+                Log.d(TAG, "VCP Peer device connected, this is not last member, skip update to APM ");
+            } else {
+                Log.d(TAG, "VCP Peer device not connected, this is last member, update to APM");
+                service.setAbsoluteVolumeSupport(grpDev, isAbsVolSupported, ApmConst.AudioProfiles.VCP);
+            }
+        }
+    }
+
+    public boolean isVcpPeerDeviceConnected(BluetoothDevice device, int setid) {
+      boolean isVcpPeerConnected = false;
+      if (setid == INVALID_SET_ID)
+          return isVcpPeerConnected;
+      List<BluetoothDevice> members = mCsipManager.getSetMembers(setid);
+      if (members == null) {
+          Log.d(TAG, "No set member found");
+          return isVcpPeerConnected;
+      }
+      Iterator<BluetoothDevice> i = members.iterator();
+      if (i != null) {
+          while (i.hasNext()) {
+              BluetoothDevice addr = i.next();
+              if (!Objects.equals(addr, device) && (mVcpController.getConnectionState(addr) == BluetoothProfile.STATE_CONNECTED)) {
+                  isVcpPeerConnected = true;
+                  Log.d(TAG, "At least one other vcp member is in connected state");
+                  break;
+              }
+          }
+      }
+      return isVcpPeerConnected;
+    }
+
+    private int convertToAudioStreamVolume(int volume) {
+        // Rescale volume to match AudioSystem's volume
+        return (int) Math.round((double) volume * mAudioStreamMax / VCP_MAX_VOL);
+    }
+
+    private int convertToVcpVolume(int volume) {
+        return (int) Math.ceil((double) volume * VCP_MAX_VOL / mAudioStreamMax);
+    }
+
+    private void applyVolToOtherMembers(BluetoothDevice device, int setid, int volume, int audioType) {
+        if (setid == INVALID_SET_ID)
+            return;
+        List<BluetoothDevice> members = mCsipManager.getSetMembers(setid);
+        if (members == null) {
+            Log.d(TAG, "No set member found");
+            return;
+        }
+        Iterator<BluetoothDevice> i = members.iterator();
+        if (i != null) {
+            while (i.hasNext()) {
+                BluetoothDevice addr = i.next();
+                if (!Objects.equals(addr, device)) {
+                    Log.d(TAG, "send vol changed to addr " + addr);
+                    mVcpController.setAbsoluteVolume(addr, volume, audioType);
+                }
+            }
+        }
+    }
+
+    private void applyMuteStateToOtherMembers(BluetoothDevice device, int setid, boolean muteState) {
+        if (setid == INVALID_SET_ID)
+            return;
+        List<BluetoothDevice> members = mCsipManager.getSetMembers(setid);
+        if (members == null) {
+            Log.d(TAG, "No set member found");
+            return;
+        }
+        Iterator<BluetoothDevice> i = members.iterator();
+        if (i != null) {
+            while (i.hasNext()) {
+                BluetoothDevice addr = i.next();
+                if (!Objects.equals(addr, device)) {
+                    Log.d(TAG, "send mute state to addr " + addr);
+                    mVcpController.setMute(addr, muteState);
+                }
+            }
+        }
+    }
+
+    public int getVcpConnState(BluetoothDevice device) {
+        return mVcpController.getConnectionState(device);
+    }
+
+    public int getVcpConnMode(BluetoothDevice device) {
+        return mVcpController.getConnectionMode(device);
+    }
+
+    public boolean isVcpMute(BluetoothDevice device) {
+        return mVcpController.isMute(device);
+    }
+
+    public String getAcmName() {
+        return mAcmName;
+    }
+
+    private static class AcmBinder extends Binder implements IProfileServiceBinder {
+        AcmBinder(AcmService service) {
+        }
+        @Override
+        public void cleanup() {
+        }
+    }
+
+    private BluetoothGroupCallback mBluetoothGroupCallback = new BluetoothGroupCallback() {
+        public void onGroupClientAppRegistered(int status, int appId) {
+            Log.d(TAG, "onGroupClientAppRegistered, status: " + status + "appId: " + appId);
+            if (status == 0) {
+                mCsipManager.updateAppId(appId);
+                mIsCsipRegistered = true;
+            } else {
+                Log.e(TAG, "DeviceGroup registeration failed, status:" + status);
+            }
+        }
+
+        public void onConnectionStateChanged (int state, BluetoothDevice device) {
+            Log.d(TAG, "onConnectionStateChanged: Device: " + device + "state: " + state);
+            //notify statemachine about device connection state
+            synchronized (mStateMachines) {
+                  AcmStateMachine sm = getOrCreateStateMachine(device);
+                  Message msg = sm.obtainMessage(AcmStateMachine.CSIP_CONNECTION_STATE_CHANGED);
+                  msg.obj = state;
+                  sm.sendMessage(msg);
+            }
+        }
+
+        public void onExclusiveAccessChanged (int setId, int value, int status,
+                                                   List<BluetoothDevice> devices) {
+            Log.d(TAG, "onExclusiveAccessChanged: setId" + setId + devices + "status:" + status);
+
+            //notify the statemachine about CSIP Lock status
+            switch (status) {
+                case ALL_LOCKS_ACQUIRED: {
+                    synchronized (mStateMachines) {
+                        for (BluetoothDevice device : devices) {
+                            AcmStateMachine sm = getOrCreateStateMachine(device);
+                            if (sm == null) {
+                                //TODO:handle this case
+                                continue;
+                            }
+                            Message msg = sm.obtainMessage(AcmStateMachine.CSIP_LOCK_STATUS_LOCKED);
+                            msg.obj = setId;
+                            msg.arg1 = value;
+                            sm.sendMessage(msg);
+                        }
+                    }
+                    break;
+                }
+
+                case SOME_LOCKS_ACQUIRED_REASON_TIMEOUT: //check if need to handle separately
+                case SOME_LOCKS_ACQUIRED_REASON_DISC: {
+                    BluetoothDevice mDevice = getCsipLockRequestedDevice();
+                    if (devices.contains(mDevice)) {
+                        synchronized (mStateMachines) {
+                            for (BluetoothDevice device : devices) {
+                                AcmStateMachine sm = getOrCreateStateMachine(device);
+                                if (sm == null) {
+                                    //TODO:handle this case
+                                    continue;
+                                }
+                                Message msg = sm.obtainMessage(AcmStateMachine.CSIP_LOCK_STATUS_PARTIAL_LOCK);
+                                msg.obj = setId;
+                                msg.arg1 = value;
+                                sm.sendMessage(msg);
+                            }
+                        }
+                    } else {
+                        Log.d(TAG, "Exclusive access requested device is not present in list, release access for all devices");
+                        mCsipManager.setLock(setId, devices, mCsipManager.UNLOCK);
+                    }
+                } break;
+
+                case LOCK_DENIED: {
+                    synchronized (mStateMachines) {
+                        for (BluetoothDevice device : devices) {
+                            AcmStateMachine sm = getOrCreateStateMachine(device);
+                            if (sm == null) {
+                                //TODO:handle this case
+                                continue;
+                            }
+                            Message msg = sm.obtainMessage(AcmStateMachine.CSIP_LOCK_STATUS_DENIED);
+                            msg.obj = setId;
+                            msg.arg1 = value;
+                            sm.sendMessage(msg);
+                        }
+                    }
+                } break;
+
+                case LOCK_RELEASED: {
+                    synchronized (mStateMachines) {
+                        for (BluetoothDevice device : devices) {
+                            AcmStateMachine sm = getOrCreateStateMachine(device);
+                            if (sm == null) {
+                                //TODO:handle this case
+                                continue;
+                            }
+                            Message msg = sm.obtainMessage(AcmStateMachine.CSIP_LOCK_STATUS_RELEASED);
+                            msg.obj = setId;
+                            msg.arg1 = value;
+                            sm.sendMessage(msg);
+                        }
+                    }
+                } break;
+
+                case LOCK_RELEASED_TIMEOUT: {
+                    synchronized (mStateMachines) {
+                        for (BluetoothDevice device : devices) {
+                            AcmStateMachine sm = getOrCreateStateMachine(device);
+                            if (sm == null) {
+                                //TODO:handle this case
+                                continue;
+                            }
+                            Message msg = sm.obtainMessage(AcmStateMachine.CSIP_LOCK_STATUS_RELEASED);
+                            msg.obj = setId;
+                            msg.arg1 = value;
+                            sm.sendMessage(msg);
+                        }
+                    }
+                } break;
+
+                /*case INVALID_REQUEST_PARAMS: {
+                    synchronized (mStateMachines) {
+                        for (BluetoothDevice device : devices) {
+                            AcmStateMachine sm = getOrCreateStateMachine(device);
+                            if (sm == null) {
+                                //TODO:handle this case
+                                continue;
+                            }
+                            Message msg = sm.obtainMessage(AcmStateMachine.CSIP_LOCK_STATUS_INVALID_PARAM);
+                            msg.obj = setId;
+                            msg.arg1 = value;
+                            sm.sendMessage(msg);
+                        }
+                    }
+                } break;*/
+
+                /*case LOCK_RELEASE_NOT_ALLOWED: {
+                    synchronized (mStateMachines) {
+                        for (BluetoothDevice device : devices) {
+                            AcmStateMachine sm = getOrCreateStateMachine(device);
+                            if (sm == null) {
+                                //TODO:handle this case
+                                continue;
+                            }
+                            Message msg = sm.obtainMessage(AcmStateMachine.CSIP_LOCK_STATUS_RELEASE);
+                            msg.obj = setId;
+                            msg.arg1 = value;
+                            sm.sendMessage(msg);
+                        }
+                    }
+                } break;*/
+
+                case INVALID_VALUE:
+                    break;
+            }
+        }
+
+        //public void onSetMemberFound (int setId, BluetoothDevice device) {    }
+
+        //public void onSetDiscoveryStatusChanged (int setId, int status, int reason) {    }
+
+        //public void onLockAvailable (int setId, BluetoothDevice device) {    }
+
+        //public void onNewSetFound (int setId,  BluetoothDevice device, UUID uuid) {     }
+
+        //public void onLockStatusFetched (int setId, int lockStatus) {    }
+    };
+
+    public CsipManager getCsipManager(){
+        return mCsipManager;
+    }
+
+    class CsipManager {
+        public int LOCK = 0;
+        public int UNLOCK = 0;
+
+        int mCsipAppid;
+
+        CsipManager() {
+            LOCK = BluetoothDeviceGroup.ACCESS_GRANTED;
+            UNLOCK = BluetoothDeviceGroup.ACCESS_RELEASED;
+
+            registerCsip();
+        }
+
+        CsipManager(BluetoothGroupCallback callbacks) {
+            LOCK = BluetoothDeviceGroup.ACCESS_GRANTED;
+            UNLOCK = BluetoothDeviceGroup.ACCESS_RELEASED;
+
+            registerCsip(callbacks);
+        }
+
+        void updateAppId(int id) {
+            mCsipAppid = id;
+        }
+
+        int getAppId() {
+            return mCsipAppid;
+        }
+
+        void registerCsip() {
+            GroupService mGroupService = GroupService.getGroupService();
+            if(mGroupService != null) {
+                Log.i(TAG, "mGroupService is not null, register");
+                mGroupService.registerGroupClientModule(mBluetoothGroupCallback);
+            }
+        }
+
+        void registerCsip(BluetoothGroupCallback callbacks) {
+            GroupService mGroupService = GroupService.getGroupService();
+            if(mGroupService != null) {
+                Log.i(TAG, "mGroupService is not null, register " + callbacks);
+                mGroupService.registerGroupClientModule(callbacks);
+            }
+        }
+
+        void unregisterCsip() {
+            GroupService mGroupService = GroupService.getGroupService();
+            if (mGroupService != null)
+                mGroupService.unregisterGroupClientModule(mCsipAppid);
+        }
+
+        void connectCsip(BluetoothDevice device) {
+            GroupService mGroupService = GroupService.getGroupService();
+            mGroupService.connect(mCsipAppid, device);
+        }
+
+        void disconnectCsip(BluetoothDevice device) {
+            GroupService mGroupService = GroupService.getGroupService();
+            mGroupService.disconnect(mCsipAppid, device);
+        }
+
+        void setLock(int setId, List<BluetoothDevice> devices, int lockVal) {
+            GroupService mGroupService = GroupService.getGroupService();
+            mGroupService.setLockValue(mCsipAppid, setId, devices, lockVal);
+        }
+
+        DeviceGroup getCsipSet(int setId) {
+            GroupService mGroupService = GroupService.getGroupService();
+            return mGroupService.getCoordinatedSet(setId);
+        }
+
+        List<BluetoothDevice> getSetMembers(int setId) {
+            DeviceGroup set = getCsipSet(setId);
+            if(set != null)
+                return set.getDeviceGroupMembers();
+            return null;
+        }
+
+        int getCsipSetId(BluetoothDevice device, ParcelUuid uuid) {
+            GroupService mGroupService = GroupService.getGroupService();
+            return mGroupService.getRemoteDeviceGroupId(device, uuid);
+            //return -1;
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmStackEvent.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmStackEvent.java
new file mode 100644
index 0000000..d2c39fb
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmStackEvent.java
@@ -0,0 +1,144 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.acm;
+
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * Stack event sent via a callback from JNI to Java, or generated
+ * internally by the ACM State Machine.
+ */
+public class AcmStackEvent {
+    // Event types for STACK_EVENT message (coming from native)
+    private static final int EVENT_TYPE_NONE = 0;
+    public static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
+    public static final int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
+    public static final int EVENT_TYPE_CODEC_CONFIG_CHANGED = 3;
+
+    // Do not modify without updating the HAL bt_acm.h files.
+    // Match up with btacm_connection_state_t enum of bt_acm.h
+    static final int CONNECTION_STATE_DISCONNECTED = 0;
+    static final int CONNECTION_STATE_CONNECTING = 1;
+    static final int CONNECTION_STATE_CONNECTED = 2;
+    static final int CONNECTION_STATE_DISCONNECTING = 3;
+    // Match up with btacm_audio_state_t enum of bt_acm.h
+    static final int AUDIO_STATE_REMOTE_SUSPEND = 0;
+    static final int AUDIO_STATE_STOPPED = 1;
+    static final int AUDIO_STATE_STARTED = 2;
+
+    // Match up with btacm_audio_state_t enum of bt_acm.h
+    static final int CONTEXT_TYPE_UNKNOWN = 0;
+    static final int CONTEXT_TYPE_MUSIC = 1;
+    static final int CONTEXT_TYPE_VOICE = 2;
+    static final int CONTEXT_TYPE_MUSIC_VOICE = 3;
+
+     // Match up with btacm_audio_state_t enum of bt_acm.h
+    static final int PROFILE_TYPE_NONE = 0;
+
+    public int type = EVENT_TYPE_NONE;
+    public BluetoothDevice device;
+    public int valueInt1 = 0;
+    public int valueInt2 = 0;
+    public BluetoothCodecStatus codecStatus;
+
+    AcmStackEvent(int type) {
+        this.type = type;
+    }
+
+    @Override
+    public String toString() {
+        // event dump
+        StringBuilder result = new StringBuilder();
+        result.append("AcmStackEvent {type:" + eventTypeToString(type));
+        result.append(", device:" + device);
+        result.append(", state:" + eventTypeValueIntToString(type, valueInt1));
+        result.append(", context type:" + contextTypeValueIntToString(valueInt2));
+        if (codecStatus != null) {
+            result.append(", codecStatus:" + codecStatus);
+        }
+        result.append("}");
+        return result.toString();
+    }
+
+    private static String eventTypeToString(int type) {
+        switch (type) {
+            case EVENT_TYPE_NONE:
+                return "EVENT_TYPE_NONE";
+            case EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                return "EVENT_TYPE_CONNECTION_STATE_CHANGED";
+            case EVENT_TYPE_AUDIO_STATE_CHANGED:
+                return "EVENT_TYPE_AUDIO_STATE_CHANGED";
+            case EVENT_TYPE_CODEC_CONFIG_CHANGED:
+                return "EVENT_TYPE_CODEC_CONFIG_CHANGED";
+            default:
+                return "EVENT_TYPE_UNKNOWN:" + type;
+        }
+    }
+
+    private static String eventTypeValueIntToString(int type, int value) {
+        switch (type) {
+            case EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                switch (value) {
+                    case CONNECTION_STATE_DISCONNECTED:
+                        return "DISCONNECTED";
+                    case CONNECTION_STATE_CONNECTING:
+                        return "CONNECTING";
+                    case CONNECTION_STATE_CONNECTED:
+                        return "CONNECTED";
+                    case CONNECTION_STATE_DISCONNECTING:
+                        return "DISCONNECTING";
+                    default:
+                        break;
+                }
+                break;
+            case EVENT_TYPE_AUDIO_STATE_CHANGED:
+                switch (value) {
+                    case AUDIO_STATE_REMOTE_SUSPEND:
+                        return "REMOTE_SUSPEND";
+                    case AUDIO_STATE_STOPPED:
+                        return "STOPPED";
+                    case AUDIO_STATE_STARTED:
+                        return "STARTED";
+                    default:
+                        break;
+                }
+                break;
+            default:
+                break;
+        }
+        return Integer.toString(value);
+    }
+
+    private static String contextTypeValueIntToString(int value) {
+        switch (value) {
+            case CONTEXT_TYPE_UNKNOWN:
+                return "UNKNOWN";
+            case CONTEXT_TYPE_MUSIC:
+                return "MEDIA";
+            case CONTEXT_TYPE_VOICE:
+                return "CONVERSATIONAL";
+            case CONTEXT_TYPE_MUSIC_VOICE:
+                return "MEDIA+CONVERSATIONAL";
+            default:
+                return "UNKNOWN:" + value;
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmStateMachine.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmStateMachine.java
new file mode 100644
index 0000000..293387c
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/acm/AcmStateMachine.java
@@ -0,0 +1,2354 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.acm;
+//package com.android.bluetooth.apm;
+
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Intent;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import com.android.internal.util.IState;
+import com.android.bluetooth.BluetoothStatsLog;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import java.util.UUID;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Scanner;
+import android.os.SystemProperties;
+import com.android.bluetooth.btservice.AdapterService;
+import java.util.Objects;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+import com.android.bluetooth.apm.ApmConst;
+import com.android.bluetooth.apm.StreamAudioService;
+import com.android.bluetooth.vcp.VcpController;
+import android.bluetooth.BluetoothVcp;
+
+final class AcmStateMachine extends StateMachine {
+    private static final boolean DBG = true;
+    private static final String TAG = "AcmStateMachine";
+    private String  mReconfig = "";
+    public UUID uuid;
+    static final int CONNECT = 1;
+    static final int DISCONNECT = 2;
+    static final int CSIP_CONNECTION_STATE_CHANGED = 3;
+    static final int CSIP_LOCK_STATUS_LOCKED = 4;
+    static final int CSIP_LOCK_STATUS_PARTIAL_LOCK = 5;
+    static final int CSIP_LOCK_STATUS_DENIED = 6;
+    static final int CSIP_LOCK_STATUS_RELEASED = 7;
+    static final int CODEC_CONFIG_CHANGED = 8;
+    static final int GATT_CONNECTION_STATE_CHANGED = 9;
+    static final int GATT_CONNECTION_TIMEOUT = 10;
+    static final int START_STREAM = 11;
+    static final int STOP_STREAM = 12;
+    static final int START_STREAM_REQ = 13;
+    @VisibleForTesting
+    static final int STACK_EVENT = 101;
+    private static final int CONNECT_TIMEOUT = 201;
+    private static final int CSIP_LOCK_RELEASE_TIMEOUT = 301;
+    private static final int CSIP_LOCK_TIMEOUT = 401;
+    private static final int LE_AUDIO_AVAILABLE_LICENSED = 0x00000300;
+    static final int GATT_CONNECTION_TIMEOUT_MS = 30000;
+    static final int GATT_PEER_CONN_TIMEOUT = 8;
+    static final int GATT_CONN_FAILED_ESTABLISHED = 0x3E;
+
+    @VisibleForTesting
+    static int sConnectTimeoutMs = 30000;        // 30s
+    static int sCsipLockReleaseTimeoutMs = 5000;  //TODO
+    static int sCsipLockTimeoutMs = 5000;
+
+    private final int ACM_MAX_BYTES = 100;
+
+    private final int CS_PARAM_NUM_BITS  = 8;
+    private final int CS_PARAM_IND_MASK  = 0xff;
+    private final int CS_PARAM_1ST_INDEX = 0x00;
+    private final int CS_PARAM_2ND_INDEX = 0x01;
+    private final int CS_PARAM_3RD_INDEX = 0x02;
+    private final int CS_PARAM_4TH_INDEX = 0x03;
+    private final int CS_PARAM_5TH_INDEX = 0x04;
+    private final int CS_PARAM_6TH_INDEX = 0x05;
+    private final int CS_PARAM_7TH_INDEX = 0x06;
+    private final int CS_PARAM_8TH_INDEX = 0x07;
+
+    private final int CODEC_TYPE_LC3Q    = 0x10;
+
+    private static final String CODEC_NAME = "Codec";
+    private static final String STREAM_MAP = "StreamMap";
+    private static final String FRAME_DURATION = "FrameDuration";
+    private static final String SDU_BLOCK = "Blocks_forSDU";
+    private static final String RXCONFIG_INDX = "rxconfig_index";
+    private static final String TXCONFIG_INDX = "txconfig_index";
+    private static final String VERSION = "version";
+    private static final String VENDOR_META_DATA = "vendor";
+    private Disconnected mDisconnected;
+    private Connecting mConnecting;
+    private Disconnecting mDisconnecting;
+    private Connected mConnected;
+    private Streaming mStreaming;
+    private int mConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+    private int mLastConnectionState = -1;
+    private int mMusicConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+    private int mLastMusicConnectionState = -1;
+    private int mVoiceConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+    private int mLastVoiceConnectionState = -1;
+
+    private AcmService mAcmService;
+
+    private AcmNativeInterface mAcmNativeInterface;
+    private BluetoothGatt mBluetoothGatt;
+    private final BluetoothDevice mDevice;
+    private BluetoothDevice mGroupAddress;
+    private boolean mIsMusicPlaying = false;
+    private boolean mIsVoicePlaying = false;
+    private VcpController mVcpController;
+    private BluetoothCodecStatus mMusicCodecStatus;
+    private BluetoothCodecStatus mVoiceCodecStatus;
+
+    static final int CONTEXT_TYPE_UNKNOWN = 0;
+    static final int CONTEXT_TYPE_MUSIC = 1;
+    static final int CONTEXT_TYPE_VOICE = 2;
+    static final int CONTEXT_TYPE_MUSIC_VOICE = 3;
+
+    private int mContextTypeToDisconnect = CONTEXT_TYPE_UNKNOWN;
+    private int mCurrentContextType = CONTEXT_TYPE_UNKNOWN;
+    private int mProfileType = 0;
+    private int mPreferredContext = CONTEXT_TYPE_UNKNOWN;
+
+    private boolean IsDisconnectRequested = false;
+    private boolean IsReconfigRequested = false;
+    private int mCsipConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+    private boolean mDeviceLocked = false;
+    private boolean mCsipLockRequested = false;
+    private boolean mIsDeviceWhitelisted = false;
+    private int mSetId;
+    private boolean mAcmMTUChangeRequested = false;
+    private int cached_state;
+    private boolean mIsUpdateProfDisConnection = false;
+
+    AcmStateMachine(BluetoothDevice device, AcmService acmService,
+                     AcmNativeInterface acmNativeInterface, Looper looper) {
+        super(TAG, looper);
+        setDbg(DBG);
+        mDevice = device;
+        mAcmService = acmService;
+        mAcmNativeInterface = acmNativeInterface;
+
+        mDisconnected = new Disconnected();
+        mConnecting = new Connecting();
+        mDisconnecting = new Disconnecting();
+        mConnected = new Connected();
+        mStreaming = new Streaming();
+
+        addState(mDisconnected);
+        addState(mConnecting);
+        addState(mDisconnecting);
+        addState(mConnected);
+        addState(mStreaming);
+
+        mCurrentContextType = CONTEXT_TYPE_UNKNOWN;
+        mDeviceLocked = false;
+        IsDisconnectRequested = false;
+        IsReconfigRequested = false;
+        mIsDeviceWhitelisted = false;
+        setInitialState(mDisconnected);
+        mSetId = -1;
+        cached_state = -1;
+        mAcmMTUChangeRequested = false;
+        mIsUpdateProfDisConnection = false;
+
+        if (mBluetoothGatt != null) {
+            Log.e(TAG, "disconnect gatt");
+            mBluetoothGatt.disconnect();
+            mBluetoothGatt.close();
+            mBluetoothGatt = null;
+        }
+    }
+
+    static AcmStateMachine make(BluetoothDevice device, AcmService acmService,
+                                  AcmNativeInterface acmNativeInterface, Looper looper) {
+        Log.i(TAG, "make acm for device " + device);
+        AcmStateMachine acmSm = new AcmStateMachine(device, acmService, acmNativeInterface,
+                                                    looper);
+        acmSm.start();
+        return acmSm;
+    }
+
+    public void doQuit() {
+        log("doQuit for device " + mDevice);
+        if (mIsMusicPlaying) {
+            // Stop if audio is still playing
+            log("doQuit: stopped Music playing " + mDevice);
+            mIsMusicPlaying = false;
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if (service != null)
+                service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_NOT_PLAYING, CONTEXT_TYPE_MUSIC);
+        }
+        if (mIsVoicePlaying) {
+            log("doQuit: stopped voice streaming " + mDevice);
+            mIsVoicePlaying = false;
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if (service != null)
+                service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_NOT_PLAYING, ApmConst.AudioFeatures.CALL_AUDIO);
+        }
+        quitNow();
+    }
+
+    public void cleanup() {
+        log("cleanup for device " + mDevice);
+        if (mBluetoothGatt != null) {
+            Log.e(TAG, "disconnect gatt");
+            mBluetoothGatt.disconnect();
+            mBluetoothGatt.close();
+            mBluetoothGatt = null;
+        }
+    }
+
+    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
+        @Override
+        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+            Log.i(TAG, "onConnectionStateChange: Status: " + status + " newState: " + newState);
+            if (status == BluetoothGatt.GATT_SUCCESS /* || status == 0x16 */) {
+                if (newState == BluetoothProfile.STATE_CONNECTED) {
+                    mBluetoothGatt.requestMtu(ACM_MAX_BYTES);
+                    mAcmMTUChangeRequested = true;
+                    cached_state = newState;
+                } else {
+                    if (mAcmMTUChangeRequested == true) {
+                        mAcmMTUChangeRequested = false;
+                    }
+
+                    if (cached_state != -1) {
+                        cached_state = -1;
+                    }
+
+                    Message m = obtainMessage(GATT_CONNECTION_STATE_CHANGED);
+                    m.obj = newState;
+                    sendMessage(m);
+                }
+            } else if (status == GATT_PEER_CONN_TIMEOUT ||
+                       status == GATT_CONN_FAILED_ESTABLISHED) {
+                BluetoothDevice target = gatt.getDevice();
+                Log.i(TAG, "[ACM] remote side disconnection, for device add in BG WL: " +target);
+                mBluetoothGatt.close();
+                mBluetoothGatt = target.connectGatt(mAcmService, true, mGattCallback,
+                        BluetoothDevice.TRANSPORT_LE, false, (BluetoothDevice.PHY_LE_1M_MASK |
+                        BluetoothDevice.PHY_LE_2M_MASK | BluetoothDevice.PHY_LE_CODED_MASK),
+                        null, true);
+                mIsDeviceWhitelisted = true;
+            } else {
+                mBluetoothGatt.close();
+                mBluetoothGatt = null;
+                Log.i(TAG, "[ACM] Failed to connect GATT server.");
+            }
+        }
+
+        @Override
+        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+            log("onMtuChanged: mtu: " + mtu +
+                " mAcmMTUChangeRequested: " + mAcmMTUChangeRequested +
+                " cached_state: " + cached_state);
+            if (mAcmMTUChangeRequested == true) {
+                if (cached_state != -1) {
+                    Message m = obtainMessage(GATT_CONNECTION_STATE_CHANGED);
+                    m.obj = cached_state;
+                    sendMessage(m);
+                    mAcmMTUChangeRequested = false;
+                    cached_state = -1;
+                }
+            } else {
+                log("onMtuChanged: Remote initiated trigger");
+                //Do nothing
+            }
+        }
+    };
+
+    @VisibleForTesting
+    class Disconnected extends State {
+        @Override
+        public void enter() {
+            //Disconnect unicast VCP 1st
+            mVcpController = VcpController.getVcpController();
+            if (mVcpController != null) {
+                Log.d(TAG, "Disconnect VCP for " + mDevice);
+                mVcpController.disconnect(mDevice, BluetoothVcp.MODE_UNICAST);
+            } else {
+                Log.d(TAG, "mVcpController is null");
+            }
+
+            Message currentMessage = getCurrentMessage();
+            Log.i(TAG, "Enter Disconnected(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+            synchronized (this) {
+                mConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+                mMusicConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+                mVoiceConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+            }
+            removeDeferredMessages(DISCONNECT);
+            removeMessages(CSIP_LOCK_RELEASE_TIMEOUT);
+            removeMessages(GATT_CONNECTION_TIMEOUT);
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if (mLastMusicConnectionState != -1) {
+                // Don't broadcast during startup
+                if (mIsMusicPlaying) {
+                    Log.i(TAG, "Disconnected: stop music streaming: " + mDevice);
+                    mIsMusicPlaying = false;
+                    service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_NOT_PLAYING, CONTEXT_TYPE_MUSIC);
+                }
+                if (mLastVoiceConnectionState != -1) {
+                    if (mIsVoicePlaying) {
+                        Log.i(TAG, "Disconnected: stop voice streaming: " + mDevice);
+                        mIsVoicePlaying = false;
+                        service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_NOT_PLAYING, ApmConst.AudioFeatures.CALL_AUDIO);
+                    }
+                }
+                // ensure when one device is disconnected, need to update the channel mode.
+                mAcmService.updateLeaChannelMode(BluetoothA2dp.STATE_NOT_PLAYING, mDevice);
+                //Unlock, device state changed from connected to disconnected
+
+                Log.i(TAG, " mIsUpdateProfDisConnection: " + mIsUpdateProfDisConnection);
+                if (mDevice.getBondState() == BluetoothDevice.BOND_NONE ||
+                    mIsUpdateProfDisConnection) {
+                    Log.i(TAG, "Device is unpaired/mIsUpdateProfDisConnection has been set," +
+                                           " update disconnected to APM for " + mDevice);
+                    mIsUpdateProfDisConnection = false;
+                    if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                        Log.d(TAG, "Fellow device is already connected, not last member ");
+                        service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                        if (mLastVoiceConnectionState != -1)
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                    } else {
+                        Log.d(TAG, "Last member to disconnect");
+                        service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                        if (mLastVoiceConnectionState != -1)
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                    }
+                    mDeviceLocked = false;
+                    //assuming disconnected state we are moving hence update AcmDevice hash map
+                    mAcmService.handleAcmDeviceStateChange(mDevice, mConnectionState, mSetId);
+                } else if (mDeviceLocked) {
+                    Log.i(TAG, "Access RELEASED for device " + mDevice);
+                    List<BluetoothDevice> members = new ArrayList<BluetoothDevice>();
+                    members.add(mDevice);
+                    mAcmService.getCsipManager().setLock(mSetId, members, mAcmService.getCsipManager().UNLOCK);
+                    mDeviceLocked = false;
+                } else if (mCsipConnectionState == BluetoothProfile.STATE_CONNECTED) {
+                    Log.i(TAG, "Device access is already RELEASED, Go for DeviceGroup Disconnect " + mDevice);
+                    mAcmService.getCsipManager().disconnectCsip(mDevice);
+                } else {
+                    if (mBluetoothGatt != null && !mIsDeviceWhitelisted) {
+                        Log.d(TAG, "remove other devices from BG WL");
+                        mAcmService.removePeersFromBgWl(mDevice, mSetId);
+                        Log.i(TAG, "Go for GATT Disconnect " + mDevice);
+                        mBluetoothGatt.disconnect();
+                    } else {
+                        Log.d(TAG, "mBluetoothGatt is NULL or device is in BG WL ");
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, not last member ");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                            if (mLastVoiceConnectionState != -1)
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                        } else {
+                            Log.d(TAG, "Last member to disconnect");
+                            if (!mIsDeviceWhitelisted) {
+                                mAcmService.removePeersFromBgWl(mDevice, mSetId);
+                                Log.d(TAG, "remove other devices from BG WL");
+                            }
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                            if (mLastVoiceConnectionState != -1)
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                        }
+                        //assuming disconnected state we are moving hence update AcmDevice hash map
+                        mAcmService.handleAcmDeviceStateChange(mDevice, mConnectionState, mSetId);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void exit() {
+            Message currentMessage = getCurrentMessage();
+            log("Exit Disconnected(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+            mConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+            mLastMusicConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+            mLastVoiceConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Disconnected process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT: {
+                    mCurrentContextType = message.arg1;
+                    mProfileType = message.arg2;
+                    mPreferredContext = (int)message.obj;
+                    Log.i(TAG, "Connecting " + contextTypeToString(mCurrentContextType) + " to " + mDevice);
+                    if (mAcmService.IsLockSupportAvailable(mDevice)) {
+                        Log.d(TAG, "Exclusive Access support available, go for GATT connect");
+                        //if lock support available then go for CSIP connect
+                        if (mBluetoothGatt != null) {
+                            mBluetoothGatt.close();
+                        }
+                        mBluetoothGatt = mDevice.connectGatt(mAcmService, false, mGattCallback,
+                                BluetoothDevice.TRANSPORT_LE, false, (BluetoothDevice.PHY_LE_1M_MASK |
+                                BluetoothDevice.PHY_LE_2M_MASK | BluetoothDevice.PHY_LE_CODED_MASK),
+                                null, true);
+
+                        sendMessageDelayed(GATT_CONNECTION_TIMEOUT, GATT_CONNECTION_TIMEOUT_MS);
+                        transitionTo(mConnecting);
+                        break;
+                    } else {
+                        Log.d(TAG, "Exclusive Access support not available, go for GATT connect");
+                        if (mBluetoothGatt != null) {
+                            mBluetoothGatt.close();
+                        }
+                        mBluetoothGatt = mDevice.connectGatt(mAcmService, false, mGattCallback,
+                                BluetoothDevice.TRANSPORT_LE, false, (BluetoothDevice.PHY_LE_1M_MASK |
+                                BluetoothDevice.PHY_LE_2M_MASK | BluetoothDevice.PHY_LE_CODED_MASK),
+                                null, true);
+
+                        sendMessageDelayed(GATT_CONNECTION_TIMEOUT, GATT_CONNECTION_TIMEOUT_MS);
+                        transitionTo(mConnecting);
+                        /*if (!mAcmNativeInterface.connectAcm(mDevice, message.arg1, message.arg2, (int)message.obj)) {
+                            Log.e(TAG, "Disconnected: error connecting to " + mDevice);
+                            break;
+                        }
+                        transitionTo(mConnecting);*/
+                    }
+                } break;
+
+                case GATT_CONNECTION_STATE_CHANGED: {
+                    removeMessages(GATT_CONNECTION_TIMEOUT);
+                    int st = (int)message.obj;
+                    if (st == BluetoothProfile.STATE_DISCONNECTED) {
+                        Log.d(TAG, "GATT Disconnected");
+                        mIsDeviceWhitelisted = false;
+                        StreamAudioService service = StreamAudioService.getStreamAudioService();
+                        if (mCurrentContextType == CONTEXT_TYPE_MUSIC) {
+                          if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                              Log.d(TAG, "Fellow device is already connected, not last member ");
+                              service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                          } else {
+                              Log.d(TAG, "Last member to disconnect MEDIA");
+                              service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                          }
+                        } else if (mCurrentContextType == CONTEXT_TYPE_VOICE) {
+                          if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                              Log.d(TAG, "Fellow device is already connected, not last member ");
+                              service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                          } else {
+                              Log.d(TAG, "Last member to disconnect VOICE");
+                              service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                          }
+                        } else if (mCurrentContextType == CONTEXT_TYPE_MUSIC_VOICE) {
+                          if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                              Log.d(TAG, "Fellow device is already connected, not last member ");
+                              service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                              service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                          } else {
+                              Log.d(TAG, "Last member to disconnect MEDIA+VOICE");
+                              service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                              service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                          }
+                        }
+                        //assuming disconneted state we are moving hence update AcmDevice hash map
+                        mAcmService.handleAcmDeviceStateChange(mDevice, BluetoothProfile.STATE_DISCONNECTED, mSetId);
+                        mBluetoothGatt.close();
+                    } else if (st == BluetoothProfile.STATE_CONNECTED) {
+                        mIsDeviceWhitelisted = false;
+                        Log.d(TAG, "GATT connected from background, go for profile connection");
+                        AdapterService mAdapterService = AdapterService.getAdapterService();
+                        if (mAdapterService != null && !mAdapterService.connectAllEnabledProfiles(mDevice)) {
+                            Log.e(TAG, "Disconnected: error connecting to " + mDevice);
+                            break;
+                        }
+                        break;
+                    }
+                } break;
+
+                case DISCONNECT:
+                    Log.w(TAG, "Disconnected: DISCONNECT ignored: " + mDevice);
+                    break;
+
+                case GATT_CONNECTION_TIMEOUT: {
+                    Log.d(TAG, "GATT connection Timeout");
+                    break;
+                }
+
+                case CSIP_CONNECTION_STATE_CHANGED:
+                    int state = (int)message.obj;
+                    if (state == BluetoothProfile.STATE_DISCONNECTED)
+                        mCsipConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+                    if (mBluetoothGatt != null)
+                        mBluetoothGatt.disconnect();
+                    break;
+
+                case CSIP_LOCK_STATUS_RELEASED: {
+                    removeMessages(CSIP_LOCK_RELEASE_TIMEOUT);
+                    //disconnect CSIP
+                    int value = (int)message.arg1;
+                    Log.d(TAG, "Exclusive Access state changed:" + value);
+                    if (value == mAcmService.getCsipManager().UNLOCK) {
+                        if (mCsipConnectionState == BluetoothProfile.STATE_CONNECTED) {
+                           mAcmService.getCsipManager().disconnectCsip(mDevice);
+                        }
+                    }
+                    mCsipLockRequested = false;
+                    mDeviceLocked = false;
+                    break;
+                }
+
+                case STACK_EVENT:
+                    AcmStackEvent event = (AcmStackEvent) message.obj;
+                    log("Disconnected: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case AcmStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1, event.valueInt2);
+                            break;
+                        case AcmStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED:
+                            processCodecConfigEvent(event.codecStatus, event.valueInt2);
+                            break;
+                        default:
+                            Log.e(TAG, "Disconnected: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Disconnected state
+        private void processConnectionEvent(int state, int contextType) {
+            switch (state) {
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTED:
+                    Log.w(TAG, "Ignore ACM DISCONNECTED event: " + mDevice);
+                    break;
+                case AcmStackEvent.CONNECTION_STATE_CONNECTING:{
+                        // Reject the connection and stay in Disconnected state itself
+                        Log.w(TAG, "Incoming A2DP Connecting request rejected: " + mDevice);
+                        mAcmNativeInterface.disconnectAcm(mDevice, contextType);
+                    }
+                    break;
+                case AcmStackEvent.CONNECTION_STATE_CONNECTED: {//Shouldn't come
+                    Log.w(TAG, "ACM Connected from Disconnected state: " + mDevice);
+                    // Reject the connection and stay in Disconnected state itself
+                    Log.w(TAG, "Incoming ACM Connected request rejected: " + mDevice);
+                    mAcmNativeInterface.disconnectAcm(mDevice, contextType);
+                    }
+                    break;
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    Log.w(TAG, "Ignore ACM DISCONNECTING event: " + mDevice);
+                    break;
+                default:
+                    Log.e(TAG, "Incorrect state: " + state + " device: " + mDevice);
+                    break;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    class Connecting extends State {
+        @Override
+        public void enter() {
+            //Connect unicast VCP 1st
+            mVcpController = VcpController.getVcpController();
+            if (mVcpController != null) {
+                Log.d(TAG, "Connect VCP for " + mDevice);
+                mVcpController.connect(mDevice, BluetoothVcp.MODE_UNICAST);
+            } else {
+                Log.d(TAG, "mVcpController is null");
+            }
+
+            Message currentMessage = getCurrentMessage();
+            Log.i(TAG, "Enter Connecting(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+            sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs);
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            //this context type here is global context type, if not based on current
+            //and prev states per context, form this contextType here
+            synchronized (this) {
+                mConnectionState = BluetoothProfile.STATE_CONNECTING;
+            }
+            mSetId = mAcmService.getCsipManager().getCsipSetId(mDevice, null /*ACM_UUID*/); //TODO: UUID what to set ?
+            mGroupAddress = mAcmService.getGroup(mDevice);
+            Log.d(TAG, "Group bd address  " + mGroupAddress);
+            mAcmService.handleAcmDeviceStateChange(mDevice, mConnectionState, mSetId);
+            if (mCurrentContextType == CONTEXT_TYPE_MUSIC) {
+              mMusicConnectionState = BluetoothProfile.STATE_CONNECTING;
+              if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                  Log.d(TAG, "Fellow device is already connected, not first member ");
+                  service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+              } else {
+                  Log.d(TAG, "First member of group to connect MUSIC");
+                  service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+              }
+            } else if (mCurrentContextType == CONTEXT_TYPE_VOICE) {
+              mVoiceConnectionState = BluetoothProfile.STATE_CONNECTING;
+              if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                  Log.d(TAG, "Fellow device is already connected, not first member ");
+                  service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+              } else {
+                  Log.d(TAG, "First member of group to connect VOICE");
+                  service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+              }
+            } else if (mCurrentContextType == CONTEXT_TYPE_MUSIC_VOICE) {
+              mMusicConnectionState = BluetoothProfile.STATE_CONNECTING;
+              mVoiceConnectionState = BluetoothProfile.STATE_CONNECTING;
+              if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                  Log.d(TAG, "Fellow device is already connected, not first member ");
+                  service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                  service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+              } else {
+                  Log.d(TAG, "First member of group to connect MUSIC & VOICE");
+                  service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                  service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+              }
+            }
+        }
+
+        @Override
+        public void exit() {
+            Message currentMessage = getCurrentMessage();
+            log("Exit Connecting(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+            if (mCurrentContextType == CONTEXT_TYPE_MUSIC) {
+              mLastMusicConnectionState = BluetoothProfile.STATE_CONNECTING;
+            } else if (mCurrentContextType == CONTEXT_TYPE_VOICE) {
+              mLastVoiceConnectionState = BluetoothProfile.STATE_CONNECTING;
+            } else if (mCurrentContextType == CONTEXT_TYPE_MUSIC_VOICE) {
+              mLastMusicConnectionState = BluetoothProfile.STATE_CONNECTING;
+              mLastVoiceConnectionState = BluetoothProfile.STATE_CONNECTING;
+            }
+            removeMessages(CONNECT_TIMEOUT);
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Connecting process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    deferMessage(message); // Do we need to defer this ? It shouldn't have come.
+                    break;
+                case CONNECT_TIMEOUT: {
+                    Log.w(TAG, "Connecting connection timeout: " + mDevice);
+                    //check if CSIP is connected
+                    if (mCsipConnectionState == BluetoothProfile.STATE_CONNECTED)
+                        mAcmService.getCsipManager().disconnectCsip(mDevice);
+                    mAcmNativeInterface.disconnectAcm(mDevice, mCurrentContextType);
+                    AcmStackEvent event =
+                            new AcmStackEvent(AcmStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+                    event.device = mDevice;
+                    event.valueInt1 = AcmStackEvent.CONNECTION_STATE_DISCONNECTED;
+                    event.valueInt2 = mCurrentContextType;
+                    sendMessage(STACK_EVENT, event);
+                    break;
+                }
+                case DISCONNECT: {
+                    // Cancel connection, shouldn't come
+                    mContextTypeToDisconnect = (int)message.obj;
+                    //check if disconnect is for individual context type
+                    IState state = mDisconnected;
+                    Log.i(TAG, "Connecting: connection canceled to " + mDevice);
+                    mAcmNativeInterface.disconnectAcm(mDevice, mContextTypeToDisconnect);
+                    if ((mMusicConnectionState == BluetoothProfile.STATE_CONNECTING) &&
+                        (mVoiceConnectionState == BluetoothProfile.STATE_CONNECTING)) {
+                        if (mContextTypeToDisconnect != CONTEXT_TYPE_MUSIC_VOICE) {
+                            /*only 1/2 contexts are being disconnected,
+                            remain in connecting state but broadcast the connection state*/
+                            state = mConnecting;
+                        } else {
+                            //disconnect is for both context type then disconnect CSIP
+                            if (mCsipConnectionState == BluetoothProfile.STATE_CONNECTED)
+                                mAcmService.getCsipManager().disconnectCsip(mDevice);
+                        }
+                    }
+                    processTransitionContextState(mConnecting, mDisconnected, mContextTypeToDisconnect);
+                    StreamAudioService service = StreamAudioService.getStreamAudioService();
+                    if (state == mConnecting) {
+                        if (mContextTypeToDisconnect == CONTEXT_TYPE_MUSIC)
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                        else if (mContextTypeToDisconnect == CONTEXT_TYPE_VOICE)
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                    } else {
+                        transitionTo(state);
+                    }
+                    break;
+                }
+
+                case GATT_CONNECTION_STATE_CHANGED: {
+                    removeMessages(GATT_CONNECTION_TIMEOUT);
+                    int st = (int)message.obj;
+                    if (st == BluetoothProfile.STATE_CONNECTED) {
+                        mIsDeviceWhitelisted = false;
+                        Log.d(TAG, "GATT connected, go for connect");
+                        if (!mAcmNativeInterface.connectAcm(mDevice, mCurrentContextType, mProfileType, mPreferredContext)) {
+                            Log.e(TAG, "Disconnected: error connecting to " + mDevice);
+                            break;
+                        }
+                    } else if (st == BluetoothProfile.STATE_DISCONNECTED) {
+                        Log.d(TAG, "GATT Disconnected");
+                        mIsDeviceWhitelisted = false;
+                        StreamAudioService service = StreamAudioService.getStreamAudioService();
+                        service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                        service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                        transitionTo(mDisconnected);
+                    }
+                    /*if (st == BluetoothProfile.STATE_CONNECTED) {
+                        Log.d(TAG, "GATT connected, go for DeviceGroup connect");
+                        mAcmService.getCsipManager().connectCsip(mDevice);
+                    } else if (st == BluetoothProfile.STATE_DISCONNECTED) {
+                        Log.d(TAG, "GATT Disconnected");
+                        StreamAudioService service = StreamAudioService.getStreamAudioService();
+                        service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                    }*/
+                } break;
+
+                case GATT_CONNECTION_TIMEOUT: {
+                    Log.d(TAG, "GATT connection Timeout");
+                    if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                        Log.d(TAG, "Peer device is connected, add to BG WL");
+                        mIsDeviceWhitelisted = true;
+                        mBluetoothGatt.close();
+
+                        mBluetoothGatt = mDevice.connectGatt(mAcmService, true, mGattCallback,
+                                BluetoothDevice.TRANSPORT_LE, false, (BluetoothDevice.PHY_LE_1M_MASK |
+                                BluetoothDevice.PHY_LE_2M_MASK | BluetoothDevice.PHY_LE_CODED_MASK),
+                                null, true);
+
+                    } else {
+                        Log.d(TAG, "No member is in connected state, do not add in BG WL");
+                        mIsDeviceWhitelisted = false;
+                        if (mBluetoothGatt != null) {
+                            Log.e(TAG, "Disconnect gatt and make gatt instance null.");
+                            mBluetoothGatt.disconnect();
+                            mBluetoothGatt.close();
+                            mBluetoothGatt = null;
+                        }
+                    }
+                    transitionTo(mDisconnected);
+                    break;
+                }
+
+                case CSIP_CONNECTION_STATE_CHANGED: {
+                    int state = (int)message.obj;
+                    if (state == BluetoothProfile.STATE_CONNECTED) {
+                        Log.e(TAG, "DeviceGroup Connected to  " + mDevice);
+                        mCsipConnectionState = BluetoothProfile.STATE_CONNECTED;
+                        mSetId = mAcmService.getCsipManager().getCsipSetId(mDevice, null /*ACM_UUID*/); //TODO: UUID what to set ?
+                        Iterator<BluetoothDevice> i = mAcmService.getCsipManager().getSetMembers(mSetId).iterator();
+                        List<BluetoothDevice> members = new ArrayList<BluetoothDevice>();
+                        if (i != null) {
+                            while (i.hasNext()) {
+                                BluetoothDevice device = i.next();
+                                if (mAcmService.getCsipConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
+                                    members.add(device);
+                                }
+                            }
+                        }
+                        //setlockvalue takes device list
+                        mCsipLockRequested = true;
+                        mAcmService.getCsipManager().setLock(mSetId, members, mAcmService.getCsipManager().LOCK);
+                    } else {
+                        Log.e(TAG, "DeviceGroup Connection failed to  " + mDevice);
+                        mCsipConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+                        mCsipLockRequested = false;
+                        mDeviceLocked = false;
+                        transitionTo(mDisconnected);
+                    }
+                    break;
+                }
+
+                case CSIP_LOCK_STATUS_LOCKED: {
+                    mCsipLockRequested = false;
+                    int value = (int)message.arg1;
+                    Log.d(TAG, "Exclusive Access state changed:" + value);
+                    int setId = (int)message.obj;
+                    int st = mAcmService.getCsipConnectionState(mDevice);
+                    if ((mDeviceLocked && st == BluetoothProfile.STATE_CONNECTED)) {
+                        Log.w(TAG, "Device access is already granted and DeviceGroup is in connected state");
+                        break;
+                    }
+                    if (value == mAcmService.getCsipManager().LOCK) {
+                        mDeviceLocked = true;
+                        if (!mAcmNativeInterface.connectAcm(mDevice, mCurrentContextType, mProfileType, mPreferredContext)) {
+                            Log.e(TAG, "Disconnected: error connecting to " + mDevice);
+                            mAcmService.getCsipManager().disconnectCsip(mDevice);
+                            transitionTo(mDisconnected);
+                            break;
+                        }
+                    } else {
+                        mDeviceLocked = false;
+                        Log.w(TAG, "Exclusive Access failed to " + setId);
+                        transitionTo(mDisconnected);
+                    }
+                    break;
+                }
+
+                case CSIP_LOCK_STATUS_PARTIAL_LOCK: {
+                    //check if requested lock is from this device
+                    if (mCsipLockRequested) {
+                        int value = (int)message.arg1;
+                        Log.d(TAG, "Exclusive Access state changed:" + value);
+                        int setId = (int)message.obj;
+                        int st = mAcmService.getCsipConnectionState(mDevice);
+                        if (mDeviceLocked && st == BluetoothProfile.STATE_CONNECTED) {
+                            Log.w(TAG, "Device access is already granted and DeviceGroup is in connected state");
+                            break;
+                        }
+                        if (value == mAcmService.getCsipManager().LOCK) {
+                            mDeviceLocked = true;
+                            if (!mAcmNativeInterface.connectAcm(mDevice, mCurrentContextType, mProfileType, mPreferredContext)) {
+                                Log.e(TAG, "Disconnected: error connecting to " + mDevice);
+                                mAcmService.getCsipManager().disconnectCsip(mDevice);
+                                transitionTo(mDisconnected);
+                                break;
+                            }
+                        } else {
+                            mDeviceLocked = false;
+                            Log.w(TAG, "Exclusive Access failed to " + setId);
+                            transitionTo(mDisconnected);
+                        }
+                        mCsipLockRequested = false;
+                    } else {
+                        //lock is not requested from this device TODO: check if
+                        Log.d(TAG, "Exclusive Access is not requested from this device: " + mDevice);
+                        mDeviceLocked = true;
+                    }
+                }
+                break;
+
+                case CSIP_LOCK_RELEASE_TIMEOUT:
+                    //TODO: lock release individual ?
+                    Log.d(TAG, "Exclusive Access timeout to " + mDevice);
+                    List<BluetoothDevice> set = new ArrayList<BluetoothDevice>();
+                    set.add(mDevice);
+                    mAcmService.getCsipManager().setLock(mSetId, set, mAcmService.getCsipManager().UNLOCK);
+                    mDeviceLocked = false;
+                    break;
+
+                case CSIP_LOCK_STATUS_DENIED:
+                    //lock denied fail connection
+                    Log.e(TAG, "DeviceGroup Connection failed to  " + mDevice);
+                    mCsipLockRequested = false;
+                    mDeviceLocked = false;
+                    break;
+
+                case CSIP_LOCK_STATUS_RELEASED:
+                    removeMessages(CSIP_LOCK_RELEASE_TIMEOUT);
+                    mCsipLockRequested = false;
+                    mDeviceLocked = false;
+                    break;
+
+                case STACK_EVENT:
+                    AcmStackEvent event = (AcmStackEvent) message.obj;
+                    log("Connecting: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case AcmStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1, event.valueInt2);
+                            break;
+                        case AcmStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED:
+                            processCodecConfigEvent(event.codecStatus, event.valueInt2);
+                            break;
+                        case AcmStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
+                            break;
+                        default:
+                            Log.e(TAG, "Connecting: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Connecting state
+        private void processConnectionEvent(int state, int contextType) {
+            IState smState;
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            switch (state) {
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTED: {
+                    smState = mDisconnected;
+                    Log.w(TAG, "Connecting device disconnected: " + mDevice);
+                    if ((mMusicConnectionState == BluetoothProfile.STATE_CONNECTING) &&
+                        (mVoiceConnectionState == BluetoothProfile.STATE_CONNECTING)) {
+                        if (contextType != CONTEXT_TYPE_MUSIC_VOICE) {
+                            /*only 1/2 contexts are being disconnected, remain in connecting state but broadcast the connection state*/
+                            smState = mConnecting;
+                        }
+                    }
+                    processTransitionContextState(mConnecting, mDisconnected, contextType);
+                    if (smState == mConnecting) {
+                        if (contextType == CONTEXT_TYPE_MUSIC) {
+                            if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                                Log.d(TAG, "Fellow device is already connected, update MUSIC");
+                                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                            } else {
+                                Log.d(TAG, "First member of group to connect MUSIC");
+                                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                            }
+                        } else if (contextType == CONTEXT_TYPE_VOICE) {
+                            if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                                Log.d(TAG, "Fellow device is already connected, update VOICE");
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                            } else {
+                                Log.d(TAG, "First member of group to connect VOICE");
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                            }
+                        }
+                    } else {
+                        transitionTo(smState);
+                    }
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_CONNECTED: {
+                    // start lock release timer  TODO:when CSIP support is available
+                    //sendMessageDelayed(CSIP_LOCK_RELEASE_TIMEOUT, sCsipLockReleaseTimeoutMs);
+                    if (contextType == CONTEXT_TYPE_MUSIC) {
+                        mMusicConnectionState = BluetoothProfile.STATE_CONNECTED;
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update MUSIC");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                        } else {
+                            Log.d(TAG, "First member of group to connect MUSIC");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                        }
+                    } else if (contextType == CONTEXT_TYPE_VOICE) {
+                        mVoiceConnectionState = BluetoothProfile.STATE_CONNECTED;
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                        } else {
+                            Log.d(TAG, "First member of group to connect VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                        }
+                    }
+                    transitionTo(mConnected);
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_CONNECTING:
+                    // Ignored - probably an event that the outgoing connection was initiated
+                    break;
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTING: {
+                    Log.w(TAG, "Connecting device disconnecting: " + mDevice);
+                    transitionTo(mDisconnecting);
+                } break;
+
+                default:
+                    Log.e(TAG, "Incorrect event: " + state);
+                    break;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    class Disconnecting extends State {
+        @Override
+        public void enter() {
+            //Disconnect unicast VCP 1st
+            mVcpController = VcpController.getVcpController();
+            if (mVcpController != null) {
+                Log.d(TAG, "Disconnect VCP for " + mDevice);
+                mVcpController.disconnect(mDevice, BluetoothVcp.MODE_UNICAST);
+            } else {
+                Log.d(TAG, "mVcpController is null");
+            }
+
+            Message currentMessage = getCurrentMessage();
+            Log.i(TAG, "Enter Disconnecting(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+            sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs);
+            synchronized (this) {
+                mConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+            }
+            mAcmService.handleAcmDeviceStateChange(mDevice, mConnectionState, mSetId);
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if (mContextTypeToDisconnect == CONTEXT_TYPE_MUSIC && mMusicConnectionState != BluetoothProfile.STATE_DISCONNECTED) {
+              mMusicConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+              if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                Log.d(TAG, "Fellow device is already connected, update MEDIA");
+                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+              } else {
+                Log.d(TAG, "Last member to disconnect, update MEDIA");
+                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+              }
+            } else if (mContextTypeToDisconnect == CONTEXT_TYPE_VOICE && mVoiceConnectionState != BluetoothProfile.STATE_DISCONNECTED) {
+              mVoiceConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+              if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                Log.d(TAG, "Fellow device is already connected, update VOICE ");
+                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+              } else {
+                Log.d(TAG, "Last member to disconnect, update VOICE");
+                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+              }
+            } else if (mContextTypeToDisconnect == CONTEXT_TYPE_MUSIC_VOICE && mMusicConnectionState != BluetoothProfile.STATE_DISCONNECTED
+                       && mVoiceConnectionState != BluetoothProfile.STATE_DISCONNECTED) {
+              mMusicConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+              mVoiceConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+              if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                Log.d(TAG, "Fellow device is already connected, update MEDIA+VOICE");
+                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+              } else {
+                Log.d(TAG, "Last member to disconnect, update MEDIA+VOICE");
+                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+              }
+            }
+        }
+
+        @Override
+        public void exit() {
+            Message currentMessage = getCurrentMessage();
+            log("Exit Disconnecting(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+            mLastConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+            removeMessages(CONNECT_TIMEOUT);
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Disconnecting process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    deferMessage(message);
+                    break;
+                case CONNECT_TIMEOUT: {
+                    Log.w(TAG, "Disconnecting connection timeout: " + mDevice);
+                    mAcmNativeInterface.disconnectAcm(mDevice, mCurrentContextType);
+                    AcmStackEvent event =
+                            new AcmStackEvent(AcmStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+                    event.device = mDevice;
+                    event.valueInt1 = AcmStackEvent.CONNECTION_STATE_DISCONNECTED;
+                    event.valueInt2 = mCurrentContextType;
+                    sendMessage(STACK_EVENT, event);
+                    break;
+                }
+                case DISCONNECT:
+                    deferMessage(message);
+                    break;
+                case GATT_CONNECTION_STATE_CHANGED:
+                    deferMessage(message);
+                    break;
+                case STACK_EVENT:
+                    AcmStackEvent event = (AcmStackEvent) message.obj;
+                    log("Disconnecting: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case AcmStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1, event.valueInt2);
+                            break;
+                        case AcmStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED:
+                            processCodecConfigEvent(event.codecStatus, event.valueInt2);
+                            break;
+                        case AcmStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
+                        default:
+                            Log.e(TAG, "Disconnecting: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Disconnecting state
+        private void processConnectionEvent(int event, int contextType) {
+            IState smState;
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            switch (event) {
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTED: {
+                    Log.w(TAG, "Disconnecting device disconnected " + mDevice);
+                    processTransitionContextState(mDisconnecting, mDisconnected, contextType);
+                    if (contextType == CONTEXT_TYPE_MUSIC) {
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update MUSIC");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                        } else {
+                          Log.d(TAG, "Last member to disconnect, update MUSIC");
+                          service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                        }
+                    } else if (contextType == CONTEXT_TYPE_VOICE) {
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                        } else {
+                            Log.d(TAG, "Last member to disconnect, update VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                        }
+                    }
+                    if (mMusicConnectionState == BluetoothProfile.STATE_DISCONNECTED &&
+                        mVoiceConnectionState == BluetoothProfile.STATE_DISCONNECTED) {
+                        transitionTo(mDisconnected);
+                    }
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_CONNECTED: {
+                    //TODO:sendMessageDelayed(CSIP_LOCK_RELEASE_TIMEOUT, sCsipLockReleaseTimeoutMs);
+                    // Reject the connection and stay in Disconnecting state
+                    Log.w(TAG, "Incoming ACM Connected request rejected: " + mDevice);
+                    mAcmNativeInterface.disconnectAcm(mDevice, contextType);
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    if (contextType == CONTEXT_TYPE_MUSIC)
+                      service.onConnectionStateChange(mDevice, BluetoothProfile.STATE_DISCONNECTING, CONTEXT_TYPE_MUSIC, false);
+                    else if (contextType == CONTEXT_TYPE_VOICE)
+                    service.onConnectionStateChange(mDevice, BluetoothProfile.STATE_DISCONNECTING, CONTEXT_TYPE_VOICE, false);
+                    else {
+                      service.onConnectionStateChange(mDevice, BluetoothProfile.STATE_DISCONNECTING, CONTEXT_TYPE_MUSIC, false);
+                    service.onConnectionStateChange(mDevice, BluetoothProfile.STATE_DISCONNECTING, CONTEXT_TYPE_VOICE, false);
+                    }
+                    Log.d(TAG, "Updating disconnecting state to APM " + mDevice + "contextType " + contextType);
+                    IsDisconnectRequested = false;
+                    // We are already disconnecting, do nothing
+                    break;
+                default:
+                    Log.e(TAG, "Incorrect event: " + event);
+                    break;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    class Connected extends State {
+        @Override
+        public void enter() {
+            Message currentMessage = getCurrentMessage();
+            Log.i(TAG, "Enter Connected(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+            synchronized (this) {
+                mConnectionState = BluetoothProfile.STATE_CONNECTED;
+            }
+            mAcmService.handleAcmDeviceStateChange(mDevice, mConnectionState, mSetId);
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_NOT_PLAYING, CONTEXT_TYPE_MUSIC);
+        }
+
+        @Override
+        public void exit() {
+            Message currentMessage = getCurrentMessage();
+            log("Exit Connected(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+            mLastConnectionState = BluetoothProfile.STATE_CONNECTED;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Connected process message(" + mDevice + "): " + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT: {
+                    if (message.arg1 == CONTEXT_TYPE_MUSIC && mMusicConnectionState != BluetoothProfile.STATE_DISCONNECTED)
+                        break;
+                    if (message.arg1 == CONTEXT_TYPE_VOICE && mVoiceConnectionState != BluetoothProfile.STATE_DISCONNECTED)
+                        break;
+                    if (message.arg1 == CONTEXT_TYPE_MUSIC_VOICE && mVoiceConnectionState != BluetoothProfile.STATE_DISCONNECTED
+                         && mMusicConnectionState != BluetoothProfile.STATE_DISCONNECTED)
+                        break;
+                    mCurrentContextType += message.arg1;
+                    Log.i(TAG, "mCurrentContextType now is " + contextTypeToString(mCurrentContextType));
+                    mProfileType = message.arg2;
+                    mPreferredContext = (int)message.obj;
+                    Log.i(TAG, "Connecting " + contextTypeToString(message.arg1) + " to " + mDevice);
+                    if (mAcmService.IsLockSupportAvailable(mDevice)) {
+                        Log.d(TAG, "Exclusive Access support available, gatt should already be connected");
+                        //if lock support available then go for CSIP connect
+                        //mBluetoothGatt = mDevice.connectGatt(mAcmService, false, mGattCallback, BluetoothDevice.TRANSPORT_LE, 7);
+                        //sendMessageDelayed(GATT_CONNECTION_TIMEOUT, GATT_CONNECTION_TIMEOUT_MS);
+                        //transitionTo(mConnecting);
+                        //break;
+                    } else {
+                        Log.d(TAG, "Exclusive Access support not available, gatt should already be connected");
+                        //mBluetoothGatt = mDevice.connectGatt(mAcmService, false, mGattCallback, BluetoothDevice.TRANSPORT_LE, 7);
+                        //sendMessageDelayed(GATT_CONNECTION_TIMEOUT, GATT_CONNECTION_TIMEOUT_MS);
+                        //transitionTo(mConnecting);
+                    }
+                    if (!mAcmNativeInterface.connectAcm(mDevice, message.arg1, message.arg2, (int)message.obj)) {
+                        Log.e(TAG, "Disconnected: error connecting to " + mDevice + " remain in connected");
+                    }
+                } break;
+
+                case DISCONNECT: {//disconnect request goes individual
+                    IsDisconnectRequested = true;
+                    mIsDeviceWhitelisted = false;
+                    mContextTypeToDisconnect = (int)message.obj;
+                    IState state = mDisconnecting;
+                    boolean disconnected_flag = false;
+                    //check if disconnect is for individual context type
+                    Log.i(TAG, "Disconnecting " + contextTypeToString(mContextTypeToDisconnect) +  " from " + mDevice);
+                    if (!mAcmNativeInterface.disconnectAcm(mDevice, mContextTypeToDisconnect)) {
+                        Log.e(TAG, "error disconnecting " + contextTypeToString(mContextTypeToDisconnect) +  " from " + mDevice);
+                        transitionTo(mDisconnected);
+                        disconnected_flag = true;
+                    }
+                    if ((mMusicConnectionState == BluetoothProfile.STATE_CONNECTED) &&
+                        (mVoiceConnectionState == BluetoothProfile.STATE_CONNECTED)) {
+                        if (mContextTypeToDisconnect != CONTEXT_TYPE_MUSIC_VOICE) {
+                            /*only 1/2 contexts are being disconnected,
+                            remain in connected state but broadcast the connection state*/
+                            state = mConnected;
+                        }
+                    }
+                    StreamAudioService service = StreamAudioService.getStreamAudioService();
+                    processTransitionContextState(mConnected, (disconnected_flag ? mDisconnected : mDisconnecting), mContextTypeToDisconnect);
+                    if (state == mConnected) {
+                        if (mContextTypeToDisconnect == CONTEXT_TYPE_MUSIC) {
+                            if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                                Log.d(TAG, "Fellow device is already connected, update MUSIC");
+                                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                            } else {
+                              Log.d(TAG, "Last member to disconnect, update MUSIC");
+                              service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                            }
+                        } else if (mContextTypeToDisconnect == CONTEXT_TYPE_VOICE) {
+                            if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                                Log.d(TAG, "Fellow device is already connected, update VOICE");
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                            } else {
+                                Log.d(TAG, "Last member to disconnect, update VOICE");
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                            }
+                        }
+                    } else {
+                        transitionTo(state);
+                    }
+                    /*mSetId = mSetCoordinator.getRemoteDeviceSetId(mDevice, null); // TODO: UUID ?
+                    List<BluetoothDevice> members = new ArrayList<BluetoothDevice>();
+                    members.add(mDevice);
+                    //setlockvalue takes device list
+                    mCsipLockRequested = true;
+                    mDeviceLocked = false;
+                    mSetCoordinator.setLockValue(mAcmService.mCsipAppId, mSetId, members, BluetoothCsip.LOCK);*/
+                } break;
+
+                case CSIP_LOCK_STATUS_LOCKED: {
+                    mCsipLockRequested = false;
+                    int value = (int)message.arg1;
+                    int setId = (int)message.obj;
+                    int st = mAcmService.getCsipConnectionState(mDevice);
+                    Log.d(TAG, "Exclusive Access state changed:" + value);
+                    if (value == mAcmService.getCsipManager().LOCK) {
+                        mDeviceLocked = true;
+                        if (IsDisconnectRequested) {
+                            Log.i(TAG, "Disconnecting " + contextTypeToString(mContextTypeToDisconnect) +  " from " + mDevice);
+                            if (!mAcmNativeInterface.disconnectAcm(mDevice, mContextTypeToDisconnect)) { // this context Type is passed in disconnect api from APM
+                                Log.e(TAG, "error disconnecting " + contextTypeToString(mContextTypeToDisconnect) +  " from " + mDevice);
+                                transitionTo(mDisconnected);
+                            }
+                            transitionTo(mDisconnecting);
+                        } else if (IsReconfigRequested) {
+                            Log.w(TAG, "Reconfig requested Exclusive Access");
+                            if (!mAcmNativeInterface.ChangeCodecConfigPreference(mDevice, mReconfig)) { // this context Type is passed in disconnect api from APM
+                                Log.e(TAG, "reconfig error " + mDevice);
+                                break;
+                            }
+                        }
+                    }
+                } break;
+
+                case CSIP_CONNECTION_STATE_CHANGED:
+                    int state = (int)message.obj;
+                    if (state == BluetoothProfile.STATE_DISCONNECTED)
+                        mCsipConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+                    break;
+
+                case CSIP_LOCK_RELEASE_TIMEOUT:
+                    //lock release individual ?
+                    Log.d(TAG, "Exclusive Access timeout to " + mDevice);
+                    List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+                    devices.add(mDevice);
+                    mAcmService.getCsipManager().setLock(mSetId, devices, mAcmService.getCsipManager().UNLOCK);
+                    mDeviceLocked = false;
+                    break;
+
+                case CSIP_LOCK_STATUS_RELEASED:
+                    // ignore disconnect CSIP
+                    removeMessages(CSIP_LOCK_RELEASE_TIMEOUT);
+                    mDeviceLocked = false;
+                    break;
+
+                case CODEC_CONFIG_CHANGED: {
+                    IsReconfigRequested = true;
+                    mReconfig = mAcmService.getAcmName();
+                    if (!mAcmNativeInterface.ChangeCodecConfigPreference(mDevice, mReconfig)) {
+                        Log.e(TAG, "reconfig error " + mDevice);
+                        break;
+                    }
+                    /*int setId = (int)message.obj;
+                    int st = mAcmService.getCsipConnectionState(mDevice);
+                    if ((mDeviceLocked && st == BluetoothProfile.STATE_CONNECTED)) {
+                        Log.w(TAG, "Device access is already granted and DeviceGroup is in connected state");
+                        if (!mAcmNativeInterface.ChangeCodecConfigPreference(mDevice, mReconfig)) {
+                            Log.e(TAG, "reconfig error " + mDevice);
+                            break;
+                        }
+                        break;
+                    }
+                    //mSetId = mSetCoordinator.getRemoteDeviceSetId(mDevice, null ); //TODO: UUID what to set ?
+                    List<BluetoothDevice> members = new ArrayList<BluetoothDevice>();
+                    Iterator<BluetoothDevice> i = mAcmService.getCsipManager().getSetMembers(mSetId).iterator();
+                    if (i != null) {
+                        while (i.hasNext()) {
+                            BluetoothDevice device = i.next();
+                            if (mAcmService.getCsipConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
+                                members.add(device);
+                            }
+                        }
+                    }
+                    mAcmService.getCsipManager().setLock(mSetId, members, mAcmService.getCsipManager().LOCK);*/
+                } break;
+
+                case START_STREAM: {
+                    int value = (int)message.obj;
+                    if (!mAcmNativeInterface.startStream(mDevice, value)) {
+                        Log.e(TAG, "start stream error " + mDevice);
+                        break;
+                    }
+                    /*int setId = (int)message.obj;
+                    int st = mAcmService.getCsipConnectionState(mDevice);
+                    if ((mDeviceLocked && st == BluetoothProfile.STATE_CONNECTED)) {
+                        Log.w(TAG, "Device access is already granted and DeviceGroup is in connected state");
+                        if (!mAcmNativeInterface.ChangeCodecConfigPreference(mDevice, mReconfig)) {
+                            Log.e(TAG, "reconfig error " + mDevice);
+                            break;
+                        }
+                        break;
+                    }
+                    //mSetId = mSetCoordinator.getRemoteDeviceSetId(mDevice, null ); //TODO: UUID what to set ?
+                    List<BluetoothDevice> members = new ArrayList<BluetoothDevice>();
+                    Iterator<BluetoothDevice> i = mAcmService.getCsipManager().getSetMembers(mSetId).iterator();
+                    if (i != null) {
+                        while (i.hasNext()) {
+                            BluetoothDevice device = i.next();
+                            if (mAcmService.getCsipConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
+                                members.add(device);
+                            }
+                        }
+                    }
+                    mAcmService.getCsipManager().setLock(mSetId, members, mAcmService.getCsipManager().LOCK);*/
+                } break;
+
+                case START_STREAM_REQ: {
+                    if (!mAcmService.isPeerDeviceStreamingMusic(mDevice, mSetId)) {
+                        mAcmService.StartStream(mGroupAddress, CONTEXT_TYPE_VOICE);
+                    }
+                } break;
+
+                case STACK_EVENT:
+                    AcmStackEvent event = (AcmStackEvent) message.obj;
+                    log("Connected: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case AcmStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1, event.valueInt2);
+                            break;
+                        case AcmStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
+                            processAudioStateEvent(event.valueInt1, event.valueInt2);
+                            break;
+                        case AcmStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED:
+                            processCodecConfigEvent(event.codecStatus, event.valueInt2);
+                            break;
+                        default:
+                            Log.e(TAG, "Connected: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+
+                case GATT_CONNECTION_STATE_CHANGED: {
+                    Log.e(TAG, "Connection state as disconnected " + mDevice);
+                    removeMessages(GATT_CONNECTION_TIMEOUT);
+                    int st = (int)message.obj;
+                    if (st == BluetoothProfile.STATE_DISCONNECTED) {
+                        Log.d(TAG, " GATT Disconnected");
+                        mIsDeviceWhitelisted = false;
+                        mIsUpdateProfDisConnection = true;
+                        transitionTo(mDisconnected);
+                    } break;
+                }
+
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Connected state
+        private void processConnectionEvent(int event, int contextType) {
+            IState smState;
+            switch (event) {
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTED: {
+                  StreamAudioService service = StreamAudioService.getStreamAudioService();
+                  mCurrentContextType -= contextType;
+                  Log.i(TAG, "mCurrentContextType now is " + contextTypeToString(mCurrentContextType));
+                  processTransitionContextState(mDisconnecting, mDisconnected, contextType);
+                  if (contextType == CONTEXT_TYPE_MUSIC) {
+                      if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                          Log.d(TAG, "Fellow device is already connected, update MUSIC");
+                          service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                      } else {
+                          Log.d(TAG, "Last member to disconnect, update MUSIC");
+                          service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                      }
+                  } else if (mContextTypeToDisconnect == CONTEXT_TYPE_VOICE) {
+                      if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                          Log.d(TAG, "Fellow device is already connected, update VOICE");
+                          service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                      } else {
+                          Log.d(TAG, "Last member to disconnect, update VOICE");
+                          service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                      }
+                  }
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_CONNECTED: {
+                    StreamAudioService service = StreamAudioService.getStreamAudioService();
+                    //TODO:sendMessageDelayed(CSIP_LOCK_RELEASE_TIMEOUT, sCsipLockReleaseTimeoutMs);
+                    Log.w(TAG, "ACM CONNECTED event for device: " + mDevice + " context type: " + contextTypeToString(contextType));
+                    if (contextType == CONTEXT_TYPE_MUSIC) {
+                        mMusicConnectionState = BluetoothProfile.STATE_CONNECTED;
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update MUSIC");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                        } else {
+                            Log.d(TAG, "First member of group to connect MUSIC");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                        }
+                    } else if (contextType == CONTEXT_TYPE_VOICE) {
+                        mVoiceConnectionState = BluetoothProfile.STATE_CONNECTED;
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                        } else {
+                            Log.d(TAG, "First member of group to connect VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                        }
+                    }
+
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_CONNECTING: {
+                    Log.w(TAG, "Ignore ACM CONNECTED event: " + mDevice);
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTING: {
+                    if ((contextType == CONTEXT_TYPE_MUSIC) &&
+                        (mMusicConnectionState == BluetoothProfile.STATE_DISCONNECTING)) {
+                        Log.w(TAG, "Ignore Disconnecting for media - already disconnecting");
+                    } else if ((contextType == CONTEXT_TYPE_VOICE) &&
+                               (mVoiceConnectionState == BluetoothProfile.STATE_DISCONNECTING)) {
+                        Log.w(TAG, "Ignore Disconnecting for voice - already disconnecting");
+                    } else {
+                        smState = mDisconnecting;
+                        Log.w(TAG, "Connected device disconnecting: " + mDevice);
+                        if ((mMusicConnectionState == BluetoothProfile.STATE_CONNECTED) &&
+                            (mVoiceConnectionState == BluetoothProfile.STATE_CONNECTED)) {
+                            if (contextType != CONTEXT_TYPE_MUSIC_VOICE) {
+                                /*only 1/2 contexts are being disconnected,
+                                remain in connecting state but broadcast the connection state*/
+                                smState = mConnected;
+                            }
+                        }
+                        processTransitionContextState(mConnected, mDisconnecting, contextType);
+                        StreamAudioService service = StreamAudioService.getStreamAudioService();
+                        if (smState == mConnected) {
+                            if (contextType == CONTEXT_TYPE_MUSIC) {
+                                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                            } else if (contextType == CONTEXT_TYPE_VOICE) {
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                            }
+                        } else {
+                            transitionTo(smState);
+                        }
+                    }
+                } break;
+
+                default:
+                    Log.e(TAG, "Connection State Device: " + mDevice + " bad event: " + event);
+                    break;
+            }
+        }
+
+
+        // in Connected state
+        private void processAudioStateEvent(int state,  int contextType) {
+            Log.i(TAG, "Connected: processAudioStateEvent: state: " + state + " mIsMusicPlaying: " + mIsMusicPlaying);
+            switch (state) {
+                case AcmStackEvent.AUDIO_STATE_STARTED: {
+                    if (contextType == CONTEXT_TYPE_MUSIC)
+                      mIsMusicPlaying = true;
+                    else if (contextType == CONTEXT_TYPE_VOICE)
+                      mIsVoicePlaying = true;
+                    transitionTo(mStreaming);
+                } break;
+                case AcmStackEvent.AUDIO_STATE_REMOTE_SUSPEND:
+                case AcmStackEvent.AUDIO_STATE_STOPPED: {
+                    StreamAudioService service = StreamAudioService.getStreamAudioService();
+                    synchronized (this) {
+                        if (contextType == CONTEXT_TYPE_MUSIC) {
+                            if (mIsMusicPlaying) {
+                                Log.i(TAG, "Connected: stopped media playing: " + mDevice);
+                                mIsMusicPlaying = false;
+                                service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_NOT_PLAYING, ApmConst.AudioFeatures.MEDIA_AUDIO);
+                            }
+                        } else if (contextType == CONTEXT_TYPE_VOICE) {
+                            if (mIsVoicePlaying) {
+                                Log.i(TAG, "Connected: stopped voice playing: " + mDevice);
+                                mIsVoicePlaying = false;
+                                service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_NOT_PLAYING, ApmConst.AudioFeatures.CALL_AUDIO);
+                            }
+                        }
+                    }
+                } break;
+                default:
+                    Log.e(TAG, "Audio State Device: " + mDevice + " bad state: " + state);
+                    break;
+            }
+        }
+    }
+
+
+    @VisibleForTesting
+    class Streaming extends State {
+        @Override
+        public void enter() {
+            Message currentMessage = getCurrentMessage();
+            Log.i(TAG, "Enter Streaming(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+
+            if ((mMusicConnectionState == BluetoothProfile.STATE_CONNECTED)) {
+                removeDeferredMessages(CONNECT);
+            }
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if (mIsMusicPlaying) {
+                Log.i(TAG, "start playing media: " + mDevice);
+                service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_PLAYING, ApmConst.AudioFeatures.MEDIA_AUDIO);
+                mAcmService.updateLeaChannelMode(BluetoothA2dp.STATE_PLAYING, mDevice);
+            } else if (mIsVoicePlaying) {
+                Log.i(TAG, "start playing voice: " + mDevice);
+                service.onStreamStateChange(mDevice, BluetoothHeadset.STATE_AUDIO_CONNECTED, ApmConst.AudioFeatures.CALL_AUDIO);
+                setVoiceParameters();
+                setCallAudioOn(true);
+            }
+        }
+
+        @Override
+        public void exit() {
+            Message currentMessage = getCurrentMessage();
+            log("Exit Streaming(" + mDevice + "): " + (currentMessage == null ? "null"
+                    : messageWhatToString(currentMessage.what)));
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if (mIsMusicPlaying)
+              service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_NOT_PLAYING, ApmConst.AudioFeatures.MEDIA_AUDIO);
+            else if (mIsVoicePlaying) {
+              service.onStreamStateChange(mDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, ApmConst.AudioFeatures.CALL_AUDIO);
+              setCallAudioOn(false);
+            }
+            mIsMusicPlaying = false;
+            mIsVoicePlaying = false;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Streaming process message(" + mDevice + "): " + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT: {
+                    if (message.arg1 == CONTEXT_TYPE_MUSIC && mMusicConnectionState != BluetoothProfile.STATE_DISCONNECTED)
+                        break;
+                    if (message.arg1 == CONTEXT_TYPE_VOICE && mVoiceConnectionState != BluetoothProfile.STATE_DISCONNECTED)
+                        break;
+                    if (message.arg1 == CONTEXT_TYPE_MUSIC_VOICE && mVoiceConnectionState != BluetoothProfile.STATE_DISCONNECTED
+                         && mMusicConnectionState != BluetoothProfile.STATE_DISCONNECTED)
+                        break;
+                    mCurrentContextType += message.arg1;
+                    Log.i(TAG, "mCurrentContextType now is " + contextTypeToString(mCurrentContextType));
+                    mProfileType = message.arg2;
+                    mPreferredContext = (int)message.obj;
+                    Log.i(TAG, "Connecting " + contextTypeToString(message.arg1) + " to " + mDevice);
+                    if (mAcmService.IsLockSupportAvailable(mDevice)) {
+                        Log.d(TAG, "Exclusive Access support available, gatt should already be connected");
+                        //if lock support available then go for CSIP connect
+                        //mBluetoothGatt = mDevice.connectGatt(mAcmService, false, mGattCallback, BluetoothDevice.TRANSPORT_LE, 7);
+                        //sendMessageDelayed(GATT_CONNECTION_TIMEOUT, GATT_CONNECTION_TIMEOUT_MS);
+                        //transitionTo(mConnecting);
+                        //break;
+                    } else {
+                        Log.d(TAG, "Exclusive Access support not available, gatt should already be connected");
+                        //mBluetoothGatt = mDevice.connectGatt(mAcmService, false, mGattCallback, BluetoothDevice.TRANSPORT_LE, 7);
+                        //sendMessageDelayed(GATT_CONNECTION_TIMEOUT, GATT_CONNECTION_TIMEOUT_MS);
+                        //transitionTo(mConnecting);
+                    }
+                    if (!mAcmNativeInterface.connectAcm(mDevice, message.arg1, message.arg2, (int)message.obj)) {
+                        Log.e(TAG, "Disconnected: error connecting to " + mDevice + " remain in streaming");
+                    }
+                } break;
+
+                case DISCONNECT: {//disconnect request goes individual
+                    IsDisconnectRequested = true;
+                    mIsDeviceWhitelisted = false;
+                    mContextTypeToDisconnect = (int)message.obj;
+                    IState state = mDisconnecting;
+                    boolean disconnected_flag = false;
+                    //check if disconnect is for individual context type
+                    Log.i(TAG, "Disconnecting " + contextTypeToString(mContextTypeToDisconnect) +  " from " + mDevice);
+                    if (!mAcmNativeInterface.disconnectAcm(mDevice, mContextTypeToDisconnect)) {
+                        Log.e(TAG, "error disconnecting " + contextTypeToString(mContextTypeToDisconnect) +  " from " + mDevice);
+                        transitionTo(mDisconnected);
+                        disconnected_flag = true;
+                    }
+                    if ((mMusicConnectionState == BluetoothProfile.STATE_CONNECTED)
+                          && (mVoiceConnectionState == BluetoothProfile.STATE_CONNECTED)) {
+                        if (mContextTypeToDisconnect != CONTEXT_TYPE_MUSIC_VOICE) {
+                            /*only 1/2 contexts are being disconnected,
+                            remain in connected state but broadcast the connection state*/
+                            state = mConnected;
+                        }
+                    }
+                    StreamAudioService service = StreamAudioService.getStreamAudioService();
+                    processTransitionContextState(mConnected, (disconnected_flag ? mDisconnected : mDisconnecting), mContextTypeToDisconnect);
+                    if (state == mConnected) {
+                        if (mContextTypeToDisconnect == CONTEXT_TYPE_MUSIC) {
+                            if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                                Log.d(TAG, "Fellow device is already connected, update MUSIC");
+                                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                            } else {
+                              Log.d(TAG, "Last member to disconnect, update MUSIC");
+                              service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                            }
+                            transitionTo(state);
+                        } else if (mContextTypeToDisconnect == CONTEXT_TYPE_VOICE) {
+                            if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                                Log.d(TAG, "Fellow device is already connected, update VOICE");
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                            } else {
+                                Log.d(TAG, "Last member to disconnect, update VOICE");
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                            }
+                        }
+                    } else {
+                        transitionTo(state);
+                    }
+                    /*mSetId = mSetCoordinator.getRemoteDeviceSetId(mDevice, null); // TODO: UUID ?
+                    List<BluetoothDevice> members = new ArrayList<BluetoothDevice>();
+                    members.add(mDevice);
+                    //setlockvalue takes device list
+                    mCsipLockRequested = true;
+                    mDeviceLocked = false;
+                    mSetCoordinator.setLockValue(mAcmService.mCsipAppId, mSetId, members, BluetoothCsip.LOCK);*/
+                } break;
+
+                case CSIP_LOCK_STATUS_LOCKED: {
+                    mCsipLockRequested = false;
+                    int value = (int)message.arg1;
+                    int setId = (int)message.obj;
+                    int st = mAcmService.getCsipConnectionState(mDevice);
+                    Log.d(TAG, "Exclusive Access state changed:" + value);
+                    if (value == mAcmService.getCsipManager().LOCK) {
+                        mDeviceLocked = true;
+                        if (IsDisconnectRequested) {
+                            Log.i(TAG, "Disconnecting " + contextTypeToString(mContextTypeToDisconnect) +  " from " + mDevice);
+                            if (!mAcmNativeInterface.disconnectAcm(mDevice, mContextTypeToDisconnect)) { // this context Type is passed in disconnect api from APM
+                                Log.e(TAG, "error disconnecting " + contextTypeToString(mContextTypeToDisconnect) +  " from " + mDevice);
+                                transitionTo(mDisconnected);
+                            }
+                            transitionTo(mDisconnecting);
+                        } else if (IsReconfigRequested) {
+                            Log.w(TAG, "Reconfig requested Exclusive Access");
+                            if (!mAcmNativeInterface.ChangeCodecConfigPreference(mDevice, mReconfig)) { // this context Type is passed in disconnect api from APM
+                                Log.e(TAG, "reconfig error " + mDevice);
+                                break;
+                            }
+                        }
+                    }
+                } break;
+
+                case CSIP_CONNECTION_STATE_CHANGED:
+                    int state = (int)message.obj;
+                    if (state == BluetoothProfile.STATE_DISCONNECTED)
+                        mCsipConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+                    break;
+
+                case CSIP_LOCK_RELEASE_TIMEOUT:
+                    //lock release individual ?
+                    Log.d(TAG, "Exclusive Access timeout to " + mDevice);
+                    List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+                    devices.add(mDevice);
+                    mAcmService.getCsipManager().setLock(mSetId, devices, mAcmService.getCsipManager().UNLOCK);
+                    mDeviceLocked = false;
+                    break;
+
+                case CSIP_LOCK_STATUS_RELEASED:
+                    // ignore disconnect CSIP
+                    removeMessages(CSIP_LOCK_RELEASE_TIMEOUT);
+                    mDeviceLocked = false;
+                    break;
+
+                case CODEC_CONFIG_CHANGED: {
+                    IsReconfigRequested = true;
+                    mReconfig = mAcmService.getAcmName();
+                    if (!mAcmNativeInterface.ChangeCodecConfigPreference(mDevice, mReconfig)) {
+                        Log.e(TAG, "reconfig error " + mDevice);
+                        break;
+                    }
+                    /*int setId = (int)message.obj;
+                    int st = mAcmService.getCsipConnectionState(mDevice);
+                    if ((mDeviceLocked && st == BluetoothProfile.STATE_CONNECTED)) {
+                        Log.w(TAG, "Device is already acquired and DeviceGroup is in connected state");
+                        if (!mAcmNativeInterface.ChangeCodecConfigPreference(mDevice, mReconfig)) {
+                            Log.e(TAG, "reconfig error " + mDevice);
+                            break;
+                        }
+                        break;
+                    }
+                    //mSetId = mSetCoordinator.getRemoteDeviceSetId(mDevice, null); //TODO: UUID what to set ?
+                    List<BluetoothDevice> members = new ArrayList<BluetoothDevice>();
+                    Iterator<BluetoothDevice> i = mAcmService.getCsipManager().getSetMembers(setId).iterator();
+                    if (i != null) {
+                        while (i.hasNext()) {
+                            BluetoothDevice device = i.next();
+                            if (mAcmService.getCsipConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
+                                members.add(device);
+                            }
+                        }
+                    }
+                    mAcmService.getCsipManager().setLock(setId,
+                                members, mAcmService.getCsipManager().LOCK);*/
+                } break;
+
+                case STOP_STREAM: {
+                    int value = (int)message.obj;
+                    if (!mAcmNativeInterface.stopStream(mDevice, value)) {
+                        Log.e(TAG, "start stream error " + mDevice);
+                        break;
+                    }
+                    /*int setId = (int)message.obj;
+                    int st = mAcmService.getCsipConnectionState(mDevice);
+                    if ((mDeviceLocked && st == BluetoothProfile.STATE_CONNECTED)) {
+                        Log.w(TAG, "Device access is already granted and DeviceGroup is in connected state");
+                        if (!mAcmNativeInterface.ChangeCodecConfigPreference(mDevice, mReconfig)) {
+                            Log.e(TAG, "reconfig error " + mDevice);
+                            break;
+                        }
+                        break;
+                    }
+                    //mSetId = mSetCoordinator.getRemoteDeviceSetId(mDevice, null ); //TODO: UUID what to set ?
+                    List<BluetoothDevice> members = new ArrayList<BluetoothDevice>();
+                    Iterator<BluetoothDevice> i = mAcmService.getCsipManager().getSetMembers(mSetId).iterator();
+                    if (i != null) {
+                        while (i.hasNext()) {
+                            BluetoothDevice device = i.next();
+                            if (mAcmService.getCsipConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
+                                members.add(device);
+                            }
+                        }
+                    }
+                    mAcmService.getCsipManager().setLock(mSetId, members, mAcmService.getCsipManager().LOCK);*/
+                } break;
+
+                case START_STREAM:
+                    int value = (int)message.obj;
+                    if (value == CONTEXT_TYPE_VOICE && mIsMusicPlaying) {
+                        deferMessage(obtainMessage(START_STREAM_REQ, value));
+                        Log.wtf(TAG, "Defer START request for voice context");
+                    }
+                    break;
+
+                case STACK_EVENT:
+                    AcmStackEvent event = (AcmStackEvent) message.obj;
+                    log("Streaming: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case AcmStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1, event.valueInt2);
+                            break;
+                        case AcmStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
+                            processAudioStateEvent(event.valueInt1, event.valueInt2);
+                            break;
+                        case AcmStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED:
+                            processCodecConfigEvent(event.codecStatus, event.valueInt2);
+                            break;
+                        default:
+                            Log.e(TAG, "Streaming: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                    default:
+                        return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Streaming state
+        private void processConnectionEvent(int event, int contextType) {
+            IState smState;
+            switch (event) {
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTED: {
+                    //TODO: sendMessageDelayed(CSIP_LOCK_RELEASE_TIMEOUT, sCsipLockReleaseTimeoutMs);
+                    Log.w(TAG, "Streaming device disconnected: " + mDevice);
+                    StreamAudioService service = StreamAudioService.getStreamAudioService();
+                    mCurrentContextType -= contextType;
+                    Log.i(TAG, "mCurrentContextType now is " + contextTypeToString(mCurrentContextType));
+                    processTransitionContextState(mDisconnecting, mDisconnected, contextType);
+                    if (contextType == CONTEXT_TYPE_MUSIC) {
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update MUSIC");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                        } else {
+                            Log.d(TAG, "Last member to disconnect, update MUSIC");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                        }
+                        transitionTo(mConnected);
+                    } else if (mContextTypeToDisconnect == CONTEXT_TYPE_VOICE) {
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                        } else {
+                            Log.d(TAG, "Last member to disconnect, update VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                        }
+                    }
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_CONNECTED: {
+                    StreamAudioService service = StreamAudioService.getStreamAudioService();
+                    //TODO:sendMessageDelayed(CSIP_LOCK_RELEASE_TIMEOUT, sCsipLockReleaseTimeoutMs);
+                    Log.w(TAG, "ACM CONNECTED event for device: " + mDevice + " context type: " + contextTypeToString(contextType));
+                    if (contextType == CONTEXT_TYPE_MUSIC) {
+                        mMusicConnectionState = BluetoothProfile.STATE_CONNECTED;
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update MUSIC");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                        } else {
+                            Log.d(TAG, "First member of group to connect MUSIC");
+                            service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, true);
+                        }
+                    } else if (contextType == CONTEXT_TYPE_VOICE) {
+                        mVoiceConnectionState = BluetoothProfile.STATE_CONNECTED;
+                        if (mAcmService.isPeerDeviceConnected(mDevice, mSetId)) {
+                            Log.d(TAG, "Fellow device is already connected, update VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                        } else {
+                            Log.d(TAG, "First member of group to connect VOICE");
+                            service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, true);
+                        }
+                    }
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_CONNECTING: {
+                    Log.w(TAG, "ACM CONNECTING event: " + mDevice);
+                } break;
+
+                case AcmStackEvent.CONNECTION_STATE_DISCONNECTING: {
+                    if ((contextType == CONTEXT_TYPE_MUSIC) &&
+                        (mMusicConnectionState == BluetoothProfile.STATE_DISCONNECTING)) {
+                        Log.w(TAG, "Ignore Disconnecting for media - already disconnecting");
+                    } else if ((contextType == CONTEXT_TYPE_VOICE) &&
+                               (mVoiceConnectionState == BluetoothProfile.STATE_DISCONNECTING)) {
+                        Log.w(TAG, "Ignore Disconnecting for voice - already disconnecting");
+                    } else {
+                        smState = mDisconnecting;
+                        Log.w(TAG, "Connected device disconnecting: " + mDevice);
+                        if ((mMusicConnectionState == BluetoothProfile.STATE_CONNECTED) &&
+                            (mVoiceConnectionState == BluetoothProfile.STATE_CONNECTED)) {
+                            if (contextType != CONTEXT_TYPE_MUSIC_VOICE) {
+                                /*only 1/2 contexts are being disconnected,
+                                remain in connecting state but broadcast the connection state*/
+                                smState = mConnected;
+                            }
+                        }
+                        processTransitionContextState(mConnected, mDisconnecting, contextType);
+                        StreamAudioService service = StreamAudioService.getStreamAudioService();
+                        if (smState == mConnected) {
+                            if (contextType == CONTEXT_TYPE_MUSIC) {
+                                service.onConnectionStateChange(mDevice, mMusicConnectionState, CONTEXT_TYPE_MUSIC, false);
+                            } else if (contextType == CONTEXT_TYPE_VOICE) {
+                                service.onConnectionStateChange(mDevice, mVoiceConnectionState, ApmConst.AudioFeatures.CALL_AUDIO, false);
+                            }
+                        } else {
+                            transitionTo(smState);
+                        }
+                    }
+                } break;
+
+                default:
+                    Log.e(TAG, "Connection State Device: " + mDevice + " bad event: " + event);
+                    break;
+            }
+        }
+
+        // in Streaming state
+        private void processAudioStateEvent(int state,  int contextType) {
+            Log.i(TAG, "Streaming: processAudioStateEvent: state: " + state + "mIsMusicPlaying: " + mIsMusicPlaying);
+            switch (state) {
+                case AcmStackEvent.AUDIO_STATE_STARTED:
+                    Log.i(TAG, "Streaming: already started: " + mDevice);
+                    break;
+                case AcmStackEvent.AUDIO_STATE_REMOTE_SUSPEND:
+                case AcmStackEvent.AUDIO_STATE_STOPPED:
+                    synchronized (this) {
+                        StreamAudioService service = StreamAudioService.getStreamAudioService();
+                        if (contextType == CONTEXT_TYPE_MUSIC) {
+                            if (mIsMusicPlaying) {
+                                Log.i(TAG, "Streaming: stopped media playing: " + mDevice);
+                                mIsMusicPlaying = false;
+                                service.onStreamStateChange(mDevice, BluetoothA2dp.STATE_NOT_PLAYING, CONTEXT_TYPE_MUSIC);
+                                if (mAcmService.isShoPendingStop()) {
+                                    Log.i(TAG, "Streaming: SHO was pending earlier, complete now");
+                                    mAcmService.resetShoPendingStop();
+                                    service.onActiveDeviceChange(null, ApmConst.AudioFeatures.MEDIA_AUDIO);
+                                }
+                                transitionTo(mConnected);
+                            }
+                        }
+                        if (contextType == CONTEXT_TYPE_VOICE) {
+                            if (mIsVoicePlaying) {
+                                Log.i(TAG, "Streaming: stopped voice playing: " + mDevice);
+                                mIsVoicePlaying = false;
+                                service.onStreamStateChange(mDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, ApmConst.AudioFeatures.CALL_AUDIO);
+                                setCallAudioOn(false);
+                                if (mAcmService.isVoiceShoPendingStop()) {
+                                    Log.i(TAG, "Voice SHO was pending earlier, complete now");
+                                    mAcmService.resetVoiceShoPendingStop();
+                                    service.onActiveDeviceChange(mAcmService.getVoiceActiveDevice(), ApmConst.AudioFeatures.CALL_AUDIO);
+                                }
+                                transitionTo(mConnected);
+                            }
+                        }
+                    }
+                    break;
+                default:
+                    Log.e(TAG, "Audio State Device: " + mDevice + " bad state: " + state);
+                    break;
+            }
+        }
+    }
+
+    private String getFrameDuration() {
+        BluetoothCodecConfig mCodecConfig = mVoiceCodecStatus.getCodecConfig();
+        long cs1 = mCodecConfig.getCodecSpecific1() >> 32 & 0xff;
+        if (cs1 == 0x00) {
+            return "7.5";
+        } else {
+            return "10";
+        }
+    }
+
+    private String getLc3BlocksPerSdu() {
+        BluetoothCodecConfig mCodecConfig = mVoiceCodecStatus.getCodecConfig();
+        long cs1 = mCodecConfig.getCodecSpecific1() >> 40 & 0xff;
+        return Integer.toString((int)cs1);
+    }
+
+    private String getCodectype() {
+        BluetoothCodecConfig mCodecConfig = mVoiceCodecStatus.getCodecConfig();
+        long cs3 = (mCodecConfig.getCodecSpecific3() >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_1ST_INDEX)) & CS_PARAM_IND_MASK;
+        if (cs3 == CODEC_TYPE_LC3Q) {
+          return "LC3Q";
+        } else {
+          return "LC3";
+        }
+    }
+
+    private String getLc3qValues() {
+        BluetoothCodecConfig mCodecConfig = mVoiceCodecStatus.getCodecConfig();
+        long cs3 = mCodecConfig.getCodecSpecific3();
+        long cs4 = mCodecConfig.getCodecSpecific4();
+
+        String vsMetaDataLc3qVal = String.join(",", new String[]{
+               String.format("%02X", ((cs4 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_8TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs4 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_7TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs4 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_6TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs4 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_5TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs4 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_4TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs4 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_3RD_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs4 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_2ND_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs4 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_1ST_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs3 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_8TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", (((cs3 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_2ND_INDEX)) & CS_PARAM_IND_MASK) & 0x01)),
+               String.format("%02X", ((cs3 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_1ST_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs3 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_7TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs3 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_6TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs3 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_5TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs3 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_4TH_INDEX)) & CS_PARAM_IND_MASK)),
+               String.format("%02X", ((cs3 >>
+                (CS_PARAM_NUM_BITS * CS_PARAM_3RD_INDEX)) & CS_PARAM_IND_MASK))
+          });
+        Log.i(TAG, "getLc3qValues() for " + mDevice + ": " + vsMetaDataLc3qVal);
+        return vsMetaDataLc3qVal;
+    }
+
+    private String getRxTxConfigIndex() {
+        BluetoothCodecConfig mCodecConfig = mVoiceCodecStatus.getCodecConfig();
+        int sampleRate = mCodecConfig.getSampleRate();
+        long cs1 = mCodecConfig.getCodecSpecific1() >> 0 & 0xff;
+        if (sampleRate == BluetoothCodecConfig.SAMPLE_RATE_8000) {
+            if (cs1 == 0x01) {
+                return "0";
+            } else {
+                return "1";
+            }
+        } else if (sampleRate == BluetoothCodecConfig.SAMPLE_RATE_16000) {
+            if (cs1 == 0x01) {
+                return "2";
+            } else {
+                return "3";
+            }
+        } else if (sampleRate == BluetoothCodecConfig.SAMPLE_RATE_32000) {
+            if (cs1 == 0x01) {
+                return "4";
+            } else {
+                return "5";
+            }
+        }
+        return "0";
+    }
+
+    private void setVoiceParameters() {
+        String keyValuePairs = String.join(";", new String[]{
+                CODEC_NAME + "=" + "LC3",
+                STREAM_MAP + "=" + "(0, 0, M, 0, 1, L),(1, 0, M, 1, 1, R)",
+                FRAME_DURATION + "=" + getFrameDuration(),
+                SDU_BLOCK + "=" + getLc3BlocksPerSdu(),
+                RXCONFIG_INDX + "=" + getRxTxConfigIndex(),
+                TXCONFIG_INDX + "=" + getRxTxConfigIndex(),
+                VERSION + "=" + "21",
+                VENDOR_META_DATA + "=" + getLc3qValues()
+        });
+        Log.i(TAG, "setVoiceParameters for " + mDevice + ": " + keyValuePairs);
+        StreamAudioService service = StreamAudioService.getStreamAudioService();
+        service.setCallAudioParam(keyValuePairs);
+    }
+
+    private void setCallAudioOn(boolean on) {
+        Log.i(TAG, "set Call Audio On: " + on);
+        StreamAudioService service = StreamAudioService.getStreamAudioService();
+        service.setCallAudioOn(on);
+    }
+
+    int getConnectionState() {
+        return mConnectionState;
+    }
+
+    int getCsipConnectionState() {
+        return mCsipConnectionState;
+    }
+
+    BluetoothDevice getDevice() {
+        return mDevice;
+    }
+
+    BluetoothDevice getPeerDevice() {
+        BluetoothDevice d = null;
+        List<BluetoothDevice> members = mAcmService.getCsipManager().getSetMembers(mSetId);
+        if (members == null) {
+            Log.d(TAG, "No set member found");
+            return d;
+        }
+        Iterator<BluetoothDevice> i = members.iterator();
+        if (i != null) {
+            while (i.hasNext()) {
+                d = i.next();
+                if (!(Objects.equals(d, mDevice))) {
+                    Log.d(TAG, "Device: " + d);
+                    break;
+                }
+            }
+        }
+        return d;
+    }
+
+    void removeDevicefromBgWL() {
+        Log.d(TAG, "remove device from BG WL");
+        if (mBluetoothGatt != null && mIsDeviceWhitelisted) {
+            mIsDeviceWhitelisted = false;
+            mBluetoothGatt.disconnect();
+        }
+    }
+
+    boolean isConnected() {
+        synchronized (this) {
+            return (getConnectionState() == BluetoothProfile.STATE_CONNECTED);
+        }
+    }
+
+    boolean isCsipLockRequested() {
+        synchronized (this) {
+            return mCsipLockRequested;
+        }
+    }
+
+    boolean isMusicPlaying() {
+        synchronized (this) {
+            return mIsMusicPlaying;
+        }
+    }
+
+    boolean isVoicePlaying() {
+        synchronized (this) {
+            return mIsVoicePlaying;
+        }
+    }
+
+    private void processTransitionContextState(IState prevState, IState nextState, int contextType) {
+        int pState = AcmStateToBluetoothProfileState(prevState);
+        int nState = AcmStateToBluetoothProfileState(nextState);
+        if (contextType == CONTEXT_TYPE_MUSIC) {
+            mLastMusicConnectionState = pState;
+            mMusicConnectionState = nState;
+        } else if (contextType == CONTEXT_TYPE_VOICE) {
+            mLastVoiceConnectionState = pState;
+            mVoiceConnectionState = nState;
+        } else if (contextType == CONTEXT_TYPE_MUSIC_VOICE) {
+            mLastMusicConnectionState = pState;
+            mLastVoiceConnectionState = pState;
+            mMusicConnectionState = nState;
+            mVoiceConnectionState = nState;
+        }
+    }
+
+    // NOTE: This event is processed in any state
+    @VisibleForTesting
+    void processCodecConfigEvent(BluetoothCodecStatus newCodecStatus, int contextType) {
+        Log.d(TAG,"ProcessCodecConfigEvent: context type :" + contextType);
+        if (contextType == CONTEXT_TYPE_MUSIC) {
+            BluetoothCodecConfig mCodecConfig = newCodecStatus.getCodecConfig();
+            long cs3 = mCodecConfig.getCodecSpecific3();
+            cs3 |= LE_AUDIO_AVAILABLE_LICENSED;
+            BluetoothCodecConfig mCodecConfigLc3 = new BluetoothCodecConfig(
+                                                       mCodecConfig.getCodecType(),
+                                                       mCodecConfig.getCodecPriority(),
+                                                       mCodecConfig.getSampleRate(),
+                                                       mCodecConfig.getBitsPerSample(),
+                                                       mCodecConfig.getChannelMode(),
+                                                       mCodecConfig.getCodecSpecific1(), mCodecConfig.getCodecSpecific2(),
+                                                       cs3, mCodecConfig.getCodecSpecific4());
+            mMusicCodecStatus = new BluetoothCodecStatus(mCodecConfigLc3,
+                                                         newCodecStatus.getCodecsLocalCapabilities(),
+                                                         newCodecStatus.getCodecsSelectableCapabilities());
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            service.onMediaCodecConfigChange(mGroupAddress, mMusicCodecStatus, contextType);
+        } else if (contextType == CONTEXT_TYPE_VOICE) {
+            mVoiceCodecStatus = newCodecStatus;
+        }
+    }
+
+    @Override
+    protected String getLogRecString(Message msg) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(messageWhatToString(msg.what));
+        builder.append(": ");
+        builder.append("arg1=")
+                .append(msg.arg1)
+                .append(", arg2=")
+                .append(msg.arg2)
+                .append(", obj=")
+                .append(msg.obj);
+        return builder.toString();
+    }
+
+    private static boolean sameSelectableCodec(BluetoothCodecStatus prevCodecStatus,
+            BluetoothCodecStatus newCodecStatus) {
+        if (prevCodecStatus == null) {
+            return false;
+        }
+        return BluetoothCodecStatus.sameCapabilities(
+                prevCodecStatus.getCodecsSelectableCapabilities(),
+                newCodecStatus.getCodecsSelectableCapabilities());
+    }
+
+    private static String messageWhatToString(int what) {
+        switch (what) {
+            case CONNECT:
+                return "CONNECT";
+            case DISCONNECT:
+                return "DISCONNECT";
+            case STACK_EVENT:
+                return "STACK_EVENT";
+            case CONNECT_TIMEOUT:
+                return "CONNECT_TIMEOUT";
+            default:
+                break;
+        }
+        return Integer.toString(what);
+    }
+
+    private static String contextTypeToString(int contextType) {
+        switch (contextType) {
+            case CONTEXT_TYPE_UNKNOWN:
+                return "UNKNOWN";
+            case CONTEXT_TYPE_MUSIC:
+                return "MEDIA";
+            case CONTEXT_TYPE_VOICE:
+                return "CONVERSATIONAL";
+            case CONTEXT_TYPE_MUSIC_VOICE:
+                return "MEDIA+CONVERSATIONAL";
+            default:
+                break;
+        }
+        return Integer.toString(contextType);
+    }
+
+    private static String profileStateToString(int state) {
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return "DISCONNECTED";
+            case BluetoothProfile.STATE_CONNECTING:
+                return "CONNECTING";
+            case BluetoothProfile.STATE_CONNECTED:
+                return "CONNECTED";
+            case BluetoothProfile.STATE_DISCONNECTING:
+                return "DISCONNECTING";
+            default:
+                break;
+        }
+        return Integer.toString(state);
+    }
+
+    /*private static String musicAudioStateToString(int state) {
+        switch (state) {
+            case BluetoothA2dp.STATE_PLAYING:
+                return "PLAYING";
+            case BluetoothA2dp.STATE_NOT_PLAYING:
+                return "NOT_PLAYING";
+            default:
+                break;
+        }
+        return Integer.toString(state);
+    }*/
+
+    private static String voiceAudioStateToString(int state) {
+        switch (state) {
+          case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+            return "AUDIO_DISCONNECTED";
+          case BluetoothHeadset.STATE_AUDIO_CONNECTING:
+            return "AUDIO_CONNECTING";
+          case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+            return "AUDIO_CONNECTED";
+          case BluetoothHeadset.STATE_AUDIO_DISCONNECTING:
+            return "AUDIO_DISCONNECTING";
+          default:
+            break;
+        }
+        return Integer.toString(state);
+    }
+
+    private static int AcmStateToBluetoothProfileState(IState state) {
+        if (state instanceof Disconnected) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        } else if (state instanceof Connecting) {
+            return BluetoothProfile.STATE_CONNECTING;
+        } else if (state instanceof Connected) {
+            return BluetoothProfile.STATE_CONNECTED;
+        } else if (state instanceof Disconnecting) {
+            return BluetoothProfile.STATE_DISCONNECTING;
+        }
+        Log.w(TAG, "Unknown State");
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    public void dump(StringBuilder sb) {
+        ProfileService.println(sb, "mDevice: " + mDevice);
+        ProfileService.println(sb, "  StateMachine: " + this.toString());
+        ProfileService.println(sb, "  mIsMusicPlaying: " + mIsMusicPlaying);
+        synchronized (this) {
+            if (mVoiceCodecStatus != null) {
+                ProfileService.println(sb, " Voice mCodecConfig: " + mVoiceCodecStatus.getCodecConfig());
+            }
+            if (mMusicCodecStatus != null) {
+                ProfileService.println(sb, " Music mCodecConfig: " + mMusicCodecStatus.getCodecConfig());
+            }
+        }
+        // Dump the state machine logs
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+        super.dump(new FileDescriptor(), printWriter, new String[]{});
+        printWriter.flush();
+        stringWriter.flush();
+        ProfileService.println(sb, "  StateMachineLog:");
+        Scanner scanner = new Scanner(stringWriter.toString());
+        while (scanner.hasNextLine()) {
+            String line = scanner.nextLine();
+            ProfileService.println(sb, "    " + line);
+        }
+        scanner.close();
+    }
+
+    @Override
+    protected void log(String msg) {
+        if (DBG) {
+            super.log(msg);
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/ActiveDeviceManagerService.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/ActiveDeviceManagerService.java
new file mode 100644
index 0000000..dfcabf1
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/ActiveDeviceManagerService.java
@@ -0,0 +1,1444 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+
+/**
+ * Bluetooth ActiveDeviceManagerService. There is one instance each for
+ *  voice and media profile management..
+ *  - "Idle" and "Active" are steady states.
+ *  - "Activating" and "Deactivating" are transient states until the
+ *     SHO / Deactivation is completed.
+ *
+ *
+ *                               (Idle)
+ *                             |        ^
+ *                   SetActive |        | Removed
+ *                             V        |
+ *                 (Activating)          (Deactivating)
+ *                             |        ^
+ *                 Activated   |        | setActive(NULL) / removeDevice
+ *                             V        |
+ *                      (Active / Broadcasting)
+ *
+ *
+ */
+
+package com.android.bluetooth.apm;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothHeadset;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.ActiveDeviceManager;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.a2dp.A2dpService;
+import com.android.bluetooth.hearingaid.HearingAidService;
+import com.android.bluetooth.hfp.HeadsetService;
+import com.android.bluetooth.mcp.McpService;
+import com.android.bluetooth.broadcast.BroadcastService;
+import com.android.bluetooth.cc.CCService;
+import android.content.Intent;
+import android.content.Context;
+import android.os.Looper;
+import android.os.Message;
+import android.os.HandlerThread;
+import android.os.UserHandle;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.media.AudioManager;
+
+import com.android.bluetooth.BluetoothStatsLog;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.Boolean;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.lang.Integer;
+import java.util.Scanner;
+import java.util.Objects;
+
+public class ActiveDeviceManagerService {
+    private static final boolean DBG = true;
+    private static final String TAG = "APM: ActiveDeviceManagerService";
+    private static ActiveDeviceManagerService sActiveDeviceManager = null;
+    private AudioManager mAudioManager;
+    private HandlerThread[] thread = new HandlerThread[AudioType.SIZE];
+    private ShoStateMachine[] sm = new ShoStateMachine[AudioType.SIZE];
+    private ApmNativeInterface apmNative;
+    private Context mContext;
+    private boolean txStreamSuspended = false;
+    private final Lock lock = new ReentrantLock();
+    private final Condition mediaHandoffComplete = lock.newCondition();
+    private final Condition voiceHandoffComplete = lock.newCondition();
+    static class Event {
+        static final int SET_ACTIVE = 1;
+        static final int ACTIVE_DEVICE_CHANGE = 2;
+        static final int REMOVE_DEVICE = 3;
+        static final int DEVICE_REMOVED = 4;
+        static final int ACTIVATE_TIMEOUT = 5;
+        static final int DEACTIVATE_TIMEOUT = 6;
+        static final int SUSPEND_RECORDING = 7;
+        static final int RESUME_RECORDING = 8;
+        static final int RETRY_DEACTIVATE = 9;
+        static final int STOP_SM = 0;
+    }
+
+    public static final int SHO_SUCCESS = 0;
+    public static final int SHO_PENDING = 1;
+    public static final int SHO_FAILED = 2;
+    public static final int ALREADY_ACTIVE = 3;
+
+    static final int RETRY_LIMIT = 4;
+    static final int ACTIVATE_TIMEOUT_DELAY = 3000;
+    static final int DEACTIVATE_TIMEOUT_DELAY = 2000;
+    static final int DEACTIVATE_TRY_DELAY = 500;
+
+    private ActiveDeviceManagerService (Context context) {
+        thread[AudioType.MEDIA] = new HandlerThread("ActiveDeviceManager.MediaThread");
+        thread[AudioType.MEDIA].start();
+        Looper mediaLooper = thread[AudioType.MEDIA].getLooper();
+        sm[AudioType.MEDIA] = new ShoStateMachine(AudioType.MEDIA, mediaLooper);
+
+        thread[AudioType.VOICE] = new HandlerThread("ActiveDeviceManager.VoiceThread");
+        thread[AudioType.VOICE].start();
+        Looper voiceLooper = thread[AudioType.VOICE].getLooper();
+        sm[AudioType.VOICE] = new ShoStateMachine(AudioType.VOICE, voiceLooper);
+
+        mContext = context;
+        apmNative = ApmNativeInterface.getInstance();
+        apmNative.init();
+
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+            Objects.requireNonNull(mAudioManager,
+                               "AudioManager cannot be null when ActiveDeviceManagerService starts");
+    }
+
+    public static ActiveDeviceManagerService get(Context context) {
+        if(sActiveDeviceManager == null) {
+            sActiveDeviceManager = new ActiveDeviceManagerService(context);
+            ActiveDeviceManagerServiceIntf.init(sActiveDeviceManager);
+        }
+        return sActiveDeviceManager;
+    }
+
+    public static ActiveDeviceManagerService get() {
+        return sActiveDeviceManager;
+    }
+
+    public boolean setActiveDevice(BluetoothDevice device, Integer mAudioType, Boolean isUIReq, Boolean playReq) {
+        Log.d(TAG, "setActiveDevice(" + device + ") audioType: " + mAudioType);
+        boolean isCallActive = false;
+        if(ApmConst.AudioFeatures.CALL_AUDIO == mAudioType) {
+            CallAudio mCallAudio = CallAudio.get();
+            isCallActive = mCallAudio.isAudioOn();
+        }
+
+        synchronized(sm[mAudioType]) {
+            sm[mAudioType].mSHOQueue.device = device;
+            sm[mAudioType].mSHOQueue.isUIReq = isUIReq;
+            sm[mAudioType].mSHOQueue.PlayReq = (playReq || isCallActive);
+            sm[mAudioType].mSHOQueue.isBroadcast = false;
+            sm[mAudioType].mSHOQueue.isRecordingMode = false;
+            sm[mAudioType].mSHOQueue.isGamingMode = false;
+        }
+
+        if(device != null) {
+            sm[mAudioType].sendMessage(Event.SET_ACTIVE);
+        }
+        else {
+            if (mAudioType == AudioType.MEDIA && sm[mAudioType].mState == sm[mAudioType].mBroadcasting) {
+                Log.d(TAG, "LE Broadcast is active, ignore REMOVE_DEVICE");
+            } else {
+                sm[mAudioType].sendMessage(Event.REMOVE_DEVICE);
+            }
+        }
+        return isUIReq;
+    }
+
+    public boolean setActiveDevice(BluetoothDevice device, Integer mAudioType, Boolean isUIReq) {
+        return setActiveDevice(device, mAudioType, isUIReq, false);
+    }
+
+    public boolean setActiveDevice(BluetoothDevice device, Integer mAudioType) {
+        return setActiveDevice(device, mAudioType, false, false);
+    }
+
+    public boolean setActiveDeviceBlocking(BluetoothDevice device, Integer mAudioType) {
+        Log.d(TAG, "setActiveDeviceBlocking: Enter");
+        if(ApmConst.AudioFeatures.CALL_AUDIO == mAudioType) {
+            setActiveDevice(device, mAudioType, false, false);
+            try {
+                lock.lock();
+                voiceHandoffComplete.await();
+            } catch (InterruptedException e) {
+                Log.d(TAG, "setActiveDeviceBlocking: Unblocked because of exception: " + e);
+            } finally {
+                Log.d(TAG, "setActiveDeviceBlocking: unlock");
+                lock.unlock();
+            }
+        }
+        Log.d(TAG, "setActiveDeviceBlocking: Exit");
+        return true;
+    }
+
+    public BluetoothDevice getQueuedDevice(Integer mAudioType) {
+        return sm[mAudioType].mSHOQueue.device;
+    }
+
+    public boolean removeActiveDevice(Integer mAudioType, Boolean forceStopAudio) {
+        sm[mAudioType].mSHOQueue.forceStopAudio = forceStopAudio;
+        setActiveDevice(null, mAudioType, false);
+        return true;
+    }
+
+    public BluetoothDevice getActiveDevice(Integer mAudioType) {
+        return sm[mAudioType].Current.Device;
+    }
+
+    public int getActiveProfile(Integer mAudioType) {
+        if(sm[mAudioType].mState == sm[mAudioType].mBroadcasting) {
+            // Use Current.Profile here
+            return ApmConst.AudioProfiles.BROADCAST_LE;
+        } else if(sm[mAudioType].mState == sm[mAudioType].mActive) {
+            return sm[mAudioType].Current.Profile;
+        }
+        return ApmConst.AudioProfiles.NONE;
+    }
+
+    public boolean onActiveDeviceChange(BluetoothDevice device, Integer mAudioType) {
+        return onActiveDeviceChange(device, mAudioType, ApmConst.AudioProfiles.NONE);
+    }
+
+    public boolean onActiveDeviceChange(BluetoothDevice device, Integer mAudioType, Integer mProfile) {
+        if (device != null || mProfile == ApmConst.AudioProfiles.BROADCAST_LE) {
+            DeviceProfileCombo mDeviceProfileCombo = new DeviceProfileCombo(device, mProfile);
+            sm[mAudioType].sendMessage(Event.ACTIVE_DEVICE_CHANGE, mDeviceProfileCombo);
+        }
+        else
+            sm[mAudioType].sendMessage(Event.DEVICE_REMOVED);
+        return true;
+    }
+
+    public boolean enableBroadcast(BluetoothDevice device) {
+        synchronized(sm[AudioType.MEDIA]) {
+            sm[AudioType.MEDIA].mSHOQueue.device = device;
+            sm[AudioType.MEDIA].mSHOQueue.isBroadcast = true;
+            sm[AudioType.MEDIA].mSHOQueue.PlayReq = false;
+        }
+        sm[AudioType.MEDIA].sendMessage(Event.SET_ACTIVE);
+        return true;
+    }
+
+    public boolean disableBroadcast() {
+        Log.d(TAG, "disableBroadcast");
+        synchronized(sm[AudioType.MEDIA]) {
+            sm[AudioType.MEDIA].mSHOQueue.isBroadcast = false;
+            sm[AudioType.MEDIA].mSHOQueue.PlayReq = false;
+        }
+        if (sm[AudioType.MEDIA].mState == sm[AudioType.MEDIA].mBroadcasting) {
+            sm[AudioType.MEDIA].sendMessage(Event.REMOVE_DEVICE);
+        }
+        return true;
+    }
+
+    public boolean enableGaming(BluetoothDevice device) {
+        if (sm[AudioType.MEDIA].mState == sm[AudioType.MEDIA].mGamingMode &&
+                        device.equals(getActiveDevice(AudioType.MEDIA))) {
+            Log.d(TAG, "Device already in Gaming Mode");
+            return true;
+        }
+
+        Log.d(TAG, "enableGaming");
+        synchronized(sm[AudioType.MEDIA]) {
+            sm[AudioType.MEDIA].mSHOQueue.device = device;
+            sm[AudioType.MEDIA].mSHOQueue.isBroadcast = false;
+            sm[AudioType.MEDIA].mSHOQueue.isGamingMode = true;
+            sm[AudioType.MEDIA].mSHOQueue.PlayReq = false;
+        }
+        sm[AudioType.MEDIA].sendMessage(Event.SET_ACTIVE);
+        return true;
+    }
+
+    public boolean disableGaming(BluetoothDevice device) {
+        if (sm[AudioType.MEDIA].mState != sm[AudioType.MEDIA].mGamingMode) {
+            Log.e(TAG, "Gaming Mode not active");
+            return true;
+        }
+
+        /*MediaAudio mMediaAudio = MediaAudio.get();
+        if(mMediaAudio != null && mMediaAudio.isA2dpPlaying(device)) {
+            Log.w(TAG, "Gaming Stream is Active");
+            return false;
+        }*/
+
+        Log.d(TAG, "disableGaming");
+        synchronized(sm[AudioType.MEDIA]) {
+            sm[AudioType.MEDIA].mSHOQueue.device = device;
+            sm[AudioType.MEDIA].mSHOQueue.isGamingMode = false;
+            sm[AudioType.MEDIA].mSHOQueue.PlayReq = false;
+            sm[AudioType.MEDIA].mSHOQueue.isUIReq = true;
+        }
+
+        //sm[AudioType.MEDIA].sendMessageDelayed(Event.SET_ACTIVE, DEACTIVATE_TRY_DELAY);
+        sm[AudioType.MEDIA].sendMessage(Event.SET_ACTIVE);
+        return true;
+    }
+
+    public boolean enableRecording(BluetoothDevice device) {
+        Log.d(TAG, "enableRecording: " + device);
+
+        MediaAudio mMediaAudio = MediaAudio.get();
+        if(txStreamSuspended == false) {
+            Log.d(TAG, "Set A2dpSuspended=true");
+            mAudioManager.setParameters("A2dpSuspended=true");
+            txStreamSuspended = true;
+        }
+
+        synchronized(sm[AudioType.MEDIA]) {
+            sm[AudioType.MEDIA].mSHOQueue.device = device;
+            sm[AudioType.MEDIA].mSHOQueue.isBroadcast = false;
+            sm[AudioType.MEDIA].mSHOQueue.isGamingMode = false;
+            sm[AudioType.MEDIA].mSHOQueue.isRecordingMode = true;
+            sm[AudioType.MEDIA].mSHOQueue.PlayReq = false;
+            sm[AudioType.MEDIA].mSHOQueue.isUIReq = true;
+        }
+        sm[AudioType.MEDIA].sendMessage(Event.SET_ACTIVE);
+        return true;
+    }
+
+    public boolean disableRecording(BluetoothDevice device) {
+        Log.d(TAG, "disableRecording: " + device);
+
+        synchronized(sm[AudioType.MEDIA]) {
+            sm[AudioType.MEDIA].mSHOQueue.device = device;
+            sm[AudioType.MEDIA].mSHOQueue.isRecordingMode = false;
+            sm[AudioType.MEDIA].mSHOQueue.PlayReq = false;
+        }
+
+        if (sm[AudioType.MEDIA].mState == sm[AudioType.MEDIA].mRecordingMode) {
+            sm[AudioType.MEDIA].sendMessage(Event.SET_ACTIVE);
+        }
+        return true;
+    }
+
+    public boolean suspendRecording(Boolean suspend) {
+        Log.d(TAG, "suspendRecording: " + suspend);
+
+        if(sm[AudioType.MEDIA].mState == sm[AudioType.MEDIA].mRecordingMode) {
+          if(suspend) {
+            sm[AudioType.MEDIA].sendMessage(Event.SUSPEND_RECORDING);
+          } else {
+            sm[AudioType.MEDIA].sendMessage(Event.RESUME_RECORDING);
+          }
+        }
+        return true;
+    }
+
+    public boolean isRecordingActive(BluetoothDevice device) {
+        Log.d(TAG, "isRecordingActive");
+        return sm[AudioType.MEDIA].mState == sm[AudioType.MEDIA].mRecordingMode;
+    }
+
+    public boolean isStableState(int mAudioType) {
+        State state = sm[mAudioType].mState;
+        return !(sm[mAudioType].mActivating == state || sm[mAudioType].mDeactivating == state);
+    }
+
+    private void broadcastActiveDeviceChange(BluetoothDevice device, int mAudioType) {
+        if (DBG) {
+            Log.d(TAG, "broadcastActiveDeviceChange(" + device + ")");
+        }
+        Intent intent;
+
+        /*if (mAdapterService != null)
+            BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED, BluetoothProfile.A2DP,
+                      mAdapterService.obfuscateAddress(device), 0);*/
+
+        if(mAudioType == AudioType.MEDIA)
+            intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
+        else
+            intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        if(mAudioType == AudioType.MEDIA) {
+            A2dpService mA2dpService = A2dpService.getA2dpService();
+            if(mA2dpService == null) {
+                Log.e(TAG, "A2dp Service not ready");
+                return;
+            }
+            mA2dpService.sendBroadcast(intent, BLUETOOTH_CONNECT);
+        } else {
+            HeadsetService mHeadsetService = HeadsetService.getHeadsetService();
+            if(mHeadsetService == null) {
+                Log.e(TAG, "Headset Service not ready");
+                return;
+            }
+            mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT,
+                   Utils.getTempAllowlistBroadcastOptions());
+        }
+    }
+
+    public void disable() {
+        Log.d(TAG, "disable() called");
+        sm[AudioType.MEDIA].sendMessage(Event.STOP_SM);
+        sm[AudioType.VOICE].sendMessage(Event.STOP_SM);
+    }
+
+    public void cleanup() {
+        sm[AudioType.VOICE].doQuit();
+        sm[AudioType.MEDIA].doQuit();
+        thread[AudioType.VOICE].quitSafely();
+        thread[AudioType.MEDIA].quitSafely();
+        thread[AudioType.VOICE] = null;
+        thread[AudioType.MEDIA] = null;
+        sActiveDeviceManager = null;
+    }
+
+    private class DeviceProfileCombo {
+        BluetoothDevice Device;
+        BluetoothDevice absoluteDevice;
+        int Profile;
+
+        DeviceProfileCombo(BluetoothDevice mDevice, int mProfile) {
+            Device = mDevice;
+            Profile = mProfile;
+        }
+
+        DeviceProfileCombo() {
+            Device = null;
+            Profile = ApmConst.AudioProfiles.NONE;
+        }
+    }
+
+    private final class ShoStateMachine extends StateMachine {
+        private static final boolean DBG = true;
+        private static final String TAG = "APM: ActiveDeviceManagerService";
+
+        static final int IDLE = 0;
+        static final int ACTIVATING = 1;
+        static final int ACTIVE = 2;
+        static final int DEACTIVATING = 3;
+        static final int BROADCAST_ACTIVE = 4;
+        static final int GAMING_ACTIVE = 5;
+        static final int RECORDING_ACTIVE = 6;
+
+        private Idle mIdle;
+        private Activating mActivating;
+        private Active mActive;
+        private Deactivating mDeactivating;
+        private Broadcasting mBroadcasting;
+        private Gaming mGamingMode;
+        private Recording mRecordingMode;
+
+        private DeviceProfileCombo Current;
+        private DeviceProfileCombo Target;
+        private SHOReq mTargetSHO;
+        private SHOReq mSHOQueue;
+
+        private DeviceProfileMap dpm;
+
+        private int mAudioType;
+        private State mState;
+        private State mPrevState = null;
+        private BluetoothDevice mPrevActiveDevice;
+        private int mPrevActiveProfile = ApmConst.AudioProfiles.NONE;
+        boolean enabled;
+        boolean updatePending = false;
+        boolean mRecordingSuspended = false;
+        private String sAudioType;
+
+        ShoStateMachine (int audioType, Looper looper) {
+            super(TAG, looper);
+            setDbg(DBG);
+
+            mIdle = new Idle();
+            mActivating = new Activating();
+            mActive = new Active();
+            mDeactivating = new Deactivating();
+            mBroadcasting = new Broadcasting();
+            mGamingMode = new Gaming();
+            mRecordingMode = new Recording();
+
+            Current = new DeviceProfileCombo();
+            Target = new DeviceProfileCombo();
+            mSHOQueue = new SHOReq();
+            mTargetSHO = new SHOReq();
+
+            addState(mIdle);
+            addState(mActivating);
+            addState(mActive);
+            addState(mDeactivating);
+            addState(mBroadcasting);
+            addState(mGamingMode);
+            addState(mRecordingMode);
+
+            mAudioType = audioType;
+            if(mAudioType == AudioType.MEDIA)
+                sAudioType = new String("MEDIA");
+            else if(mAudioType == AudioType.VOICE)
+                sAudioType = new String("VOICE");
+
+            enabled =  true;
+            setInitialState(mIdle);
+            start();
+        }
+
+        public void doQuit () {
+            Log.i(TAG, "Stopping SHO StateMachine for " + mAudioType);
+            sAudioType = null;
+            quitNow();
+        }
+
+       /* public void cleanUp {
+
+        }*/
+
+        private String messageWhatToString(int msg) {
+            switch (msg) {
+                case Event.SET_ACTIVE:
+                    return "SET ACTIVE";
+                case Event.ACTIVE_DEVICE_CHANGE:
+                    return "ACTIVE DEVICE CHANGED";
+                case Event.REMOVE_DEVICE:
+                    return "REMOVE DEVICE";
+                case Event.DEVICE_REMOVED:
+                    return "REMOVED";
+                case Event.ACTIVATE_TIMEOUT:
+                    return "SET ACTIVE TIMEOUT";
+                case Event.DEACTIVATE_TIMEOUT:
+                    return "REMOVE DEVICE TIMEOUT";
+                case Event.STOP_SM:
+                    return "STOP STATE MACHINE";
+                default:
+                    break;
+            }
+            return Integer.toString(msg);
+        }
+
+        int startSho(BluetoothDevice device, int profile) {
+            MediaAudio mMediaAudio = MediaAudio.get();
+            int ret = SHO_FAILED;
+            StreamAudioService streamAudioService;
+            Log.e(TAG, ": startSho() for device: " + device + ", for profile: " + profile);
+            switch (profile) {
+                case ApmConst.AudioProfiles.A2DP:
+                    A2dpService a2dpService = A2dpService.getA2dpService();
+                    if(a2dpService != null)
+                        // pass play status here
+                        ret = a2dpService.setActiveDevice(device, false);
+                    break;
+                case ApmConst.AudioProfiles.HFP:
+                    HeadsetService headsetService = HeadsetService.getHeadsetService();
+                    if(headsetService != null)
+                        ret = headsetService.setActiveDeviceHF(device);
+                    break;
+                case ApmConst.AudioProfiles.TMAP_MEDIA:
+                case ApmConst.AudioProfiles.BAP_MEDIA:
+                    streamAudioService = StreamAudioService.getStreamAudioService();
+                    ret = streamAudioService.setActiveDevice(device, ApmConst.AudioProfiles.BAP_MEDIA, false);
+                    if (ret == ActiveDeviceManagerService.ALREADY_ACTIVE) {
+                      ret = SHO_SUCCESS;
+                    }
+                    break;
+                case ApmConst.AudioProfiles.BAP_RECORDING:
+                    streamAudioService = StreamAudioService.getStreamAudioService();
+                    ret = streamAudioService.setActiveDevice(device, ApmConst.AudioProfiles.BAP_RECORDING, false);
+                    if (ret == ActiveDeviceManagerService.ALREADY_ACTIVE) {
+                      ret = SHO_SUCCESS;
+                    }
+                    break;
+                case ApmConst.AudioProfiles.TMAP_CALL:
+                case ApmConst.AudioProfiles.BAP_CALL:
+                    streamAudioService = StreamAudioService.getStreamAudioService();
+                    ret = streamAudioService.setActiveDevice(device, profile, false);
+                    break;
+                case ApmConst.AudioProfiles.BAP_GCP:
+                    streamAudioService = StreamAudioService.getStreamAudioService();
+                    ret = streamAudioService.setActiveDevice(device, ApmConst.AudioProfiles.BAP_GCP, false);
+                    if (ret == ActiveDeviceManagerService.ALREADY_ACTIVE) {
+                      ret = SHO_SUCCESS;
+                    }
+                    break;
+                case ApmConst.AudioProfiles.BROADCAST_LE:
+                    //ret = SHO_SUCCESS;//broadcastService.setActiveDevice();
+                    BroadcastService mBroadcastService = BroadcastService.getBroadcastService();
+                    if (mBroadcastService != null)
+                        ret = mBroadcastService.setActiveDevice(device);
+                    break;
+                case ApmConst.AudioProfiles.HAP_BREDR:
+                    HearingAidService hearingAidService = HearingAidService.getHearingAidService();
+                    ret = hearingAidService.setActiveDevice(device) ?  SHO_SUCCESS : SHO_FAILED;
+                    break;
+            }
+            return ret;
+        }
+
+        class Idle extends State {
+            @Override
+            public void enter() {
+                synchronized (this) {
+                    mState = mIdle;
+                }
+                Current.Device = null;
+                //2 Update dependent profiles
+                if(mPrevState != null && mPrevActiveDevice != null) {
+                    broadcastActiveDeviceChange (null, mAudioType);
+
+                    if (mAudioType == AudioType.MEDIA &&
+                        mPrevActiveProfile != ApmConst.AudioProfiles.HAP_BREDR) {
+                        mPrevActiveProfile = ApmConst.AudioProfiles.NONE;
+                        MediaAudio mMediaAudio = MediaAudio.get();
+                        boolean suppressNoisyIntent = !mTargetSHO.forceStopAudio
+                                && (mMediaAudio.getConnectionState(mPrevActiveDevice)
+                                == BluetoothProfile.STATE_CONNECTED);
+                        /*TODO: Add profile check here*/
+                        if(mAudioManager != null) {
+                            log("De-Activate Device " + mPrevActiveDevice + " Noisy Intent: " + suppressNoisyIntent);
+                            mAudioManager.handleBluetoothA2dpActiveDeviceChange(
+                               mPrevActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
+                               BluetoothProfile.A2DP, suppressNoisyIntent, -1);
+                        }
+                    }
+
+                    VolumeManager mVolumeManager = VolumeManager.get();
+                    if(mVolumeManager != null) {
+                        mVolumeManager.onActiveDeviceChange(Current.Device, mAudioType);
+                    }
+                }
+
+                if(txStreamSuspended && mAudioType == AudioType.MEDIA) {
+                    mAudioManager.setParameters("A2dpSuspended=false");
+                    txStreamSuspended = false;
+                }
+
+                if(!enabled)
+                    log("state machine stopped");
+            }
+
+            @Override
+            public void exit() {
+                mPrevState = mIdle;
+                mPrevActiveDevice = null;
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                log("Idle: Process Message (" + mAudioType + "): "
+                        + messageWhatToString(message.what));
+                if(!enabled) {
+                    log("State Machine not running. Returning");
+                    return NOT_HANDLED;
+                }
+
+                switch(message.what) {
+                    case Event.SET_ACTIVE:
+                        transitionTo(mActivating);
+                        break;
+
+                    case Event.ACTIVE_DEVICE_CHANGE:
+                        /* Might move to active here*/
+                    case Event.REMOVE_DEVICE:
+                        log("Idle: Process Message Ignored");
+                        break;
+                    case Event.STOP_SM:
+                        enabled = false;
+                        log("state machine stopped");
+                        break;
+                    default:
+                        return NOT_HANDLED;
+                }
+                return HANDLED;
+            }
+        }
+
+        class Activating extends State {
+            int ret;
+            @Override
+            public void enter() {
+                synchronized (this) {
+                    mState = mActivating;
+                    updatePending = true;
+
+                    Target.Device = mSHOQueue.device;
+                    Target.absoluteDevice = Target.Device;
+                    mTargetSHO.copy(mSHOQueue);
+                    mSHOQueue.reset();
+                    Log.w(TAG, "Activating " + sAudioType + " Device: " + Target.Device);
+                }
+
+                dpm = DeviceProfileMap.getDeviceProfileMapInstance();
+                if (mTargetSHO.isBroadcast) {
+                    Target.Profile = dpm.getProfile(Target.Device, ApmConst.AudioFeatures.BROADCAST_AUDIO);
+                    mSHOQueue.device = Current.Device;
+                    mSHOQueue.isUIReq = false;
+                } else if (mTargetSHO.isRecordingMode) {
+                    mSHOQueue.device = Current.Device;
+                    Target.Profile = ApmConst.AudioProfiles.BAP_RECORDING;
+                    mSHOQueue.isUIReq = false;
+                } else if (mTargetSHO.isGamingMode) {
+                    /*Only single profile supports gaming Mode*/
+                    Target.Profile = ApmConst.AudioProfiles.BAP_GCP;
+                } else {
+                    Target.Profile = dpm.getProfile(Target.Device, mAudioType);
+                }
+
+                if(Target.Profile == ApmConst.AudioProfiles.BAP_CALL ||
+                        Target.Profile == ApmConst.AudioProfiles.BAP_MEDIA ||
+                        Target.Profile == ApmConst.AudioProfiles.BAP_RECORDING ||
+                        Target.Profile == ApmConst.AudioProfiles.BAP_GCP) {
+                    StreamAudioService streamAudioService = StreamAudioService.getStreamAudioService();
+                    Target.Device = streamAudioService.getDeviceGroup(Target.Device);
+                }
+
+                if(Target.Device == null) {
+                    Log.e(TAG, "Target Device is null, Returning");
+                    transitionTo(mPrevState);
+                    updatePending = false;
+                    return;
+                }
+
+                if(Target.Device.equals(Current.Device) && Target.Profile == Current.Profile){
+                    Log.d(TAG,"Target Device: " + Target.Device + " and Profile: " + Target.Profile +
+                                                            " already active");
+                    transitionTo(mPrevState);
+                    updatePending = false;
+                    return;
+                }
+
+                if(Current.Device == null || isSameProfile(Current.Profile, Target.Profile, mAudioType)) {
+                    /* Single Step SHO*/
+                    ActivateDevice(Target, mTargetSHO);
+                } else {
+                    /*Multi Step SHO*/
+                    ret = startSho(null, Current.Profile);
+                    if(SHO_PENDING == ret) {
+                        sendMessageDelayed(Event.DEACTIVATE_TIMEOUT, DEACTIVATE_TIMEOUT_DELAY);
+                    } else if(ret == SHO_FAILED) {
+                        mTargetSHO.retryCount = 1;
+                        sendMessageDelayed(Event.RETRY_DEACTIVATE, DEACTIVATE_TRY_DELAY);
+                    } else if(SHO_SUCCESS == ret) {
+                        mPrevState = mIdle;
+                        Current.Device = null;
+                        ActivateDevice(Target, mTargetSHO);
+                    }
+                }
+            }
+
+            @Override
+            public void exit() {
+                removeMessages(Event.ACTIVATE_TIMEOUT);
+                mPrevState = mActivating;
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                log("Activating: Process Message (" + mAudioType + "): "
+                        + messageWhatToString(message.what));
+
+                switch(message.what) {
+                    case Event.SET_ACTIVE:
+                        log("New SHO request while handling previous. Add to queue");
+                        removeDeferredMessages(Event.REMOVE_DEVICE);
+                        removeDeferredMessages(Event.SET_ACTIVE);
+                        deferMessage(message);
+                        break;
+
+                    case Event.ACTIVE_DEVICE_CHANGE:
+                        DeviceProfileCombo mDeviceProfileCombo = (DeviceProfileCombo)message.obj;
+                        removeMessages(Event.ACTIVATE_TIMEOUT);
+                        if (Target.Profile == ApmConst.AudioProfiles.BAP_GCP
+                                    && Target.Profile == mDeviceProfileCombo.Profile) {
+                            Current.Device = Target.Device;
+                            Current.Profile = Target.Profile;
+                            transitionTo(mGamingMode);
+                        } else if (Target.Profile == ApmConst.AudioProfiles.BROADCAST_LE
+                                    && Target.Profile == mDeviceProfileCombo.Profile) {
+                            Current.Device = Target.Device;
+                            Current.Profile = Target.Profile;
+                            transitionTo(mBroadcasting);
+                        } else if(Target.Device != null && Target.Device.equals(mDeviceProfileCombo.Device)) {
+                            Current.Device = mDeviceProfileCombo.Device;
+                            Current.Profile = Target.Profile;
+                            Current.absoluteDevice = Target.absoluteDevice;
+                            transitionTo(mActive);
+                        }
+                        break;
+
+                    case Event.REMOVE_DEVICE:
+                        removeDeferredMessages(Event.REMOVE_DEVICE);
+                        deferMessage(message);
+                        break;
+
+                    case Event.DEVICE_REMOVED:
+                        mPrevState = mIdle;
+                        Current.Device = null;
+                        removeMessages(Event.DEACTIVATE_TIMEOUT);
+                        ActivateDevice(Target, mTargetSHO);
+                        break;
+
+                    case Event.RETRY_DEACTIVATE:
+                        ret = startSho(null, Current.Profile);
+                        if(SHO_PENDING == ret) {
+                            mTargetSHO.retryCount = 0;
+                            sendMessageDelayed(Event.DEACTIVATE_TIMEOUT, DEACTIVATE_TIMEOUT_DELAY);
+                        } else if(ret == SHO_FAILED) {
+                            if(mTargetSHO.retryCount >= RETRY_LIMIT) {
+                                updatePending = false;
+                                transitionTo(mPrevState);
+                            } else {
+                                mTargetSHO.retryCount++;
+                                sendMessageDelayed(Event.RETRY_DEACTIVATE, DEACTIVATE_TRY_DELAY);
+                            }
+                        } else if(SHO_SUCCESS == ret) {
+                            mTargetSHO.retryCount = 0;
+                            mPrevState = mIdle;
+                            Current.Device = null;
+                            ActivateDevice(Target, mTargetSHO);
+                        }
+                        break;
+
+                    case Event.ACTIVATE_TIMEOUT:
+                    case Event.DEACTIVATE_TIMEOUT:
+                        transitionTo(mPrevState);
+                        break;
+
+                    case Event.STOP_SM:
+                        deferMessage(message);
+                        break;
+
+                    default:
+                        return NOT_HANDLED;
+                }
+                return HANDLED;
+            }
+
+            void ActivateDevice(DeviceProfileCombo mTarget, SHOReq mTargetSHO) {
+                ret = startSho(mTarget.Device, mTarget.Profile);
+                if(SHO_PENDING == ret) {
+                    Current.Device = mTarget.Device;
+                    Current.absoluteDevice = mTarget.absoluteDevice;
+                    Current.Profile = mTarget.Profile;
+                    if(mAudioType == AudioType.MEDIA) {
+                        sendActiveDeviceMediaUpdate(Current);
+                    }
+                    sendMessageDelayed(Event.ACTIVATE_TIMEOUT, ACTIVATE_TIMEOUT_DELAY);
+                } else if(ret == SHO_FAILED) {
+                    if (mState == mBroadcasting) {
+                        mTargetSHO.forceStopAudio = true;
+                        Log.d(TAG,"Previous state was broadcasting, moving to idle");
+                    }
+                    updatePending = false;
+                    transitionTo(mPrevState);
+                } else if(SHO_SUCCESS == ret) {
+                    Current.Device = mTarget.Device;
+                    Current.absoluteDevice = mTarget.absoluteDevice;
+                    Current.Profile = mTarget.Profile;
+                    if(mTargetSHO.isBroadcast) {
+                        transitionTo(mBroadcasting);
+                    } else if (mTargetSHO.isGamingMode) {
+                        transitionTo(mGamingMode);
+                    } else if (mTargetSHO.isRecordingMode) {
+                        transitionTo(mRecordingMode);
+                    } else {
+                        transitionTo(mActive);
+                    }
+                } else if(ALREADY_ACTIVE == ret) {
+                    transitionTo(mActive);
+                }
+            }
+        }
+
+        class Deactivating extends State {
+            int ret;
+            @Override
+            public void enter() {
+                synchronized (this) {
+                    mState = mDeactivating;
+                }
+                if (mPrevState == mBroadcasting) {
+                    mPrevState = mIdle;
+                }
+                Target.Device = null;
+                Target.Profile = Current.Profile;
+                mTargetSHO.copy(mSHOQueue);
+                mSHOQueue.reset();
+
+                ret = startSho(Target.Device, Target.Profile);
+                Log.d(TAG, "ret: " + ret);
+                if (SHO_SUCCESS == ret) {
+                    transitionTo(mIdle);
+                } else if (SHO_PENDING == ret) {
+                    sendMessageDelayed(Event.DEACTIVATE_TIMEOUT, DEACTIVATE_TIMEOUT_DELAY);
+                } else {
+                    transitionTo(mPrevState);
+                }
+            }
+
+            @Override
+            public void exit() {
+                removeMessages(Event.DEACTIVATE_TIMEOUT);
+                mPrevState = mDeactivating;
+                mPrevActiveDevice =  Current.Device;
+                Current.Device = null;
+                mPrevActiveProfile = Current.Profile;
+                Current.Profile = ApmConst.AudioProfiles.NONE; // Add profile value here
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                log("Deactivating: Process Message (" + mAudioType + "): "
+                        + messageWhatToString(message.what));
+
+                switch(message.what) {
+                    case Event.SET_ACTIVE:
+                        log("New SHO request while handling previous. Add to queue");
+                        removeDeferredMessages(Event.SET_ACTIVE);
+                        deferMessage(message);
+                        break;
+
+                    case Event.ACTIVE_DEVICE_CHANGE:
+                        break;
+
+                    case Event.REMOVE_DEVICE:
+                        break;
+
+                    case Event.DEVICE_REMOVED:
+                        removeMessages(Event.DEACTIVATE_TIMEOUT);
+                        transitionTo(mIdle);
+                        break;
+
+                    case Event.DEACTIVATE_TIMEOUT:
+                        transitionTo(mPrevState);
+
+                    case Event.STOP_SM:
+                        deferMessage(message);
+                        break;
+
+                    default:
+                        return NOT_HANDLED;
+                }
+                return HANDLED;
+            }
+        }
+
+        class Active extends State {
+            int ret;
+            @Override
+            public void enter() {
+                synchronized (this) {
+                    mState = mActive;
+                }
+                if(updatePending) {
+                    if(mAudioType == AudioType.MEDIA)
+                        sendActiveDeviceMediaUpdate(Current);
+                    else if(mAudioType == AudioType.VOICE)
+                        sendActiveDeviceVoiceUpdate(Current);
+                }
+                if(txStreamSuspended && mAudioType == AudioType.MEDIA) {
+                    mAudioManager.setParameters("A2dpSuspended=false");
+                    txStreamSuspended = false;
+                } else if (mAudioType == AudioType.VOICE) {
+                    lock.lock();
+                    voiceHandoffComplete.signal();
+                    lock.unlock();
+                    Log.d(TAG, "Voice Active: unlock by signal");
+                }
+            }
+
+            @Override
+            public void exit() {
+            //2 update dependent profiles
+                mPrevState = mActive;
+                mPrevActiveDevice = Current.Device;
+                VolumeManager mVolumeManager = VolumeManager.get();
+                if(mVolumeManager != null) {
+                    mVolumeManager.saveVolume(mAudioType);
+                }
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                log("Active: Process Message (" + mAudioType + "): "
+                        + messageWhatToString(message.what));
+
+                switch(message.what) {
+                    case Event.SET_ACTIVE:
+                        if(mSHOQueue.device == null) {
+                            Log.w(TAG, "Invalid request");
+                            break;
+                        }
+                        transitionTo(mActivating);
+                        break;
+
+                    case Event.ACTIVE_DEVICE_CHANGE:
+                        // might have to handle
+                        break;
+
+                    case Event.REMOVE_DEVICE:
+                        transitionTo(mDeactivating);
+                        break;
+
+                    case Event.DEVICE_REMOVED:
+                        //might have to handle
+                        break;
+
+                    case Event.STOP_SM:
+                        transitionTo(mDeactivating);
+                        enabled = false;
+                        break;
+
+                    default:
+                        return NOT_HANDLED;
+                }
+                return HANDLED;
+            }
+        }
+
+        class Gaming extends State {
+            int ret;
+            @Override
+            public void enter() {
+                synchronized (this) {
+                    mState = mGamingMode;
+                }
+                if(updatePending) {
+                    sendActiveDeviceGamingUpdate(Current);
+                }
+                if(txStreamSuspended) {
+                    mAudioManager.setParameters("A2dpSuspended=false");
+                    txStreamSuspended = false;
+                }
+            }
+
+            @Override
+            public void exit() {
+            //2 update dependent profiles
+                mPrevState = mGamingMode;
+                mPrevActiveDevice = Current.Device;
+                VolumeManager mVolumeManager = VolumeManager.get();
+                if(mVolumeManager != null) {
+                    mVolumeManager.saveVolume(mAudioType);
+                }
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                log("Gaming: Process Message (" + mAudioType + "): "
+                        + messageWhatToString(message.what));
+
+                switch(message.what) {
+                    case Event.SET_ACTIVE:
+                        if(mSHOQueue.device == null) {
+                            Log.w(TAG, "Invalid request");
+                            break;
+                        }
+                        if(!mSHOQueue.isUIReq && Current.Device.equals(mSHOQueue.device)) {
+                            Log.w(TAG, "Spurious request for same device. Ignore");
+                            mSHOQueue.reset();
+                            break;
+                        }
+                        /*MediaAudio mMediaAudio = MediaAudio.get();
+                        if(mMediaAudio != null && mMediaAudio.isA2dpPlaying(mSHOQueue.device)) {
+                            if(!(mSHOQueue.isBroadcast || mSHOQueue.isRecordingMode)) {
+                                Log.w(TAG, "Gaming streaming is on");
+                                break;
+                            }
+                        }*/
+                        transitionTo(mActivating);
+                        break;
+
+                    case Event.ACTIVE_DEVICE_CHANGE:
+                        // might have to handle
+                        break;
+
+                    case Event.REMOVE_DEVICE:
+                        transitionTo(mDeactivating);
+                        break;
+
+                    case Event.DEVICE_REMOVED:
+                        //might have to handle
+                        break;
+
+                    case Event.STOP_SM:
+                        transitionTo(mDeactivating);
+                        enabled = false;
+                        break;
+
+                    default:
+                        return NOT_HANDLED;
+                }
+                return HANDLED;
+            }
+        }
+
+        class Broadcasting extends State {
+            int ret;
+            @Override
+            public void enter() {
+                synchronized (this) {
+                    mState = mBroadcasting;
+                }
+                if (updatePending) {
+                    apmNative.activeDeviceUpdate(Current.Device, Current.Profile, mAudioType);
+                    broadcastActiveDeviceChange(Current.Device, AudioType.MEDIA);
+                    mAudioManager.avrcpSupportsAbsoluteVolume(Current.Device.getAddress(), false);
+                    // Update active device to null in VolumeManager while enter broadcasting state
+                    VolumeManager mVolumeManager = VolumeManager.get();
+                    if(mVolumeManager != null) {
+                        mVolumeManager.onActiveDeviceChange(null,
+                                                            ApmConst.AudioFeatures.MEDIA_AUDIO);
+                    }
+                    int rememberedVolume = 15;
+                    mAudioManager.handleBluetoothA2dpActiveDeviceChange(
+                            Current.Device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
+                            true, rememberedVolume);
+                    updatePending = false;
+                }
+                if(txStreamSuspended) {
+                    mAudioManager.setParameters("A2dpSuspended=false");
+                    txStreamSuspended = false;
+                }
+            }
+
+            @Override
+            public void exit() {
+                mPrevState = mBroadcasting;
+                mPrevActiveDevice = Current.Device;
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                log("Broadcasting: Process Message (" + mAudioType + "): "
+                        + messageWhatToString(message.what));
+
+                switch(message.what) {
+                    case Event.SET_ACTIVE:
+                        if(mSHOQueue.isUIReq)
+                            transitionTo(mActivating);
+                        break;
+
+                    case Event.ACTIVE_DEVICE_CHANGE:
+                        break;
+
+                    case Event.REMOVE_DEVICE:
+                        if(mSHOQueue.device == null) {
+                            transitionTo(mDeactivating);
+                        } else {
+                            transitionTo(mActivating);
+                        }
+                        break;
+
+                    case Event.DEVICE_REMOVED:
+                        break;
+
+                    case Event.STOP_SM:
+                        transitionTo(mDeactivating);
+                        enabled = false;
+                        break;
+
+                    default:
+                        return NOT_HANDLED;
+                }
+                return HANDLED;
+            }
+        }
+
+        class Recording extends State {
+            int ret;
+            @Override
+            public void enter() {
+                synchronized (this) {
+                    mState = mRecordingMode;
+                }
+                if(updatePending) {
+                    sendActiveDeviceRecordingUpdate(Current);
+                }
+                mRecordingSuspended = false;
+            }
+
+            @Override
+            public void exit() {
+                mPrevState = mRecordingMode;
+                mPrevActiveDevice = Current.Device;
+                HeadsetService hfpService = HeadsetService.getHeadsetService();
+
+                mAudioManager.handleBluetoothA2dpActiveDeviceChange(
+                                        mPrevActiveDevice,
+                                        BluetoothProfile.STATE_DISCONNECTED,
+                                        BluetoothProfile.A2DP_SINK,
+                                        true, -1);
+                if(mRecordingSuspended) {
+                  mAudioManager.setParameters("A2dpCaptureSuspend=false");
+                  mRecordingSuspended = false;
+                }
+                CallAudio mCallAudio = CallAudio.get();
+                boolean isInCall = mCallAudio != null &&
+                                   mCallAudio.isVoiceOrCallActive();
+                if(isInCall) {
+                  Log.d(TAG, " reset txStreamSuspended as call is active" );
+                  txStreamSuspended = false;
+                }
+                VolumeManager mVolumeManager = VolumeManager.get();
+                if(mVolumeManager != null) {
+                    mVolumeManager.saveVolume(mAudioType);
+                }
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                log("Recording: Process Message (" + mAudioType + "): "
+                        + messageWhatToString(message.what));
+
+                switch(message.what) {
+                    case Event.SET_ACTIVE:
+                        if (mSHOQueue.device == null) {
+                            transitionTo(mDeactivating);
+                        } else {
+                            transitionTo(mActivating);
+                        }
+                        break;
+
+                    case Event.ACTIVE_DEVICE_CHANGE:
+                        break;
+
+                    case Event.REMOVE_DEVICE:
+                        transitionTo(mDeactivating);
+                        break;
+
+                    case Event.DEVICE_REMOVED:
+                        //might have to handle
+                        break;
+                    case Event.SUSPEND_RECORDING: {
+                        if(mRecordingSuspended) break;
+                        mAudioManager.setParameters("A2dpCaptureSuspend=true");
+                        mRecordingSuspended = true;
+                    } break;
+
+                    case Event.RESUME_RECORDING: {
+                        if(!mRecordingSuspended) break;
+                        mAudioManager.setParameters("A2dpCaptureSuspend=false");
+                        mRecordingSuspended = false;
+                    } break;
+                    case Event.STOP_SM:
+                        transitionTo(mDeactivating);
+                        enabled = false;
+                        break;
+
+                    default:
+                        return NOT_HANDLED;
+                }
+                return HANDLED;
+            }
+        }
+
+        void sendActiveDeviceMediaUpdate(DeviceProfileCombo Current) {
+            if(Current.Profile == ApmConst.AudioProfiles.HAP_BREDR) {
+                if(mPrevActiveDevice != null) {
+                    broadcastActiveDeviceChange (null, AudioType.MEDIA );
+                    ActiveDeviceManager mDeviceManager = AdapterService.getAdapterService().getActiveDeviceManager();
+                    mDeviceManager.onActiveDeviceChange(null, ApmConst.AudioFeatures.MEDIA_AUDIO);
+                    mAudioManager.handleBluetoothA2dpActiveDeviceChange(
+                                mPrevActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
+                                BluetoothProfile.A2DP, true, -1);
+                }
+                return;
+            }
+            apmNative.activeDeviceUpdate(Current.Device, Current.Profile, AudioType.MEDIA);
+            Log.d(TAG, "sendActiveDeviceMediaUpdate: mPrevActiveDevice: "
+                        + mPrevActiveDevice + ", Current.Device: " + Current.Device);
+
+            MediaAudio mMediaAudio = MediaAudio.get();
+            mMediaAudio.refreshCurrentCodec(Current.Device);
+
+            broadcastActiveDeviceChange (Current.absoluteDevice, AudioType.MEDIA);
+            ActiveDeviceManager mDeviceManager = AdapterService.getAdapterService().getActiveDeviceManager();
+            mDeviceManager.onActiveDeviceChange(Current.Device, ApmConst.AudioFeatures.MEDIA_AUDIO);
+            if(Current.Profile == ApmConst.AudioProfiles.A2DP) {
+                A2dpService mA2dpService = A2dpService.getA2dpService();
+                mA2dpService.broadcastActiveCodecConfig();
+            }
+            //2 Update dependent profiles
+            VolumeManager mVolumeManager = VolumeManager.get();
+            if(mVolumeManager != null) {
+                mVolumeManager.onActiveDeviceChange(Current.Device,
+                                                    ApmConst.AudioFeatures.MEDIA_AUDIO);
+            }
+
+            McpService mMcpService = McpService.getMcpService();
+            if (mMcpService != null) {
+                mMcpService.SetActiveDevices(Current.absoluteDevice, Current.Profile);
+            }
+            int deviceVolume = 7;
+            if(mVolumeManager != null) {
+                deviceVolume = mVolumeManager.getActiveVolume(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            }
+            if(mAudioManager != null) {
+                mAudioManager.handleBluetoothA2dpActiveDeviceChange(
+                        Current.Device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
+                        true, deviceVolume);
+            }
+            updatePending = false;
+        }
+
+        void sendActiveDeviceGamingUpdate(DeviceProfileCombo Current) {
+            apmNative.activeDeviceUpdate(Current.Device, Current.Profile, AudioType.MEDIA);
+
+            MediaAudio mMediaAudio = MediaAudio.get();
+            mMediaAudio.refreshCurrentCodec(Current.Device);
+
+            broadcastActiveDeviceChange (Current.absoluteDevice, AudioType.MEDIA);
+            ActiveDeviceManager mDeviceManager = AdapterService.getAdapterService().getActiveDeviceManager();
+            mDeviceManager.onActiveDeviceChange(Current.Device, ApmConst.AudioFeatures.MEDIA_AUDIO);
+
+            //2 Update dependent profiles
+            VolumeManager mVolumeManager = VolumeManager.get();
+            int deviceVolume = 7;
+            if(mVolumeManager != null) {
+                mVolumeManager.onActiveDeviceChange(Current.Device,
+                                                    ApmConst.AudioFeatures.MEDIA_AUDIO);
+                deviceVolume = mVolumeManager.getActiveVolume(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            }
+            if(mAudioManager != null) {
+                mAudioManager.handleBluetoothA2dpActiveDeviceChange(
+                        Current.Device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
+                        true, deviceVolume);
+
+                /*Add back channel call here*/
+            }
+            updatePending = false;
+        }
+
+        void sendActiveDeviceVoiceUpdate(DeviceProfileCombo Current) {
+            Log.d(TAG, "sendActiveDeviceVoiceUpdate");
+            if(Current.Profile == ApmConst.AudioProfiles.HAP_BREDR) {
+                if(mPrevActiveDevice != null) {
+                    broadcastActiveDeviceChange (null, AudioType.VOICE);
+                    ActiveDeviceManager mDeviceManager = AdapterService.getAdapterService().getActiveDeviceManager();
+                    mDeviceManager.onActiveDeviceChange(Current.Device, ApmConst.AudioFeatures.CALL_AUDIO);
+                }
+                return;
+            }
+            broadcastActiveDeviceChange (Current.absoluteDevice, AudioType.VOICE);
+            ActiveDeviceManager mDeviceManager = AdapterService.getAdapterService().getActiveDeviceManager();
+            mDeviceManager.onActiveDeviceChange(Current.Device, ApmConst.AudioFeatures.CALL_AUDIO);
+            VolumeManager mVolumeManager = VolumeManager.get();
+            if(mVolumeManager != null) {
+                mVolumeManager.onActiveDeviceChange(Current.Device,
+                                                    ApmConst.AudioFeatures.CALL_AUDIO);
+            }
+            CCService ccService = CCService.getCCService();
+            if (ccService != null) {
+                ccService.setActiveDevice(Current.absoluteDevice);
+            }
+
+            if(mTargetSHO.PlayReq) {
+                CallAudio mCallAudio = CallAudio.get();
+                mCallAudio.connectAudio();
+            }
+
+            updatePending = false;
+        }
+
+        void sendActiveDeviceRecordingUpdate(DeviceProfileCombo Current) {
+            apmNative.activeDeviceUpdate(Current.Device, Current.Profile, AudioType.MEDIA);
+
+            Log.d(TAG, "sendActiveDeviceRecordingUpdate: mPrevActiveDevice: "
+                         + mPrevActiveDevice + ", Current.Device: " + Current.Device);
+            MediaAudio mMediaAudio = MediaAudio.get();
+            mMediaAudio.refreshCurrentCodec(Current.Device);
+            if (mAudioManager != null) {
+                mAudioManager.handleBluetoothA2dpActiveDeviceChange(Current.Device,
+                        BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, false, -1); // TO-Check
+            }
+            updatePending = false;
+        }
+
+        boolean isSameProfile (int p1, int p2, int audioType) {
+            if(p1 == p2) {
+                return true;
+            }
+
+            if(audioType == AudioType.MEDIA) {
+                int leMediaMask = ApmConst.AudioProfiles.TMAP_MEDIA |
+                                  ApmConst.AudioProfiles.BAP_MEDIA |
+                                  ApmConst.AudioProfiles.BAP_RECORDING |
+                                  ApmConst.AudioProfiles.BAP_GCP;
+                if((leMediaMask & p1) > 0 && (leMediaMask & p2) > 0) {
+                    return true;
+                }
+            } else if(audioType == AudioType.VOICE) {
+                int leVoiceMask = ApmConst.AudioProfiles.TMAP_CALL | ApmConst.AudioProfiles.BAP_CALL;
+                if((leVoiceMask & p1) > 0 && (leVoiceMask & p2) > 0) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    static class AudioType {
+        public static int VOICE = ApmConst.AudioFeatures.CALL_AUDIO;
+        public static int MEDIA = ApmConst.AudioFeatures.MEDIA_AUDIO;
+
+        public static int SIZE = 2;
+    }
+
+    private class SHOReq {
+        BluetoothDevice device;
+        boolean PlayReq;
+        int retryCount;
+        boolean isBroadcast;
+        boolean isGamingMode;
+        boolean isRecordingMode;
+        boolean isUIReq;
+        boolean forceStopAudio;
+
+        void copy(SHOReq src) {
+            device = src.device;
+            PlayReq = src.PlayReq;
+            retryCount = src.retryCount;
+            isBroadcast = src.isBroadcast;
+            isGamingMode = src.isGamingMode;
+            isRecordingMode = src.isRecordingMode;
+            isUIReq = src.isUIReq;
+            forceStopAudio = src.forceStopAudio;
+        }
+
+        void reset() {
+            device = null;
+            PlayReq = false;
+            retryCount = 0;
+            isBroadcast = false;
+            isGamingMode = false;
+            isRecordingMode = false;
+            isUIReq = false;
+            forceStopAudio = false;
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/ApmConst.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/ApmConst.java
new file mode 100644
index 0000000..45fab0d
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/ApmConst.java
@@ -0,0 +1,70 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.apm;
+
+public class ApmConst {
+
+    private static boolean leAudioEnabled = false;
+    public static final String groupAddress = "9E:8B:00:00:00";
+
+    public static boolean getLeAudioEnabled() {
+        return leAudioEnabled;
+    }
+
+    protected static void setLeAudioEnabled(boolean leAudioSupport) {
+        leAudioEnabled = leAudioSupport;
+    }
+
+    public static class AudioFeatures {
+
+        public static final int CALL_AUDIO = 0;
+        public static final int MEDIA_AUDIO = 1;
+        public static final int CALL_CONTROL = 2;
+        public static final int MEDIA_CONTROL = 3;
+        public static final int MEDIA_VOLUME_CONTROL = 4;
+        public static final int CALL_VOLUME_CONTROL = 5;
+        public static final int BROADCAST_AUDIO = 6;
+        public static final int HEARING_AID = 7;
+        public static final int MAX_AUDIO_FEATURES = 8;
+
+    }
+
+    public static class AudioProfiles {
+
+        public static final int NONE            = 0x0000;
+        public static final int A2DP            = 0x0001;
+        public static final int HFP             = 0x0002;
+        public static final int AVRCP           = 0x0004;
+        public static final int TMAP_MEDIA      = 0x0008;
+        public static final int BAP_MEDIA       = 0x0010;
+        public static final int MCP             = 0x0020;
+        public static final int CCP             = 0x0040;
+        public static final int VCP             = 0x0080;
+        public static final int HAP_BREDR       = 0x0100;
+        public static final int HAP_LE          = 0x0200;
+        public static final int BROADCAST_BREDR = 0x0400;
+        public static final int BROADCAST_LE    = 0x0800;
+        public static final int TMAP_CALL       = 0x1000;
+        public static final int BAP_CALL        = 0x2000;
+        public static final int BAP_GCP         = 0x4000;
+        public static final int BAP_RECORDING   = 0x8000;
+
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/ApmNativeInterface.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/ApmNativeInterface.java
new file mode 100644
index 0000000..b437023
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/ApmNativeInterface.java
@@ -0,0 +1,131 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.apm;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.util.Log;
+import java.util.List;
+
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A2DP Native Interface to/from JNI.
+ */
+public class ApmNativeInterface {
+    private static final String TAG = "ApmNativeInterface";
+    private static final boolean DBG = true;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static ApmNativeInterface sInstance;
+    private BluetoothAdapter mAdapter;
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static {
+        classInitNative();
+    }
+
+    @VisibleForTesting
+    private ApmNativeInterface() {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mAdapter == null) {
+            Log.w(TAG, "No Bluetooth Adapter Available");
+        }
+    }
+
+    /**
+     * Get singleton instance.
+     */
+    public static ApmNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new ApmNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Initializes the native interface.
+     */
+    public void init() {
+        initNative();
+    }
+
+    /**
+     * Cleanup the native interface.
+     */
+    public void cleanup() {
+        cleanupNative();
+    }
+
+    /**
+     * Report new active device to stack.
+     *
+     * @param device: new active device
+     * @param profile: new active profile
+     * @return true on success, otherwise false.
+     */
+    public boolean activeDeviceUpdate(BluetoothDevice device, int profile, int audioType) {
+        return activeDeviceUpdateNative(getByteAddress(device), profile, audioType);
+    }
+
+    /**
+     * Report Content Control ID to stack.
+     *
+     * @param id: Content Control ID
+     * @param profile: content control profile
+     * @return true on success, otherwise false.
+     */
+    public boolean setContentControl(int id, int profile) {
+        return setContentControlNative(id, profile);
+    }
+
+    private BluetoothDevice getDevice(byte[] address) {
+        return mAdapter.getRemoteDevice(address);
+    }
+
+    private byte[] getByteAddress(BluetoothDevice device) {
+        if (device == null) {
+            return Utils.getBytesFromAddress("00:00:00:00:00:00");
+        }
+        return Utils.getBytesFromAddress(device.getAddress());
+    }
+
+    //Current logic is implemented only for CALL_AUDIO
+    //Proper Audio_type needs to be sent to device profile map
+    //for other audio features
+    private int getActiveProfile(byte[] address, int audio_type) {
+        DeviceProfileMap dpm = DeviceProfileMap.getDeviceProfileMapInstance();
+        BluetoothDevice device = getDevice(address);
+        int profile = dpm.getProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
+        return profile;
+    }
+
+    // Native methods that call into the JNI interface
+    private static native void classInitNative();
+    private native void initNative();
+    private native void cleanupNative();
+    private native boolean activeDeviceUpdateNative(byte[] address, int profile, int audioType);
+    private native boolean setContentControlNative(int id, int profile);
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/CallAudio.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/CallAudio.java
new file mode 100644
index 0000000..0d84691
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/CallAudio.java
@@ -0,0 +1,758 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.apm;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import android.bluetooth.BluetoothDevice;
+import com.android.bluetooth.hfp.HeadsetService;
+import com.android.bluetooth.hfp.HeadsetA2dpSync;
+import com.android.bluetooth.apm.ApmConst;
+import com.android.bluetooth.apm.MediaAudio;
+import com.android.bluetooth.apm.CallControl;
+import android.media.AudioManager;
+import com.android.bluetooth.apm.ActiveDeviceManagerService;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.AdapterService;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
+import com.android.bluetooth.Utils;
+import android.content.Context;
+import java.lang.Integer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import android.util.Log;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import android.content.Intent;
+import android.os.UserHandle;
+
+public class CallAudio {
+
+    private static CallAudio mCallAudio;
+    private static final String TAG = "APM: CallAudio";
+    Map<String, CallDevice> mCallDevicesMap;
+    private Context mContext;
+    private AudioManager mAudioManager;
+    private ActiveDeviceManagerService mActiveDeviceManager;
+    private AdapterService mAdapterService;
+    public static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+    public static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
+    public static final String BLUETOOTH_PRIVILEGED =
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+    private static final int MAX_DEVICES = 200;
+    public boolean mVirtualCallStarted;
+    private CallControl mCallControl = null;
+
+    private CallAudio(Context context) {
+        Log.d(TAG, "Initialization");
+        mContext = context;
+        mCallDevicesMap = new ConcurrentHashMap<String, CallDevice>();
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        mActiveDeviceManager = ActiveDeviceManagerService.get();
+        mAdapterService = AdapterService.getAdapterService();
+        mCallControl = CallControl.get();
+    }
+
+    public static CallAudio init(Context context) {
+        if(mCallAudio == null) {
+            mCallAudio = new CallAudio(context);
+            CallAudioIntf.init(mCallAudio);
+        }
+        return mCallAudio;
+    }
+
+    public static CallAudio get() {
+        return mCallAudio;
+    }
+
+    public boolean connect(BluetoothDevice device) {
+    Log.i(TAG, "connect: " + device);
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        if(device == null)
+            return false;
+        boolean status;
+        if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+            Log.e(TAG, "Cannot connect to " + device + " : CONNECTION_POLICY_FORBIDDEN");
+            return false;
+        }
+
+        DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+        if (dMap == null)
+            return false;
+
+        int profileID = dMap.getSupportedProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
+        if (profileID == ApmConst.AudioProfiles.NONE) {
+            Log.e(TAG, "Can Not connect to " + device + ". Device does not support call service.");
+            return false;
+        }
+
+        CallDevice mCallDevice = mCallDevicesMap.get(device.getAddress());
+        if(mCallDevice == null) {
+            if(mCallDevicesMap.size() >= MAX_DEVICES)
+                return false;
+            mCallDevice = new CallDevice(device, profileID);
+            mCallDevicesMap.put(device.getAddress(), mCallDevice);
+        } else if(mCallDevice.deviceConnStatus != BluetoothProfile.STATE_DISCONNECTED) {
+            Log.i(TAG, "Device already connected");
+            return false;
+        }
+
+        if((ApmConst.AudioProfiles.HFP & profileID) == ApmConst.AudioProfiles.HFP) {
+            HeadsetService service = HeadsetService.getHeadsetService();
+            if (service == null) {
+                return false;
+            }
+            service.connectHfp(device);
+        }
+
+        StreamAudioService mStreamService = StreamAudioService.getStreamAudioService();
+        if(mStreamService != null &&
+                (ApmConst.AudioProfiles.BAP_CALL & profileID) == ApmConst.AudioProfiles.BAP_CALL) {
+            mStreamService.connectLeStream(device, profileID);
+        }
+        return true;
+    }
+
+    public boolean connect(BluetoothDevice device, Boolean allProfiles) {
+        if(allProfiles) {
+            DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+            if (dMap == null)
+                return false;
+
+            int profileID = dMap.getSupportedProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
+            if((ApmConst.AudioProfiles.HFP & profileID) == ApmConst.AudioProfiles.HFP) {
+                HeadsetService service = HeadsetService.getHeadsetService();
+                if (service == null) {
+                    return false;
+                }
+                return service.connectHfp(device);
+            } else {
+                /*Common connect for LE Media and Call handled from StreamAudioService*/
+                return true;
+            }
+        }
+
+        return connect(device);
+    }
+
+    public boolean disconnect(BluetoothDevice device) {
+        Log.i(TAG, " disconnect: " + device);
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
+        CallDevice mCallDevice;
+
+        if(device == null)
+            return false;
+
+        mCallDevice = mCallDevicesMap.get(device.getAddress());
+        if(mCallDevice == null) {
+            Log.e(TAG, "Ignore: Device " + device + " not present in list");
+            return false;
+        }
+
+        if (mCallDevice.profileConnStatus[CallDevice.SCO_STREAM] != BluetoothProfile.STATE_DISCONNECTED) {
+            HeadsetService service = HeadsetService.getHeadsetService();
+            if(service != null) {
+                service.disconnectHfp(device);
+            }
+        }
+		
+        if (mCallDevice.profileConnStatus[CallDevice.LE_STREAM] != BluetoothProfile.STATE_DISCONNECTED) {
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if(service != null) {
+                service.disconnectLeStream(device, true, false);
+            }
+        }
+
+        return true;
+    }
+
+    public boolean disconnect(BluetoothDevice device, Boolean allProfiles) {
+        if(allProfiles) {
+            CallDevice mCallDevice = mCallDevicesMap.get(device.getAddress());
+            if(mCallDevice == null) {
+                Log.e(TAG, "Ignore: Device " + device + " not present in list");
+                return false;
+            }
+            if(mCallDevice.profileConnStatus[CallDevice.SCO_STREAM] != BluetoothProfile.STATE_DISCONNECTED) {
+                return disconnect(device);
+            } else {
+                /*Common connect for LE Media and Call handled from StreamAudioService*/
+                return true;
+            }
+        }
+
+        return disconnect(device);
+    }
+
+    public boolean startScoUsingVirtualVoiceCall() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        Log.d(TAG, "startScoUsingVirtualVoiceCall");
+        BluetoothDevice mActivedevice = null;
+        int profile;
+        mActiveDeviceManager = ActiveDeviceManagerService.get();
+        if(mActiveDeviceManager != null) {
+            mActivedevice = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO);
+            if(mActivedevice == null) {
+                Log.e(TAG, "startScoUsingVirtualVoiceCall failed. Active Device is null");
+                return false;
+            }
+        } else {
+            return false;
+        }
+
+        checkA2dpState();
+
+        profile = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.CALL_AUDIO);
+        switch(profile) {
+            case ApmConst.AudioProfiles.HFP:
+                HeadsetService headsetService = HeadsetService.getHeadsetService();
+                if(headsetService != null) {
+                    if(headsetService.startScoUsingVirtualVoiceCall()) {
+                        mVirtualCallStarted = true;
+                        return true;
+                    }
+                }
+                break;
+            case ApmConst.AudioProfiles.BAP_CALL:
+            case ApmConst.AudioProfiles.TMAP_CALL:
+                StreamAudioService mStreamAudioService = StreamAudioService.getStreamAudioService();
+                if(mStreamAudioService != null) {
+                    if(mStreamAudioService.startStream(mActivedevice)) {
+                        mVirtualCallStarted = true;
+                        mCallControl = CallControl.get();
+                        if (mCallControl != null) {
+                            mCallControl.setVirtualCallActive(true);
+                        }
+                        return true;
+                    }
+                }
+                break;
+            default:
+                Log.e(TAG, "Unhandled profile");
+                break;
+        }
+
+        Log.e(TAG, "startScoUsingVirtualVoiceCall failed. Device: " + mActivedevice);
+        if(ApmConst.AudioProfiles.HFP != profile) {
+            HeadsetService service = HeadsetService.getHeadsetService();
+            if(service != null) {
+                service.getHfpA2DPSyncInterface().releaseA2DP(null);
+            }
+        }
+        return false;
+    }
+
+    public boolean stopScoUsingVirtualVoiceCall() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        Log.d(TAG, "stopScoUsingVirtualVoiceCall");
+        BluetoothDevice mActivedevice = null;
+        int profile;
+        mActiveDeviceManager = ActiveDeviceManagerService.get();
+        if(mActiveDeviceManager != null) {
+            mActivedevice = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO);
+            if(mActivedevice == null) {
+                Log.e(TAG, "stopScoUsingVirtualVoiceCall failed. Active Device is null");
+                return false;
+            }
+        } else {
+            return false;
+        }
+
+        profile = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.CALL_AUDIO);
+        switch(profile) {
+            case ApmConst.AudioProfiles.HFP:
+                HeadsetService headsetService = HeadsetService.getHeadsetService();
+                if(headsetService != null) {
+                    mVirtualCallStarted = false;
+                    return headsetService.stopScoUsingVirtualVoiceCall();
+                }
+                break;
+            case ApmConst.AudioProfiles.BAP_CALL:
+            case ApmConst.AudioProfiles.TMAP_CALL:
+                StreamAudioService mStreamAudioService = StreamAudioService.getStreamAudioService();
+                if(mStreamAudioService != null) {
+                    mVirtualCallStarted = false;
+                    mCallControl = CallControl.get();
+                    if (mCallControl != null) {
+                        mCallControl.setVirtualCallActive(false);
+                    }
+                    return mStreamAudioService.stopStream(mActivedevice);
+                }
+                break;
+            default:
+                Log.e(TAG, "Unhandled profile");
+                break;
+        }
+
+        Log.e(TAG, "stopScoUsingVirtualVoiceCall failed. Device: " + mActivedevice);
+        return false;
+    }
+
+    void remoteDisconnectVirtualVoiceCall(BluetoothDevice device) {
+        if(device == null)
+            return;
+        ActiveDeviceManagerService mActiveDeviceManager = ActiveDeviceManagerService.get();
+        if(device.equals(mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO)) &&
+                        mActiveDeviceManager.isStableState(ApmConst.AudioFeatures.CALL_AUDIO)) {
+            stopScoUsingVirtualVoiceCall();
+        }
+    }
+
+    int getProfile(BluetoothDevice mDevice) {
+        DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+        int profileID = dMap.getProfile(mDevice, ApmConst.AudioFeatures.CALL_AUDIO);
+        Log.d(TAG," getProfile for device " + mDevice + " profileID " + profileID);
+        return profileID;
+    }
+
+    void checkA2dpState() {
+        MediaAudio sMediaAudio = MediaAudio.get();
+        BluetoothDevice sMediaActivedevice =
+        mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
+        //if(sMediaAudio.isA2dpPlaying(sMediaActivedevice)) {
+            Log.d(TAG," suspendA2DP isA2dpPlaying true " + " for device " + sMediaActivedevice);
+            int profileID = mActiveDeviceManager.getActiveProfile(
+                                ApmConst.AudioFeatures.CALL_AUDIO);
+            if(ApmConst.AudioProfiles.HFP != profileID) {
+                HeadsetService service = HeadsetService.getHeadsetService();
+                if(service != null) {
+                    service.getHfpA2DPSyncInterface().suspendA2DP(
+                    HeadsetA2dpSync.A2DP_SUSPENDED_BY_CS_CALL, null);
+                }
+            }
+        //}
+    }
+
+    public boolean connectAudio() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        BluetoothDevice mActivedevice = null;
+        boolean status = false;
+
+        mActiveDeviceManager = ActiveDeviceManagerService.get();
+        if(mActiveDeviceManager != null) {
+                mActivedevice = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO);
+        }
+        Log.i(TAG, "connectAudio: device=" + mActivedevice + ", " + Utils.getUidPidString());
+
+        int profileID = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.CALL_AUDIO);
+        checkA2dpState();
+
+        if(ApmConst.AudioProfiles.HFP == profileID) {
+            HeadsetService service = HeadsetService.getHeadsetService();
+            if (service == null) {
+                status = false;
+            }
+            status = service.connectAudio(mActivedevice);
+        } else if(ApmConst.AudioProfiles.BAP_CALL == profileID) {
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if(service != null) {
+                status = service.startStream(mActivedevice);
+            }
+        } else {
+            Log.e(TAG, "Unhandled connect audio request for profile: " + profileID);
+            status = false;
+        }
+
+        if(status == false) {
+            Log.e(TAG, "failed connect audio request for device: " + mActivedevice);
+            if(ApmConst.AudioProfiles.HFP != profileID) {
+                HeadsetService service = HeadsetService.getHeadsetService();
+                if(service != null) {
+                    service.getHfpA2DPSyncInterface().releaseA2DP(null);
+                }
+            }
+        }
+
+        return status;
+    }
+
+    public boolean disconnectAudio() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        BluetoothDevice mActivedevice = null;
+        boolean mStatus = false;
+
+        if(mActiveDeviceManager != null) {
+                mActivedevice = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO);
+        }
+        Log.i(TAG, "disconnectAudio: device=" + mActivedevice + ", " + Utils.getUidPidString());
+
+        int profileID = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.CALL_AUDIO);
+
+        if(ApmConst.AudioProfiles.HFP == profileID) {
+            HeadsetService service = HeadsetService.getHeadsetService();
+            if (service == null) {
+                    mStatus = false;
+            }
+            mStatus = service.disconnectAudio();
+        } else if(ApmConst.AudioProfiles.BAP_CALL == profileID) {
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if(service != null) {
+                mStatus = service.stopStream(mActivedevice);
+            }
+        } else {
+            Log.e(TAG, "Unhandled disconnectAudio request for profile: " + profileID);
+            mStatus = true;
+        }
+
+        if(ApmConst.AudioProfiles.HFP != profileID) {
+            HeadsetService service = HeadsetService.getHeadsetService();
+            if(service != null) {
+                service.getHfpA2DPSyncInterface().releaseA2DP(null);
+            }
+        }
+        return mStatus;
+    }
+
+    public boolean setConnectionPolicy(BluetoothDevice device, Integer connectionPolicy) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
+                "Need BLUETOOTH_PRIVILEGED permission");
+        boolean mStatus;
+
+        Log.d(TAG, "setConnectionPolicy: device=" + device
+            + ", connectionPolicy=" + connectionPolicy + ", " + Utils.getUidPidString());
+
+        mStatus = mAdapterService.getDatabase()
+            .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, connectionPolicy);
+
+        if (mStatus &&
+                connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            connect(device);
+        } else if (mStatus &&
+                connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+            disconnect(device);
+        }
+        return mStatus;
+    }
+
+    public int getConnectionPolicy(BluetoothDevice device) {
+        if(mAdapterService != null) {
+            int connPolicy;
+            connPolicy = mAdapterService.getDatabase()
+                .getProfileConnectionPolicy(device, BluetoothProfile.HEADSET);
+            Log.d(TAG, "getConnectionPolicy: device=" + device
+                    + ", connectionPolicy=" + connPolicy);
+            return connPolicy;
+        } else {
+            return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+
+        }
+    }
+
+    public int getAudioState(BluetoothDevice device) {
+        if(device == null)
+            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+        CallDevice mCallDevice;
+        mCallDevice = mCallDevicesMap.get(device.getAddress());
+        if (mCallDevice == null) {
+            Log.w(TAG, "getAudioState: device " + device + " was never connected/connecting");
+            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+        }
+        return mCallDevice.scoStatus;
+    }
+
+    private List<BluetoothDevice> getNonIdleAudioDevices() {
+        if(mCallDevicesMap.size() == 0) {
+            return new ArrayList<>(0);
+        }
+
+        ArrayList<BluetoothDevice> devices = new ArrayList<>();
+        for (CallDevice mCallDevice : mCallDevicesMap.values()) {
+            if (mCallDevice.scoStatus != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+                devices.add(mCallDevice.mDevice);
+            }
+        }
+        return devices;
+    }
+
+    public boolean isAudioOn() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        int numConnectedAudioDevices = getNonIdleAudioDevices().size();
+        Log.d(TAG," isAudioOn: The number of audio connected devices "
+                      + numConnectedAudioDevices);
+             return numConnectedAudioDevices > 0;
+    }
+
+    public List<BluetoothDevice> getConnectedDevices() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        Log.i(TAG, "getConnectedDevices: ");
+        if(mCallDevicesMap.size() == 0) {
+            Log.i(TAG, "no device is Connected:");
+            return new ArrayList<>(0);
+        }
+
+        List<BluetoothDevice> connectedDevices = new ArrayList<>();
+        for(CallDevice mCallDevice : mCallDevicesMap.values()) {
+            if(mCallDevice.deviceConnStatus == BluetoothProfile.STATE_CONNECTED) {
+                connectedDevices.add(mCallDevice.mDevice);
+            }
+        }
+        Log.i(TAG, "ConnectedDevices: = " + connectedDevices.size());
+        return connectedDevices;
+    }
+
+    public int getConnectionState(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+        if(device == null)
+            return BluetoothProfile.STATE_DISCONNECTED;
+        CallDevice mCallDevice;
+        mCallDevice = mCallDevicesMap.get(device.getAddress());
+        if(mCallDevice != null)
+            return mCallDevice.deviceConnStatus;
+
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    public boolean isVoiceOrCallActive() {
+        boolean isVoiceActive = isAudioOn() || mVirtualCallStarted;
+        HeadsetService mHeadsetService = HeadsetService.getHeadsetService();
+        if(mHeadsetService != null) {
+            isVoiceActive = isVoiceActive || mHeadsetService.isScoOrCallActive();
+        }
+        return isVoiceActive;
+    }
+
+    private void broadcastConnStateChange(BluetoothDevice device, int fromState, int toState) {
+        Log.d(TAG,"broadcastConnectionState " + device + ": " + fromState + "->" + toState);
+        HeadsetService mHeadsetService = HeadsetService.getHeadsetService();
+        if(mHeadsetService == null) {
+            Log.w(TAG,"broadcastConnectionState: HeadsetService not initialized. Return!");
+            return;
+        }
+
+        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL,
+                BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    private void broadcastAudioState(BluetoothDevice device, int fromState, int toState) {
+         Log.d(TAG,"broadcastAudioState " + device + ": " + fromState + "->" + toState);
+        HeadsetService mHeadsetService = HeadsetService.getHeadsetService();
+        if(mHeadsetService == null) {
+            Log.d(TAG,"broadcastAudioState: HeadsetService not initialized. Return!");
+            return;
+        }
+
+        Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL,
+                BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    public void onConnStateChange(BluetoothDevice device, Integer state, Integer profile) {
+        int prevState;
+        Log.w(TAG, "onConnStateChange: profile: " + profile + " state: "
+                                                  + state + " for device " + device);
+        if(device == null)
+            return;
+        CallDevice mCallDevice = mCallDevicesMap.get(device.getAddress());
+
+        if(mCallDevice == null) {
+            if(state == BluetoothProfile.STATE_DISCONNECTED)
+                return;
+            if(mCallDevicesMap.size() >= MAX_DEVICES) {
+                return;
+            }
+            mCallDevice = new CallDevice(device, profile, state);
+            mCallDevicesMap.put(device.getAddress(), mCallDevice);
+            broadcastConnStateChange(device, BluetoothProfile.STATE_DISCONNECTED, state);
+            return;
+        }
+
+        int profileIndex = mCallDevice.getProfileIndex(profile);
+        DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+        prevState = mCallDevice.deviceConnStatus;
+        mCallDevice.profileConnStatus[profileIndex] = state;
+
+        if(state == BluetoothProfile.STATE_DISCONNECTED) {
+            dMap.profileConnectionUpdate(device, ApmConst.AudioFeatures.CALL_AUDIO, profile, false);
+        }
+
+        int otherProfileConnectionState = mCallDevice.profileConnStatus[(profileIndex+1)%2];
+        Log.w(TAG, " otherProfileConnectionState: " + otherProfileConnectionState);
+
+        switch(otherProfileConnectionState) {
+        /*Send Broadcast based on state of other profile*/
+            case BluetoothProfile.STATE_DISCONNECTED:
+                broadcastConnStateChange(device, prevState, state);
+                mCallDevice.deviceConnStatus = state;
+                if(state == BluetoothProfile.STATE_CONNECTED) {
+                    int supportedProfiles = dMap.getSupportedProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
+                    if(profile == ApmConst.AudioProfiles.HFP &&
+                            (supportedProfiles & ApmConst.AudioProfiles.BAP_CALL) == ApmConst.AudioProfiles.BAP_CALL) {
+                        Log.w(TAG, "Connect LE Voice after HFP auto connect from remote");
+                        StreamAudioService mStreamService = StreamAudioService.getStreamAudioService();
+                        if(mStreamService != null) {
+                            mStreamService.connectLeStream(device, ApmConst.AudioProfiles.BAP_CALL);
+                        }
+                    } else {
+                        ActiveDeviceManagerService mActiveDeviceManager = ActiveDeviceManagerService.get();
+                        mActiveDeviceManager.setActiveDevice(device, ApmConst.AudioFeatures.CALL_AUDIO);
+                    }
+                }
+                break;
+            case BluetoothProfile.STATE_CONNECTING:
+                int preferredProfile = dMap.getProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
+                boolean isPreferredProfile = (preferredProfile == profile);
+                if(state == BluetoothProfile.STATE_CONNECTED && isPreferredProfile) {
+                    broadcastConnStateChange(device, prevState, state);
+                    mCallDevice.deviceConnStatus = state;
+                }
+                break;
+            case BluetoothProfile.STATE_DISCONNECTING:
+                if(state == BluetoothProfile.STATE_CONNECTING ||
+                        state == BluetoothProfile.STATE_CONNECTED) {
+                    broadcastConnStateChange(device, prevState, state);
+                    mCallDevice.deviceConnStatus = state;
+                }
+                break;
+            case BluetoothProfile.STATE_CONNECTED:
+                if(state == BluetoothProfile.STATE_CONNECTED) {
+                    if(prevState != state) {
+                        broadcastConnStateChange(device, prevState, state);
+                        mCallDevice.deviceConnStatus = state;
+                    }
+                    ActiveDeviceManagerService mActiveDeviceManager =
+                                     ActiveDeviceManagerService.get();
+                    mActiveDeviceManager.setActiveDevice(device, ApmConst.AudioFeatures.CALL_AUDIO);
+                } else if(state == BluetoothProfile.STATE_DISCONNECTED) {
+                    if(prevState != BluetoothProfile.STATE_CONNECTED) {
+                        broadcastConnStateChange(device, prevState, BluetoothProfile.STATE_CONNECTED);
+                        mCallDevice.deviceConnStatus = BluetoothProfile.STATE_CONNECTED;
+                    } else {
+                        ActiveDeviceManagerService mActiveDeviceManager =
+                                     ActiveDeviceManagerService.get();
+                        if(device.equals(mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO))) {
+                            mActiveDeviceManager.setActiveDevice(device, ApmConst.AudioFeatures.CALL_AUDIO);
+                        }
+                    }
+                }
+                break;
+        }
+
+        if(state == BluetoothProfile.STATE_CONNECTED) {
+            dMap.profileConnectionUpdate(device, ApmConst.AudioFeatures.CALL_AUDIO, profile, true);
+        }
+    }
+
+    public void onAudioStateChange(BluetoothDevice device, Integer state) {
+        int prevStatus;
+        if(device == null)
+            return;
+        CallDevice mCallDevice = mCallDevicesMap.get(device.getAddress());
+        if(mCallDevice == null) {
+            return;
+        }
+
+        if(mCallDevice.scoStatus == state)
+            return;
+
+        HeadsetService service = HeadsetService.getHeadsetService();
+        int profileID = mActiveDeviceManager.getActiveProfile(
+                               ApmConst.AudioFeatures.CALL_AUDIO);
+        BluetoothDevice mActivedevice = mActiveDeviceManager.getActiveDevice(
+                               ApmConst.AudioFeatures.CALL_AUDIO);
+        if (service != null) {
+            if(!(service.shouldCallAudioBeActive() || mVirtualCallStarted)) {
+                if(ApmConst.AudioProfiles.BAP_CALL  == profileID) {
+                    StreamAudioService mStreamAudioService =
+                                StreamAudioService.getStreamAudioService();
+                    if(mStreamAudioService != null) {
+                        Log.w(TAG, "Call not active, disconnect stream");
+                        mStreamAudioService.stopStream(mActivedevice);
+                    }
+                }
+            }
+        }
+
+        prevStatus = mCallDevice.scoStatus;
+        mCallDevice.scoStatus = state;
+        VolumeManager mVolumeManager = VolumeManager.get();
+        mVolumeManager.updateStreamState(device, state, ApmConst.AudioFeatures.CALL_AUDIO);
+        broadcastAudioState(device, prevStatus, state);
+        if(state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+            if(ApmConst.AudioProfiles.HFP != profileID) {
+                if(service != null) {
+                    service.getHfpA2DPSyncInterface().releaseA2DP(null);
+                }
+            }
+          //mAudioManager.setBluetoothScoOn(false);
+        } /*else {
+          mAudioManager.setBluetoothScoOn(true);
+        }*/
+    }
+
+    public void setAudioParam(String param) {
+        mAudioManager.setParameters(param);
+    }
+
+    public void setBluetoothScoOn(boolean on) {
+        mAudioManager.setBluetoothScoOn(on);
+    }
+
+    public AudioManager getAudioManager() {
+        return mAudioManager;
+    }
+
+    class CallDevice {
+        BluetoothDevice mDevice;
+        int[] profileConnStatus = new int[2];
+        int deviceConnStatus;
+        int scoStatus;
+
+        public static final int SCO_STREAM = 0;
+        public static final int LE_STREAM = 1;
+
+        CallDevice(BluetoothDevice device, int profile, int state) {
+            mDevice = device;
+            if(profile == ApmConst.AudioProfiles.HFP) {
+                profileConnStatus[SCO_STREAM] = state;
+                profileConnStatus[LE_STREAM] = BluetoothProfile.STATE_DISCONNECTED;
+            } else {
+                profileConnStatus[LE_STREAM] = state;
+                profileConnStatus[SCO_STREAM] = BluetoothProfile.STATE_DISCONNECTED;
+            }
+            deviceConnStatus = state;
+            scoStatus = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;;
+        }
+
+        CallDevice(BluetoothDevice device, int profile) {
+            this(device, profile, BluetoothProfile.STATE_DISCONNECTED);
+        }
+
+        public int getProfileIndex(int profile) {
+            if(profile == ApmConst.AudioProfiles.HFP)
+                return SCO_STREAM;
+            else
+                return LE_STREAM;
+        }
+    }
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/CallControl.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/CallControl.java
new file mode 100644
index 0000000..40e5fd1
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/CallControl.java
@@ -0,0 +1,164 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.apm;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
+import com.android.bluetooth.hfp.HeadsetService;
+import com.android.bluetooth.hfp.HeadsetSystemInterface;
+import com.android.bluetooth.apm.ApmConst;
+import com.android.bluetooth.cc.CCService;
+import android.media.AudioManager;
+import com.android.bluetooth.apm.ActiveDeviceManagerService;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.net.Uri;
+import android.telephony.PhoneNumberUtils;
+import android.telecom.PhoneAccount;
+import android.os.SystemProperties;
+
+import android.util.Log;
+
+
+public class CallControl {
+
+    private static CallControl mCallControl;
+    private static final String TAG = "CallControl";
+    private static Context mContext;
+    private static ActiveDeviceManagerService mActiveDeviceManager;
+    private static boolean isCCEnabled = true;
+    private CallControl(Context context) {
+        Log.d(TAG, "Initialization");
+        mContext = context;
+    }
+
+    public static void init(Context context) {
+        if(mCallControl == null) {
+            mCallControl = new CallControl(context);
+         CallControlIntf.init(mCallControl);
+        }
+        isCCEnabled = SystemProperties.getBoolean("persist.vendor.service.bt.cc", true);
+        Log.d(TAG, "isCCEnabled" + isCCEnabled);
+    }
+
+    public static CallControl get() {
+        return mCallControl;
+    }
+
+    public void phoneStateChanged(Integer numActive, Integer numHeld, Integer callState, String number,
+            Integer type, String name, Boolean isVirtualCall) {
+       Log.d(TAG, "phoneStateChanged");
+       if(isCCEnabled == true) {
+         CCService.getCCService().phoneStateChanged(numActive, numHeld,callState,number,
+                                    type, name, isVirtualCall);
+       }
+    }
+
+    public void setVirtualCallActive(boolean state) {
+       if(isCCEnabled == true) {
+          CCService.getCCService().setVirtualCallActive(state);
+       }
+    }
+
+   public void clccResponse(Integer index, Integer direction, Integer status, Integer mode, Boolean mpty,
+                String number, Integer type) {
+      Log.d(TAG, "clccResponse");
+      if (isCCEnabled == true) {
+        CCService.getCCService().clccResponse(index, direction, status, mode, mpty, number, type);
+      }
+    }
+
+    public void updateBearerTechnology(Integer tech) {
+       Log.d(TAG, "updateBearerTechnology");
+       if (isCCEnabled == true) {
+         CCService.getCCService().updateBearerProviderTechnology(tech);
+       }
+    }
+
+    public void updateSignalStatus(Integer signal) {
+       Log.d(TAG, "updateSignalStatus");
+       if (isCCEnabled == true) {
+         CCService.getCCService().updateSignalStrength(signal);
+       }
+    }
+
+    public void updateBearerName(String name) {
+       Log.d(TAG, "updateBearerProviderName");
+       if (isCCEnabled == true) {
+         CCService.getCCService().updateBearerProviderName(name);
+       }
+    }
+
+    public void updateOriginateResult(BluetoothDevice device, Integer event, Integer res) {
+       Log.d(TAG, "updateOriginateResult");
+       if (isCCEnabled == true) {
+         CCService.getCCService().updateOriginateResult(device, event, res);
+       }
+    }
+    public static void listenForPhoneState (int events) {
+        Log.d(TAG, "listenForPhoneState");
+        BluetoothDevice dummyDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice("CC:CC:CC:CC:CC:CC");
+        HeadsetService.getHeadsetService().getSystemInterfaceObj().getHeadsetPhoneState().listenForPhoneState(dummyDevice, events);
+    }
+
+    public static void answerCall (BluetoothDevice device) {
+        Log.d(TAG, "answerCall");
+        HeadsetService.getHeadsetService().getSystemInterfaceObj().answerCall(device);
+    }
+
+    public static void hangupCall (BluetoothDevice device) {
+        Log.d(TAG, "hangupCall");
+        HeadsetService.getHeadsetService().getSystemInterfaceObj().hangupCall(device);
+    }
+
+    public static void terminateCall (BluetoothDevice device, int index) {
+        Log.d(TAG, "terminateCall");
+        HeadsetService.getHeadsetService().getSystemInterfaceObj().terminateCall(device, index);
+    }
+
+    public static boolean processChld (BluetoothDevice device, int chld) {
+        Log.d(TAG, "processChld");
+        return HeadsetService.getHeadsetService().getSystemInterfaceObj().processChld(chld);
+    }
+
+    public static boolean holdCall (BluetoothDevice device, int index) {
+        Log.d(TAG, "holdCall");
+        return HeadsetService.getHeadsetService().getSystemInterfaceObj().holdCall(index);
+    }
+
+    public static boolean listCurrentCalls () {
+        Log.d(TAG, "listCurrentCalls");
+        return HeadsetService.getHeadsetService().getSystemInterfaceObj().listCurrentCalls();
+    }
+
+   public static boolean dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber) {
+        Log.i(TAG, "dialOutgoingCall: from " + fromDevice);
+        HeadsetService service = HeadsetService.getHeadsetService();
+        if (service != null) {
+            service.dialOutgoingCallInternal(fromDevice, dialNumber);
+        }
+        return true;
+    }
+
+    public static void dial (BluetoothDevice device, String dialNumber) {
+        dialOutgoingCall(device, dialNumber);
+    }
+
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/DeviceProfileMap.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/DeviceProfileMap.java
new file mode 100644
index 0000000..0901f98
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/DeviceProfileMap.java
@@ -0,0 +1,814 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.apm;
+
+import android.content.SharedPreferences;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothUuid;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.content.Context;
+import com.android.internal.util.ArrayUtils;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.lang.Integer;
+import java.lang.Boolean;
+import java.util.Objects;
+
+public class DeviceProfileMap {
+
+    Map<BluetoothDevice, Integer> mSupportedProfileMap = new HashMap();
+    Map<BluetoothDevice, int []> mActiveProfileMap = new HashMap();
+    Map<BluetoothDevice, Integer> mConnectedProfileMap = new HashMap();
+    private Context mContext;
+    private static DeviceProfileMap DPMSingleInstance = null;
+    public static int [] mPreferredProfileList = new int[ApmConst.AudioFeatures.MAX_AUDIO_FEATURES];
+    public static final String SUPPORTED_PROFILE_MAP = "bluetooth_supported_profile_map";
+    public final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
+    public final String ACTION_POWER_OFF = "android.intent.action.QUICKBOOT_POWEROFF";
+    private final Object mLock = new Object();
+
+    private static final int LeMediaProfiles = ApmConst.AudioProfiles.BAP_MEDIA
+                                             | ApmConst.AudioProfiles.TMAP_MEDIA
+                                             | ApmConst.AudioProfiles.BAP_GCP
+                                             | ApmConst.AudioProfiles.BAP_RECORDING;
+
+    private static final int LeCallProfiles = ApmConst.AudioProfiles.BAP_CALL
+                                             | ApmConst.AudioProfiles.TMAP_CALL;
+
+    // private constructor restricted to this class itself
+    private DeviceProfileMap() {
+        Log.w(LOGTAG, "DeviceProfileMap object creation");
+    }
+
+    // static method to create instance of Singleton class
+    public static DeviceProfileMap getDeviceProfileMapInstance() {
+            if (DPMSingleInstance == null) {
+            DPMSingleInstance = new DeviceProfileMap();
+            DeviceProfileMapIntf.init(DPMSingleInstance);
+        }
+        return DPMSingleInstance;
+    }
+
+    private static final String LOGTAG = "DeviceProfileMap";
+    private SharedPreferences getSupportedProfileMap() {
+         return mContext.getSharedPreferences(SUPPORTED_PROFILE_MAP, Context.MODE_PRIVATE);
+    }
+
+    /**
+     * Initialize the device profile map
+     */
+    public synchronized boolean init(Context context) {
+        Log.d(LOGTAG, "init: ");
+        // populate the supported profile list.
+        mContext = context;
+        Map<String, ?> allKeys = getSupportedProfileMap().getAll();
+        SharedPreferences.Editor mSupportedProfileMapEditor = getSupportedProfileMap().edit();
+
+        for (Map.Entry<String, ?> entry : allKeys.entrySet()) {
+             String key = entry.getKey();
+             Object value = entry.getValue();
+             BluetoothDevice mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().
+                                                      getRemoteDevice(key);
+             if (value instanceof Integer && mBluetoothDevice.getBondState()
+                                            == BluetoothDevice.BOND_BONDED) {
+                 mSupportedProfileMap.put(mBluetoothDevice, (Integer) value);
+                 Log.d(LOGTAG, "address " + key + " from the Supported Profile Map: " + value);
+             } else {
+                 Log.d(LOGTAG, "Removing " + key + " from the Supported Profile map");
+                 mSupportedProfileMapEditor.remove(key);
+             }
+        }
+        mSupportedProfileMapEditor.apply();
+        //intialize the preferred profile list
+        int mPreferredProfileVal =
+                    SystemProperties.getInt("persist.vendor.qcom.bluetooth.default_profiles", 0);
+        Log.d(LOGTAG, "init: Preferred Profile = " + mPreferredProfileVal);
+        int mfeature = 0;
+        while (mfeature < ApmConst.AudioFeatures.MAX_AUDIO_FEATURES) {
+            switch(mfeature) {
+                case ApmConst.AudioFeatures.CALL_AUDIO:
+                    /* default preferred profile for call */
+                    mPreferredProfileList[mfeature] = ApmConst.AudioProfiles.HFP;
+                    if((mPreferredProfileVal &
+                                    ApmConst.AudioProfiles.TMAP_CALL) != 0) {
+                        mPreferredProfileList[mfeature] = ApmConst.AudioProfiles.TMAP_CALL;
+                    } else if((mPreferredProfileVal &
+                                    ApmConst.AudioProfiles.BAP_CALL) != 0) {
+                        mPreferredProfileList[mfeature] = ApmConst.AudioProfiles.BAP_CALL;
+                    }
+                    break;
+                case ApmConst.AudioFeatures.MEDIA_AUDIO:
+                    mPreferredProfileList[mfeature] = ApmConst.AudioProfiles.A2DP;
+                    if((mPreferredProfileVal &
+                                    ApmConst.AudioProfiles.TMAP_MEDIA) != 0) {
+                        mPreferredProfileList[mfeature] = ApmConst.AudioProfiles.TMAP_MEDIA;
+                    } else if((mPreferredProfileVal &
+                                    ApmConst.AudioProfiles.BAP_RECORDING) != 0) {
+                        mPreferredProfileList[mfeature] = ApmConst.AudioProfiles.BAP_RECORDING;
+                    } else if((mPreferredProfileVal &
+                                    ApmConst.AudioProfiles.BAP_MEDIA) != 0) {
+                        mPreferredProfileList[mfeature] = ApmConst.AudioProfiles.BAP_MEDIA;
+                    }
+                    break;
+                case ApmConst.AudioFeatures.CALL_CONTROL:
+                    mPreferredProfileList[mfeature] = ((mPreferredProfileVal &
+                                    ApmConst.AudioProfiles.CCP) != 0) ?
+                                    ApmConst.AudioProfiles.CCP : ApmConst.AudioProfiles.HFP;
+                    break;
+                 case ApmConst.AudioFeatures.MEDIA_CONTROL:
+                    mPreferredProfileList[mfeature] = ((mPreferredProfileVal &
+                                    ApmConst.AudioProfiles.AVRCP) != 0) ?
+                                    ApmConst.AudioProfiles.AVRCP : ApmConst.AudioProfiles.MCP;
+                    break;
+                case ApmConst.AudioFeatures.CALL_VOLUME_CONTROL:
+                    mPreferredProfileList[mfeature] = ((mPreferredProfileVal &
+                                    (ApmConst.AudioProfiles.BAP_CALL | ApmConst.AudioProfiles.TMAP_CALL)) != 0) ?
+                                    ApmConst.AudioProfiles.VCP : ApmConst.AudioProfiles.HFP;
+                    break;
+                case ApmConst.AudioFeatures.MEDIA_VOLUME_CONTROL:
+                    mPreferredProfileList[mfeature] = ((mPreferredProfileVal &
+                                    ApmConst.AudioProfiles.AVRCP) != 0) ?
+                                    ApmConst.AudioProfiles.AVRCP : ApmConst.AudioProfiles.VCP;
+                    break;
+                case ApmConst.AudioFeatures.HEARING_AID:
+                    mPreferredProfileList[mfeature] = ((mPreferredProfileVal &
+                                    ApmConst.AudioProfiles.HAP_BREDR) != 0) ?
+                                    ApmConst.AudioProfiles.HAP_BREDR : ApmConst.AudioProfiles.HAP_LE;
+                    break;
+                case ApmConst.AudioFeatures.BROADCAST_AUDIO:
+                    mPreferredProfileList[mfeature] = ((mPreferredProfileVal &
+                           ApmConst.AudioProfiles.BROADCAST_BREDR) != 0) ?
+                           ApmConst.AudioProfiles.BROADCAST_BREDR : ApmConst.AudioProfiles.BROADCAST_LE;
+                    break;
+                default :
+                    break;
+                }
+                Log.w(LOGTAG, "init: Preferred Profile = " + mPreferredProfileList[mfeature] +
+                                                       " for audio feature " + mfeature);
+                mfeature++;
+        }
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_UUID);
+        filter.addAction(ACTION_SHUTDOWN);
+        filter.addAction(ACTION_POWER_OFF);
+        mContext.registerReceiver(mDeviceProfileMapReceiver, filter);
+
+        return true;
+    }
+
+    private final BroadcastReceiver mDeviceProfileMapReceiver = new BroadcastReceiver() {
+        @Override
+         public void onReceive(Context context, Intent intent) {
+             String action = intent.getAction();
+             if (action == null) {
+                 Log.w(LOGTAG, "mDeviceProfileMapReceiver, action is null");
+                 return;
+             }
+             switch (action) {
+                case BluetoothDevice.ACTION_UUID: {
+                     BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                     Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
+                     handleDeviceUuidEvent(device, uuids);
+                     break;
+                }
+                case ACTION_SHUTDOWN:
+                case ACTION_POWER_OFF:
+                    handleDeviceShutdown();
+                    break;
+                default:
+                     Log.w(LOGTAG, "Unknown action " + action);
+             }
+         }
+    };
+
+     private void handleDeviceUuidEvent(BluetoothDevice device, Parcelable[] uuids) {
+         Log.d(LOGTAG, "UUIDs found, device: " + device);
+         if (uuids != null) {
+             ParcelUuid[] uuidsToSend = new ParcelUuid[uuids.length];
+             for (int i = 0; i < uuidsToSend.length; i++) {
+                 uuidsToSend[i] = (ParcelUuid) uuids[i];
+                 Log.d(LOGTAG,"index=" + i + "uuid=" + uuidsToSend[i]);
+             }
+             checkIfProfileSupported(device, uuidsToSend);
+         }
+     }
+
+    private void checkIfProfileSupported(BluetoothDevice device, ParcelUuid[] remoteDeviceUuids) {
+        ParcelUuid ADV_AUDIO_T_MEDIA =
+            ParcelUuid.fromString("00006AD0-0000-1000-8000-00805F9B34FB");
+
+        ParcelUuid HEARINGAID_ADV_AUDIO =
+            ParcelUuid.fromString("00006AD2-0000-1000-8000-00805F9B34FB");
+
+        ParcelUuid ADV_AUDIO_P_MEDIA =
+            ParcelUuid.fromString("00006AD1-0000-1000-8000-00805F9B34FB");
+
+        ParcelUuid ADV_AUDIO_P_VOICE =
+            ParcelUuid.fromString("00006AD4-0000-1000-8000-00805F9B34FB");
+
+        ParcelUuid ADV_AUDIO_T_VOICE =
+            ParcelUuid.fromString("00006AD5-0000-1000-8000-00805F9B34FB");
+
+        ParcelUuid ADV_AUDIO_G_MEDIA =
+            ParcelUuid.fromString("12994B7E-6d47-4215-8C9E-AAE9A1095BA3");
+
+        ParcelUuid ADV_AUDIO_W_RECORDING =
+            ParcelUuid.fromString("2587DB3C-CE70-4FC9-935F-777AB4188FD7");
+
+        if (ArrayUtils.contains(remoteDeviceUuids, BluetoothUuid.HFP)) {
+            updateSupportedProfileMap(device, ApmConst.AudioProfiles.HFP);
+        }
+        if (ArrayUtils.contains(remoteDeviceUuids, BluetoothUuid.A2DP_SINK)) {
+            updateSupportedProfileMap(device, ApmConst.AudioProfiles.A2DP);
+        }
+        if (ArrayUtils.contains(remoteDeviceUuids, BluetoothUuid.HEARING_AID)) {
+            updateSupportedProfileMap(device, ApmConst.AudioProfiles.HAP_BREDR);
+        }
+        if (ArrayUtils.contains(remoteDeviceUuids, BluetoothUuid.AVRCP_CONTROLLER)) {
+            updateSupportedProfileMap(device, ApmConst.AudioProfiles.AVRCP);
+        }
+        if (ArrayUtils.contains(remoteDeviceUuids, ADV_AUDIO_T_MEDIA)) {
+            updateSupportedProfileMap(device, ApmConst.AudioProfiles.TMAP_MEDIA);
+        }
+        if (ArrayUtils.contains(remoteDeviceUuids, ADV_AUDIO_T_VOICE)) {
+            updateSupportedProfileMap(device, ApmConst.AudioProfiles.TMAP_CALL);
+        }
+        if (ArrayUtils.contains(remoteDeviceUuids, ADV_AUDIO_P_MEDIA)) {
+            updateSupportedProfileMap(device, ApmConst.AudioProfiles.BAP_MEDIA);
+        }
+        if (ArrayUtils.contains(remoteDeviceUuids, ADV_AUDIO_P_VOICE)) {
+            updateSupportedProfileMap(device, ApmConst.AudioProfiles.BAP_CALL);
+        }
+        if (ArrayUtils.contains(remoteDeviceUuids, ADV_AUDIO_W_RECORDING)) {
+            updateSupportedProfileMap(device, ApmConst.AudioProfiles.BAP_RECORDING);
+        }
+    }
+
+    private void updateSupportedProfileMap(BluetoothDevice device, int mProfile) {
+        synchronized (mLock) {
+            int mSupportedProfileBitMap = 0;
+            if(!mSupportedProfileMap.containsKey(device)) {
+                //device is not added in supported map, add to it
+                Log.d(LOGTAG, "updateSupportedProfileMap: device " + device +
+                                 "is not added in supported map, add it");
+                mSupportedProfileMap.put(device, mSupportedProfileBitMap);
+            } else {
+                mSupportedProfileBitMap = mSupportedProfileMap.get(device);
+            }
+            Log.d(LOGTAG, "updateSupportedProfileMap: device " + device + " for profile "
+                    + mProfile + " mSupportedProfileBitMap " + mSupportedProfileBitMap);
+            mSupportedProfileBitMap = mSupportedProfileBitMap | mProfile;
+            mSupportedProfileMap.put(device, mSupportedProfileBitMap);
+            Log.d(LOGTAG, "updateSupportedProfileMap: device " + device + " for profile "
+                    + mProfile + " mSupportedProfileBitMap " + mSupportedProfileBitMap);
+        }
+    }
+
+    public int getAllSupportedProfile(BluetoothDevice device) {
+        int mSupportedProfileBitMap = 0;
+        synchronized (mLock) {
+            if(!mSupportedProfileMap.containsKey(device)) {
+                Log.d(LOGTAG, "No Supported Profile found for the device " + device);
+            } else {
+                mSupportedProfileBitMap = mSupportedProfileMap.get(device);
+            }
+            Log.d(LOGTAG, "getAllSupportedProfile: Supported Profile for the device "
+                + device + " mSupportedProfileBitMap " + Integer.toHexString(mSupportedProfileBitMap));
+        }
+        return mSupportedProfileBitMap;
+    }
+
+    public int getProfile(BluetoothDevice device, Integer mAudioFeature) {
+        int profileMap = getSupportedProfile(device, mAudioFeature);
+        int preferredProfile = profileMap;
+        int [] mAciveProfileArray = mActiveProfileMap.get(device);
+
+        if(profileMap == ApmConst.AudioProfiles.NONE)
+            return ApmConst.AudioProfiles.NONE;
+
+        switch(mAudioFeature) {
+            case ApmConst.AudioFeatures.CALL_AUDIO:
+                int mActiveProfileForCallAudio = mAciveProfileArray[mAudioFeature];
+                if(mActiveProfileForCallAudio != ApmConst.AudioProfiles.NONE) {
+                    //active profile is set
+                    preferredProfile = mActiveProfileForCallAudio;
+                    return preferredProfile;
+                }
+
+                if(profileMap == ApmConst.AudioProfiles.HAP_BREDR ||
+                        profileMap == ApmConst.AudioProfiles.HAP_LE) {
+                    preferredProfile = profileMap;
+                    return preferredProfile;
+                }
+
+                int mHFP = profileMap & ApmConst.AudioProfiles.HFP;
+                int mLeCall = profileMap & ApmConst.AudioProfiles.TMAP_CALL;
+                if(mLeCall == ApmConst.AudioProfiles.NONE)
+                    mLeCall = profileMap & ApmConst.AudioProfiles.BAP_CALL;
+
+                if(mHFP != ApmConst.AudioProfiles.NONE &&
+                        mLeCall != ApmConst.AudioProfiles.NONE) {
+                    preferredProfile = mPreferredProfileList[mAudioFeature];
+                } else if(mHFP != ApmConst.AudioProfiles.NONE) {
+                    preferredProfile = mHFP;
+                } else {
+                    preferredProfile = mLeCall;
+                }
+
+                Log.d(LOGTAG, "getProfile: device " + device + " preferredProfile: "
+                    + preferredProfile + " for CALL_AUDIO");
+                break;
+
+            case ApmConst.AudioFeatures.MEDIA_AUDIO:
+                int mActiveProfileForMediaAudio = mAciveProfileArray[mAudioFeature];
+                if(mActiveProfileForMediaAudio != ApmConst.AudioProfiles.NONE) {
+                    //active profile is set
+                    preferredProfile = mActiveProfileForMediaAudio;
+                    Log.d(LOGTAG, "getProfile: device " + device + " Active Profile: "
+                            + preferredProfile + " for MEDIA_AUDIO");
+                    return preferredProfile;
+                }
+
+                if(profileMap == ApmConst.AudioProfiles.HAP_BREDR ||
+                        profileMap == ApmConst.AudioProfiles.HAP_LE) {
+                    preferredProfile = profileMap;
+                    return preferredProfile;
+                }
+
+                int mA2dp = profileMap & ApmConst.AudioProfiles.A2DP;
+                int mLeMedia = profileMap & ApmConst.AudioProfiles.TMAP_MEDIA;
+                if(mLeMedia == ApmConst.AudioProfiles.NONE)
+                    mLeMedia = profileMap & ApmConst.AudioProfiles.BAP_MEDIA;
+
+                if(mA2dp != ApmConst.AudioProfiles.NONE &&
+                        mLeMedia != ApmConst.AudioProfiles.NONE) {
+                    preferredProfile = mPreferredProfileList[mAudioFeature];
+                } else if(mA2dp != ApmConst.AudioProfiles.NONE) {
+                    preferredProfile = mA2dp;
+                } else {
+                    if((preferredProfile & ApmConst.AudioProfiles.BAP_RECORDING)
+                                != ApmConst.AudioProfiles.NONE)
+                        preferredProfile = mPreferredProfileList[mAudioFeature];
+                    else
+                        preferredProfile = mLeMedia;
+                }
+
+                Log.d(LOGTAG, "getProfile: device " + device + " preferredProfile: "
+                    + preferredProfile + " for MEDIA_AUDIO");
+                break;
+        }
+        return preferredProfile;
+    }
+
+    public int getSupportedProfile(BluetoothDevice device, Integer mAudioFeature) {
+        int [] mAciveProfileArray;
+
+        Log.d(LOGTAG, "getSupportedProfile: for the device " + device + " AudioFeature " + mAudioFeature);
+        if(!mActiveProfileMap.containsKey(device)) {
+            // intialize the active profile but map for the device
+            Log.d(LOGTAG, "getSupportedProfile: intialize the active profile map for the device " + device);
+            mAciveProfileArray = new int[ApmConst.AudioFeatures.MAX_AUDIO_FEATURES];
+            Arrays.fill(mAciveProfileArray, ApmConst.AudioProfiles.NONE);
+            mActiveProfileMap.put(device, mAciveProfileArray);
+        } else {
+            mAciveProfileArray = mActiveProfileMap.get(device);
+        }
+        //get the supprted profile list
+        int mSupportedProfileBitMap = 0;
+        if(!mSupportedProfileMap.containsKey(device)) {
+            //device is not added in supported map, add to it
+            Log.d(LOGTAG, "getSupportedProfile: device " + device + " is not added in supported map, add it");
+            mSupportedProfileMap.put(device, mSupportedProfileBitMap);
+        } else {
+            mSupportedProfileBitMap = mSupportedProfileMap.get(device);
+        }
+        Log.d(LOGTAG, "getSupportedProfile: supported Profiles for the device " + device
+            + " val = " + Integer.toHexString(mSupportedProfileBitMap));
+
+        switch(mAudioFeature) {
+            case ApmConst.AudioFeatures.CALL_AUDIO:
+            {
+                int mIsHapBREDRSupported = mSupportedProfileBitMap & ApmConst.AudioProfiles.HAP_BREDR;
+                int mIsHapLESupported = mSupportedProfileBitMap & ApmConst.AudioProfiles.HAP_LE;
+
+                if(mIsHapBREDRSupported != ApmConst.AudioProfiles.NONE) {
+                    return ApmConst.AudioProfiles.HAP_BREDR;
+                } else if (mIsHapLESupported != ApmConst.AudioProfiles.NONE) {
+                    return ApmConst.AudioProfiles.HAP_LE;
+                }
+
+                int mCallAudioProfile = mSupportedProfileBitMap & ( ApmConst.AudioProfiles.HFP
+                                | ApmConst.AudioProfiles.BAP_CALL
+                                | ApmConst.AudioProfiles.TMAP_CALL);
+
+                Log.d(LOGTAG, "getSupportedProfile: device " + device + " supports: "
+                    + mCallAudioProfile + " for CALL_AUDIO");
+
+                return mCallAudioProfile;
+            }
+
+            case ApmConst.AudioFeatures.MEDIA_AUDIO:
+            {
+                int mIsHapBREDRSupported = mSupportedProfileBitMap & ApmConst.AudioProfiles.HAP_BREDR;
+                int mIsHapLESupported = mSupportedProfileBitMap & ApmConst.AudioProfiles.HAP_LE;
+
+                if(mIsHapBREDRSupported != ApmConst.AudioProfiles.NONE) {
+                    return ApmConst.AudioProfiles.HAP_BREDR;
+                } else if (mIsHapLESupported != ApmConst.AudioProfiles.NONE) {
+                    return ApmConst.AudioProfiles.HAP_LE;
+                }
+
+                int mMediaAudioProfile = mSupportedProfileBitMap & ( ApmConst.AudioProfiles.A2DP
+                                | ApmConst.AudioProfiles.BAP_MEDIA
+                                | ApmConst.AudioProfiles.TMAP_MEDIA
+                                | ApmConst.AudioProfiles.BAP_RECORDING);
+
+                Log.d(LOGTAG, "getSupportedProfile: device " + device + " supports: "
+                    + mMediaAudioProfile + " for MEDIA_AUDIO");
+
+                return mMediaAudioProfile;
+            }
+            case ApmConst.AudioFeatures.MEDIA_CONTROL:
+            {
+                int mActiveProfileForMediaControl = mAciveProfileArray
+                                        [ApmConst.AudioFeatures.MEDIA_CONTROL];
+                Log.d(LOGTAG, "getSupportedProfile: device " + device + " ActiveProfile For MediaControl " +
+                        mActiveProfileForMediaControl);
+                return mActiveProfileForMediaControl;
+            }
+            case ApmConst.AudioFeatures.CALL_CONTROL:
+            {
+                int mActiveProfileForCallControl = mAciveProfileArray
+                                            [ApmConst.AudioFeatures.CALL_CONTROL];
+                Log.d(LOGTAG, "getSupportedProfile: device " + device + " ActiveProfile For Call Control " +
+                     mActiveProfileForCallControl);
+                return mActiveProfileForCallControl;
+            }
+            case ApmConst.AudioFeatures.MEDIA_VOLUME_CONTROL:
+            {
+                int mActiveProfileForMediaVolControl = mAciveProfileArray
+                                          [ApmConst.AudioFeatures.MEDIA_VOLUME_CONTROL];
+                    Log.d(LOGTAG, "getSupportedProfile: device " + device +
+                      " ActiveProfile For Media Volume Control " + mActiveProfileForMediaVolControl);
+
+                    return mActiveProfileForMediaVolControl;
+            }
+            case ApmConst.AudioFeatures.CALL_VOLUME_CONTROL:
+            {
+                int mActiveProfileForCallVolControl = mAciveProfileArray
+                                          [ApmConst.AudioFeatures.CALL_VOLUME_CONTROL];
+                Log.d(LOGTAG, "getSupportedProfile: device " + device +
+                       " ActiveProfile For call Volume Control " + mActiveProfileForCallVolControl);
+
+                return mActiveProfileForCallVolControl;
+
+            }
+            case ApmConst.AudioFeatures.BROADCAST_AUDIO:
+            {
+                int mActiveProfileForBroadCastAudio = mAciveProfileArray
+                                           [ApmConst.AudioFeatures.BROADCAST_AUDIO];
+                if(mActiveProfileForBroadCastAudio != ApmConst.AudioProfiles.NONE) {
+                    //active profile is set
+                    return mActiveProfileForBroadCastAudio;
+                }
+
+                int mIsBroadCastBREDRSupported = mSupportedProfileBitMap &
+                                              ApmConst.AudioProfiles.BROADCAST_BREDR;
+                int mIsBroadCastLESupported =
+                         mSupportedProfileBitMap & ApmConst.AudioProfiles.BROADCAST_LE;
+                Log.d(LOGTAG, "getSupportedProfile: device " + device + " mIsBroadCastBREDRSupported "
+                    + mIsBroadCastBREDRSupported + " mIsBroadCastLESupported "
+                                                    + mIsBroadCastLESupported);
+
+                if((mIsBroadCastBREDRSupported != 0) && (mIsBroadCastLESupported != 0)) {
+                    return mPreferredProfileList[ApmConst.AudioFeatures.BROADCAST_AUDIO];
+                } else if (mIsBroadCastBREDRSupported != 0) {
+                    return ApmConst.AudioProfiles.BROADCAST_BREDR;
+                } else if(mIsBroadCastLESupported != 0) {
+                    return ApmConst.AudioProfiles.BROADCAST_LE;
+                }
+                break;
+            }
+            case ApmConst.AudioFeatures.HEARING_AID:
+            {
+                int mActiveProfileForHearingAid = mAciveProfileArray
+                                               [ApmConst.AudioFeatures.HEARING_AID];
+                if(mActiveProfileForHearingAid != ApmConst.AudioProfiles.NONE) {
+                    //active profile is set
+                    return mActiveProfileForHearingAid;
+                }
+
+                int mIsHAPBREDRSupported = mSupportedProfileBitMap & ApmConst.AudioProfiles.HAP_BREDR;
+                int mIsHAPLESupported = mSupportedProfileBitMap & ApmConst.AudioProfiles.HAP_LE;
+                Log.d(LOGTAG, "getSupportedProfile: device " + device + " mIsHAPBREDRSupported "
+                    + mIsHAPBREDRSupported + " mIsHAPLESupported " + mIsHAPLESupported);
+
+                if((mIsHAPBREDRSupported != 0) && (mIsHAPLESupported !=0)) {
+                    return mPreferredProfileList[ApmConst.AudioFeatures.HEARING_AID];
+                } else if (mIsHAPBREDRSupported != 0) {
+                    return ApmConst.AudioProfiles.HAP_BREDR;
+                } else if(mIsHAPLESupported != 0) {
+                    return ApmConst.AudioProfiles.HAP_LE;
+                }
+                break;
+            }
+            default :
+            {
+                Log.w(LOGTAG, "getSupportedProfile: no profile supported for" +
+                                 mAudioFeature + " device " + device);
+                return ApmConst.AudioProfiles.NONE;
+            }
+        }
+        return ApmConst.AudioProfiles.NONE;
+    }
+
+    public void profileDescoveryUpdate (BluetoothDevice device, Integer mAudioProfile) {
+        int mSupportedProfileBitMap = 0;
+        if(mSupportedProfileMap.containsKey(device)) {
+            mSupportedProfileBitMap = mSupportedProfileMap.get(device);
+        }
+
+        mSupportedProfileBitMap = mSupportedProfileBitMap | mAudioProfile;
+        mSupportedProfileMap.put(device, mSupportedProfileBitMap);
+    }
+
+    public void profileConnectionUpdate(BluetoothDevice device, Integer mAudioFeature,
+           Integer mAudioProfile, Boolean mProfileStatus) {
+        int mSupportedProfileBitMap = 0;
+        int mConnectedProfileBitMap = 0;
+        int [] mAciveProfileArray;
+
+        Log.d(LOGTAG, "profileConnectionUpdate: device : " + device + " AudioProfile "
+                + mAudioProfile + " ProfileStatus " + mProfileStatus);
+
+        synchronized (mLock) {
+            // get the Connected profile list
+            if(mConnectedProfileMap.containsKey(device)) {
+                mConnectedProfileBitMap = mConnectedProfileMap.get(device);
+            }
+
+            // get the Supported profile list
+            if(mSupportedProfileMap.containsKey(device)) {
+                mSupportedProfileBitMap = mSupportedProfileMap.get(device);
+            }
+
+            if(!mActiveProfileMap.containsKey(device)) {
+                // intialize the active profile but map for the device
+                Log.d(LOGTAG, "profileConnectionUpdate: intialize the active " +
+                              " profile map for the device " + device);
+                mAciveProfileArray = new int[ApmConst.AudioFeatures.MAX_AUDIO_FEATURES];
+                Arrays.fill(mAciveProfileArray, ApmConst.AudioProfiles.NONE);
+                mActiveProfileMap.put(device, mAciveProfileArray);
+            } else {
+                mAciveProfileArray = mActiveProfileMap.get(device);
+            }
+            // update the Audio profile connection in list
+            if (mProfileStatus) {
+                mSupportedProfileBitMap = mSupportedProfileBitMap | mAudioProfile;
+                mConnectedProfileBitMap = mConnectedProfileBitMap | mAudioProfile;
+
+                //update the active profile list
+                if(mAciveProfileArray[mAudioFeature] == ApmConst.AudioProfiles.NONE) {
+                    mAciveProfileArray[mAudioFeature] = mAudioProfile;
+                } else if(mAciveProfileArray[mAudioFeature] != mAudioProfile) {
+                // diffrent profile connected for the same mAudioFeature need to update the active list.
+                    int mPreferredProfile = mPreferredProfileList[mAudioFeature];
+                    Log.d(LOGTAG, "profileConnectionUpdate: PreferredProfile for audio feature " +
+                          mAudioFeature + " is " + mPreferredProfile + " device " + device);
+                    if((mPreferredProfile != ApmConst.AudioProfiles.NONE)
+                                         && (mConnectedProfileBitMap & mPreferredProfile) != 0) {
+                        mAciveProfileArray[mAudioFeature] = mPreferredProfile;
+                    }
+                }
+            } else {
+                mConnectedProfileBitMap = mConnectedProfileBitMap & ~mAudioProfile;
+                // profile disconnect for active profile
+                if(mAciveProfileArray[mAudioFeature] == mAudioProfile) {
+                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                    // do we need to update the active profile with other connected profile for that audio feature.
+                    switch(mAudioFeature) {
+                        case ApmConst.AudioFeatures.CALL_AUDIO:
+                            if(mAudioProfile == ApmConst.AudioProfiles.HFP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.TMAP_CALL) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.TMAP_CALL;
+                                } else if ((mConnectedProfileBitMap & ApmConst.AudioProfiles.BAP_CALL) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.BAP_CALL;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            } else if (mAudioProfile == ApmConst.AudioProfiles.TMAP_CALL
+                                    || mAudioProfile == ApmConst.AudioProfiles.BAP_CALL) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.HFP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.HFP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            }
+                            break;
+                        case ApmConst.AudioFeatures.MEDIA_AUDIO:
+                            if(mAudioProfile == ApmConst.AudioProfiles.A2DP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.TMAP_MEDIA) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.TMAP_MEDIA;
+                                } else if ((mConnectedProfileBitMap & ApmConst.AudioProfiles.BAP_MEDIA) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.BAP_MEDIA;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            } else if (mAudioProfile == ApmConst.AudioProfiles.TMAP_MEDIA
+                                    || mAudioProfile == ApmConst.AudioProfiles.BAP_MEDIA) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.A2DP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.A2DP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            }
+                            break;
+                        case ApmConst.AudioFeatures.CALL_CONTROL:
+                            if(mAudioProfile == ApmConst.AudioProfiles.HFP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.CCP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.CCP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            } else if (mAudioProfile == ApmConst.AudioProfiles.CCP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.HFP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.HFP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            }
+                            break;
+                        case ApmConst.AudioFeatures.MEDIA_CONTROL:
+                            if(mAudioProfile == ApmConst.AudioProfiles.AVRCP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.MCP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.MCP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            } else if (mAudioProfile == ApmConst.AudioProfiles.MCP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.AVRCP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.AVRCP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            }
+                            break;
+                        case ApmConst.AudioFeatures.MEDIA_VOLUME_CONTROL:
+                            if(mAudioProfile == ApmConst.AudioProfiles.AVRCP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.VCP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.VCP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            } else if (mAudioProfile == ApmConst.AudioProfiles.VCP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.AVRCP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.AVRCP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            }
+                            break;
+                        case ApmConst.AudioFeatures.CALL_VOLUME_CONTROL:
+                            if(mAudioProfile == ApmConst.AudioProfiles.HFP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.VCP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.VCP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            } else if (mAudioProfile == ApmConst.AudioProfiles.VCP) {
+                                if((mConnectedProfileBitMap & ApmConst.AudioProfiles.HFP) > 0) {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.HFP;
+                                } else {
+                                    mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                                }
+                            }
+                            break;
+                        default:
+                            mAciveProfileArray[mAudioFeature] = ApmConst.AudioProfiles.NONE;
+                    }
+                }
+            }
+
+            mActiveProfileMap.put(device, mAciveProfileArray);
+            Log.d(LOGTAG, "profileConnectionUpdate: supported Profiles for the device " + device
+                                    + " val " + Integer.toHexString(mSupportedProfileBitMap));
+            Log.d(LOGTAG, "profileConnectionUpdate: connected Profiles for the device " + device
+                                    + " val " + Integer.toHexString(mConnectedProfileBitMap));
+
+            mConnectedProfileMap.put(device, mConnectedProfileBitMap);
+            mSupportedProfileMap.put(device, mSupportedProfileBitMap);
+        }
+    }
+
+    public boolean isProfileConnected(BluetoothDevice device, Integer mAudioProfile) {
+        if (device == null) return false;
+        int mConnectedProfileBitMap = mConnectedProfileMap.get(device);
+        Log.d(LOGTAG, "isProfileConnected: device: " + device
+               + " mAudioProfile: " + mAudioProfile + " mConnectedProfileMap: " + mConnectedProfileBitMap);
+
+        return ((mConnectedProfileBitMap & mAudioProfile) == mAudioProfile);
+    }
+
+    public void setActiveProfile(BluetoothDevice device, Integer mAudioFeature, Integer mAudioProfile) {
+        int [] mAciveProfileArray;
+        Log.d(LOGTAG, "setActiveProfile: device : " + device + " AudioProfile "
+            + mAudioProfile + " AudioFeature " + mAudioFeature);
+        synchronized (mLock) {
+            if(!mActiveProfileMap.containsKey(device)) {
+                Log.d(LOGTAG, "setActiveProfile: intialize the active profile map for the device "
+                       + device);
+                mAciveProfileArray = new int[ApmConst.AudioFeatures.MAX_AUDIO_FEATURES];
+                Arrays.fill(mAciveProfileArray, ApmConst.AudioProfiles.NONE);
+                mActiveProfileMap.put(device, mAciveProfileArray);
+            } else {
+                mAciveProfileArray = mActiveProfileMap.get(device);
+            }
+            mAciveProfileArray[mAudioFeature] = mAudioProfile;
+            mActiveProfileMap.put(device, mAciveProfileArray);
+        }
+    }
+
+    static int getLeMediaProfiles() {
+        return LeMediaProfiles;
+    }
+
+    static int getLeCallProfiles() {
+        return LeCallProfiles;
+    }
+
+    public synchronized void handleDeviceShutdown() {
+        Log.d(LOGTAG, "handleDeviceShutdown: started");
+
+        //store the supported profiles for the bonded devices
+        SharedPreferences.Editor pref = getSupportedProfileMap().edit();
+        for (BluetoothDevice mBluetoothDevice : mSupportedProfileMap.keySet()) {
+            int mSupportedProfilesVal = mSupportedProfileMap.get(mBluetoothDevice);
+            Log.d(LOGTAG, "cleanup: supported Profiles for the device " + mBluetoothDevice
+                                   + " val = " + Integer.toHexString(mSupportedProfilesVal));
+            if(mBluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
+                pref.putInt(mBluetoothDevice.getAddress(), mSupportedProfilesVal);
+            }
+        }
+        pref.apply();
+        mSupportedProfileMap.clear();
+        mActiveProfileMap.clear();
+        mConnectedProfileMap.clear();
+
+        Log.d(LOGTAG, "handleDeviceShutdown: Done");
+    }
+
+    public synchronized void cleanup () {
+        if(DPMSingleInstance == null) {
+            Log.w(LOGTAG, "cleanup called without initialization, Returning");
+            return;
+        }
+
+        Log.d(LOGTAG, "cleanup: started");
+
+        //store the supported profiles for the bonded devices
+        SharedPreferences.Editor pref = getSupportedProfileMap().edit();
+        for (BluetoothDevice mBluetoothDevice : mSupportedProfileMap.keySet()) {
+            int mSupportedProfilesVal = mSupportedProfileMap.get(mBluetoothDevice);
+            Log.d(LOGTAG, "cleanup: supported Profiles for the device " + mBluetoothDevice
+                                   + " val = " + Integer.toHexString(mSupportedProfilesVal));
+            if(mBluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
+                pref.putInt(mBluetoothDevice.getAddress(), mSupportedProfilesVal);
+            }
+        }
+        pref.apply();
+        mSupportedProfileMap.clear();
+        mActiveProfileMap.clear();
+        mConnectedProfileMap.clear();
+        DPMSingleInstance = null;
+        mContext.unregisterReceiver(mDeviceProfileMapReceiver);
+
+        Log.d(LOGTAG, "cleanup: Done");
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/MediaAudio.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/MediaAudio.java
new file mode 100644
index 0000000..057c0eb
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/MediaAudio.java
@@ -0,0 +1,1204 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.apm;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import com.android.bluetooth.Utils;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.IBluetoothA2dp;
+
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.ActiveDeviceManager;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.a2dp.A2dpService;
+import com.android.bluetooth.apm.ActiveDeviceManagerService;
+import com.android.bluetooth.apm.ApmConst;
+import com.android.bluetooth.broadcast.BroadcastService;
+import com.android.bluetooth.acm.AcmService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.util.Log;
+import android.util.StatsLog;
+import com.android.bluetooth.hfp.HeadsetService;
+
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+
+public class MediaAudio {
+    private static MediaAudio sMediaAudio;
+    private AdapterService mAdapterService;
+//    private BapBroadcastService mBapBroadcastService;
+    private ActiveDeviceManagerService mActiveDeviceManager;
+    private Context mContext;
+    BapBroadcastManager mBapBroadcastManager;
+    Map<String, MediaDevice> mMediaDevices;
+
+    final ArrayList <String> supported_codec = new ArrayList<String>( List.of(
+        "LC3"
+    ));
+
+    private BroadcastReceiver mCodecConfigReceiver;
+    private BroadcastReceiver mQosConfigReceiver;
+    public static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+    public static final String BLUETOOTH_PRIVILEGED =
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+    public static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
+    public static final String ACTION_UPDATE_CODEC_CONFIG =
+                "qti.intent.bluetooth.action.UPDATE_CODEC_CONFIG";
+    public static final String CODEC_ID =
+                "qti.bluetooth.extra.CODEC_ID";
+    public static final String CODEC_CONFIG =
+                "qti.bluetooth.extra.CODEC_CONFIG";
+    public static final String CHANNEL_MODE =
+                "qti.bluetooth.extra.CHANNEL_MODE";
+    public static final String ACTION_UPDATE_QOS_CONFIG =
+                "qti.intent.bluetooth.action.UPDATE_QOS_CONFIG";
+    public static final String QOS_CONFIG =
+                "qti.bluetooth.extra.QOS_CONFIG";
+    private static final int MAX_DEVICES = 200;
+    public static final String TAG = "APM: MediaAudio";
+    public static final boolean DBG = true;
+
+    private static final long AUDIO_RECORDING_MASK = 0x00030000;
+    private static final long AUDIO_RECORDING_OFF = 0x00010000;
+    private static final long AUDIO_RECORDING_ON = 0x00020000;
+
+    private static final long GAMING_OFF = 0x00001000;
+    private static final long GAMING_ON = 0x00002000;
+    private static final long GAMING_MODE_MASK = 0x00007000;
+
+    private static boolean mIsRecordingEnabled;
+
+    private AudioManager mAudioManager;
+
+    private MediaAudio(Context context) {
+        Log.i(TAG, "initialization");
+
+        mContext = context;
+        mMediaDevices = new ConcurrentHashMap<String, MediaDevice>();
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+            Objects.requireNonNull(mAudioManager,
+                               "AudioManager cannot be null when A2dpService starts");
+
+        mAdapterService = AdapterService.getAdapterService();
+        mActiveDeviceManager = ActiveDeviceManagerService.get();
+
+        mBapBroadcastManager = new BapBroadcastManager();
+
+        IntentFilter codecFilter = new IntentFilter();
+        codecFilter.addAction(ACTION_UPDATE_CODEC_CONFIG);
+        mCodecConfigReceiver = new LeCodecConfig();
+        context.registerReceiver(mCodecConfigReceiver, codecFilter);
+
+        IntentFilter qosFilter = new IntentFilter();
+        qosFilter.addAction(ACTION_UPDATE_QOS_CONFIG);
+        mQosConfigReceiver = new QosConfigReceiver();
+        context.registerReceiver(mQosConfigReceiver, qosFilter);
+
+        mIsRecordingEnabled =
+            SystemProperties.getBoolean("persist.vendor.service.bt.recording_supported", false);
+
+        //2 Setup Codec Config here
+    }
+
+    public static MediaAudio init(Context context) {
+        if(sMediaAudio == null) {
+            sMediaAudio = new MediaAudio(context);
+            MediaAudioIntf.init(sMediaAudio);
+        }
+        return sMediaAudio;
+    }
+
+    public static MediaAudio get() {
+        return sMediaAudio;
+    }
+
+    public boolean connect(BluetoothDevice device) {
+        return connect (device, false, false);
+    }
+
+    public boolean connect(BluetoothDevice device, Boolean allProfile) {
+        return connect (device, allProfile, false);
+    }
+
+    public boolean autoConnect(BluetoothDevice device) {
+        Log.e(TAG, "autoConnect: " + device);
+        return connect(device, false, true);
+    }
+
+    private boolean connect(BluetoothDevice device, boolean allProfile, boolean autoConnect) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+
+        Log.e(TAG, "connect: " + device + " allProfile: " + allProfile +
+                                          " autoConnect: " + autoConnect);
+        if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+            Log.e(TAG, "Cannot connect to " + device + " : CONNECTION_POLICY_FORBIDDEN");
+            return false;
+        }
+
+        DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+        if (dMap == null)
+            return false;
+
+        int peer_supported_profiles = dMap.getAllSupportedProfile(device);
+        boolean is_peer_support_recording =
+                ((peer_supported_profiles & ApmConst.AudioProfiles.BAP_RECORDING) != 0);
+        int profileID = dMap.getSupportedProfile(device, ApmConst.AudioFeatures.MEDIA_AUDIO);
+        if (profileID == ApmConst.AudioProfiles.NONE) {
+            Log.e(TAG, "Can Not connect to " + device + ". Device does not support media service.");
+            return false;
+        }
+
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+        if(mMediaDevice == null) {
+            if(mMediaDevices.size() >= MAX_DEVICES)
+                return false;
+            mMediaDevice = new MediaDevice(device, profileID);
+            mMediaDevices.put(device.getAddress(), mMediaDevice);
+        } else if(mMediaDevice.deviceConnStatus != BluetoothProfile.STATE_DISCONNECTED) {
+            Log.i(TAG, "Device already connected");
+            return false;
+        }
+
+        if((ApmConst.AudioProfiles.A2DP & profileID) == ApmConst.AudioProfiles.A2DP) {
+            A2dpService service = A2dpService.getA2dpService();
+            if(service != null) {
+                service.connectA2dp(device);
+            }
+        }
+
+        BluetoothDevice groupDevice = device;
+        StreamAudioService mStreamService = StreamAudioService.getStreamAudioService();
+
+        if(mStreamService != null &&
+                (ApmConst.AudioProfiles.BAP_MEDIA & profileID) == ApmConst.AudioProfiles.BAP_MEDIA) {
+            int defaultMediaProfile = ApmConst.AudioProfiles.BAP_MEDIA;
+
+            /* handle common conect of call and media audio */
+            if(autoConnect) {
+                groupDevice = mStreamService.getDeviceGroup(device);
+                Log.i(TAG, "Auto Connect Request. Connecting group: " + groupDevice);
+            }
+
+            /*int defaultMusicProfile = dMap.getProfile(device, ApmConst.AudioFeatures.MEDIA_AUDIO);
+            if((ApmConst.AudioProfiles.A2DP & defaultMusicProfile) == ApmConst.AudioProfiles.A2DP) {
+                Log.i(TAG, "A2DP is default profile for Music, configure BAP for Gaming");
+                defaultMediaProfile = ApmConst.AudioProfiles.BAP_GCP;
+            }*/
+
+            if(mIsRecordingEnabled) {
+                Log.i(TAG, "Add Recording profile to LE connect request");
+                defaultMediaProfile = defaultMediaProfile | ApmConst.AudioProfiles.BAP_RECORDING;
+            }
+
+            if(allProfile) {
+                int callProfileID = dMap.getSupportedProfile(device, ApmConst.AudioFeatures.CALL_AUDIO);
+                if((callProfileID & ApmConst.AudioProfiles.BAP_CALL) == ApmConst.AudioProfiles.BAP_CALL) {
+                    Log.i(TAG, "Add BAP_CALL to LE connect request");
+                    mStreamService.connectLeStream(groupDevice,
+                        defaultMediaProfile | ApmConst.AudioProfiles.BAP_CALL);
+                } else {
+                    mStreamService.connectLeStream(groupDevice, defaultMediaProfile);
+                }
+            } else {
+                mStreamService.connectLeStream(groupDevice, defaultMediaProfile);
+            }
+        }
+        return true;
+    }
+
+    public boolean disconnect(BluetoothDevice device) {
+        return disconnect(device, false);
+    }
+
+    public boolean disconnect(BluetoothDevice device, Boolean allProfile) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
+        Log.i(TAG, "Disconnect: " + device);
+        MediaDevice mMediaDevice = null;
+
+        if(device == null)
+            return false;
+
+        mMediaDevice = mMediaDevices.get(device.getAddress());
+        if(mMediaDevice == null) {
+            Log.e(TAG, "Ignore: Device " + device + " not present in list");
+            return false;
+        }
+
+        if (mMediaDevice.profileConnStatus[MediaDevice.A2DP_STREAM] != BluetoothProfile.STATE_DISCONNECTED) {
+            A2dpService service = A2dpService.getA2dpService();
+            if(service != null) {
+                service.disconnectA2dp(device);
+            }
+        }
+        if (mMediaDevice.profileConnStatus[MediaDevice.LE_STREAM] != BluetoothProfile.STATE_DISCONNECTED) {
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if(service != null) {
+                DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+                if (!dMap.isProfileConnected(device, ApmConst.AudioProfiles.BAP_CALL)) {
+                    Log.d(TAG,"BAP_CALL not connected");
+                    allProfile = false;
+                }
+                service.disconnectLeStream(device, allProfile, true);
+            }
+        }
+
+        return true;
+    }
+
+    public List<BluetoothDevice> getConnectedDevices() {
+        Log.i(TAG, "getConnectedDevices: ");
+        if(mMediaDevices.size() == 0) {
+            return new ArrayList<>(0);
+        }
+
+        List<BluetoothDevice> connectedDevices = new ArrayList<>();
+        for(MediaDevice mMediaDevice : mMediaDevices.values()) {
+            if(mMediaDevice.deviceConnStatus == BluetoothProfile.STATE_CONNECTED) {
+                connectedDevices.add(mMediaDevice.mDevice);
+            }
+        }
+        return connectedDevices;
+    }
+
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(Integer[] states) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+        Log.i(TAG, "getDevicesMatchingConnectionStates: ");
+        List<BluetoothDevice> devices = new ArrayList<>();
+        if (states == null) {
+            return devices;
+        }
+
+        BluetoothDevice [] bondedDevices = null;
+        bondedDevices = mAdapterService.getBondedDevices();
+        if(bondedDevices == null) {
+            return devices;
+        }
+
+        for (BluetoothDevice device : bondedDevices) {
+            MediaDevice mMediaDevice;
+            int state = BluetoothProfile.STATE_DISCONNECTED;
+
+            DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+            if(dMap == null) {
+                return new ArrayList<>(0);
+            }
+            if(dMap.getProfile(device, ApmConst.AudioFeatures.MEDIA_AUDIO) == ApmConst.AudioProfiles.NONE) {
+                continue;
+            }
+
+            mMediaDevice = mMediaDevices.get(device.getAddress());
+            if(mMediaDevice != null)
+                state = mMediaDevice.deviceConnStatus;
+
+            for(int s: states) {
+                if(s == state) {
+                    devices.add(device);
+                    break;
+                }
+            }
+        }
+        return devices;
+    }
+
+    public int getConnectionState(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+        if(device == null)
+            return BluetoothProfile.STATE_DISCONNECTED;
+
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+        if(mMediaDevice != null)
+            return mMediaDevice.deviceConnStatus;
+
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    public int getPriority(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        if(mAdapterService != null) {
+            return mAdapterService.getDatabase()
+                .getProfileConnectionPolicy(device, BluetoothProfile.A2DP);
+        }
+        return BluetoothProfile.PRIORITY_UNDEFINED;
+    }
+
+    public boolean isA2dpPlaying(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+
+        if(device == null)
+            return false;
+
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+        if(mMediaDevice != null) {
+            Log.i(TAG, "isA2dpPlaying: " + mMediaDevice.streamStatus);
+            return (mMediaDevice.streamStatus == BluetoothA2dp.STATE_PLAYING);
+        }
+
+        return false;
+    }
+
+    public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+
+        Log.i(TAG, "getCodecStatus: for device: " + device);
+        if(device == null)
+            return null;
+
+        if (mBapBroadcastManager.isBapBroadcastActive()) {
+            return mBapBroadcastManager.getCodecStatus();
+        }
+
+        ActiveDeviceManagerService mActiveDeviceManager = ActiveDeviceManagerService.get();
+        int profile = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.MEDIA_AUDIO);
+        if(profile != ApmConst.AudioProfiles.NONE && profile != ApmConst.AudioProfiles.A2DP) {
+            StreamAudioService service = StreamAudioService.getStreamAudioService();
+            if(service != null) {
+                device = service.getDeviceGroup(device);
+            }
+        }
+
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+        Log.i(TAG, "getCodecStatus: for mMediaDevice: " + mMediaDevice);
+        if(mMediaDevice == null)
+            return null;
+
+        Log.i(TAG, "getCodecStatus: " + mMediaDevice.mCodecStatus);
+        return mMediaDevice.mCodecStatus;
+    }
+
+    public void setCodecConfigPreference(BluetoothDevice mDevice,
+                                             BluetoothCodecConfig codecConfig) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        BluetoothDevice device = mDevice;
+
+        Log.i(TAG, "setCodecConfigPreference: " + codecConfig);
+        if(device == null) {
+            if(mActiveDeviceManager != null) {
+                device = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            }
+        }
+        if(device == null)
+            return;
+
+        if (codecConfig == null) {
+            Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
+            return;
+        }
+        long cs4 = codecConfig.getCodecSpecific4();
+
+        if (mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.MEDIA_AUDIO) ==
+                                                      ApmConst.AudioProfiles.BROADCAST_LE) {
+            AcmService mAcmService = AcmService.getAcmService();
+            BluetoothDevice mPrevDevice = mActiveDeviceManager.getQueuedDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            if (mPrevDevice != null && mAcmService != null) {
+                if ((cs4 & AUDIO_RECORDING_MASK) == AUDIO_RECORDING_ON) {
+                    if (mAcmService.getConnectionState(mPrevDevice) == BluetoothProfile.STATE_CONNECTED) {
+                        device = mPrevDevice;
+                        Log.d(TAG,"Recording request, switch device to " + device);
+                    } else {
+                        Log.d(TAG,"Not DUMO device, ignore recording request");
+                        return;
+                    }
+                }
+            } else if ((cs4 & GAMING_MODE_MASK) == GAMING_ON) {
+                Log.d(TAG, "Ignore gaming mode request when broadcast is active");
+                return;
+            }
+        }
+
+        DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+        int supported_prfiles = dMap.getAllSupportedProfile(device);
+
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+        boolean peer_supports_recording =
+                ((supported_prfiles & ApmConst.AudioProfiles.BAP_RECORDING) != 0);
+
+        int profileIndex = mMediaDevice.getProfileIndex(ApmConst.AudioProfiles.BAP_MEDIA);
+        int profileIndexA2dp = mMediaDevice.getProfileIndex(ApmConst.AudioProfiles.A2DP);
+        boolean is_peer_connected_for_recording =
+                              (mMediaDevice.profileConnStatus[profileIndex] ==
+                               BluetoothProfile.STATE_CONNECTED);
+        boolean is_peer_connected_for_a2dp = (mMediaDevice.profileConnStatus[profileIndexA2dp] ==
+                               BluetoothProfile.STATE_CONNECTED);
+        Log.i(TAG, "is_peer_connected_for_recording: " + is_peer_connected_for_recording +
+                   ", is_peer_connected_for_a2dp: " + is_peer_connected_for_a2dp);
+        CallAudio mCallAudio = CallAudio.get();
+        boolean isInCall = mCallAudio != null && mCallAudio.isVoiceOrCallActive();
+        // TODO : check the FM related rx activity
+        if (mActiveDeviceManager != null &&
+            peer_supports_recording && mIsRecordingEnabled &&
+            is_peer_connected_for_recording && is_peer_connected_for_a2dp) {
+            if ((cs4 & AUDIO_RECORDING_MASK) == AUDIO_RECORDING_ON) {
+                if(!isInCall &&
+                   !mActiveDeviceManager.isRecordingActive(device)) {
+                  mActiveDeviceManager.enableRecording(device);
+                }
+
+            } else if ((cs4 & AUDIO_RECORDING_MASK) == AUDIO_RECORDING_OFF) {
+                if(mActiveDeviceManager.isRecordingActive(device)) {
+                  mActiveDeviceManager.disableRecording(device);
+                }
+            }
+        }
+
+        boolean isBapConnected = (mMediaDevice.profileConnStatus[mMediaDevice.LE_STREAM]
+                 == BluetoothProfile.STATE_CONNECTED);
+
+        if(isBapConnected) {
+            long mGamingStatus = (cs4 & GAMING_MODE_MASK);
+            if((mGamingStatus & GAMING_ON) > 0) {
+                Log.w(TAG, "Turning On Gaming Mode");
+                mActiveDeviceManager.enableGaming(device);
+                return;
+            } else if((mGamingStatus & GAMING_OFF) > 0) {
+                Log.w(TAG, "Turning Off Gaming Mode");
+                mActiveDeviceManager.disableGaming(device);
+                return;
+            }
+        }
+
+        int profileID = dMap.getProfile(device, ApmConst.AudioFeatures.MEDIA_AUDIO);
+
+        if(ApmConst.AudioProfiles.A2DP == profileID) {
+            if(codecConfig.getCodecType() ==
+                  BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) {
+              return;
+            }
+            A2dpService service = A2dpService.getA2dpService();
+            if(service != null) {
+                service.setCodecConfigPreferenceA2dp(device, codecConfig);
+                return;
+            }
+        }/* else if(ApmConst.AudioProfiles.BAP == profileID) { // once implemented
+            LeStreamService service = LeStreamService.getLeStreamService();
+            if(service != null) {
+                service.setCodecConfigPreferenceLeStream(device, codecConfig);
+                return;
+            }
+        }*/
+
+    }
+
+    public void enableOptionalCodecs(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        Log.i(TAG, "enableOptionalCodecs: ");
+
+        BluetoothCodecStatus mCodecStatus = null;
+
+        if (device == null) {
+            if(mActiveDeviceManager != null) {
+                device = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            }
+        }
+        if (device == null) {
+            Log.e(TAG, "enableOptionalCodecs: Invalid device");
+            return;
+        }
+
+        if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
+            Log.e(TAG, "enableOptionalCodecs: No optional codecs");
+            return;
+        }
+
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+
+        A2dpService service = A2dpService.getA2dpService();
+        if(service != null) {
+            int profileIndex = mMediaDevice.getProfileIndex(ApmConst.AudioProfiles.A2DP);
+            mCodecStatus = mMediaDevice.mProfileCodecStatus[profileIndex];
+            if(mCodecStatus != null) {
+                service.enableOptionalCodecsA2dp(device, mCodecStatus.getCodecConfig());
+            }
+        }
+        // 2 Should implement common codec handling when
+        //vendor codecs is introduced in LE Audio
+    }
+
+    public void disableOptionalCodecs(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        Log.i(TAG, "disableOptionalCodecs: ");
+        BluetoothCodecStatus mCodecStatus = null;
+        if (device == null) {
+            if(mActiveDeviceManager != null) {
+                device = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            }
+        }
+        if (device == null) {
+            Log.e(TAG, "disableOptionalCodecs: Invalid device");
+            return;
+        }
+
+        if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
+            Log.e(TAG, "disableOptionalCodecs: No optional codecs");
+            return;
+        }
+
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+
+        A2dpService service = A2dpService.getA2dpService();
+        if(service != null) {
+            int profileIndex = mMediaDevice.getProfileIndex(ApmConst.AudioProfiles.A2DP);
+            mCodecStatus = mMediaDevice.mProfileCodecStatus[profileIndex];
+            if(mCodecStatus != null) {
+                service.disableOptionalCodecsA2dp(device, mCodecStatus.getCodecConfig());
+            }
+        }
+        // 2 Should implement common codec handling when
+        //vendor codecs is introduced in LE Audio
+    }
+
+    public int getSupportsOptionalCodecs(BluetoothDevice device) {
+        if(mAdapterService != null)
+            return mAdapterService.getDatabase().getA2dpSupportsOptionalCodecs(device);
+        return BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
+    }
+
+    public int supportsOptionalCodecs(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        if(mAdapterService.isTwsPlusDevice(device)) {
+             Log.w(TAG, "Disable optional codec support for TWS+ device");
+             return BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
+        }
+        return getSupportsOptionalCodecs(device);
+    }
+
+    public int getOptionalCodecsEnabled(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        if(mAdapterService != null)
+            return mAdapterService.getDatabase().getA2dpOptionalCodecsEnabled(device);
+        return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
+    }
+
+    public void setOptionalCodecsEnabled(BluetoothDevice device, Integer value) {
+        Log.i(TAG, "setOptionalCodecsEnabled: " + value);
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
+                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
+                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
+            Log.w(TAG, "Unexpected value passed to setOptionalCodecsEnabled:" + value);
+            return;
+        }
+
+        if(mAdapterService != null)
+            mAdapterService.getDatabase().setA2dpOptionalCodecsEnabled(device, value);
+    }
+
+    public int getConnectionPolicy(BluetoothDevice device) {
+        if(mAdapterService != null)
+            return mAdapterService.getDatabase()
+                .getProfileConnectionPolicy(device, BluetoothProfile.A2DP);
+        return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+    }
+
+    public boolean setConnectionPolicy(BluetoothDevice device, Integer connectionPolicy) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
+                "Need BLUETOOTH_PRIVILEGED permission");
+        if (DBG) {
+            Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
+        }
+        boolean setSuccessfully;
+        setSuccessfully = mAdapterService.getDatabase()
+                .setProfileConnectionPolicy(device, BluetoothProfile.A2DP, connectionPolicy);
+
+        if (setSuccessfully && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            connect(device);
+        } else if (setSuccessfully
+                && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+            disconnect(device);
+        }
+        return setSuccessfully;
+    }
+
+    public boolean setSilenceMode(BluetoothDevice device, Boolean silence) {
+        if (DBG) {
+            Log.d(TAG, "setSilenceMode(" + device + "): " + silence);
+        }
+        BluetoothDevice mActiveDevice = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
+        if (silence && Objects.equals(mActiveDevice, device)) {
+            mActiveDeviceManager.removeActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO, true);
+        } else if (!silence && null ==
+                mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO)) {
+            // Set the device as the active device if currently no active device.
+            mActiveDeviceManager.setActiveDevice(device, ApmConst.AudioFeatures.MEDIA_AUDIO, false);
+        }
+        return true;
+    }
+
+    public void onConnStateChange(BluetoothDevice device, Integer state, Integer profile) {
+        Log.d(TAG, "onConnStateChange: profile: " + profile + " state: " + state + " for device " + device);
+        if(device == null)
+            return;
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+
+        if(mMediaDevice == null) {
+            if(state == BluetoothProfile.STATE_DISCONNECTED)
+                return;
+            if(mMediaDevices.size() >= MAX_DEVICES) {
+                return;
+            }
+            mMediaDevice = new MediaDevice(device, profile, state);
+            mMediaDevices.put(device.getAddress(), mMediaDevice);
+            broadcastConnStateChange(device, BluetoothProfile.STATE_DISCONNECTED, state);
+            return;
+        }
+
+        int profileIndex = mMediaDevice.getProfileIndex(profile);
+        int prevState = mMediaDevice.deviceConnStatus;
+        if(mMediaDevice.profileConnStatus[profileIndex] == state) {
+            Log.w(TAG, "Profile already in state: " + state + ". Return");
+            return;
+        }
+        mMediaDevice.profileConnStatus[profileIndex] = state;
+
+        if(state == BluetoothProfile.STATE_CONNECTED) {
+            DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+            dMap.profileConnectionUpdate(device, ApmConst.AudioFeatures.MEDIA_AUDIO, profile, true);
+            refreshCurrentCodec(device);
+        } else if(state == BluetoothProfile.STATE_DISCONNECTED) {
+            DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+            dMap.profileConnectionUpdate(device, ApmConst.AudioFeatures.MEDIA_AUDIO, profile, false);
+        }
+
+        int otherProfileConnectionState = mMediaDevice.profileConnStatus[(profileIndex+1)%2];
+        Log.w(TAG, " otherProfileConnectionState: " + otherProfileConnectionState);
+
+        switch(otherProfileConnectionState) {
+        /*Send Broadcast based on state of other profile*/
+            case BluetoothProfile.STATE_DISCONNECTED:
+                broadcastConnStateChange(device, prevState, state);
+                mMediaDevice.deviceConnStatus = state;
+                break;
+            case BluetoothProfile.STATE_CONNECTING:
+                if(state == BluetoothProfile.STATE_CONNECTED) {
+                    broadcastConnStateChange(device, prevState, state);
+                    mMediaDevice.deviceConnStatus = state;
+                }
+                break;
+            case BluetoothProfile.STATE_DISCONNECTING:
+                if(state == BluetoothProfile.STATE_CONNECTING ||
+                        state == BluetoothProfile.STATE_CONNECTED) {
+                    broadcastConnStateChange(device, prevState, state);
+                    mMediaDevice.deviceConnStatus = state;
+                }
+                break;
+            case BluetoothProfile.STATE_CONNECTED:
+                ActiveDeviceManagerService mActiveDeviceManager =
+                        ActiveDeviceManagerService.get();
+                if(mActiveDeviceManager == null) {
+                    break;
+                }
+
+                BluetoothDevice mActiveDevice = mActiveDeviceManager
+                            .getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
+
+                if((state == BluetoothProfile.STATE_CONNECTED) ||
+                            (state == BluetoothProfile.STATE_DISCONNECTED &&
+                            device.equals(mActiveDevice))) {
+                    Log.w(TAG, "onConnStateChange: Trigger Media handoff for Device: " + device);
+                    mActiveDeviceManager.setActiveDevice(device,
+                                              ApmConst.AudioFeatures.MEDIA_AUDIO);
+                }
+                break;
+        }
+
+        if (profileIndex == mMediaDevice.LE_STREAM &&
+             state == BluetoothProfile.STATE_DISCONNECTED) {
+            mMediaDevice.mProfileCodecStatus[profileIndex] = null;
+        }
+    }
+
+    public void onConnStateChange(BluetoothDevice device, int state, int profile, boolean isFirstMember) {
+        Log.w(TAG, "onConnStateChange: state:" + state + " for device " + device + " new group: " + isFirstMember);
+        if((state == BluetoothProfile.STATE_CONNECTED || state == BluetoothProfile.STATE_CONNECTING)
+                        && isFirstMember) {
+            StreamAudioService mStreamAudioService = StreamAudioService.getStreamAudioService();
+            BluetoothDevice groupDevice = mStreamAudioService.getDeviceGroup(device);
+            if(groupDevice != null) {
+                MediaDevice mMediaDevice = mMediaDevices.get(groupDevice.getAddress());
+                if(mMediaDevice == null) {
+                    mMediaDevice = new MediaDevice(groupDevice, profile, BluetoothProfile.STATE_CONNECTED);
+                    mMediaDevices.put(groupDevice.getAddress(), mMediaDevice);
+                } else {
+                    int profileIndex = mMediaDevice.getProfileIndex(profile);
+                    mMediaDevice.profileConnStatus[profileIndex] = BluetoothProfile.STATE_CONNECTED;
+                    mMediaDevice.deviceConnStatus = state;
+                }
+            }
+        } else if(isFirstMember && (state == BluetoothProfile.STATE_DISCONNECTING ||
+                        state == BluetoothProfile.STATE_DISCONNECTED)) {
+            StreamAudioService mStreamAudioService = StreamAudioService.getStreamAudioService();
+            BluetoothDevice groupDevice = mStreamAudioService.getDeviceGroup(device);
+            MediaDevice mMediaDevice = mMediaDevices.get(groupDevice.getAddress());
+            int prevState = BluetoothProfile.STATE_CONNECTED;
+            Log.w(TAG, "onConnStateChange: mMediaDevice: " + mMediaDevice);
+            if(mMediaDevice != null) {
+                prevState = mMediaDevice.deviceConnStatus;
+                int profileIndex = mMediaDevice.getProfileIndex(profile);
+                mMediaDevice.profileConnStatus[profileIndex] = state;
+                mMediaDevice.deviceConnStatus = state;
+                Log.w(TAG, "onConnStateChange: device: " + groupDevice + " state = " +  mMediaDevice.deviceConnStatus);
+            }
+            ActiveDeviceManager mDeviceManager = AdapterService.getAdapterService().getActiveDeviceManager();
+            mDeviceManager.onDeviceConnStateChange(groupDevice, state, prevState,
+                            ApmConst.AudioFeatures.MEDIA_AUDIO);
+        }
+        onConnStateChange(device, state, profile);
+    }
+
+    public void onStreamStateChange(BluetoothDevice device, Integer streamStatus) {
+        int prevStatus;
+        if(device == null)
+            return;
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+        if(mMediaDevice == null) {
+            return;
+        }
+
+        if(mMediaDevice.streamStatus == streamStatus)
+            return;
+
+        prevStatus = mMediaDevice.streamStatus;
+        mMediaDevice.streamStatus = streamStatus;
+        Log.d(TAG, "onStreamStateChange update to volume manager");
+        VolumeManager mVolumeManager = VolumeManager.get();
+        mVolumeManager.updateStreamState(device, streamStatus, ApmConst.AudioFeatures.MEDIA_AUDIO);
+        broadcastStreamState(device, prevStatus, streamStatus);
+    }
+
+    protected BluetoothCodecStatus convergeCodecConfig(MediaDevice mMediaDevice) {
+        BluetoothCodecStatus A2dpCodecStatus = mMediaDevice.mProfileCodecStatus[MediaDevice.A2DP_STREAM];
+        BluetoothCodecStatus BapCodecStatus = mMediaDevice.mProfileCodecStatus[MediaDevice.LE_STREAM];
+        BluetoothCodecStatus mCodecStatus = null;
+
+        if(A2dpCodecStatus == null ||
+           mMediaDevice.profileConnStatus[MediaDevice.A2DP_STREAM] !=
+                                       BluetoothProfile.STATE_CONNECTED) {
+            return BapCodecStatus;
+        }
+
+        if(BapCodecStatus == null ||
+           mMediaDevice.profileConnStatus[MediaDevice.LE_STREAM] !=
+                                       BluetoothProfile.STATE_CONNECTED) {
+            return A2dpCodecStatus;
+        }
+
+        ActiveDeviceManagerService mActiveDeviceManager = ActiveDeviceManagerService.get();
+        int mActiveProfile = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.MEDIA_AUDIO);
+        int mActiveProfileIndex = mMediaDevice.getProfileIndex(mActiveProfile);
+        BluetoothCodecConfig mCodecConfig = mMediaDevice.mProfileCodecStatus[mActiveProfileIndex].getCodecConfig();
+
+        Log.d(TAG, "convergeCodecConfig: mActiveProfile: "
+                   + mActiveProfile + ", mActiveProfileIndex: " +  mActiveProfileIndex);
+
+        BluetoothCodecConfig[] mCodecsLocalCapabilities = new BluetoothCodecConfig[
+                            A2dpCodecStatus.getCodecsLocalCapabilities().length +
+                            BapCodecStatus.getCodecsLocalCapabilities().length];
+        System.arraycopy(A2dpCodecStatus.getCodecsLocalCapabilities(), 0, mCodecsLocalCapabilities, 0,
+                         A2dpCodecStatus.getCodecsLocalCapabilities().length);
+        System.arraycopy(BapCodecStatus.getCodecsLocalCapabilities(), 0, mCodecsLocalCapabilities,
+                         A2dpCodecStatus.getCodecsLocalCapabilities().length,
+                         BapCodecStatus.getCodecsLocalCapabilities().length);
+
+        BluetoothCodecConfig[] mCodecsSelectableCapabilities = new BluetoothCodecConfig[
+                            A2dpCodecStatus.getCodecsSelectableCapabilities().length +
+                            BapCodecStatus.getCodecsSelectableCapabilities().length];
+        System.arraycopy(A2dpCodecStatus.getCodecsSelectableCapabilities(), 0, mCodecsSelectableCapabilities, 0,
+                         A2dpCodecStatus.getCodecsSelectableCapabilities().length);
+        System.arraycopy(BapCodecStatus.getCodecsSelectableCapabilities(), 0, mCodecsSelectableCapabilities,
+                         A2dpCodecStatus.getCodecsSelectableCapabilities().length,
+                         BapCodecStatus.getCodecsSelectableCapabilities().length);
+
+        mCodecStatus = new BluetoothCodecStatus(mCodecConfig,
+                mCodecsLocalCapabilities, mCodecsSelectableCapabilities);
+        return mCodecStatus;
+    }
+
+    public void onCodecConfigChange(BluetoothDevice device, BluetoothCodecStatus mCodecStatus, Integer profile) {
+        onCodecConfigChange(device, mCodecStatus, profile, true);
+    }
+
+    protected void refreshCurrentCodec(BluetoothDevice device) {
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+        if(mMediaDevice == null) {
+            return;
+        }
+
+        mMediaDevice.mCodecStatus = convergeCodecConfig(mMediaDevice);
+
+        Log.d(TAG, "refreshCurrentCodec: " + device + ", " + mMediaDevice.mCodecStatus);
+
+        broadcastCodecStatus(device, mMediaDevice.mCodecStatus);
+    }
+
+    public void onCodecConfigChange(BluetoothDevice device,
+            BluetoothCodecStatus codecStatus, Integer profile, Boolean updateAudio) {
+        Log.w(TAG, "onCodecConfigChange: for profile:" + profile + " for device "
+                + device + " update audio: " + updateAudio + " with status " + codecStatus);
+        if(device == null || codecStatus == null)
+            return;
+
+        MediaDevice mMediaDevice = mMediaDevices.get(device.getAddress());
+        BluetoothCodecStatus prevCodecStatus = null;
+        //BapBroadcastService mBapBroadcastService = BapBroadcastService.getBapBroadcastService();
+        if (mMediaDevice == null && profile == ApmConst.AudioProfiles.BROADCAST_LE) {
+            Log.d(TAG,"LE Broadcast codec change");
+        } else if(mMediaDevice == null) {
+            Log.e(TAG, "No entry in Device Profile map for device: " + device);
+            return;
+        }
+        if (mMediaDevice != null) {
+            int profileIndex = mMediaDevice.getProfileIndex(profile);
+            Log.d(TAG, "profileIndex: " + profileIndex);
+
+            if(codecStatus.equals(mMediaDevice.mProfileCodecStatus[profileIndex])) {
+                Log.w(TAG, "onCodecConfigChange: Codec already updated for the device and profile");
+                return;
+            }
+
+            mMediaDevice.mProfileCodecStatus[profileIndex] = codecStatus;
+            prevCodecStatus = mMediaDevice.mCodecStatus;
+
+            /* Check the codec status for alternate Media profile for this device */
+            if(mMediaDevice.mProfileCodecStatus[(profileIndex+1)%2] != null) {
+                mMediaDevice.mCodecStatus = convergeCodecConfig(mMediaDevice);
+            } else {
+                mMediaDevice.mCodecStatus = codecStatus;
+            }
+
+            Log.w(TAG, "BroadCasting codecstatus " + mMediaDevice.mCodecStatus +
+                                                              " for device: " + device);
+            broadcastCodecStatus(device, mMediaDevice.mCodecStatus);
+        }
+
+        if(prevCodecStatus != null && mMediaDevice != null) {
+            if (prevCodecStatus.getCodecConfig().equals(mMediaDevice.mCodecStatus.getCodecConfig())) {
+                Log.d(TAG, "Previous and current codec config are same. Return");
+                return;
+            }
+        }
+
+        ActiveDeviceManagerService mActiveDeviceManager = ActiveDeviceManagerService.get();
+        if(mActiveDeviceManager != null && (!mActiveDeviceManager.isStableState(ApmConst.AudioFeatures.MEDIA_AUDIO))) {
+            Log.d(TAG, "SHO under progress. MM Audio will be updated after SHO completes");
+            return;
+        }
+
+        if(device.equals(mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO)) && updateAudio) {
+            VolumeManager mVolumeManager = VolumeManager.get();
+            int currentVolume = mVolumeManager.getActiveVolume(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            if (profile == ApmConst.AudioProfiles.BROADCAST_LE)
+                currentVolume = 15;
+            if (mAudioManager != null) {
+                BluetoothDevice groupDevice = device;
+                if(profile == ApmConst.AudioProfiles.BAP_MEDIA) {
+                    StreamAudioService mStreamAudioService = StreamAudioService.getStreamAudioService();
+                    groupDevice = mStreamAudioService.getDeviceGroup(device);
+                }
+                Log.d(TAG, "onCodecConfigChange Calling handleBluetoothA2dpActiveDeviceChange");
+                mAudioManager.handleBluetoothA2dpActiveDeviceChange(groupDevice,
+                        BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
+                        true, currentVolume);
+            }
+        }
+    }
+
+    private void broadcastConnStateChange(BluetoothDevice device, int prevState, int newState) {
+      A2dpService mA2dpService = A2dpService.getA2dpService();
+      if (mA2dpService != null) {
+        Log.d(TAG, "Broadcast Conn State Change: " + prevState + "->" + newState + " for device " + device);
+        Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mA2dpService.sendBroadcast(intent, BLUETOOTH_CONNECT,
+             Utils.getTempAllowlistBroadcastOptions());
+      }
+    }
+
+    private void broadcastStreamState(BluetoothDevice device, int prevStatus, int streamStatus) {
+      A2dpService mA2dpService = A2dpService.getA2dpService();
+      if (mA2dpService != null) {
+        Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevStatus);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, streamStatus);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mA2dpService.sendBroadcast(intent, BLUETOOTH_CONNECT,
+             Utils.getTempAllowlistBroadcastOptions());
+      }
+    }
+
+    private void broadcastCodecStatus (BluetoothDevice device, BluetoothCodecStatus mCodecStatus) {
+      A2dpService mA2dpService = A2dpService.getA2dpService();
+      if (mA2dpService != null) {
+        Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
+        intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, mCodecStatus);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mA2dpService.sendBroadcast(intent, BLUETOOTH_CONNECT,
+             Utils.getTempAllowlistBroadcastOptions());
+      }
+    }
+
+    public boolean isValidCodec (String mCodec) {
+        return supported_codec.contains(mCodec);
+    }
+
+    public AudioManager getAudioManager() {
+        return mAudioManager;
+    }
+
+    class MediaDevice {
+        BluetoothDevice mDevice;
+        int[] profileConnStatus = new int[2];
+        int deviceConnStatus;
+        int streamStatus;
+        private BluetoothCodecStatus mCodecStatus;
+        private BluetoothCodecStatus[] mProfileCodecStatus = new BluetoothCodecStatus[2];
+
+        public static final int A2DP_STREAM = 0;
+        public static final int LE_STREAM = 1;
+
+        MediaDevice(BluetoothDevice device, int profile, int state) {
+            profileConnStatus[A2DP_STREAM] = BluetoothProfile.STATE_DISCONNECTED;
+            profileConnStatus[LE_STREAM] = BluetoothProfile.STATE_DISCONNECTED;
+            mDevice = device;
+            if((profile & ApmConst.AudioProfiles.A2DP) != ApmConst.AudioProfiles.NONE) {
+                profileConnStatus[A2DP_STREAM] = state;
+            }
+            if((profile & (ApmConst.AudioProfiles.TMAP_MEDIA | ApmConst.AudioProfiles.BAP_MEDIA)) !=
+                    ApmConst.AudioProfiles.NONE) {
+                profileConnStatus[LE_STREAM] = state;
+            }
+            deviceConnStatus = state;
+            streamStatus = BluetoothA2dp.STATE_NOT_PLAYING;
+        }
+
+        MediaDevice(BluetoothDevice device, int profile) {
+            this(device, profile, BluetoothProfile.STATE_DISCONNECTED);
+        }
+
+        public int getProfileIndex(int profile) {
+            if(profile == ApmConst.AudioProfiles.A2DP)
+                return A2DP_STREAM;
+            else
+                return LE_STREAM;
+        }
+    }
+
+    private class LeCodecConfig extends BroadcastReceiver {
+        /*am broadcast -a qti.intent.bluetooth.action.UPDATE_CODEC_CONFIG --es
+            qti.bluetooth.extra.CODEC_ID "LC3" --es qti.bluetooth.extra.CODEC_CONFIG "<ID>"*/
+
+        ArrayList <String> supported_codec_config = new ArrayList<String>( List.of(
+        /* config ID      Sampling Freq    Octets/Frame  */
+            "8_1",  /*          8               26       */
+            "8_2",  /*          8               30       */
+            "16_1", /*          16              30       */
+            "16_2", /*          16              40       */
+            "24_1", /*          24              45       */
+            "24_2", /*          24              60       */
+            "32_1", /*          32              60       */
+            "32_2", /*          32              80       */
+            "441_1",/*          44.1            98       */
+            "441_2",/*          44.1            130      */
+            "48_1", /*          48              75       */
+            "48_2", /*          48              100      */
+            "48_3", /*          48              90       */
+            "48_4", /*          48              120      */
+            "48_5", /*          48              117      */
+            "48_6", /*          48              155      */
+            "GCP_TX",
+            "GCP_TX_RX"
+        ));
+
+        Map <String, Integer> channel_mode = Map.of(
+            "NONE", 0,
+            "MONO", 1,
+            "STEREO", 2
+        );
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!ACTION_UPDATE_CODEC_CONFIG.equals(intent.getAction())) {
+                return;
+            }
+            String mCodecId = intent.getStringExtra(CODEC_ID);
+            if(mCodecId == null || !isValidCodec(mCodecId)) {
+                Log.w(TAG, "Invalid Codec " + mCodecId);
+                return;
+            }
+            String mCodecConfig = intent.getStringExtra(CODEC_CONFIG);
+            if(mCodecConfig == null || !isValidCodecConfig(mCodecConfig)) {
+                Log.w(TAG, "Invalid Codec Config " + mCodecConfig);
+                return;
+            }
+
+            int mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE;
+            String chMode = intent.getStringExtra(CHANNEL_MODE);
+            if(chMode != null && channel_mode.containsKey(chMode)) {
+                mChannelMode = channel_mode.get(chMode);
+            }
+
+            ActiveDeviceManagerService mActiveDeviceManager
+                = ActiveDeviceManagerService.get();
+            int profile = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            if(profile == ApmConst.AudioProfiles.BROADCAST_LE) {
+                /*Update Broadcast module here*/
+                mBapBroadcastManager.setCodecPreference(mCodecConfig, mChannelMode);
+            } else if (profile == ApmConst.AudioProfiles.BAP_MEDIA) {
+                BluetoothDevice device = mActiveDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
+                StreamAudioService service = StreamAudioService.getStreamAudioService();
+                service.setCodecConfig(device, mCodecConfig, mChannelMode);
+            }
+            Log.i(TAG, "Codec Config Request: Codec Name: " + mCodecId + " Config ID: "
+                    + mCodecConfig + " mChannelMode: " + mChannelMode + " for profile: " + profile);
+        }
+
+        boolean isValidCodecConfig (String mCodecConfig) {
+            return supported_codec_config.contains(mCodecConfig);
+        }
+    }
+
+    private class QosConfigReceiver extends BroadcastReceiver {
+        /*am broadcast -a qti.intent.bluetooth.action.UPDATE_QOS_CONFIG --es
+            qti.bluetooth.extra.CODEC_ID "LC3" --es qti.bluetooth.extra.QOS_CONFIG "<ID>"*/
+        boolean enable = false;
+
+        ArrayList <String> supported_Qos_config = new ArrayList<String>( List.of(
+            "8_1_1",
+            "8_2_1",
+            "16_1_1",
+            "16_2_1",
+            "24_1_1",
+            "24_2_1",
+            "32_1_1",
+            "32_2_1",
+            "441_1_1",
+            "441_2_1",
+            "48_1_1",
+            "48_2_1",
+            "48_3_1",
+            "48_4_1",
+            "48_5_1",
+            "48_6_1",
+
+            "8_1_2",
+            "8_2_2",
+            "16_1_2",
+            "16_2_2",
+            "24_1_2",
+            "24_2_2",
+            "32_1_2",
+            "32_2_2",
+            "441_1_2",
+            "441_2_2",
+            "48_1_2",
+            "48_2_2",
+            "48_3_2",
+            "48_4_2",
+            "48_5_2",
+            "48_6_2"
+        ));
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if(!enable)
+                return;
+            if (!ACTION_UPDATE_QOS_CONFIG.equals(intent.getAction())) {
+                return;
+            }
+
+            String mCodecId = intent.getStringExtra(CODEC_ID);
+            if(mCodecId == null || !isValidCodec(mCodecId)) {
+                Log.w(TAG, "Invalid Codec " + mCodecId);
+                return;
+            }
+            String mQosConfig = intent.getStringExtra(QOS_CONFIG);
+            if(mQosConfig == null || !isValidQosConfig(mQosConfig)) {
+                Log.w(TAG, "Invalid QosConfig " + mQosConfig);
+                return;
+            }
+
+            ActiveDeviceManagerService mActiveDeviceManager
+                = ActiveDeviceManagerService.get();
+            int profile = mActiveDeviceManager.getActiveProfile(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            if(profile == ApmConst.AudioProfiles.BROADCAST_LE) {
+                /*Update Broadcast module here*/
+            } else if (profile == ApmConst.AudioProfiles.BAP_MEDIA) {
+                /*Update ACM here*/
+            }
+            Log.i(TAG, "New Qos Config ID: " + mQosConfig + " for profile: " + profile);
+        }
+
+        boolean isValidQosConfig(String mQosConfig) {
+            return supported_Qos_config.contains(mQosConfig);
+        }
+    }
+
+    class BapBroadcastManager {
+        void setCodecPreference(String codecConfig, int channelMode) {
+            BroadcastService mBroadcastService = BroadcastService.getBroadcastService();
+            if(mBroadcastService != null) {
+                mBroadcastService.setCodecPreference(codecConfig, channelMode);
+            }
+        }
+
+        BluetoothCodecStatus getCodecStatus() {
+            BroadcastService mBroadcastService = BroadcastService.getBroadcastService();
+            if(mBroadcastService != null) {
+                return mBroadcastService.getCodecStatus();
+            }
+            return null;
+        }
+
+        boolean isBapBroadcastActive() {
+            BroadcastService mBroadcastService = BroadcastService.getBroadcastService();
+            if(mBroadcastService != null) {
+                return mBroadcastService.isBroadcastActive();
+            }
+            return false;
+        }
+    }
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/MediaControlManager.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/MediaControlManager.java
new file mode 100644
index 0000000..1f197cb
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/MediaControlManager.java
@@ -0,0 +1,211 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.apm;
+
+import android.os.Binder;
+import android.os.HandlerThread;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.AudioAttributes;
+import android.media.AudioPlaybackConfiguration;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+import android.media.session.MediaSession.QueueItem;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.util.Log;
+import android.util.StatsLog;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.bluetooth.avrcp.MediaController;
+import com.android.bluetooth.apm.StreamAudioService;
+import com.android.bluetooth.mcp.McpService;
+
+public class MediaControlManager {
+    private static final boolean DBG = true;
+    private static final String TAG = "APM: MediaControlManager";
+
+    static MediaControlManager mMediaControlManager = null;
+
+    PlaybackCallback mPlaybackCallbackCb;
+    //MediaControlCallback mMediaControlCallbackCb;
+    BroadcastReceiver mMediaControlReceiver;
+    private static Context mContext;
+    //private AudioManager mAudioManager;
+    private Handler mHandler;
+    private McpService mMcpService;
+    public static final String MusicPlayerControlServiceName = "com.android.bluetooth.mcp.McpService";
+    public static final int MUSIC_PLAYER_CONTROL = McpService.MUSIC_PLAYER_CONTROL;
+    private MediaControlManager () {
+        mPlaybackCallbackCb =  new PlaybackCallback();
+        //mMediaControlCallbackCb = new MediaControlCallback();
+        mMediaControlReceiver = new MediaControlReceiver();
+    }
+
+    public static MediaControlManager get() {
+        if(mMediaControlManager == null) {
+            mMediaControlManager = new MediaControlManager();
+        }
+        Log.v(TAG, "get");
+        return mMediaControlManager;
+    }
+
+    public static void make(Context context) {
+        if(mMediaControlManager == null) {
+            mMediaControlManager = new MediaControlManager();
+            mMediaControlManager.init(context);
+            MediaControlManagerIntf.init(mMediaControlManager);
+            Log.v(TAG, "init, New mMediaControlManager instance");
+        }
+    }
+
+    public void init(Context context) {
+        mContext = context;
+
+
+
+        /*mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+
+        HandlerThread thread = new HandlerThread("MediaControlThread");
+        Looper looper = thread.getLooper();
+        mHandler = new Handler(looper);
+        mAudioManager.registerAudioPlaybackCallback(mPlaybackCallbackCb,
+                        mHandler);*/
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        mContext.registerReceiver(mMediaControlReceiver, filter);
+        Log.v(TAG, "init done");
+    }
+
+    private void updateCurrentPlayer (int playerId, int browseId) {
+    }
+
+    void handlePassthroughCmd(int op, int state) {
+    }
+
+    private class PlaybackCallback extends AudioManager.AudioPlaybackCallback {
+        @Override
+        public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
+            super.onPlaybackConfigChanged(configs);
+
+            /*Update Playback config*/
+        }
+    }
+
+
+    public void onMetadataChanged(MediaMetadata metadata) {
+        /*Update Metadata change*/
+        Log.v(TAG, "onMetadataChanged");
+        mMcpService = McpService.getMcpService();
+        if (mMcpService != null) {
+           mMcpService.updateMetaData(metadata);
+        }
+    }
+
+    public synchronized void onPlaybackStateChanged(PlaybackState state) {
+        /*Update Playback State*/
+        Log.v(TAG, "onPlaybackStateChanged");
+        mMcpService = McpService.getMcpService();
+        if (mMcpService != null) {
+           mMcpService.updatePlaybackState(state);
+        }
+
+    }
+
+    public synchronized void onPackageChanged(String packageName) {
+        Log.v(TAG, "onPackageChanged");
+        mMcpService = McpService.getMcpService();
+        boolean removed = false;
+        if (packageName == null)
+            removed = true;
+        if (mMcpService != null) {
+           mMcpService.updatePlayerName(packageName, removed);
+        }
+    }
+    public void onSessionDestroyed(String packageName) {
+        Log.v(TAG, "onSessionDestroyed");
+        mMcpService = McpService.getMcpService();
+        if (mMcpService != null) {
+           mMcpService.updatePlayerName(packageName, true);
+        }
+    }
+
+    public void onQueueChanged(List<MediaSession.QueueItem> queue) {
+
+    }
+
+    private class MediaControlReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String packageName = null;
+            String action = intent.getAction();
+            boolean removed = false;
+            Log.v(TAG, "action " + action);
+            if(action == null)
+                return;
+
+            switch(action) {
+                case Intent.ACTION_PACKAGE_REMOVED:
+                    packageName = intent.getData().getSchemeSpecificPart();
+                    /*handle package removed*/
+                    removed = true;
+                    break;
+                case Intent.ACTION_PACKAGE_ADDED:
+                    packageName = intent.getData().getSchemeSpecificPart();
+                    /*handle package added*/
+                    break;
+                case Intent.ACTION_PACKAGE_CHANGED:
+                    packageName = intent.getData().getSchemeSpecificPart();
+                    /*handle package changed*/
+                    break;
+                default :
+                    break;
+            }
+            mMcpService = McpService.getMcpService();
+            if (mMcpService != null) {
+                mMcpService.updatePlayerName(packageName, removed);
+            }
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/StreamAudioService.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/StreamAudioService.java
new file mode 100644
index 0000000..fc42703
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/StreamAudioService.java
@@ -0,0 +1,382 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.apm;
+
+import static com.android.bluetooth.Utils.enforceBluetoothPermission;
+import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
+
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.IBluetoothVcp;
+
+import android.os.Binder;
+import android.os.HandlerThread;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.acm.AcmService;
+
+public class StreamAudioService extends ProfileService {
+    private static final boolean DBG = true;
+    private static final String TAG = "APM: StreamAudioService:";
+    public static final int LE_AUDIO_UNICAST = 26;
+
+    public static final String CoordinatedAudioServiceName = "com.android.bluetooth.acm.AcmService";
+    public static final int COORDINATED_AUDIO_UNICAST = AcmService.ACM_AUDIO_UNICAST;
+
+    private static StreamAudioService sStreamAudioService;
+    private ActiveDeviceManagerService mActiveDeviceManager;
+    private MediaAudio mMediaAudio;
+    private VolumeManager mVolumeManager;
+    private final Object mVolumeManagerLock = new Object();
+    @Override
+    protected void create() {
+        Log.i(TAG, "create()");
+    }
+
+    private static final int BAP       = 0x01;
+    private static final int GCP       = 0x02;
+    private static final int WMCP      = 0x04;
+    private static final int VMCP      = 0x08;
+    private static final int BAP_CALL  = 0x10;
+
+    private static final int MEDIA_CONTEXT = 1;
+    private static final int VOICE_CONTEXT = 2;
+
+    @Override
+    protected boolean start() {
+        if(sStreamAudioService != null) {
+            Log.i(TAG, "StreamAudioService already started");
+            return true;
+        }
+        Log.i(TAG, "start()");
+
+        ApmConst.setLeAudioEnabled(true);
+        ApmConstIntf.init();
+
+        setStreamAudioService(this);
+
+        mActiveDeviceManager = ActiveDeviceManagerService.get(this);
+        mMediaAudio = MediaAudio.init(this);
+
+        DeviceProfileMap dpm = DeviceProfileMap.getDeviceProfileMapInstance();
+        dpm.init(this);
+        CallAudio mCallAudio = CallAudio.init(this);
+        synchronized (mVolumeManagerLock) {
+            mVolumeManager = VolumeManager.init(this);
+        }
+
+        Log.i(TAG, "start() complete");
+        return true;
+    }
+
+    @Override
+    protected boolean stop() {
+        Log.w(TAG, "stop() called");
+        if (sStreamAudioService == null) {
+            Log.w(TAG, "stop() called before start()");
+            return true;
+        }
+
+        if (mActiveDeviceManager != null) {
+            mActiveDeviceManager.disable();
+            mActiveDeviceManager.cleanup();
+        }
+
+        DeviceProfileMap dMap = DeviceProfileMap.getDeviceProfileMapInstance();
+        dMap.cleanup();
+        return true;
+    }
+
+    @Override
+    protected void cleanup() {
+        Log.i(TAG, "cleanup()");
+        synchronized (mVolumeManagerLock) {
+            mVolumeManager.cleanup();
+            mVolumeManager = null;
+        }
+        setStreamAudioService(null);
+    }
+
+    public boolean connectLeStream(BluetoothDevice device, int profile) {
+        AcmService mAcmService = AcmService.getAcmService();
+        int mContext = getContext(profile);
+
+        if(mContext == 0) {
+            Log.e(TAG, "No valid context for profiles passed");
+            return false;
+        }
+        return mAcmService.connect(device, mContext, getAcmProfileID(profile), MEDIA_CONTEXT);
+        //return mAcmService.connect(device, VOICE_CONTEXT, BAP_CALL, VOICE_CONTEXT);
+        //return mAcmService.connect(device, MEDIA_CONTEXT, BAP|WMCP, MEDIA_CONTEXT);
+    }
+
+    public boolean disconnectLeStream(BluetoothDevice device, boolean callAudio, boolean mediaAudio) {
+        AcmService mAcmService = AcmService.getAcmService();
+        if(callAudio && mediaAudio)
+            return mAcmService.disconnect(device, VOICE_CONTEXT | MEDIA_CONTEXT);
+            //return mAcmService.disconnect(device, VOICE_CONTEXT);
+            //return mAcmService.disconnect(device, MEDIA_CONTEXT);
+        else if(mediaAudio)
+            return mAcmService.disconnect(device, MEDIA_CONTEXT);
+        else if(callAudio)
+            return mAcmService.disconnect(device, VOICE_CONTEXT);
+
+        return false;
+    }
+
+    public boolean startStream(BluetoothDevice device) {
+        AcmService mAcmService = AcmService.getAcmService();
+        return mAcmService.StartStream(device, VOICE_CONTEXT);
+    }
+
+    public boolean stopStream(BluetoothDevice device) {
+        AcmService mAcmService = AcmService.getAcmService();
+        return mAcmService.StopStream(device, VOICE_CONTEXT);
+    }
+
+    public int setActiveDevice(BluetoothDevice device, int profile, boolean playReq) {
+        AcmService mAcmService = AcmService.getAcmService();
+        if (mAcmService == null && device == null) {
+            Log.w(TAG, ": device is null, fake success.");
+            return mActiveDeviceManager.SHO_SUCCESS;
+        }
+
+        if(ApmConst.AudioProfiles.BAP_MEDIA == profile) {
+            return mAcmService.setActiveDevice(device, MEDIA_CONTEXT, BAP, playReq);
+        } else if(ApmConst.AudioProfiles.BAP_GCP == profile){
+            return mAcmService.setActiveDevice(device, MEDIA_CONTEXT, GCP, playReq);
+        } else if(ApmConst.AudioProfiles.BAP_RECORDING == profile){
+            return mAcmService.setActiveDevice(device, MEDIA_CONTEXT, WMCP, playReq);
+        } else {
+            return mAcmService.setActiveDevice(device, VOICE_CONTEXT, BAP_CALL, playReq);
+            //return mAcmService.setActiveDevice(device, MEDIA_CONTEXT, BAP, playReq);
+        }
+    }
+
+    public void setCodecConfig(BluetoothDevice device, String codecID, int channelMode) {
+        AcmService mAcmService = AcmService.getAcmService();
+        mAcmService.ChangeCodecConfigPreference(device, codecID);
+    }
+
+    public BluetoothDevice getDeviceGroup(BluetoothDevice device){
+        AcmService mAcmService = AcmService.getAcmService();
+        return mAcmService.getGroup(device);
+    }
+
+    public void onConnectionStateChange(BluetoothDevice device, int state, int audioType, boolean primeDevice) {
+        MediaAudio mMediaAudio = MediaAudio.get();
+        CallAudio mCallAudio = CallAudio.get();
+        int profile = ApmConst.AudioFeatures.MAX_AUDIO_FEATURES;
+        if(audioType == ApmConst.AudioFeatures.CALL_AUDIO) {
+            mCallAudio.onConnStateChange(device, state, ApmConst.AudioProfiles.BAP_CALL);
+        } else if(audioType == ApmConst.AudioFeatures.MEDIA_AUDIO) {
+            boolean isCsipDevice = (device != null) &&
+                        getDeviceGroup(device).getAddress().contains(ApmConst.groupAddress);
+            if(isCsipDevice)
+                mMediaAudio.onConnStateChange(device, state, ApmConst.AudioProfiles.BAP_MEDIA, primeDevice);
+            else
+                mMediaAudio.onConnStateChange(device, state, ApmConst.AudioProfiles.BAP_MEDIA);
+        }
+    }
+
+    public void onStreamStateChange(BluetoothDevice device, int state, int audioType) {
+        MediaAudio mMediaAudio = MediaAudio.get();
+        CallAudio mCallAudio = CallAudio.get();
+        if(audioType == ApmConst.AudioFeatures.MEDIA_AUDIO)
+            mMediaAudio.onStreamStateChange(device, state);
+        else if(audioType == ApmConst.AudioFeatures.CALL_AUDIO)
+             mCallAudio.onAudioStateChange(device, state);
+    }
+
+    public void onActiveDeviceChange(BluetoothDevice device, int audioType) {
+        if (mActiveDeviceManager != null)
+            mActiveDeviceManager.onActiveDeviceChange(device, audioType);
+    }
+
+    public void onMediaCodecConfigChange(BluetoothDevice device, BluetoothCodecStatus codecStatus, int audioType) {
+        MediaAudio mMediaAudio = MediaAudio.get();
+        mMediaAudio.onCodecConfigChange(device, codecStatus, ApmConst.AudioProfiles.BAP_MEDIA);
+    }
+
+    public void onMediaCodecConfigChange(BluetoothDevice device, BluetoothCodecStatus codecStatus, int audioType, boolean updateAudio) {
+        MediaAudio mMediaAudio = MediaAudio.get();
+        mMediaAudio.onCodecConfigChange(device, codecStatus, ApmConst.AudioProfiles.BAP_MEDIA, updateAudio);
+    }
+
+    public void setCallAudioParam(String param) {
+        CallAudio mCallAudio = CallAudio.get();
+        mCallAudio.setAudioParam(param);
+    }
+
+    public void setCallAudioOn(boolean on) {
+        CallAudio mCallAudio = CallAudio.get();
+        mCallAudio.setBluetoothScoOn(on);
+    }
+
+    public int getVcpConnState(BluetoothDevice device) {
+        synchronized (mVolumeManagerLock) {
+            if (mVolumeManager == null)
+                return BluetoothProfile.STATE_DISCONNECTED;
+            return mVolumeManager.getConnectionState(device);
+        }
+    }
+
+    public int getConnectionMode(BluetoothDevice device) {
+        synchronized (mVolumeManagerLock) {
+            if (mVolumeManager == null)
+                return BluetoothProfile.STATE_DISCONNECTED;
+            return mVolumeManager.getConnectionMode(device);
+        }
+    }
+
+    public void setAbsoluteVolume(BluetoothDevice device, int volume) {
+        synchronized (mVolumeManagerLock) {
+            if (mVolumeManager != null)
+                mVolumeManager.updateBroadcastVolume(device, volume);
+        }
+    }
+
+    public int getAbsoluteVolume(BluetoothDevice device) {
+        synchronized (mVolumeManagerLock) {
+            if (mVolumeManager == null)
+                return 7;
+            return mVolumeManager.getBassVolume(device);
+        }
+    }
+
+    public void setMute(BluetoothDevice device, boolean muteStatus) {
+        synchronized (mVolumeManagerLock) {
+            if (mVolumeManager != null)
+                mVolumeManager.setMute(device, muteStatus);
+        }
+    }
+
+    public boolean isMute(BluetoothDevice device) {
+        synchronized (mVolumeManagerLock) {
+            if (mVolumeManager == null)
+                return false;
+            return mVolumeManager.getMuteStatus(device);
+        }
+    }
+
+    private int getContext(int profileID) {
+        int context = 0;
+        if((DeviceProfileMap.getLeMediaProfiles() & profileID) > 0) {
+            context = (context|MEDIA_CONTEXT);
+        }
+
+        if((DeviceProfileMap.getLeCallProfiles() & profileID) > 0) {
+            context = (context|VOICE_CONTEXT);
+        }
+        return context;
+    }
+
+    private int getAcmProfileID (int ProfileID) {
+        int AcmProfileID = 0;
+        if((ApmConst.AudioProfiles.BAP_MEDIA & ProfileID) == ApmConst.AudioProfiles.BAP_MEDIA)
+            AcmProfileID = BAP;
+        if((ApmConst.AudioProfiles.BAP_CALL & ProfileID) == ApmConst.AudioProfiles.BAP_CALL)
+            AcmProfileID = AcmProfileID | BAP_CALL;
+        if((ApmConst.AudioProfiles.BAP_GCP & ProfileID) == ApmConst.AudioProfiles.BAP_GCP)
+            AcmProfileID = AcmProfileID | GCP;
+        if((ApmConst.AudioProfiles.BAP_RECORDING & ProfileID) == ApmConst.AudioProfiles.BAP_RECORDING)
+            AcmProfileID = AcmProfileID | WMCP;
+        return AcmProfileID;
+    }
+
+    @Override
+    protected IProfileServiceBinder initBinder() {
+        return new LeAudioUnicastBinder(this);
+    }
+
+    private static class LeAudioUnicastBinder extends IBluetoothVcp.Stub implements IProfileServiceBinder {
+
+        StreamAudioService mService;
+        LeAudioUnicastBinder(StreamAudioService service) {
+            mService = service;
+        }
+
+        @Override
+        public void cleanup() {
+        }
+
+        @Override
+        public int getConnectionState(BluetoothDevice device) {
+            if(mService == null)
+                return BluetoothProfile.STATE_DISCONNECTED;
+            return mService.getVcpConnState(device);
+        }
+
+        @Override
+        public int getConnectionMode(BluetoothDevice device) {
+            if(mService != null) {
+                return mService.getConnectionMode(device);
+            }
+            return 0;
+        }
+
+        @Override
+        public void setAbsoluteVolume(BluetoothDevice device, int volume) {
+            if(mService != null) {
+                mService.setAbsoluteVolume(device, volume);
+            }
+        }
+
+        @Override
+        public int getAbsoluteVolume(BluetoothDevice device) {
+            if(mService == null)
+                return 7;
+            return mService.getAbsoluteVolume(device);
+        }
+
+        @Override
+        public void setMute (BluetoothDevice device, boolean enableMute) {
+            if(mService != null) {
+                mService.setMute(device, enableMute);
+            }
+        }
+
+        @Override
+        public boolean isMute(BluetoothDevice device) {
+            if(mService != null) {
+                return mService.isMute(device);
+            }
+            return false;
+        }
+    }
+
+    public static StreamAudioService getStreamAudioService() {
+        return sStreamAudioService;
+    }
+
+    private static synchronized void setStreamAudioService(StreamAudioService instance) {
+        if (DBG) {
+            Log.d(TAG, "setStreamAudioService(): set to: " + instance);
+        }
+        sStreamAudioService = instance;
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/VolumeManager.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/VolumeManager.java
new file mode 100644
index 0000000..a571957
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/apm/VolumeManager.java
@@ -0,0 +1,779 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.apm;
+
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothHeadset;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.a2dp.A2dpService;
+import com.android.bluetooth.avrcp.Avrcp_ext;
+import com.android.bluetooth.acm.AcmService;
+import com.android.bluetooth.bc.BCService;
+import com.android.bluetooth.hfp.HeadsetService;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.List;
+import java.util.Map;
+
+public class VolumeManager {
+    public static final String TAG = "APM: VolumeManager";
+    private static VolumeManager mVolumeManager = null;
+    private DeviceVolume mMedia;
+    private DeviceVolume mCall;
+    private DeviceVolume mBroadcast;
+    private DeviceProfileMap dpm;
+    private MediaAudio mMediaAudio;
+    private CallAudio mCallAudio;
+    private static Context mContext;
+    BroadcastReceiver mVolumeManagerReceiver;
+    Map<String, Integer> AbsVolumeSupport;
+
+    public static final String CALL_VOLUME_MAP = "bluetooth_call_volume_map";
+    public static final String MEDIA_VOLUME_MAP = "bluetooth_media_volume_map";
+    public static final String BROADCAST_VOLUME_MAP = "bluetooth_broadcast_volume_map";
+    public final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
+    public final String ACTION_POWER_OFF = "android.intent.action.QUICKBOOT_POWEROFF";
+
+    private VolumeManager() {
+        mCall = new DeviceVolume(mContext, CALL_VOLUME_MAP);
+        mMedia = new DeviceVolume(mContext, MEDIA_VOLUME_MAP);
+        mBroadcast = new DeviceVolume(mContext, BROADCAST_VOLUME_MAP);
+
+        dpm = DeviceProfileMap.getDeviceProfileMapInstance();
+        mMediaAudio = MediaAudio.get();
+        mCallAudio = CallAudio.get();
+
+        AbsVolumeSupport = new ConcurrentHashMap<String, Integer>();
+
+        mVolumeManagerReceiver = new VolumeManagerReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
+        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        filter.addAction(ACTION_SHUTDOWN);
+        filter.addAction(ACTION_POWER_OFF);
+        filter.addAction(BleBroadcastAudioScanAssistManager.ACTION_BROADCAST_SOURCE_INFO);
+        mContext.registerReceiver(mVolumeManagerReceiver, filter);
+    }
+
+    public static VolumeManager init (Context context) {
+        mContext = context;
+
+        if(mVolumeManager == null) {
+            mVolumeManager = new VolumeManager();
+            VolumeManagerIntf.init(mVolumeManager);
+        }
+
+        return mVolumeManager;
+    }
+
+    public void cleanup() {
+        Log.i(TAG, "cleanup");
+        handleDeviceShutdown();
+        synchronized (mVolumeManager) {
+            mCall = null;
+            mMedia = null;
+            mBroadcast = null;
+            mContext.unregisterReceiver(mVolumeManagerReceiver);
+            mVolumeManagerReceiver = null;
+            AbsVolumeSupport.clear();
+            AbsVolumeSupport = null;
+            mVolumeManager = null;
+        }
+    }
+
+    public static VolumeManager get() {
+        return mVolumeManager;
+    }
+
+    private DeviceVolume VolumeType(int mAudioType) {
+        if(ApmConst.AudioFeatures.CALL_AUDIO == mAudioType) {
+            return mCall;
+        } else if(ApmConst.AudioFeatures.MEDIA_AUDIO == mAudioType) {
+            return mMedia;
+        } else if(ApmConst.AudioFeatures.BROADCAST_AUDIO == mAudioType) {
+            return mBroadcast;
+        }
+        return null;
+    }
+
+    public int getConnectionMode(BluetoothDevice device) {
+        AcmService mAcmService = AcmService.getAcmService();
+        if(mAcmService == null) {
+            return -1;
+        }
+        return mAcmService.getVcpConnMode(device);
+    }
+
+    public void setMediaAbsoluteVolume (Integer volume) {
+        if(mMedia.mDevice == null) {
+            Log.e (TAG, "setMediaAbsoluteVolume: No Device Active for Media. Ignore");
+            return;
+        }
+        mMedia.updateVolume(volume);
+
+        if(ApmConst.AudioProfiles.AVRCP == mMedia.mProfile) {
+            Avrcp_ext mAvrcp = Avrcp_ext.get();
+            if(mAvrcp != null) {
+                Log.i (TAG, "setMediaAbsoluteVolume: Updating new volume to AVRCP: " + volume);
+                mAvrcp.setAbsoluteVolume(volume);
+            }
+        } else if(ApmConst.AudioProfiles.VCP == mMedia.mProfile) {
+            AcmService mAcmService = AcmService.getAcmService();
+            if(mAcmService != null) {
+                Log.i (TAG, "setMediaAbsoluteVolume: Updating new volume to VCP: " + volume);
+                mMedia.updateVolume(volume);
+                mAcmService.setAbsoluteVolume(mMedia.mDevice, volume, ApmConst.AudioFeatures.MEDIA_AUDIO);
+            }
+        }
+    }
+
+    public void updateMediaStreamVolume (Integer volume) {
+        if(mMedia.mDevice == null) {
+            Log.e (TAG, "updateMediaStreamVolume: No Device Active for Media. Ignore");
+            return;
+        }
+
+        if(mMedia.mSupportAbsoluteVolume) {
+            /* Ignore: Will update volume via API call */
+            return;
+        }
+        mMedia.updateVolume(volume);
+    }
+
+    public void updateBroadcastVolume (BluetoothDevice device, int volume) {
+        int callAudioState = mCallAudio.getAudioState(device);
+        boolean isCall = (callAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTING ||
+                 callAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED);
+        if (isCall) {
+            Log.e(TAG, "Call in progress, ignore volume change");
+            return;
+        }
+
+        mBroadcast.updateVolume(device, volume);
+        AcmService mAcmService = AcmService.getAcmService();
+        BluetoothDevice mGroupDevice = mAcmService.getGroup(device);
+        mAcmService.setAbsoluteVolume(mGroupDevice, volume, ApmConst.AudioFeatures.BROADCAST_AUDIO);
+        mBroadcast.updateVolume(mGroupDevice, volume);
+    }
+
+    public void setMute(BluetoothDevice device, boolean muteStatus) {
+        AcmService mAcmService = AcmService.getAcmService();
+        BluetoothDevice mGroupDevice = mAcmService.getGroup(device);
+        mAcmService.setMute(mGroupDevice, muteStatus);
+    }
+
+    public void restoreCallVolume (Integer volume) {
+        if(mCall.mDevice == null) {
+            Log.e (TAG, "restoreCallVolume: No Device Active for Call. Ignore");
+            return;
+        }
+
+        if(ApmConst.AudioProfiles.HFP == mCall.mProfile) {
+            // Ignore restoring call volume for HFP case
+            Log.w (TAG, "restoreCallVolume: Ignore restore call volume for HFP");
+        } else if(ApmConst.AudioProfiles.VCP == mCall.mProfile) {
+            AcmService mAcmService = AcmService.getAcmService();
+            if(mAcmService != null) {
+                Log.i (TAG, "restoreCallVolume: Updating new volume to VCP: " + volume);
+                mCall.updateVolume(volume);
+                mAcmService.setAbsoluteVolume(mCall.mDevice, volume, ApmConst.AudioFeatures.CALL_AUDIO);
+            }
+            // TODO: Restore call volume to MM-Audio also
+        }
+    }
+
+    public void setCallVolume (Intent intent) {
+        if(mCall.mDevice == null) {
+            Log.e (TAG, "setCallVolume: No Device Active for Call. Ignore");
+            return;
+        }
+
+        int volume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
+        if(ApmConst.AudioProfiles.HFP == mCall.mProfile) {
+            Log.i (TAG, "setCallVolume: Updating new volume to HFP: " + volume);
+            HeadsetService headsetService = HeadsetService.getHeadsetService();
+            headsetService.setIntentScoVolume(intent);
+        } else if(ApmConst.AudioProfiles.VCP == mCall.mProfile) {
+            Log.i (TAG, "setCallVolume: mCall volume: " + mCall.mVolume + ", volume: " + volume);
+            // Avoid updating same call volume after remote volume change
+            if (volume == mCall.mVolume) {
+                Log.w (TAG, "setCallVolume: Ignore updating same call volume to remote");
+                return;
+            }
+            AcmService mAcmService = AcmService.getAcmService();
+            if(mAcmService != null) {
+                Log.i (TAG, "setCallVolume: Updating new volume to VCP: " + volume);
+                mCall.updateVolume(volume);
+                mAcmService.setAbsoluteVolume(mCall.mDevice, volume, ApmConst.AudioFeatures.CALL_AUDIO);
+            }
+        }
+    }
+
+    public int getConnectionState(BluetoothDevice device) {
+        AcmService mAcmService = AcmService.getAcmService();
+        return mAcmService.getVcpConnState(device);
+    }
+
+    public void onConnStateChange(BluetoothDevice device, Integer state, Integer profile) {
+        Log.d (TAG, "onConnStateChange: state: " + state + " Profile: " + profile);
+        if (device == null) {
+            Log.e (TAG, "onConnStateChange: device is null. Ignore");
+            return;
+        }
+
+        AcmService mAcmService = AcmService.getAcmService();
+        BluetoothDevice mGroupDevice;
+        if(mAcmService != null) {
+            mGroupDevice = mAcmService.getGroup(device);
+        } else {
+            mGroupDevice = device;
+        }
+
+        if (mGroupDevice.equals(mMedia.mDevice)) {
+            mMedia.mProfile =
+                    dpm.getProfile(mGroupDevice, ApmConst.AudioFeatures.MEDIA_VOLUME_CONTROL);
+        }
+        if (mGroupDevice.equals(mCall.mDevice)) {
+            mCall.mProfile =
+                    dpm.getProfile(mGroupDevice, ApmConst.AudioFeatures.CALL_VOLUME_CONTROL);
+        }
+
+        if (state == BluetoothProfile.STATE_CONNECTED) {
+            int audioType = getActiveAudioType(device);
+            if (ApmConst.AudioFeatures.MEDIA_AUDIO == audioType && mMedia.mProfile == profile) {
+                Log.d (TAG, "onConnStateChange: Media is streaming or active, update media volume");
+                setMediaAbsoluteVolume(mMedia.mVolume);
+            } else if (ApmConst.AudioFeatures.CALL_AUDIO == audioType &&
+                    mCall.mProfile == profile) {
+                Log.d (TAG, "onConnStateChange: Call is streaming, update call volume");
+                restoreCallVolume(mCall.mVolume);
+            } else if (ApmConst.AudioFeatures.BROADCAST_AUDIO == audioType) {
+                Log.d (TAG, "onConnStateChange: Broadcast is streaming, update broadcast volume");
+                updateBroadcastVolume(device, getBassVolume(device));
+            }
+        }
+    }
+
+    public void onVolumeChange(Integer volume, Integer audioType, Boolean showUI) {
+        int flag = showUI ? AudioManager.FLAG_SHOW_UI : 0;
+        if(audioType == ApmConst.AudioFeatures.CALL_AUDIO){
+            mCall.updateVolume(volume);
+            mCallAudio.getAudioManager().setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO,
+                    volume, flag);
+        } else if(audioType == ApmConst.AudioFeatures.MEDIA_AUDIO) {
+            mMedia.updateVolume(volume);
+            mMediaAudio.getAudioManager().setStreamVolume(AudioManager.STREAM_MUSIC, volume,
+                    flag | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
+        }
+    }
+
+    public void onVolumeChange(BluetoothDevice device, Integer volume, Integer audioType) {
+        if ((VolumeType(audioType) == mCall && device.equals(mCall.mDevice)) ||
+            (VolumeType(audioType) == mMedia && device.equals(mMedia.mDevice))) {
+            onVolumeChange(volume, audioType, true);
+        } else {
+            mBroadcast.updateVolume(device, volume);
+        }
+    }
+
+    public void onMuteStatusChange(BluetoothDevice device, boolean isMute, int audioType) {
+    }
+
+
+    public void onActiveDeviceChange(BluetoothDevice device, int audioType) {
+        if(device == null) {
+            synchronized(mVolumeManager) {
+                if(VolumeType(audioType) != null)
+                    VolumeType(audioType).reset();
+            }
+        } else {
+            int mProfile = dpm.getProfile(device, audioType == ApmConst.AudioFeatures.CALL_AUDIO?
+                    ApmConst.AudioFeatures.CALL_VOLUME_CONTROL:ApmConst.AudioFeatures.MEDIA_VOLUME_CONTROL);
+            DeviceVolume mDeviceVolume = VolumeType(audioType);
+            mDeviceVolume.updateDevice(device, mProfile);
+            Log.i(TAG, "ActiveDeviceChange: device: " + mDeviceVolume.mDevice + ". AudioType: " + audioType);
+            if(mDeviceVolume.equals(mMedia)) {
+                int mAbsVolSupportProfiles = AbsVolumeSupport.getOrDefault(device.getAddress(), 0);
+                boolean isAbsSupported = ((mProfile & mAbsVolSupportProfiles) != 0) ? true : false;
+                Log.i(TAG, "isAbsoluteVolumeSupport:  " + isAbsSupported);
+                mDeviceVolume.mSupportAbsoluteVolume = isAbsSupported;
+                mMediaAudio.getAudioManager().avrcpSupportsAbsoluteVolume (
+                        device.getAddress(), isAbsSupported);
+
+                Log.i(TAG, "ActiveDeviceChange: Profile: " + mProfile + ". New Volume: " + mDeviceVolume.mVolume);
+                if (!isBroadcastAudioSynced(device) ||
+                    (mMediaAudio.isA2dpPlaying(device) && mMediaAudio.getAudioManager().isMusicActive())) {
+                    setMediaAbsoluteVolume(mDeviceVolume.mVolume);
+                }
+            }
+        }
+    }
+
+    public void updateStreamState(BluetoothDevice device, Integer streamState, Integer audioType) {
+        boolean isMusicActive = false;
+        if (device == null) {
+            Log.e (TAG, "updateStreamState: device is null. Ignore");
+            return;
+        }
+        if (audioType == ApmConst.AudioFeatures.MEDIA_AUDIO &&
+            streamState == BluetoothA2dp.STATE_PLAYING) {
+            isMusicActive = mMediaAudio.getAudioManager().isMusicActive();
+        }
+        Log.d(TAG, "updateStreamState, device: " + device + " type: " + audioType
+                + " streamState: " + streamState + " isMusicActive: " + isMusicActive);
+
+        AcmService mAcmService = AcmService.getAcmService();
+        BluetoothDevice mGroupDevice;
+        if(mAcmService != null) {
+            mGroupDevice = mAcmService.getGroup(device);
+        } else {
+            mGroupDevice = device;
+        }
+
+        if ((audioType == ApmConst.AudioFeatures.MEDIA_AUDIO &&
+                streamState == BluetoothA2dp.STATE_NOT_PLAYING) ||
+                (audioType == ApmConst.AudioFeatures.CALL_AUDIO &&
+                streamState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED)) {
+            if (isBroadcastAudioSynced(device)) {
+                handleBroadcastAudioSynced(device);
+            }
+        } else if (audioType == ApmConst.AudioFeatures.MEDIA_AUDIO &&
+                streamState == BluetoothA2dp.STATE_PLAYING && isMusicActive) {
+            if (mGroupDevice.equals(mMedia.mDevice)) {
+                Log.d(TAG, "Restore volume for A2dp streaming");
+                setMediaAbsoluteVolume(mMedia.mVolume);
+            }
+        } else if (audioType == ApmConst.AudioFeatures.CALL_AUDIO &&
+                streamState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+            if (mGroupDevice.equals(mCall.mDevice)) {
+                Log.d(TAG, "Restore volume for call");
+                restoreCallVolume(mCall.mVolume);
+            }
+        }
+    }
+
+    public int getActiveAudioType(BluetoothDevice device) {
+        int callAudioState = mCallAudio.getAudioState(device);
+        boolean isCall = (callAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTING ||
+                 callAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED);
+        int audioType = -1;
+
+        if (device == null) {
+            Log.e (TAG, "getActiveAudioType: device is null. Ignore");
+            return audioType;
+        }
+
+        AcmService mAcmService = AcmService.getAcmService();
+        BluetoothDevice mGroupDevice;
+        if(mAcmService != null) {
+            mGroupDevice = mAcmService.getGroup(device);
+        } else {
+            mGroupDevice = device;
+        }
+
+        if (mMediaAudio.isA2dpPlaying(device) &&
+                mMediaAudio.getAudioManager().isMusicActive()) {
+            if (mGroupDevice.equals(mMedia.mDevice)) {
+                Log.d(TAG, "Active Media audio is streaming");
+                audioType = ApmConst.AudioFeatures.MEDIA_AUDIO;
+            }
+        } else if (isCall) {
+            if (mGroupDevice.equals(mCall.mDevice)) {
+                Log.d(TAG, "Active Call audio is streaming");
+                audioType = ApmConst.AudioFeatures.CALL_AUDIO;
+            }
+        } else if (isBroadcastAudioSynced(device)) {
+            Log.d(TAG, "Broadcast audio is streaming");
+            audioType = ApmConst.AudioFeatures.BROADCAST_AUDIO;
+        } else {
+            Log.d(TAG, "None of audio is streaming");
+            ActiveDeviceManagerService activeDeviceManager =
+                    ActiveDeviceManagerService.get(mContext);
+            BluetoothDevice activeDevice =
+                    activeDeviceManager.getActiveDevice(ApmConst.AudioFeatures.MEDIA_AUDIO);
+            if (mGroupDevice.equals(mMedia.mDevice) && mGroupDevice.equals(activeDevice)) {
+                Log.d(TAG, "Peer is Media active, set for media type by default");
+                audioType = ApmConst.AudioFeatures.MEDIA_AUDIO;
+            } else {
+                Log.d(TAG, "Inactive peer, unknow audio type");
+            }
+        }
+
+        Log.d(TAG, "getActiveAudioType: ret " + audioType);
+        return audioType;
+    }
+
+    /*Should be called by AVRCP and VCP after every connection*/
+    public void setAbsoluteVolumeSupport(BluetoothDevice device, Boolean isSupported,
+            Integer initVol, Integer profile) {
+        setAbsoluteVolumeSupport(device, isSupported, profile);
+    }
+
+    public void setAbsoluteVolumeSupport(BluetoothDevice device, Boolean isSupported,
+            Integer profile) {
+        Log.i(TAG, "setAbsoluteVolumeSupport device " + device + " profile " + profile
+                + " isSupported " + isSupported);
+        if(device == null)
+            return;
+
+        int mProfile = dpm.getProfile(device, ApmConst.AudioFeatures.MEDIA_VOLUME_CONTROL);
+        int mAbsVolSupportProfiles = AbsVolumeSupport.getOrDefault(device.getAddress(), 0);
+        if (isSupported) {
+            mAbsVolSupportProfiles = mAbsVolSupportProfiles | profile;
+        } else {
+            mAbsVolSupportProfiles = mAbsVolSupportProfiles & ~profile;
+        }
+
+        if(device.equals(mMedia.mDevice)) {
+            boolean isAbsSupported = ((mProfile & mAbsVolSupportProfiles) != 0) ? true : false;
+            Log.i(TAG, "Update abs volume support:  " + isAbsSupported);
+            mMedia.mSupportAbsoluteVolume = isAbsSupported;
+            mMediaAudio.getAudioManager().avrcpSupportsAbsoluteVolume (
+                    device.getAddress(), isAbsSupported);
+
+            if(mMedia.mProfile == ApmConst.AudioProfiles.NONE) {
+                mMedia.mProfile = mProfile;
+                Log.i(TAG, "setAbsoluteVolumeSupport: Profile: " + mMedia.mProfile);
+            }
+        }
+        AbsVolumeSupport.put(device.getAddress(), mAbsVolSupportProfiles);
+    }
+
+    public void saveVolume(Integer audioType) {
+        VolumeType(audioType).saveVolume();
+    }
+
+    public int getSavedVolume(BluetoothDevice device, Integer audioType) {
+        return VolumeType(audioType).getSavedVolume(device);
+    }
+
+    public int getActiveVolume(Integer audioType) {
+        return VolumeType(audioType).mVolume;
+    }
+
+    public int getBassVolume(BluetoothDevice device) {
+        AcmService mAcmService = AcmService.getAcmService();
+        BluetoothDevice mGroupDevice = mAcmService.getGroup(device);
+        int volume = mBroadcast.getVolume(mGroupDevice);
+        Log.i(TAG, "getBassVolume: " + device + " volume: " + volume);
+        return volume;
+    }
+
+    public boolean getMuteStatus(BluetoothDevice device) {
+        AcmService mAcmService = AcmService.getAcmService();
+        if(mAcmService == null) {
+            return false;
+        }
+        return mAcmService.isVcpMute(device);
+    }
+
+    boolean isBroadcastAudioSynced(BluetoothDevice device) {
+        BCService mBCService = BCService.getBCService();
+        if (mBCService == null || device == null) return false;
+        List<BleBroadcastSourceInfo> srcInfos =
+                mBCService.getAllBroadcastSourceInformation(device);
+        if (srcInfos == null || srcInfos.size() == 0) {
+            Log.e(TAG, "source Infos not available");
+            return false;
+        }
+
+        for (int i=0; i<srcInfos.size(); i++) {
+            if (srcInfos.get(i).getAudioSyncState() ==
+                    BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED) {
+                Log.d(TAG, "Remote synced audio to broadcast source");
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void handleBroadcastAudioSynced(BluetoothDevice device) {
+        if (device == null) {
+            return;
+        }
+
+        AcmService mAcmService = AcmService.getAcmService();
+        BluetoothDevice mGroupDevice;
+        if(mAcmService != null) {
+            mGroupDevice = mAcmService.getGroup(device);
+        } else {
+            mGroupDevice = device;
+        }
+
+        int callAudioState = mCallAudio.getAudioState(device);
+        boolean isCall = (callAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTING ||
+                callAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED);
+
+        if (mGroupDevice.equals(mMedia.mDevice) && mMediaAudio.isA2dpPlaying(device)) {
+            Log.d (TAG, "Active media device and streaming, not restore broadcast volume");
+        } else if (mGroupDevice.equals(mCall.mDevice) && isCall) {
+            Log.d (TAG, "Active call device and in call, not restore broadcast volume");
+        } else {
+            Log.d (TAG, "Restore broadcast volume while remote synced audio");
+            updateBroadcastVolume(device, getBassVolume(device));
+        }
+    }
+
+    void handleDeviceUnbond(BluetoothDevice device) {
+        if(device == mCall.mDevice) {
+            mCall.reset();
+        }
+        if(device == mMedia.mDevice) {
+            mMedia.reset();
+        }
+
+        mCall.removeDevice(device);
+        mMedia.removeDevice(device);
+        mBroadcast.removeDevice(device);
+    }
+
+    void handleDeviceShutdown() {
+        Log.i(TAG, "handleDeviceShutdown Save Volume start");
+        if(mCall.mDevice != null) {
+            mCall.saveVolume();
+            mCall.reset();
+        }
+        if(mMedia.mDevice != null) {
+            mMedia.saveVolume();
+            mMedia.reset();
+        }
+        mBroadcast.saveVolume();
+        Log.i(TAG, "handleDeviceShutdown Save Volume end");
+    }
+
+    class DeviceVolume {
+        BluetoothDevice mDevice;
+        int mVolume;
+        int mProfile;
+        boolean mSupportAbsoluteVolume;
+        Map<String, Integer> mBassVolMap;
+
+        Context mContext;
+        private String mAudioTypeStr;
+        public static final int SAFE_VOL = 7;
+        public String mVolumeMap;
+
+        DeviceVolume(Context context, String map) {
+            this.reset();
+            mContext = context;
+            mVolumeMap = map;
+            mSupportAbsoluteVolume = false;
+
+            if(map == "bluetooth_call_volume_map") {
+                 mAudioTypeStr = "Call";
+            }
+            else if(map == "bluetooth_media_volume_map") {
+                mAudioTypeStr = "Media";
+            }
+            else {
+                mAudioTypeStr = "Broadcast";
+                mBassVolMap = new ConcurrentHashMap<String, Integer>();
+            }
+
+            Map<String, ?> allKeys = getVolumeMap().getAll();
+            SharedPreferences.Editor pref = getVolumeMap().edit();
+            for (Map.Entry<String, ?> entry : allKeys.entrySet()) {
+                String key = entry.getKey();
+                Object value = entry.getValue();
+                BluetoothDevice d = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(key);
+
+                if (value instanceof Integer && d.getBondState() == BluetoothDevice.BOND_BONDED) {
+                    if (mAudioTypeStr.equals("Broadcast")) {
+                        mBassVolMap.put(key, (Integer) value);
+                        Log.w(TAG, "address " + key + " from the broadcast volume map volume :" + value);
+                    }
+                } else {
+                    Log.w(TAG, "Removing " + key + " from the " + mAudioTypeStr + " volume map");
+                    pref.remove(key);
+                }
+            }
+            pref.apply();
+        }
+
+        void updateDevice (BluetoothDevice device, int profile) {
+            mDevice = device;
+            mProfile = profile;
+
+            mVolume = getSavedVolume(device);
+            Log.i (TAG, "New " + mAudioTypeStr + " device: " + mDevice + " Vol: " + mVolume);
+        }
+
+        int getSavedVolume (BluetoothDevice device) {
+            int mSavedVolume;
+            SharedPreferences pref = getVolumeMap();
+            mSavedVolume = pref.getInt(device.getAddress(), SAFE_VOL);
+            return mSavedVolume;
+        }
+
+        void updateVolume (int volume) {
+            mVolume = volume;
+        }
+
+        void updateVolume (BluetoothDevice device, int volume) {
+            if(mAudioTypeStr.equals("Broadcast")) {
+                Log.i(TAG, "updateVolume, device " + device + " volume: " + volume);
+                mBassVolMap.put(device.getAddress(), volume);
+            }
+        }
+        int getVolume(BluetoothDevice device) {
+            if(device == null) {
+                Log.e (TAG, "Null Device passed");
+                return 7;
+            }
+            if(mAudioTypeStr.equals("Broadcast")) {
+                if(mBassVolMap.containsKey(device.getAddress())) {
+                    return mBassVolMap.getOrDefault(device.getAddress(), 7);
+                } else {
+                    int mSavedVolume = getSavedVolume(device);
+                    mBassVolMap.put(device.getAddress(), mSavedVolume);
+                    Log.i(TAG, "get saved volume, device " + device + " volume: " + mSavedVolume);
+                    return mSavedVolume;
+                }
+            }
+            return 7;
+        }
+        private SharedPreferences getVolumeMap() {
+            return mContext.getSharedPreferences(mVolumeMap, Context.MODE_PRIVATE);
+        }
+
+        public void saveVolume() {
+            if(mAudioTypeStr.equals("Broadcast")) {
+                saveBroadcastVolume();
+                return;
+            }
+
+            if(mDevice == null) {
+                Log.e (TAG, "saveVolume: No Device Active for " + mAudioTypeStr + ". Ignore");
+                return;
+            }
+
+            SharedPreferences.Editor pref = getVolumeMap().edit();
+            pref.putInt(mDevice.getAddress(), mVolume);
+            pref.apply();
+            Log.i (TAG, "Saved " + mAudioTypeStr + " Volume: " + mVolume + " for device: " + mDevice);
+        }
+
+        public void saveBroadcastVolume() {
+            SharedPreferences.Editor pref = getVolumeMap().edit();
+            for(Map.Entry<String, Integer> itr : mBassVolMap.entrySet()) {
+                pref.putInt(itr.getKey(), itr.getValue());
+            }
+            pref.apply();
+        }
+
+        public void saveVolume(BluetoothDevice device) {
+            if(device == null) {
+                Log.e (TAG, "Null Device passed");
+                return;
+            }
+            if(mAudioTypeStr.equals("Broadcast")) {
+                int mVol = mBassVolMap.getOrDefault(device.getAddress(), 7);
+                SharedPreferences.Editor pref = getVolumeMap().edit();
+                pref.putInt(device.getAddress(), mVol);
+                pref.apply();
+            }
+        }
+
+        void removeDevice(BluetoothDevice device) {
+            if(mAudioTypeStr.equals("Broadcast")) {
+                Log.i (TAG, "Remove device " + device + " from broadcast volume map ");
+                mBassVolMap.remove(device.getAddress());
+            }
+            SharedPreferences.Editor pref = getVolumeMap().edit();
+            pref.remove(device.getAddress());
+            pref.apply();
+        }
+
+        void reset () {
+            Log.i (TAG, "Reset " + mAudioTypeStr + " Device: " + mDevice);
+            mDevice = null;
+            mVolume = SAFE_VOL;
+            mProfile = ApmConst.AudioProfiles.NONE;
+        }
+    }
+
+    private class VolumeManagerReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if(action == null) 
+                return;
+
+            switch(action) {
+                case AudioManager.VOLUME_CHANGED_ACTION:
+                    int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                    int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
+                    if(streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
+                        setCallVolume(intent);
+                    } else {
+                        updateMediaStreamVolume(volumeValue);
+                    }
+                    break;
+
+                case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
+                    int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                            BluetoothDevice.ERROR);
+                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                    if(device == null)
+                        return;
+
+                    if(state == BluetoothDevice.BOND_NONE) {
+                        handleDeviceUnbond(device);
+                    }
+                    break;
+
+                case BleBroadcastAudioScanAssistManager.ACTION_BROADCAST_SOURCE_INFO:
+                    BleBroadcastSourceInfo sourceInfo = intent.getParcelableExtra(
+                                      BleBroadcastSourceInfo.EXTRA_SOURCE_INFO);
+                    device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+                    if (device == null || sourceInfo == null) {
+                        Log.w (TAG, "Bluetooth Device or Source info is null");
+                        break;
+                    }
+
+                    if (sourceInfo.getAudioSyncState() ==
+                            BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED) {
+                        handleBroadcastAudioSynced(device);
+                    }
+                    break;
+
+                case ACTION_SHUTDOWN:
+                case ACTION_POWER_OFF:
+                    handleDeviceShutdown();
+                    break;
+            }
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BCService.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BCService.java
new file mode 100644
index 0000000..ebcbf81
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BCService.java
@@ -0,0 +1,1691 @@
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.bc;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+
+import android.app.ActivityManager;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.IBluetoothSyncHelper;
+import android.bluetooth.IBluetoothManager;
+import android.bluetooth.IBleBroadcastAudioScanAssistCallback;
+import android.bluetooth.BluetoothSyncHelper;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BleBroadcastSourceChannel;
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+import android.bluetooth.BleBroadcastAudioScanAssistCallback;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.os.ParcelUuid;
+import android.bluetooth.BluetoothUuid;
+import java.util.ArrayList;
+import android.os.ServiceManager;
+
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.PeriodicAdvertisingCallback;
+import android.bluetooth.le.PeriodicAdvertisingManager;
+import android.bluetooth.le.PeriodicAdvertisingReport;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+
+import com.android.bluetooth.BluetoothMetricsProto;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.MetricsLogger;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.bluetooth.btservice.AdapterService;
+//*_CSIP
+//CSIP related imports
+import com.android.bluetooth.groupclient.GroupService;
+import android.bluetooth.BluetoothGroupCallback;
+import android.bluetooth.DeviceGroup;
+//_CSIP*/
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.UUID;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.NoSuchElementException;
+import android.os.SystemProperties;
+
+
+import com.android.internal.util.ArrayUtils;
+/** @hide */
+public class BCService extends ProfileService {
+    private static final boolean DBG = true;
+    private static final String TAG = BCService.class.getSimpleName();
+
+    private static final ParcelUuid CAS_UUID = null;//ParcelUuid.fromString("000089FF-0000-1000-8000-00805F9B34FB");
+
+    public static final String BC_ID = "0000184F-0000-1000-8000-00805F9B34FB";
+    public static final String BS_ID = "00001852-0000-1000-8000-00805F9B34FB";
+    private static BCService sBCService;
+    private static final int MAX_BASS_CLIENT_STATE_MACHINES = 10;
+    private static final int MAX_BASS_CLIENT_CSET_MEMBERS = 10;
+    private final Map<BluetoothDevice, BassClientStateMachine> mStateMachines =
+             new HashMap<>();
+    private HandlerThread mStateMachinesThread;
+    private final Map<Integer, BassCsetManager> mSetManagers =
+             new HashMap<>();
+    private HandlerThread mSetManagerThread;
+
+    private AdapterService mAdapterService;
+
+    private Map<BluetoothDevice, ArrayList<IBleBroadcastAudioScanAssistCallback>> mAppCallbackMap =
+             new HashMap<BluetoothDevice, ArrayList<IBleBroadcastAudioScanAssistCallback>>();
+
+    private BassUtils bassUtils = null;
+    public static final int INVALID_SYNC_HANDLE = -1;
+    public static final int INVALID_ADV_SID = -1;
+    public static final int INVALID_ADV_ADDRESS_TYPE = -1;
+    public static final int INVALID_ADV_INTERVAL = -1;
+    public static final int INVALID_BROADCAST_ID = -1;
+    private Map<BluetoothDevice, BluetoothDevice> mActiveSourceMap;
+
+    //*_CSIP
+    //CSET interfaces
+    private GroupService mSetCoordinator = GroupService.getGroupService();
+    public int mCsipAppId = -1;
+    private int mQueuedOps = 0;
+    //_CSIP*/
+
+    /*Caching the PAresults from Broadcast source*/
+    /*This is stored at service so that each device state machine can access
+    and use it as needed. Once the periodic sync in cancelled, this data will bre
+    removed to ensure stable data won't used*/
+    /*broadcastSrcDevice, syncHandle*/
+    private Map<BluetoothDevice, Integer> mSyncHandleMap;
+    /*syncHandle, parsed BaseData data*/
+    private Map<Integer, BaseData> mSyncHandleVsBaseInfo;
+    /*bcastSrcDevice, corresponding PAResultsMap*/
+    private Map<BluetoothDevice, PAResults> mPAResultsMap;
+    public class PAResults {
+        public BluetoothDevice mDevice;
+        public int mAddressType;
+        public int mAdvSid;
+        public int mSyncHandle;
+        public byte metaDataLength;
+        public byte[] metaData;
+        public int mPAInterval;
+        public int mBroadcastId;
+
+        PAResults(BluetoothDevice device, int addressType,
+                         int syncHandle, int advSid, int paInterval, int broadcastId) {
+            mDevice = device;
+            mAddressType = addressType;
+            mAdvSid = advSid;
+            mSyncHandle = syncHandle;
+            mPAInterval = paInterval;
+            mBroadcastId = broadcastId;
+        }
+
+        public void updateSyncHandle(int syncHandle) {
+            mSyncHandle = syncHandle;
+        }
+
+        public void updateAdvSid(int advSid) {
+            mAdvSid = advSid;
+        }
+
+        public void updateAddressType(int addressType) {
+            mAddressType = addressType;
+        }
+
+        public void updateAdvInterval(int advInterval) {
+            mPAInterval = advInterval;
+        }
+
+        public void updateBroadcastId(int broadcastId) {
+            mBroadcastId = broadcastId;
+        }
+
+        public void print() {
+            log("-- PAResults --");
+            log("mDevice:" + mDevice);
+            log("mAddressType:" + mAddressType);
+            log("mAdvSid:" + mAdvSid);
+            log("mSyncHandle:" + mSyncHandle);
+            log("mPAInterval:" + mPAInterval);
+            log("mBroadcastId:" + mBroadcastId);
+            log("-- END: PAResults --");
+        }
+    };
+
+    public void updatePAResultsMap(BluetoothDevice device, int addressType, int syncHandle, int advSid, int advInterval, int bId) {
+          log("updatePAResultsMap: device: " + device);
+          log("updatePAResultsMap: syncHandle: " + syncHandle);
+          log("updatePAResultsMap: advSid: " + advSid);
+          log("updatePAResultsMap: addressType: " + addressType);
+          log("updatePAResultsMap: advInterval: " + advInterval);
+          log("updatePAResultsMap: broadcastId: " + bId);
+          log("mSyncHandleMap" + mSyncHandleMap);
+          log("mPAResultsMap" + mPAResultsMap);
+          //Cache the SyncHandle
+          if (mSyncHandleMap != null) {
+              Integer i = new Integer(syncHandle);
+              mSyncHandleMap.put(device, i);
+          }
+          if (mPAResultsMap != null) {
+              PAResults paRes = mPAResultsMap.get(device);
+              if (paRes == null) {
+                  log("PAResmap: add >>>");
+                  paRes = new PAResults (device, addressType,
+                              syncHandle, advSid, advInterval, bId);
+                  if (paRes != null) {
+                      paRes.print();
+                      mPAResultsMap.put(device, paRes);
+                  }
+              } else {
+                  if (advSid != INVALID_ADV_SID) {
+                      paRes.updateAdvSid(advSid);
+                  }
+                  if (syncHandle != INVALID_SYNC_HANDLE) {
+                      paRes.updateSyncHandle(syncHandle);
+                  }
+                  if (addressType != INVALID_ADV_ADDRESS_TYPE) {
+                      paRes.updateAddressType(addressType);
+                  }
+                  if (advInterval != INVALID_ADV_INTERVAL) {
+                      paRes.updateAdvInterval(advInterval);
+                  }
+                  if (bId != INVALID_BROADCAST_ID) {
+                      paRes.updateBroadcastId(bId);
+                  }
+                  log("PAResmap: update >>>");
+                  paRes.print();
+                  mPAResultsMap.replace(device, paRes);
+              }
+          }
+          log(">>mPAResultsMap" + mPAResultsMap);
+      }
+
+      public PAResults getPAResults(BluetoothDevice device) {
+          PAResults res = null;
+          if (mPAResultsMap != null) {
+            res = mPAResultsMap.get(device);
+          } else {
+            Log.e(TAG, "getPAResults: mPAResultsMap is null");
+        }
+        return res;
+      }
+      public PAResults clearPAResults(BluetoothDevice device) {
+          PAResults res = null;
+          if (mPAResultsMap != null) {
+            res = mPAResultsMap.remove(device);
+          } else {
+            Log.e(TAG, "getPAResults: mPAResultsMap is null");
+        }
+        return res;
+      }
+
+      public void updateBASE(int syncHandlemap, BaseData base) {
+         if (mSyncHandleVsBaseInfo != null) {
+             log("updateBASE : mSyncHandleVsBaseInfo>>");
+             mSyncHandleVsBaseInfo.put(syncHandlemap, base);
+         } else {
+             Log.e(TAG, "updateBASE: mSyncHandleVsBaseInfo is null");
+         }
+      }
+
+    public BaseData getBASE(int syncHandlemap) {
+        BaseData base = null;
+        if (mSyncHandleVsBaseInfo != null) {
+            log("getBASE : syncHandlemap::" + syncHandlemap);
+            base = mSyncHandleVsBaseInfo.get(syncHandlemap);
+        } else {
+            Log.e(TAG, "getBASE: mSyncHandleVsBaseInfo is null");
+        }
+        log("getBASE returns" + base);
+        return base;
+    }
+
+    public void clearBASE(int syncHandlemap) {
+        if (mSyncHandleVsBaseInfo != null) {
+            log("clearBASE : mSyncHandleVsBaseInfo>>");
+            mSyncHandleVsBaseInfo.remove(syncHandlemap);
+        } else {
+            Log.e(TAG, "updateBASE: mSyncHandleVsBaseInfo is null");
+        }
+    }
+
+    public void setActiveSyncedSource(BluetoothDevice scanDelegator, BluetoothDevice sourceDevice) {
+        log("setActiveSyncedSource: scanDelegator" + scanDelegator + ":: sourceDevice:" + sourceDevice);
+        if (sourceDevice == null) {
+            mActiveSourceMap.remove(scanDelegator);
+        } else {
+            mActiveSourceMap.put(scanDelegator, sourceDevice);
+        }
+    }
+
+    public BluetoothDevice getActiveSyncedSource(BluetoothDevice scanDelegator) {
+        BluetoothDevice currentSource =  mActiveSourceMap.get(scanDelegator);
+        log("getActiveSyncedSource: scanDelegator" + scanDelegator + "returning " + currentSource);
+        return currentSource;
+    }
+
+     @Override
+     protected IProfileServiceBinder initBinder() {
+         return new BluetoothSyncHelperBinder(this);
+     }
+
+     //*_CSIP
+     private BluetoothGroupCallback mBluetoothGroupCallback = new BluetoothGroupCallback() {
+          public void onGroupClientAppRegistered(int status, int appId) {
+              log("onCsipAppRegistered:" + status + "appId: " + appId);
+              if (status == 0) {
+                  mCsipAppId = appId;
+              } else {
+                  Log.e(TAG, "Csip registeration failed, status:" + status);
+              }
+          }
+
+          public void onConnectionStateChanged (int state, BluetoothDevice device) {
+              log("onConnectionStateChanged: Device: " + device + "state: " + state);
+                  //notify the statemachine about CSIP connection
+                  synchronized (mStateMachines) {
+                      BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
+                      Message m = stateMachine.obtainMessage(BassClientStateMachine.CSIP_CONNECTION_STATE_CHANGED);
+                      m.obj = state;
+                      stateMachine.sendMessage(m);
+                  }
+          }
+
+          public void onNewGroupFound (int setId,  BluetoothDevice device, UUID uuid) {     }
+          public void onGroupDiscoveryStatusChanged (int setId, int status, int reason) {    }
+          public void onGroupDeviceFound (int setId, BluetoothDevice device) {    }
+          public void onExclusiveAccessChanged (int setId, int value, int status, List<BluetoothDevice> devices) {
+              log("onLockStatusChanged: setId" + setId + devices + "status:" + status);
+              BassCsetManager setMgr = null;
+              setMgr = getOrCreateCSetManager(setId, null);
+              if (setMgr == null) {
+                      return;
+              }
+              log ("sending Lock status to setId:" + setId);
+              Message m = setMgr.obtainMessage(BassCsetManager.LOCK_STATE_CHANGED);
+              m.obj = devices;
+              m.arg1 = value;
+              setMgr.sendMessage(m);
+          }
+          public void onExclusiveAccessStatusFetched (int setId, int lockStatus) {    }
+          public void onExclusiveAccessAvailable (int setId, BluetoothDevice device) {    }
+     };
+     //_CSIP*/
+
+     @Override
+     protected boolean start() {
+        if (DBG) {
+            Log.d(TAG, "start()");
+        }
+        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+                 "AdapterService cannot be null when BCService starts");
+         mStateMachines.clear();
+         mStateMachinesThread = new HandlerThread("BCService.StateMachines");
+         mStateMachinesThread.start();
+
+         mSetManagers.clear();
+         mSetManagerThread = new HandlerThread("BCService.SetManagers");
+         mSetManagerThread.start();
+
+         setBCService(this);
+         bassUtils = new BassUtils(this);
+         //Saving PSync stuff for future addition
+         mSyncHandleMap = new HashMap<BluetoothDevice, Integer>();
+         mPAResultsMap = new HashMap<BluetoothDevice, PAResults>();
+         mSyncHandleVsBaseInfo = new HashMap<Integer, BaseData>();
+         mActiveSourceMap = new HashMap<BluetoothDevice, BluetoothDevice>();
+
+         //*_CSIP
+         //CSET initialization
+         mSetCoordinator = GroupService.getGroupService();
+         if (mSetCoordinator != null) {
+             mSetCoordinator.registerGroupClientModule(mBluetoothGroupCallback);
+         }
+         //_CSIP*/
+         /*_PACS
+         mPacsClientService = PacsClientService.getPacsClientService();
+         _PACS*/
+
+         ///*_GAP
+         //GAP registeration for Bass UUID notification
+         if (mAdapterService != null) {
+             log("register for BASS UUID notif");
+            ParcelUuid bassUuid = new ParcelUuid(BassClientStateMachine.BASS_UUID);
+            mAdapterService.registerUuidSrvcDisc(bassUuid);
+         }
+         //_GAP*/
+         return true;
+     }
+
+    @Override
+    protected boolean stop() {
+        if (DBG) {
+            Log.d(TAG, "stop()");
+        }
+
+        synchronized (mStateMachines) {
+             for (BassClientStateMachine sm : mStateMachines.values()) {
+                 sm.doQuit();
+                 sm.cleanup();
+             }
+             mStateMachines.clear();
+        }
+
+        if (mStateMachinesThread != null) {
+             mStateMachinesThread.quitSafely();
+             mStateMachinesThread = null;
+        }
+
+        if (mSetManagerThread != null) {
+             mSetManagerThread.quitSafely();
+             mSetManagerThread = null;
+        }
+
+        setBCService(null);
+
+        if (mAppCallbackMap != null) {
+            mAppCallbackMap.clear();
+            mAppCallbackMap = null;
+        }
+
+        if (mSyncHandleMap != null) {
+            mSyncHandleMap.clear();
+            mSyncHandleMap = null;
+        }
+
+        if (mActiveSourceMap != null) {
+            mActiveSourceMap.clear();
+            mActiveSourceMap = null;
+        }
+        //*_CSIP
+        if (mSetCoordinator != null && mCsipAppId != -1) {
+           //mSetCoordinator.unregisterGroupClientModule(mCsipAppId);
+        }
+        //_CSIP*/
+        return true;
+     }
+
+     @Override
+     public boolean onUnbind(Intent intent) {
+        Log.d(TAG, "Need to unregister app");
+        //unregisterApp();
+        return super.onUnbind(intent);
+    }
+
+    /**
+     * Get the BCService instance
+     * @return BCService instance
+     */
+    public static synchronized BCService getBCService() {
+        if (sBCService == null) {
+            Log.w(TAG, "getBCService(): service is NULL");
+            return null;
+        }
+
+        if (!sBCService.isAvailable()) {
+            Log.w(TAG, "getBCService(): service is not available");
+            return null;
+        }
+        return sBCService;
+    }
+
+    public BassUtils getBassUtils() {
+        return bassUtils;
+    }
+
+    public BluetoothDevice getDeviceForSyncHandle(int syncHandle) {
+        BluetoothDevice dev = null;
+        if (mSyncHandleMap != null) {
+            for (Map.Entry<BluetoothDevice, Integer> entry : mSyncHandleMap.entrySet()) {
+                Integer value = entry.getValue();
+                if (value == syncHandle) {
+                    dev = entry.getKey();
+                }
+            }
+        }
+        return dev;
+    }
+
+    private static synchronized void setBCService(BCService instance) {
+        if (DBG) {
+            Log.d(TAG, "setBCService(): set to: " + instance);
+        }
+        sBCService = instance;
+    }
+
+    /**
+     * Connects the bass profile to the passed in device
+     *
+     * @param device is the device with which we will connect the Bass profile
+     * @return true if BAss profile successfully connected, false otherwise
+     */
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) {
+            Log.d(TAG, "connect(): " + device);
+        }
+        if (device == null) {
+            return false;
+        }
+
+        if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN) {
+            return false;
+        }
+        synchronized (mStateMachines) {
+            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
+
+            stateMachine.sendMessage(BassClientStateMachine.CONNECT);
+        }
+        return true;
+    }
+
+  /**
+     * Disconnects Bassclient profile for the passed in device
+     *
+     * @param device is the device with which we want to disconnected the BAss client profile
+     * @return true if Bass client profile successfully disconnected, false otherwise
+     */
+    public boolean disconnect(BluetoothDevice device) {
+
+        if (DBG) {
+            Log.d(TAG, "disconnect(): " + device);
+        }
+        if (device == null) {
+            return false;
+        }
+
+      synchronized (mStateMachines) {
+          BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
+
+          stateMachine.sendMessage(BassClientStateMachine.DISCONNECT);
+        }
+        return true;
+    }
+
+    List<BluetoothDevice> getConnectedDevices() {
+
+        synchronized (mStateMachines) {
+            List<BluetoothDevice> devices = new ArrayList<>();
+            for (BassClientStateMachine sm : mStateMachines.values()) {
+                if (sm.isConnected()) {
+                    devices.add(sm.getDevice());
+                }
+            }
+            log("getConnectedDevices: " + devices);
+            return devices;
+        }
+    }
+
+    /**
+     * Check whether can connect to a peer device.
+     * The check considers a number of factors during the evaluation.
+     *
+     * @param device the peer device to connect to
+     * @return true if connection is allowed, otherwise false
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean okToConnect(BluetoothDevice device) {
+        // Check if this is an incoming connection in Quiet mode.
+        if (mAdapterService.isQuietModeEnabled()) {
+            Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
+            return false;
+        }
+        // Check connection policy and accept or reject the connection.
+        int connectionPolicy = getConnectionPolicy(device);
+        int bondState = mAdapterService.getBondState(device);
+        // Allow this connection only if the device is bonded. Any attempt to connect while
+        // bonding would potentially lead to an unauthorized connection.
+        if (bondState != BluetoothDevice.BOND_BONDED) {
+            Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
+            return false;
+        } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN
+                && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            // Otherwise, reject the connection if connectionPolicy is not valid.
+            Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy);
+            return false;
+        }
+        return true;
+    }
+
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+
+        ArrayList<BluetoothDevice> devices = new ArrayList<>();
+        if (states == null) {
+            return devices;
+        }
+        final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
+        if (bondedDevices == null) {
+            return devices;
+        }
+        synchronized (mStateMachines) {
+            for (BluetoothDevice device : bondedDevices) {
+                final ParcelUuid[] featureUuids = device.getUuids();
+                if (!ArrayUtils.contains(featureUuids, new ParcelUuid(BassClientStateMachine.BASS_UUID))) {
+                    continue;
+                }
+                int connectionState = BluetoothProfile.STATE_DISCONNECTED;
+                BassClientStateMachine sm = getOrCreateStateMachine(device);
+                if (sm != null) {
+                    connectionState = sm.getConnectionState();
+                }
+                for (int state : states) {
+                    if (connectionState == state) {
+                        devices.add(device);
+                        break;
+                    }
+                }
+            }
+            return devices;
+        }
+    }
+
+    public int getConnectionState(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            BassClientStateMachine sm = getOrCreateStateMachine(device);
+            if (sm == null) {
+                log("getConnectionState returns STATE_DISC");
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+            return sm.getConnectionState();
+        }
+    }
+
+    /**
+     * Set the connectionPolicy of the Hearing Aid profile.
+     *
+     * @param device the remote device
+     * @param connectionPolicy the connection policy of the profile
+     * @return true on success, otherwise false
+     */
+    public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+
+        if (DBG) {
+            Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
+        }
+        boolean setSuccessfully;
+        setSuccessfully = mAdapterService.getDatabase()
+                .setProfileConnectionPolicy(device, BluetoothProfile.BC_PROFILE, connectionPolicy);
+        if (setSuccessfully && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            connect(device);
+        } else if (setSuccessfully
+                && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+            disconnect(device);
+        }
+        return setSuccessfully;
+    }
+
+    /**
+     * Get the connection policy of the profile.
+     *
+     * <p> The connection policy can be any of:
+     * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
+     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
+     * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
+     *
+     * @param device Bluetooth device
+     * @return connection policy of the device
+     * @hide
+     */
+    public int getConnectionPolicy(BluetoothDevice device) {
+
+        return mAdapterService.getDatabase()
+                .getProfileConnectionPolicy(device, BluetoothProfile.BC_PROFILE);
+    }
+
+    public void sendBroadcastSourceSelectedCallback(BluetoothDevice device, List<BleBroadcastSourceChannel> bChannels, int status){
+        ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
+        if (cbs == null) {
+            Log.e(TAG, "no App callback for this device" + device);
+            return;
+        }
+        for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
+            try {
+               cb.onBleBroadcastAudioSourceSelected(device, status, bChannels);
+            } catch (RemoteException e)  {
+               Log.e(TAG, "Exception while calling sendBroadcastSourceSelectedCallback");
+            }
+        }
+    }
+
+    public void sendAddBroadcastSourceCallback(BluetoothDevice device, byte srcId, int status){
+        ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
+        if (cbs == null) {
+            Log.e(TAG, "no App callback for this device" + device);
+            return;
+        }
+        for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
+            try {
+               cb.onBleBroadcastAudioSourceAdded(device, srcId, status);
+            } catch (RemoteException e)  {
+               Log.e(TAG, "Exception while calling onBleBroadcastAudioSourceAdded");
+            }
+        }
+    }
+
+    public void sendUpdateBroadcastSourceCallback(BluetoothDevice device, byte sourceId, int status){
+        ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
+        if (cbs == null) {
+            Log.e(TAG, "no App callback for this device" + device);
+            return;
+        }
+
+        for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
+            try {
+                cb.onBleBroadcastAudioSourceUpdated(device, sourceId, status);
+            } catch (RemoteException e)  {
+                Log.e(TAG, "Exception while calling onBleBroadcastAudioSourceUpdated");
+            }
+        }
+    }
+    public void sendRemoveBroadcastSourceCallback(BluetoothDevice device, byte sourceId, int status){
+        ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
+        if (cbs == null) {
+            Log.e(TAG, "no App callback for this device" + device);
+            return;
+        }
+
+        for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
+            try {
+                cb.onBleBroadcastAudioSourceRemoved(device, sourceId, status);
+            } catch (RemoteException e)  {
+                Log.e(TAG, "Exception while calling onBleBroadcastAudioSourceRemoved");
+            }
+        }
+    }
+    public void sendSetBroadcastPINupdatedCallback(BluetoothDevice device, byte sourceId, int status){
+        ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
+        if (cbs == null) {
+            Log.e(TAG, "no App callback for this device" + device);
+            return;
+        }
+
+        for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
+            try {
+                cb.onBleBroadcastPinUpdated(device, sourceId, status);
+            } catch (RemoteException e)  {
+                Log.e(TAG, "Exception while calling onBleBroadcastPinUpdated");
+            }
+        }
+    }
+
+    public void registerAppCallback (BluetoothDevice device, IBleBroadcastAudioScanAssistCallback cb) {
+
+        Log.i(TAG, "registerAppCallback" + device);
+
+        ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
+        if (cbs == null) {
+            Log.i(TAG, "registerAppCallback: entry exists");
+            cbs = new ArrayList<IBleBroadcastAudioScanAssistCallback>();
+        }
+        cbs.add(cb);
+        mAppCallbackMap.put(device, cbs);
+        return;
+    }
+
+    public void unregisterAppCallback (BluetoothDevice device, IBleBroadcastAudioScanAssistCallback cb) {
+
+        Log.i(TAG, "unregisterAppCallback" + device);
+
+        ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
+        if (cbs == null) {
+            Log.i(TAG, "unregisterAppCallback: cb list is null");
+            return;
+        } else {
+           boolean ret = cbs.remove(cb);
+           Log.i(TAG, "unregisterAppCallback: ret value of removal from list:" + ret);
+        }
+        if (cbs.size() != 0) {
+            mAppCallbackMap.replace(device, cbs);
+        } else {
+            Log.i(TAG, "unregisterAppCallback: Remove the cmplete entry");
+            mAppCallbackMap.remove(device);
+        }
+        return;
+    }
+
+    public boolean searchforLeAudioBroadcasters (BluetoothDevice device) {
+
+        Log.i(TAG, "searchforLeAudioBroadcasters on behalf of" + device);
+        ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
+        if (cbs == null) {
+            Log.e(TAG, "no App callback for this device" + device);
+            return false;
+        }
+        boolean ret = false;
+        if (bassUtils != null) {
+            ret = bassUtils.searchforLeAudioBroadcasters(device, cbs);
+        } else {
+            Log.e(TAG, "searchforLeAudioBroadcasters :Null Bass Util Handle" + device);
+            ret = false;
+        }
+        return ret;
+    }
+
+    public boolean stopSearchforLeAudioBroadcasters (BluetoothDevice device) {
+
+        Log.i(TAG, "stopsearchforLeAudioBroadcasters on behalf of" + device);
+        ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
+        if (cbs == null) {
+            Log.e(TAG, "no App callback for this device" + device);
+        }
+        boolean ret = false;
+        if (bassUtils != null) {
+            ret = bassUtils.stopSearchforLeAudioBroadcasters(device, cbs);
+        } else {
+            Log.e(TAG, "stopsearchforLeAudioBroadcasters :Null Bass Util Handle" + device);
+            ret = false;
+        }
+        return ret;
+    }
+
+    public boolean selectBroadcastSource (BluetoothDevice device, ScanResult scanRes, boolean isGroupOp, boolean auto) {
+
+        Log.i(TAG, "selectBroadcastSource for " + device + "isGroupOp:" + isGroupOp);
+        Log.i(TAG, "ScanResult " + scanRes);
+
+        if (scanRes == null) {
+            Log.e(TAG, "selectBroadcastSource: null Scan results");
+            return false;
+        }
+        List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
+        listOfDevices.add(device);
+        if (isRoomForBroadcastSourceAddition(listOfDevices) == false) {
+            sendBroadcastSourceSelectedCallback(device, null,
+                BleBroadcastAudioScanAssistCallback.BASS_STATUS_NO_EMPTY_SLOT);
+            return false;
+        }
+        //dummy BleSourceInfo from scanRes
+        BleBroadcastSourceInfo scanResSI = new BleBroadcastSourceInfo (scanRes.getDevice(),
+                                                            BassClientStateMachine.INVALID_SRC_ID,
+                                                            (byte)scanRes.getAdvertisingSid(),
+                                                            BleBroadcastSourceInfo.BROADCASTER_ID_INVALID,
+                                                            scanRes.getAddressType(),
+                                                            BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_INVALID,
+                                                            BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_INVALID,
+                                                            null,
+                                                            (byte)0,
+                                                            BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED,
+                                                            null,
+                                                            null);
+        if (isValidBroadcastSourceAddition(listOfDevices, scanResSI) == false) {
+            sendBroadcastSourceSelectedCallback(device,
+                null, BleBroadcastAudioScanAssistCallback.BASS_STATUS_DUPLICATE_ADDITION);
+            return false;
+        }
+        startScanOffloadInternal(device, isGroupOp);
+        synchronized (mStateMachines) {
+            BassClientStateMachine sm = getOrCreateStateMachine(device);
+            if (sm == null) {
+                return false;
+            }
+            Message m = sm.obtainMessage(BassClientStateMachine.SELECT_BCAST_SOURCE);
+            m.obj = scanRes;
+            if (auto) {
+                m.arg1 = sm.AUTO;
+            } else {
+                m.arg1 = sm.USER;
+            }
+            if (isGroupOp) {
+                m.arg2 = sm.GROUP_OP;
+            } else {
+                m.arg2 = sm.NON_GROUP_OP;
+            }
+            sm.sendMessage(m);
+        }
+        return true;
+    }
+
+    public synchronized void notifyOperationCompletion(BluetoothDevice device, int pendingOperation) {
+        log("notifyOperationCompletion: " + device + "pendingOperation: " +
+            BassClientStateMachine.messageWhatToString(pendingOperation));
+        //synchronized (mStateMachines) {
+            switch (pendingOperation) {
+                case BassClientStateMachine.START_SCAN_OFFLOAD:
+                case BassClientStateMachine.STOP_SCAN_OFFLOAD:
+                case BassClientStateMachine.ADD_BCAST_SOURCE:
+                case BassClientStateMachine.UPDATE_BCAST_SOURCE:
+                case BassClientStateMachine.REMOVE_BCAST_SOURCE:
+                case BassClientStateMachine.SET_BCAST_CODE:
+                    if (mQueuedOps > 0) {
+                        mQueuedOps = mQueuedOps - 1;
+                    } else {
+                        log("not a queued op, Internal op");
+                        return;
+                    }
+                break;
+                default:
+                     {
+                         log("notifyOperationCompletion: unhandled case");
+                         return;
+                     }
+              }
+        //}
+          if (mQueuedOps == 0) {
+              log("notifyOperationCompletion: all ops are done!");
+              //trigger unlock with last device
+              triggerUnlockforCSet(device);
+          }
+
+    }
+
+    public synchronized boolean startScanOffload (BluetoothDevice masterDevice, List<BluetoothDevice> devices) {
+
+        Log.i(TAG, "startScanOffload for " + devices);
+        for (BluetoothDevice dev : devices) {
+            BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
+            if (stateMachine == null) {
+                continue;
+            }
+            stateMachine.sendMessage(BassClientStateMachine.START_SCAN_OFFLOAD);
+            mQueuedOps = mQueuedOps + 1;
+        }
+        return true;
+    }
+
+    public synchronized boolean stopScanOffload (BluetoothDevice masterDevice, List<BluetoothDevice> devices) {
+
+        Log.i(TAG, "stopScanOffload for " + devices);
+        for (BluetoothDevice dev : devices) {
+            BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
+            if (stateMachine == null) {
+                continue;
+            }
+            stateMachine.sendMessage(BassClientStateMachine.STOP_SCAN_OFFLOAD);
+            mQueuedOps = mQueuedOps + 1;
+        }
+
+        return true;
+    }
+
+    public boolean isLocalBroadcasting() {
+        return bassUtils.isLocalLEAudioBroadcasting();
+    }
+
+    private boolean isValidBroadcastSourceAddition(List<BluetoothDevice> devices,
+                                                BleBroadcastSourceInfo srcInfo) {
+        boolean ret = true;
+
+        //run through all the device, if it is not valid
+        //to even one device to add this source, return failure
+        for (BluetoothDevice dev : devices) {
+            List<BleBroadcastSourceInfo> currentSourceInfos =
+                getAllBroadcastSourceInformation(dev);
+            if (currentSourceInfos == null) {
+                log("currentSourceInfos is null for " + dev);
+                continue;
+            }
+            for (int i=0; i<currentSourceInfos.size(); i++) {
+                if (srcInfo.matches(currentSourceInfos.get(i))) {
+                   ret = false;
+                   Log.e(TAG, "isValidBroadcastSourceAddition: fails for: " + dev + "&srcInfo" + srcInfo);
+                   break;
+                }
+            }
+        }
+
+        log("isValidBroadcastSourceInfo returns: " + ret);
+        return ret;
+    }
+
+    private boolean isRoomForBroadcastSourceAddition(List<BluetoothDevice> devices) {
+        boolean isRoomAvail = false;
+
+        //run through all the device, if it is not valid
+        //to even one device to add this source, return failure
+        for (BluetoothDevice dev : devices) {
+            isRoomAvail = false;
+            List<BleBroadcastSourceInfo> currentSourceInfos =
+                getAllBroadcastSourceInformation(dev);
+            for (int i=0; i<currentSourceInfos.size(); i++) {
+                BleBroadcastSourceInfo srcInfo = currentSourceInfos.get(i);
+                if (srcInfo.isEmptyEntry()) {
+                   isRoomAvail = true;
+                   continue;
+                }
+            }
+            if (isRoomAvail == false) {
+                Log.e(TAG, "isRoomForBroadcastSourceAddition: fails for: " + dev);
+                break;
+            }
+        }
+
+        log("isRoomForBroadcastSourceAddition returns: " + isRoomAvail);
+        return isRoomAvail;
+    }
+
+    public synchronized boolean addBroadcastSource (BluetoothDevice masterDevice, List<BluetoothDevice> devices, BleBroadcastSourceInfo srcInfo
+                                      ) {
+
+        Log.i(TAG, "addBroadcastSource for " + devices +
+                   "SourceInfo " + srcInfo);
+        if (srcInfo == null) {
+            Log.e(TAG, "addBroadcastSource: null SrcInfo");
+            return false;
+        }
+        if (isRoomForBroadcastSourceAddition(devices) == false) {
+            sendAddBroadcastSourceCallback(masterDevice,
+                BassClientStateMachine.INVALID_SRC_ID, BleBroadcastAudioScanAssistCallback.BASS_STATUS_NO_EMPTY_SLOT);
+            triggerUnlockforCSet(masterDevice);
+            return false;
+        }
+
+        if (isValidBroadcastSourceAddition(devices, srcInfo) == false) {
+            sendAddBroadcastSourceCallback(masterDevice,
+                BassClientStateMachine.INVALID_SRC_ID, BleBroadcastAudioScanAssistCallback.BASS_STATUS_DUPLICATE_ADDITION);
+            triggerUnlockforCSet(masterDevice);
+            return false;
+        }
+        for (BluetoothDevice dev : devices) {
+            BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
+            if (stateMachine == null) {
+                Log.w(TAG, "addBroadcastSource: device seem to be not avaiable, proceed");
+                continue;
+            }
+            Message m = stateMachine.obtainMessage(BassClientStateMachine.ADD_BCAST_SOURCE);
+            m.obj = srcInfo;
+            stateMachine.sendMessage(m);
+            mQueuedOps = mQueuedOps + 1;
+        }
+        return true;
+    }
+
+    private byte getSrcIdForCSMember(BluetoothDevice masterDevice, BluetoothDevice memberDevice, byte masterSrcId) {
+        byte targetSrcId = -1;
+        List<BleBroadcastSourceInfo> masterSrcInfos = getAllBroadcastSourceInformation(masterDevice);
+        List<BleBroadcastSourceInfo> memberSrcInfos = getAllBroadcastSourceInformation(memberDevice);
+        if (masterSrcInfos == null || masterSrcInfos.size() == 0 ||
+            memberSrcInfos == null || memberSrcInfos.size() == 0) {
+            Log.e(TAG, "master or member source Infos not available");
+            return targetSrcId;
+        }
+        if (masterDevice.equals(memberDevice)) {
+            log("master: " + masterDevice + "member:memberDevice");
+            return masterSrcId;
+        }
+        BluetoothDevice masterSrcDevice = null;
+        for (int i=0; i<masterSrcInfos.size(); i++) {
+            if (masterSrcInfos.get(i).getSourceId() == masterSrcId) {
+                masterSrcDevice = masterSrcInfos.get(i).getSourceDevice();
+                break;
+            }
+        }
+        if (masterSrcDevice == null) {
+            Log.e(TAG, "No matching SRC Id for the operation in masterDevice");
+            return targetSrcId;
+        }
+
+        //look for this srcAddress in member to retrieve the srcId
+        for (int i=0; i<memberSrcInfos.size(); i++) {
+            if (masterSrcDevice.equals(memberSrcInfos.get(i).getSourceDevice())) {
+                targetSrcId = masterSrcInfos.get(i).getSourceId();
+                break;
+            }
+        }
+        if (targetSrcId == -1) {
+            Log.e(TAG, "No matching SRC Address in the member Src Infos");
+        }
+        return targetSrcId;
+    }
+
+    public synchronized boolean updateBroadcastSource (BluetoothDevice masterDevice, List<BluetoothDevice> devices,
+                                    BleBroadcastSourceInfo srcInfo
+                                 ) {
+
+         int status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
+         Log.i(TAG, "updateBroadcastSource for " + devices +
+                     "masterDevice " + masterDevice +
+                     "SourceInfo " + srcInfo);
+
+         if (srcInfo == null) {
+             Log.e(TAG, "updateBroadcastSource: null SrcInfo");
+             return false;
+         }
+
+         for (BluetoothDevice dev : devices) {
+             if (getSrcIdForCSMember(masterDevice, dev, srcInfo.getSourceId()) == -1) {
+                  if (devices.size() > 1) {
+                      status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_GROUP_OP;
+                  } else {
+                      status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
+                  }
+                  sendRemoveBroadcastSourceCallback(masterDevice, BassClientStateMachine.INVALID_SRC_ID,
+                          status);
+                  triggerUnlockforCSet(masterDevice);
+                  return false;
+             }
+         }
+
+         for (BluetoothDevice dev : devices) {
+             BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
+             if (stateMachine == null) {
+                 Log.w(TAG, "updateBroadcastSource: Device seem to be not avaiable");
+                 continue;
+             }
+             byte targetSrcId = getSrcIdForCSMember(masterDevice, dev, srcInfo.getSourceId());
+             srcInfo.setSourceId(targetSrcId);
+
+             Message m = stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);
+             m.obj = srcInfo;
+             m.arg1 = stateMachine.USER;
+             stateMachine.sendMessage(m);
+             mQueuedOps = mQueuedOps + 1;
+         }
+
+        return true;
+    }
+
+    public synchronized boolean setBroadcastCode  (BluetoothDevice masterDevice, List<BluetoothDevice> devices,
+                                      BleBroadcastSourceInfo srcInfo
+                                    ) {
+
+        int status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
+        Log.i(TAG, "setBroadcastCode for " + devices +
+                   "masterDevice" + masterDevice +
+                   "Broadcast PIN" + srcInfo.getBroadcastCode());
+
+        if (srcInfo == null) {
+            Log.e(TAG, "setBroadcastCode: null SrcInfo");
+            return false;
+        }
+
+        for (BluetoothDevice dev : devices) {
+            if (getSrcIdForCSMember(masterDevice, dev, srcInfo.getSourceId()) == -1) {
+                 if (devices.size() > 1) {
+                     status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_GROUP_OP;
+                 } else {
+                     status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
+                 }
+                 sendRemoveBroadcastSourceCallback(masterDevice, BassClientStateMachine.INVALID_SRC_ID,
+                         status);
+                 triggerUnlockforCSet(masterDevice);
+                 return false;
+            }
+        }
+
+        for (BluetoothDevice dev : devices) {
+            BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
+            if (stateMachine == null) {
+                 Log.w(TAG, "setBroadcastCode: Device seem to be not avaiable");
+                 continue;
+            }
+
+            byte targetSrcId = getSrcIdForCSMember(masterDevice, dev, srcInfo.getSourceId());
+            srcInfo.setSourceId(targetSrcId);
+
+            Message m = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
+            m.obj = srcInfo;
+            m.arg1 = stateMachine.FRESH;
+            stateMachine.sendMessage(m);
+            mQueuedOps = mQueuedOps + 1;
+        }
+
+        return true;
+    }
+
+    public synchronized boolean removeBroadcastSource (BluetoothDevice masterDevice, List<BluetoothDevice> devices,
+                                    byte sourceId
+                                 ) {
+
+        int status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
+        Log.i(TAG,  "removeBroadcastSource for " + devices +
+                   "masterDevice " + masterDevice +
+                    "removeBroadcastSource: sourceId:" + sourceId);
+
+        if (sourceId == BassClientStateMachine.INVALID_SRC_ID) {
+            Log.e(TAG, "removeBroadcastSource: Invalid source Id");
+            return false;
+        }
+
+
+        for (BluetoothDevice dev : devices) {
+            if (getSrcIdForCSMember(masterDevice, dev, sourceId) == -1) {
+                 if (devices.size() > 1) {
+                     status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_GROUP_OP;
+                 } else {
+                     status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
+                 }
+                 sendRemoveBroadcastSourceCallback(masterDevice, BassClientStateMachine.INVALID_SRC_ID,
+                         status);
+                 triggerUnlockforCSet(masterDevice);
+                 return false;
+            }
+        }
+
+        for (BluetoothDevice dev : devices) {
+            BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
+            if (stateMachine == null) {
+                Log.w(TAG, "setBroadcastCode: Device seem to be not avaiable");
+                continue;
+            }
+
+            Message m = stateMachine.obtainMessage(BassClientStateMachine.REMOVE_BCAST_SOURCE);
+            m.arg1 = getSrcIdForCSMember(masterDevice, dev, sourceId);
+            log("removeBroadcastSource: send message to SM " + dev);
+            stateMachine.sendMessage(m);
+            mQueuedOps = mQueuedOps + 1;
+        }
+
+        return true;
+    }
+
+    void triggerUnlockforCSet (BluetoothDevice device) {
+        //get setId
+        int setId = getCsetId(device);
+        BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
+        if (setMgr == null) {
+            Log.e(TAG, "triggerUnlockforCSet: setMgr is NULL");
+            return;
+        }
+        //Sending UnLock to
+        log ("sending Unlock to device:" + device);
+        Message m = setMgr.obtainMessage(BassCsetManager.UNLOCK);
+        setMgr.sendMessage(m);
+    }
+    public List<BleBroadcastSourceInfo> getAllBroadcastSourceInformation (BluetoothDevice device
+                                    ) {
+        Log.i(TAG, "getAllBroadcastSourceInformation for " + device);
+        synchronized (mStateMachines) {
+            BassClientStateMachine sm = getOrCreateStateMachine(device);
+            if (sm == null) {
+                return null;
+            }
+            return sm.getAllBroadcastSourceInformation();
+        }
+    }
+
+    private BassClientStateMachine getOrCreateStateMachine(BluetoothDevice device) {
+        if (device == null) {
+            Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
+            return null;
+        }
+        synchronized (mStateMachines) {
+            BassClientStateMachine sm = mStateMachines.get(device);
+            if (sm != null) {
+                return sm;
+            }
+            // Limit the maximum number of state machines to avoid DoS attack
+            if (mStateMachines.size() >= MAX_BASS_CLIENT_STATE_MACHINES) {
+                Log.e(TAG, "Maximum number of Bassclient state machines reached: "
+                        + MAX_BASS_CLIENT_STATE_MACHINES);
+                return null;
+            }
+            if (DBG) {
+                Log.d(TAG, "Creating a new state machine for " + device);
+            }
+            sm = BassClientStateMachine.make(device, this,
+                    mStateMachinesThread.getLooper());
+            mStateMachines.put(device, sm);
+            return sm;
+        }
+    }
+
+    private BassCsetManager getOrCreateCSetManager(int setId, BluetoothDevice masterDevice) {
+        if (setId == -1) {
+            Log.e(TAG, "getOrCreateCSetManager failed: invalid setId");
+            return null;
+        }
+        synchronized (mSetManagers) {
+            BassCsetManager sm = mSetManagers.get(setId);
+            log("getOrCreateCSetManager: hashmap Entry:" + sm);
+            if (sm != null) {
+                return sm;
+            }
+            // Limit the maximum number of set manager state machines
+            if (mStateMachines.size() >= MAX_BASS_CLIENT_CSET_MEMBERS) {
+                Log.e(TAG, "Maximum number of Bassclient cset members reached: "
+                        + MAX_BASS_CLIENT_CSET_MEMBERS);
+                return null;
+            }
+            if (DBG) {
+                Log.d(TAG, "Creating a new set Manager for " + setId);
+            }
+            sm = BassCsetManager.make(setId, masterDevice, this,
+                    mSetManagerThread.getLooper());
+            mSetManagers.put(setId, sm);
+            return sm;
+        }
+    }
+
+    public boolean isLockSupportAvailable(BluetoothDevice device) {
+        boolean isLockAvail = false;
+        boolean forceNoCsip = SystemProperties.getBoolean("persist.vendor.service.bt.forceNoCsip", false);
+        if (forceNoCsip) {
+            log("forceNoCsip is set");
+            return isLockAvail;
+        }
+        //*_CSIP
+        isLockAvail = mAdapterService.isGroupExclAccessSupport(device);
+        //_CSIP*/
+
+        log("isLockSupportAvailable for:" + device + "returns " + isLockAvail);
+        return isLockAvail;
+    }
+
+    private int getCsetId(BluetoothDevice device) {
+        int setId = 1;
+        //*_CSIP
+        setId = mSetCoordinator.getRemoteDeviceGroupId(device, CAS_UUID);
+        //_CSIP*/
+        log("getCsetId return:" + setId);
+        return setId;
+    }
+    public boolean stopScanOffloadInternal (BluetoothDevice device, boolean isGroupOp) {
+        boolean ret = false;
+        log("stopScanOffloadInternal: device: " + device
+             + "isGroupOp" + isGroupOp);
+        /* Even If the request is for Grouoop, If Lock support not avaiable
+         * for that device, go ahead and treat this as single device operation
+         */
+        if (isGroupOp && isLockSupportAvailable(device) == true) {
+            int setId = getCsetId(device);
+            synchronized (mSetManagers) {
+                BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
+                if (setMgr == null) {
+                    return false;
+                 }
+                 Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_STOP_SCAN_OFFLOAD);
+                 setMgr.sendMessage(m);
+                 //queue req and return true
+                 ret = true;
+            }
+        } else {
+            List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
+            listOfDevices.add(device);
+            ret = stopScanOffload(device, listOfDevices);
+        }
+        return ret;
+    }
+
+    public boolean startScanOffloadInternal (BluetoothDevice device, boolean isGroupOp) {
+        boolean ret = false;
+        log("startScanOffloadInternal: device: " + device
+             + "isGroupOp" + isGroupOp);
+        /* Even If the request is for Grouoop, If Lock support not avaiable
+         * for that device, go ahead and treat this as single device operation
+         */
+        if (isGroupOp&& isLockSupportAvailable(device) == true) {
+            int setId = getCsetId(device);
+            synchronized (mSetManagers) {
+                BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
+                if (setMgr == null) {
+                    return false;
+                 }
+                 Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_START_SCAN_OFFLOAD);
+                 setMgr.sendMessage(m);
+                 //queue req and return true
+                 ret = true;
+            }
+        } else {
+           List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
+           listOfDevices.add(device);
+           ret = startScanOffload(device, listOfDevices);
+        }
+        return ret;
+    }
+
+    public boolean addBroadcastSourceInternal (BluetoothDevice device, BleBroadcastSourceInfo srcInfo,
+                                      boolean isGroupOp) {
+        boolean ret = false;
+        log("addBroadcastSourceInternal: device: " + device
+            + "srcInfo" + srcInfo
+            + "isGroupOp" + isGroupOp);
+        /* Even If the request is for Group, If Lock support not avaiable
+         * for that device, go ahead and treat this as single device operation
+         */
+        if (isGroupOp && isLockSupportAvailable(device) == true) {
+            int setId = getCsetId(device);
+            synchronized (mSetManagers) {
+                BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
+                if (setMgr == null) {
+                    return false;
+                 }
+                 Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_ADD_BCAST_SOURCE);
+                 m.obj = srcInfo;
+                 setMgr.sendMessage(m);
+                 //queue req and return true
+                 ret = true;
+            }
+        } else {
+           List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
+           listOfDevices.add(device);
+           ret = addBroadcastSource(device, listOfDevices, srcInfo);
+        }
+        return ret;
+    }
+
+    public boolean updateBroadcastSourceInternal (BluetoothDevice device, BleBroadcastSourceInfo srcInfo,
+                                                          boolean isGroupOp
+                                      ) {
+        boolean ret = false;
+        log("updateBroadcastSourceInternal: device: " + device
+            + "srcInfo" + srcInfo
+            + "isGroupOp" + isGroupOp);
+        /* Even If the request is for Grouoop, If Lock support not avaiable
+         * for that device, go ahead and treat this as single device operation
+         */
+        if (isGroupOp && isLockSupportAvailable(device) == true) {
+            int setId = getCsetId(device);
+            synchronized (mSetManagers) {
+                BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
+                if (setMgr == null) {
+                    return false;
+                 }
+                 Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_UPDATE_BCAST_SOURCE);
+                 m.obj = srcInfo;
+                 setMgr.sendMessage(m);
+                 //queue req and return true
+                 ret = true;
+            }
+        } else {
+           List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
+           listOfDevices.add(device);
+           ret = updateBroadcastSource(device, listOfDevices, srcInfo);
+        }
+        return ret;
+    }
+
+    protected boolean setBroadcastCodeInternal (BluetoothDevice device, BleBroadcastSourceInfo srcInfo,
+                                                   boolean isGroupOp
+                                      ) {
+        boolean ret = false;
+        log("setBroadcastCodeInternal: device: " + device
+            + "srcInfo" + srcInfo
+            + "isGroupOp" + isGroupOp);
+        /* Even If the request is for Grouoop, If Lock support not avaiable
+         * for that device, go ahead and treat this as single device operation
+         */
+        if (isGroupOp && isLockSupportAvailable(device) == true) {
+            int setId = getCsetId(device);
+            synchronized (mSetManagers) {
+                BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
+                if (setMgr == null) {
+                    return false;
+                 }
+                 Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_SET_BCAST_CODE);
+                 m.obj = srcInfo;
+                 setMgr.sendMessage(m);
+                 //queue req and return true
+                 ret = true;
+            }
+        } else {
+           List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
+           listOfDevices.add(device);
+           ret = setBroadcastCode(device, listOfDevices, srcInfo);
+        }
+        return ret;
+    }
+
+    public boolean removeBroadcastSourceInternal (BluetoothDevice device, byte sourceId, boolean isGroupOp
+                                       ) {
+         boolean ret = false;
+         /* Even If the request is for Grouoop, If Lock support not avaiable
+          * for that device, go ahead and treat this as single device operation
+          */
+         if (isGroupOp && isLockSupportAvailable(device) == true) {
+             int setId = getCsetId(device);
+             synchronized (mSetManagers) {
+                 BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
+                 if (setMgr == null) {
+                     return false;
+                  }
+                  Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_REMOVE_BCAST_SOURCE);
+                  m.arg1 = sourceId;
+                  setMgr.sendMessage(m);
+                  //queue req and return true
+                  ret = true;
+             }
+         } else {
+            List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
+            listOfDevices.add(device);
+            ret = removeBroadcastSource(device, listOfDevices, sourceId);
+         }
+         return ret;
+     }
+
+    static void log(String msg) {
+        if (BassClientStateMachine.BASS_DBG) {
+           Log.d(TAG, msg);
+        }
+    }
+
+    /**
+     * Binder object: must be a static class or memory leak may occur
+     */
+    @VisibleForTesting
+    static class BluetoothSyncHelperBinder extends IBluetoothSyncHelper.Stub
+            implements IProfileServiceBinder {
+        private BCService mService;
+
+        private BCService getService() {
+            if (!Utils.checkCallerIsSystemOrActiveUser(TAG)) {
+                return null;
+            }
+
+            if (mService != null && mService.isAvailable()) {
+                return mService;
+            }
+            return null;
+        }
+
+        BluetoothSyncHelperBinder(BCService svc) {
+            mService = svc;
+        }
+
+        @Override
+        public void cleanup() {
+            mService = null;
+        }
+
+        @Override
+        public boolean connect(BluetoothDevice device) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.connect(device);
+        }
+
+        @Override
+        public boolean disconnect(BluetoothDevice device) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.disconnect(device);
+        }
+
+        @Override
+        public List<BluetoothDevice> getConnectedDevices() {
+            BCService service = getService();
+            if (service == null) {
+                return new ArrayList<>();
+            }
+            return service.getConnectedDevices();
+        }
+
+        @Override
+        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+            BCService service = getService();
+            if (service == null) {
+                return new ArrayList<>();
+            }
+            return service.getDevicesMatchingConnectionStates(states);
+        }
+
+        @Override
+        public int getConnectionState(BluetoothDevice device) {
+            BCService service = getService();
+            if (service == null) {
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+            return service.getConnectionState(device);
+        }
+
+        @Override
+        public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+           return service.setConnectionPolicy(device, connectionPolicy);
+        }
+
+        @Override
+        public int getConnectionPolicy(BluetoothDevice device) {
+            BCService service = getService();
+            if (service == null) {
+                return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+            }
+            return service.getConnectionPolicy(device);
+        }
+        @Override
+        public boolean searchforLeAudioBroadcasters (BluetoothDevice device) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.searchforLeAudioBroadcasters(device);
+        }
+
+        @Override
+        public boolean stopSearchforLeAudioBroadcasters (BluetoothDevice device) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.stopSearchforLeAudioBroadcasters(device);
+        }
+
+        @Override
+        public boolean selectBroadcastSource (BluetoothDevice device, ScanResult scanRes, boolean isGroupOp) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.selectBroadcastSource(device, scanRes, isGroupOp, false);
+        }
+
+        @Override
+        public void registerAppCallback(BluetoothDevice device, IBleBroadcastAudioScanAssistCallback cb) {
+            BCService service = getService();
+            if (service == null) {
+                return;
+            }
+            service.registerAppCallback(device, cb);
+        }
+
+        @Override
+        public void unregisterAppCallback(BluetoothDevice device, IBleBroadcastAudioScanAssistCallback cb) {
+            BCService service = getService();
+            if (service == null) {
+                return;
+            }
+            service.unregisterAppCallback(device, cb);
+        }
+
+        @Override
+        public boolean startScanOffload(BluetoothDevice device, boolean isGroupOp) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.startScanOffloadInternal(device, isGroupOp);
+        }
+
+        @Override
+        public boolean stopScanOffload(BluetoothDevice device, boolean isGroupOp) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.stopScanOffloadInternal(device, isGroupOp);
+        }
+
+        @Override
+        public boolean addBroadcastSource(BluetoothDevice device, BleBroadcastSourceInfo srcInfo
+                                      , boolean isGroupOp) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.addBroadcastSourceInternal(device, srcInfo, isGroupOp);
+        }
+
+        @Override
+        public boolean updateBroadcastSource (BluetoothDevice device,
+                                    BleBroadcastSourceInfo srcInfo,
+                                    boolean isGroupOp) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.updateBroadcastSourceInternal(device, srcInfo, isGroupOp);
+        }
+
+        @Override
+        public boolean setBroadcastCode (BluetoothDevice device,
+                                    BleBroadcastSourceInfo srcInfo,
+                                    boolean isGroupOp) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.setBroadcastCodeInternal(device, srcInfo, isGroupOp);
+        }
+
+        @Override
+        public boolean removeBroadcastSource (BluetoothDevice device,
+                                    byte sourceId,
+                                    boolean isGroupOp) {
+            BCService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.removeBroadcastSourceInternal(device, sourceId, isGroupOp);
+        }
+        @Override
+        public List<BleBroadcastSourceInfo> getAllBroadcastSourceInformation (BluetoothDevice device
+                                    ) {
+            BCService service = getService();
+            if (service == null) {
+                return null;
+            }
+            return service.getAllBroadcastSourceInformation(device);
+       }
+   }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BaseData.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BaseData.java
new file mode 100644
index 0000000..b004afa
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BaseData.java
@@ -0,0 +1,861 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package com.android.bluetooth.bc;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import android.os.Message;
+import android.util.Log;
+import java.util.UUID;
+import java.util.Collection;
+import android.os.UserHandle;
+
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Scanner;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.lang.String;
+import java.lang.StringBuffer;
+import java.lang.Integer;
+
+import java.nio.ByteBuffer;
+import java.lang.Byte;
+import java.util.stream.IntStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BleBroadcastSourceChannel;
+///*_BMS
+import com.android.bluetooth.broadcast.BroadcastService.BisInfo;
+import com.android.bluetooth.broadcast.BroadcastService.MetadataLtv;
+//_BMS*/
+
+/**
+ * Helper class to parase the Broadcast Announcement BASE data
+ */
+final class BaseData {
+            private static final String TAG = "Bassclient-BaseData";
+            BaseInformation levelOne = new BaseInformation();
+            ArrayList<BaseInformation> levelTwo = new ArrayList<BaseInformation>();
+            ArrayList<BaseInformation> levelThree = new ArrayList<BaseInformation>();
+            int mNumBISIndicies;
+            public static byte UNKNOWN_CODEC = (byte)0xFE;
+
+            public class BaseInformation {
+            public byte[] presentationDelay = new byte[3];    //valid only if level=1
+            public byte[] codecId = new byte[5]; //valid only if level=1
+            public byte codecConfigLength;
+            public byte[] codecConfigInfo;
+            public byte metaDataLength;
+            public byte[] metaData;
+            public byte numSubGroups;
+            public byte[] bisIndicies; //valid only if level = 2
+            public byte index; //valid only if level=3 and level=2 (as subgroup Id)
+            public int subGroupId;
+            public int level;//differentiate different levels of BASE data
+            public LinkedHashSet<String> keyCodecCfgDiff;
+            public LinkedHashSet<String> keyMetadataDiff;
+            public String diffText;
+            public String description;
+
+            public byte[] consolidatedCodecId;
+            public Set<String> consolidatedMetadata;
+            public Set<String> consolidatedCodecInfo;
+            public HashMap<Integer, String> consolidatedUniqueCodecInfo;
+            public HashMap<Integer, String> consolidatedUniqueMetadata;
+
+            BaseInformation() {
+             presentationDelay = new byte[3];
+             codecId = new byte[5];
+             codecConfigLength = 0;
+             codecConfigInfo = null;
+             metaDataLength = 0;
+             metaData = null;
+             numSubGroups = 0;
+             bisIndicies = null;
+             index = (byte)0xFF;
+             level = 0;
+
+             keyCodecCfgDiff = new LinkedHashSet<String>();
+             keyMetadataDiff = new LinkedHashSet<String>();
+
+             consolidatedMetadata = new LinkedHashSet<String>();
+             consolidatedCodecInfo = new LinkedHashSet<String>();
+             consolidatedCodecId = new byte[5];
+             consolidatedUniqueMetadata = new HashMap<Integer, String>();
+             consolidatedUniqueCodecInfo = new HashMap<Integer, String>();
+             diffText = new String("");
+             description = new String("");
+             log("BaseInformation is Initialized");
+            }
+
+            boolean isCodecIdUnknown() {
+                return (codecId != null && codecId[4] == (byte)BaseData.UNKNOWN_CODEC);
+            }
+
+            void printConsolidated() {
+                    log("**BEGIN: BIS consolidated Information**");
+                    log("BIS index:" + index);
+                    log("CodecId:" + Arrays.toString(consolidatedCodecId));
+
+                    /*if (consolidatedCodecInfo != null) {
+                        Iterator<String> itr = consolidatedCodecInfo.iterator();
+                        for (int k=0; itr.hasNext(); k++) {
+                            log("consolidatedCodecInfo:[" + k + "]:" + Arrays.toString(itr.next().getBytes()));
+                        }
+                    }
+
+                    if (consolidatedMetadata != null) {
+                        Iterator<String> itr = consolidatedMetadata.iterator();
+                        for (int k=0; itr.hasNext(); k++) {
+                            log("consolidatedMetadata:[" + k + "]:" + Arrays.toString(itr.next().getBytes()));
+                        }
+                    }*/
+
+                    if (consolidatedUniqueCodecInfo != null) {
+                        for (Map.Entry<Integer,String> entry : consolidatedUniqueCodecInfo.entrySet()) {
+                            log("consolidatedUniqueCodecInfo:[" + entry.getKey() + "]:" + Arrays.toString(entry.getValue().getBytes()));
+                        }
+                    }
+
+                    if (consolidatedUniqueMetadata != null) {
+                        for (Map.Entry<Integer,String> entry : consolidatedUniqueMetadata.entrySet()) {
+                            log("consolidatedUniqueMetadata:[" + entry.getKey() + "]:" + Arrays.toString(entry.getValue().getBytes()));
+                        }
+                    }
+                    log("**END: BIS consolidated Information****");
+            }
+            void print() {
+                log("**BEGIN: Base Information**");
+                log("**Level: " + level + "***");
+                if (level == 1) {
+                    log("presentationDelay: " + Arrays.toString(presentationDelay));
+                }
+                if (level == 2) {
+                    log("codecId: " + Arrays.toString(codecId));
+                }
+                if (level == 2 || level == 3) {
+                    log("codecConfigLength: " + codecConfigLength);
+                    log("subGroupId: " + subGroupId);
+                }
+                if (codecConfigLength != (byte)0) {
+                    log("codecConfigInfo: " + Arrays.toString(codecConfigInfo));
+                }
+                if (level == 2) {
+                    log("metaDataLength: " + metaDataLength);
+                    if (metaDataLength != (byte)0) {
+                        log("metaData: " + Arrays.toString(metaData));
+                    }
+                if (level == 1 || level == 2)
+                    log("numSubGroups: " + numSubGroups);
+                }
+                if (level == 2) {
+                    log("Level2: Key Metadata differentiators");
+                    if (keyMetadataDiff != null) {
+                        Iterator<String> itr = keyMetadataDiff.iterator();
+                        for (int k=0; itr.hasNext(); k++) {
+                            log("keyMetadataDiff:[" + k + "]:" + Arrays.toString(itr.next().getBytes()));
+                        }
+                    }
+                    log("END: Level2: Key Metadata differentiators");
+
+                    log("Level2: Key CodecConfig differentiators");
+                    if (keyCodecCfgDiff != null) {
+                        Iterator<String> itr = keyCodecCfgDiff.iterator();
+                        for (int k=0; itr.hasNext(); k++) {
+                            log("LEVEL2: keyCodecCfgDiff:[" + k + "]:" + Arrays.toString(itr.next().getBytes()));
+                        }
+                    }
+                    log("END: Level2: Key CodecConfig differentiators");
+                    //log("bisIndicies: " + Arrays.toString(bisIndicies));
+                    log("LEVEL2: diffText: " + diffText);
+                }
+                if (level == 3) {
+                    log("Level3: Key CodecConfig differentiators");
+                    if (keyCodecCfgDiff != null) {
+                        Iterator<String> itr = keyCodecCfgDiff.iterator();
+                        for (int k=0; itr.hasNext(); k++) {
+                            log("LEVEL3: keyCodecCfgDiff:[" + k + "]:" + Arrays.toString(itr.next().getBytes()));
+                        }
+                    }
+                    log("END: Level3: Key CodecConfig differentiators");
+                    log("index: " + index);
+                    log("LEVEL3: diffText: " + diffText);
+                }
+                log("**END: Base Information****");
+            }
+        };
+            ///*_BMS
+            BaseData(int numSubGroups, List<BisInfo> colocatedBisInfo, Map<Integer, MetadataLtv> metaInfo) {
+                if (metaInfo == null || colocatedBisInfo == null) {
+
+                    Log.e(TAG, "BaseData Contruction with Invalid parameters");
+                    throw new IllegalArgumentException("Basedata: Parameters can't be null");
+                }
+                levelOne = new BaseInformation();
+                levelTwo = new ArrayList<BaseInformation>();
+                levelThree = new ArrayList<BaseInformation>();
+
+                levelOne.level = 1;
+                levelOne.numSubGroups = (byte)numSubGroups;
+
+                //create the level Two and update the Metadata Info
+                for (int i=0; i<numSubGroups; i++) {
+                    BaseInformation a = new BaseInformation();
+                    a.level = 2;
+                    //get Metadata Ltv
+                    byte[] metadataLtv = null;
+                    if (metaInfo != null) {
+                        Log.d(TAG, "metaInfo: " + metaInfo);
+                        MetadataLtv obj = metaInfo.get(i);
+                        if (obj != null) {
+                            metadataLtv = obj.getByteArray();
+                            Log.d(TAG, "metadataLtv: " + metadataLtv);
+                        } else {
+                            Log.d(TAG, "metadataLtv[" +i+"] is not available");
+                        }
+                    }
+                    if (metadataLtv != null) {
+                        a.metaData = new byte[(int)metadataLtv.length];
+                        System.arraycopy(metadataLtv, 0, a.metaData, 0, (int)metadataLtv.length);
+                    }
+                    levelTwo.add(a);
+                }
+
+                if (colocatedBisInfo != null) {
+                    mNumBISIndicies = colocatedBisInfo.size();
+                    for (int i = 0; i < colocatedBisInfo.size(); i++) {
+                        BisInfo bisInfo = colocatedBisInfo.get(i);
+                        BaseInformation b = new BaseInformation();
+                        b.level = 3;
+                        b.subGroupId = bisInfo.mSubGroupId;
+                        b.index = (byte)bisInfo.BisIndex;
+
+                        b.consolidatedCodecId = bisInfo.mCodecId;
+
+                        //get Metadata Ltv
+                        byte[] metadataLtv = bisInfo.BisMetadata.getByteArray();
+                        if (metadataLtv != null) {
+                            int k = 0;
+                            while (k<metadataLtv.length) {
+                                byte length = metadataLtv[k++];
+                                byte[] ltv = new byte[length+1];
+                                ltv[0] = length;
+                                System.arraycopy(metadataLtv, k, ltv, 1, length);
+                                //put in type, ltv hashmap
+                                String s = new String(ltv);
+                                b.consolidatedUniqueMetadata.put((int)ltv[1], s);
+                                log("add Metadata:::");
+                                k = k+length;
+                            }
+                        }
+
+                        //get CodecConfig ltv
+                        byte[] codecConfigLtv = bisInfo.BisCodecConfig.getByteArray();
+                        if (codecConfigLtv != null) {
+                            int k = 0;
+                            while (k<codecConfigLtv.length) {
+                                byte length = codecConfigLtv[k++];
+                                byte[] ltv = new byte[length+1];
+                                ltv[0] = length;
+                                System.arraycopy(codecConfigLtv, k, ltv, 1, length);
+                                //put in type, ltv hashmap
+                                String s = new String(ltv);
+                                b.consolidatedUniqueCodecInfo.put((int)ltv[1], s);
+                                log("add CodecConfig entry:::");
+                                k = k+length;
+                            }
+                        }
+                        //update description with "Chennel: X"
+                        b.description = "Channel: " + String.valueOf(b.index);
+                        levelThree.add(b);
+                      }
+                }
+            }
+            //_BMS*/
+            BaseData(byte[] serviceData) {
+                if (serviceData == null) {
+                    Log.e(TAG, "Invalid service data for BaseData construction");
+                    throw new IllegalArgumentException("Basedata: serviceData is null");
+                }
+                levelOne = new BaseInformation();
+                levelTwo = new ArrayList<BaseInformation>();
+                levelThree = new ArrayList<BaseInformation>();
+                mNumBISIndicies = 0;
+                log("members initialized");
+                log("BASE input" + Arrays.toString(serviceData));
+
+                //Parse Level 1 base
+                levelOne.level = 1;
+                int level1Idx = 0;
+                System.arraycopy(serviceData, level1Idx, levelOne.presentationDelay,0, 3);
+                level1Idx = level1Idx + 3;
+
+                levelOne.numSubGroups = serviceData[level1Idx++];
+                levelOne.print();
+                log("levelOne subgroups" + levelOne.numSubGroups);
+
+                int level2Idx = level1Idx;
+                for (int i =0; i<(int)levelOne.numSubGroups; i++) {
+                    log("parsing subgroup" + i);
+                    BaseInformation b = new BaseInformation();
+
+                    b.level = 2;
+                    b.subGroupId = i;
+                    b.numSubGroups = serviceData[level2Idx++];
+                    if (serviceData[level2Idx] == (byte)UNKNOWN_CODEC) {
+                        //Place It in the last byte of codecID
+                        System.arraycopy(serviceData, level2Idx, b.codecId, 4, 1);
+                        level2Idx =  level2Idx + 1;
+                        log("codecId is FE");
+                    } else {
+                        System.arraycopy(serviceData, level2Idx, b.codecId, 0, 5);
+                        level2Idx =  level2Idx + 5;
+                    }
+
+                    b.codecConfigLength =  serviceData[level2Idx++];
+                    if (b.codecConfigLength != 0) {
+                        b.codecConfigInfo = new byte[(int)b.codecConfigLength];
+                        System.arraycopy(serviceData, level2Idx, b.codecConfigInfo, 0, (int)b.codecConfigLength);
+                        level2Idx = level2Idx + (int)b.codecConfigLength;
+                    }
+                    b.metaDataLength = serviceData[level2Idx++];
+                    if (b.metaDataLength != 0) {
+                        b.metaData = new byte[(int)b.metaDataLength];
+                        System.arraycopy(serviceData, level2Idx, b.metaData, 0, (int)b.metaDataLength);
+                        level2Idx = level2Idx + (int)b.metaDataLength;
+                    }
+                    mNumBISIndicies = mNumBISIndicies + b.numSubGroups;
+                    levelTwo.add(b);
+                    b.print();
+                }
+                //Parse Level 3 Base
+                int level3Index = level2Idx;
+                for (int k=0; k<mNumBISIndicies; k++) {
+                    BaseInformation c = new BaseInformation();
+                    c.level = 3;
+                    c.index = serviceData[level3Index++];
+
+                    c.codecConfigLength =  serviceData[level3Index++];
+                    if (c.codecConfigLength != 0) {
+                        c.codecConfigInfo = new byte[(int)c.codecConfigLength];
+                        System.arraycopy(serviceData, level3Index, c.codecConfigInfo, 0, (int)c.codecConfigLength);
+                        level3Index = level3Index + (int)c.codecConfigLength;
+                   }
+                   levelThree.add(c);
+                }
+
+                consolidateBaseofLevelTwo();
+
+                //Detailed BASE parsing below
+                //log("calling updateUniquenessForLevelTwo");
+                //updateUniquenessForLevelTwo(levelOne.numSubGroups);
+                //updateDiffTextforNodes();
+            }
+
+                void consolidateBaseofLevelTwo() {
+                    int startIdx = 0;
+                    int children = 0;
+
+                    for (int i=0; i<levelTwo.size(); i++) {
+                        startIdx = startIdx+ children;
+                        children = children + levelTwo.get(i).numSubGroups;
+
+                        consolidateBaseofLevelThree(i, startIdx, levelTwo.get(i).numSubGroups);
+                    }
+
+                    //Eliminate Duplicates at Level 3
+                    for (int i=0; i<levelThree.size(); i++) {
+                        Map<Integer, String> uniqueMds = new HashMap<Integer, String> ();
+                        Map<Integer, String> uniqueCcis = new HashMap<Integer, String> ();
+
+                        Set<String> Csfs = levelThree.get(i).consolidatedCodecInfo;
+
+                        if (Csfs.size() > 0) {
+                            Iterator<String> itr = Csfs.iterator();
+                            for (int j=0; itr.hasNext(); j++) {
+                                byte[] ltvEntries = itr.next().getBytes();
+
+                                int k = 0;
+                                byte length = ltvEntries[k++];
+                                byte[] ltv = new byte[length+1];
+                                ltv[0] = length;
+                                System.arraycopy(ltvEntries, k, ltv, 1, length);
+
+                                //
+                                int type = (int)ltv[1];
+                                String s = uniqueCcis.get(type);
+                                String ltvS = new String(ltv);
+                                if (s == null) {
+                                    uniqueCcis.put(type, ltvS);
+                                } else {
+                                    //if same type exists
+                                    //replace
+                                    uniqueCcis.replace(type, ltvS);
+                                }
+                            }
+                        }
+
+                        Set<String> Mds = levelThree.get(i).consolidatedMetadata;
+                        if (Mds.size() > 0) {
+                            Iterator<String> itr = Mds.iterator();
+                            for (int j=0; itr.hasNext(); j++) {
+                                byte[] ltvEntries = itr.next().getBytes();
+
+                                int k = 0;
+                                byte length = ltvEntries[k++];
+                                byte[] ltv = new byte[length+1];
+                                ltv[0] = length;
+                                System.arraycopy(ltvEntries, k, ltv, 1, length);
+
+                                /*CHECK: This can be straight PUT, there wont be dups in Metadata with new BASE*/
+                                int type = (int)ltv[1];
+                                String s = uniqueCcis.get(type);
+                                String ltvS = new String(ltv);
+                                if (s == null) {
+                                    uniqueMds.put(type, ltvS);
+                                } else {
+                                    //if same type exists
+                                    //replace
+                                    uniqueMds.replace(type, ltvS);
+                                }
+                            }
+                        }
+
+                        levelThree.get(i).consolidatedUniqueMetadata = new HashMap<Integer, String>(uniqueMds);
+                        levelThree.get(i).consolidatedUniqueCodecInfo = new HashMap<Integer, String>(uniqueCcis);
+
+                }
+             }
+
+                void consolidateBaseofLevelThree(int parentSubgroup, int startIdx, int numNodes) {
+
+                    for (int i=startIdx; i<startIdx+numNodes||i<levelThree.size(); i++) {
+
+                        levelThree.get(i).subGroupId = levelTwo.get(parentSubgroup).subGroupId;
+
+                        log("Copy Codec Id from Level2 Parent" + parentSubgroup);
+                        System.arraycopy(levelTwo.get(parentSubgroup).consolidatedCodecId,
+                                              0 ,levelThree.get(i).consolidatedCodecId, 0, 5);
+
+                        //Metadata clone from Parent
+                        levelThree.get(i).consolidatedMetadata = new LinkedHashSet<String>(levelTwo.get(parentSubgroup).consolidatedMetadata);
+
+                        //log("Parent Cons Info>>");
+                        //levelTwo.get(parentSubgroup).printConsolidated();
+                        //CCI clone from Parent
+                        levelThree.get(i).consolidatedCodecInfo = new LinkedHashSet<String>(levelTwo.get(parentSubgroup).consolidatedCodecInfo);
+                        //log("before " + i);
+                        //levelThree.get(i).printConsolidated();
+                        //Append Level 2 Codec Config
+                        if (levelThree.get(i).codecConfigLength != 0) {
+                            log("append level 3 cci to level 3 cons:" + i);
+                            String s = new String(levelThree.get(i).codecConfigInfo);
+                            levelThree.get(i).consolidatedCodecInfo.add(s);
+                        }
+                        //log("after " + i);
+                        //levelThree.get(i).printConsolidated();
+                        //log("Parent Cons Info>>");
+                        //levelTwo.get(parentSubgroup).printConsolidated();
+                    }
+
+                }
+
+                public int getNumberOfIndicies() {
+                    return mNumBISIndicies;
+                }
+
+                public byte getNumberOfSubgroupsofBIG() {
+                    byte ret = 0;
+                    if (levelOne != null) {
+                        ret = levelOne.numSubGroups;
+                    }
+                    return ret;
+                }
+
+                public  ArrayList<BaseInformation> getBISIndexInfos() {
+                    return levelThree;
+                }
+                List<BleBroadcastSourceChannel>  getBroadcastChannels() {
+                    List<BleBroadcastSourceChannel> bChannels = new ArrayList<BleBroadcastSourceChannel>();
+                    for (int k=0; k<mNumBISIndicies; k++) {
+                        int index = levelThree.get(k).index;
+                        String desc = levelThree.get(k).description;
+                        //String desc = String.valueOf(index);
+                        BleBroadcastSourceChannel bc = new BleBroadcastSourceChannel(index, desc, false,
+                                             levelThree.get(k).subGroupId, levelThree.get(k).metaData);
+                        bChannels.add(bc);
+                    }
+                    return bChannels;
+                }
+
+                List<BleBroadcastSourceChannel> pickAllBroadcastChannels() {
+                    List<BleBroadcastSourceChannel> bChannels = new ArrayList<BleBroadcastSourceChannel>();
+                    for (int k=0; k<mNumBISIndicies; k++) {
+                        int index = levelThree.get(k).index;
+                        //String desc = levelThree.get(k).description;
+                        //String desc = String.valueOf(index);
+                        BleBroadcastSourceChannel bc = new BleBroadcastSourceChannel(index, String.valueOf(index), true,
+                                                               levelThree.get(k).subGroupId, levelThree.get(k).metaData);
+                        bChannels.add(bc);
+                    }
+                    return bChannels;
+                }
+                byte[] getMetadata(int subGroup) {
+                    if (levelTwo != null) {
+                        return levelTwo.get(subGroup).metaData;
+                    }
+                    return null;
+                }
+
+                String getMetadataString(byte[] metadataBytes) {
+                    final int _LANGUAGE = 0;
+                        //Different language
+                        final int _ENGLISH = 1;
+                        final int _SPANISH = 2;
+                    final int _DESCRIPTION = 1;
+                    String ret = new String();
+
+                    switch(metadataBytes[1]) {
+                        case _LANGUAGE:
+                            switch (metadataBytes[2]) {
+                                case _ENGLISH:
+                                    ret = "ENGLISH"; break;
+                                case _SPANISH:
+                                    ret = "SPANISH"; break;
+                                default:
+                                    ret = "UNKNOWN"; break;
+                            }
+                            break;
+                        case _DESCRIPTION:
+                            ret = "UNKNOWN";
+                            break;
+                        default:
+                            ret = "UNKNOWN";
+                    }
+                    log("getMetadataString: " + ret);
+                    return ret;
+                }
+
+                String getCodecParamString(byte[] csiBytes) {
+                    final  int LOCATION = 4;
+                    final  int LEFT = 0x01000000;
+                    final  int RIGHT =0x02000000;
+                    String ret = new String();
+
+                    //sample rate
+                    final int SAMPLE_RATE = 1;
+
+                    //frame duration
+                    final int FRAME_DURATION = 2;
+
+                    //Octets per frame
+                    final int OCTETS_PER_FRAME = 8;
+                    switch(csiBytes[1]) {
+                        case LOCATION:
+                            byte[] location = new byte[4];
+                            System.arraycopy(csiBytes, 2, location, 0, 4);
+                            ByteBuffer wrapped = ByteBuffer.wrap(location);
+                            int audioLocation = wrapped.getInt();
+                            log("audioLocation: " + audioLocation);
+
+                            switch (audioLocation) {
+                                case LEFT: ret = "LEFT"; break;
+                                case RIGHT: ret = "RIGHT"; break;
+                                case LEFT|RIGHT: ret = "LR"; break;
+                            }
+                            break;
+                        case SAMPLE_RATE:
+                            switch(csiBytes[2]) {
+                                    case 1:
+                                        ret = "8K"; break;
+                                    case 2:
+                                        ret = "16K"; break;
+                                    case 3:
+                                        ret = "24K"; break;
+                                    case 4:
+                                        ret = "32K"; break;
+                                    case 5:
+                                        ret = "44.1K"; break;
+                                    case 6:
+                                        ret = "48K"; break;
+                            }
+                            break;
+                        case FRAME_DURATION:
+                            switch(csiBytes[2]) {
+                                    case 1:
+                                        ret = "FD_1"; break;
+                            }
+                            break;
+                        case OCTETS_PER_FRAME:
+                            switch(csiBytes[2]) {
+                                    case 28:
+                                        ret = "OPF_28"; break;
+                                    case 64:
+                                        ret = "OPF_64"; break;
+                            }
+                            break;
+                        default:
+                            ret = "UNKNOWN";
+                    }
+                    log("getCodecParamString: " + ret);
+                    return ret;
+                }
+
+                void updateDiffTextforNodes() {
+                    for (int i=0; i<levelTwo.size(); i++) {
+                        if (levelTwo.get(i).keyMetadataDiff != null) {
+                            Iterator<String> itr = levelTwo.get(i).keyMetadataDiff.iterator();
+                            for (int k=0; itr.hasNext(); k++) {
+                                levelTwo.get(i).diffText = levelTwo.get(i).diffText.concat(getMetadataString(itr.next().getBytes()));
+                                levelTwo.get(i).diffText = levelTwo.get(i).diffText.concat("_");
+                            }
+                        }
+                        if (levelTwo.get(i).keyCodecCfgDiff != null) {
+                            Iterator<String> itr = levelTwo.get(i).keyCodecCfgDiff.iterator();
+                            for (int k=0; itr.hasNext(); k++) {
+                                levelTwo.get(i).diffText = levelTwo.get(i).diffText.concat(getCodecParamString(itr.next().getBytes()));
+                                levelTwo.get(i).diffText = levelTwo.get(i).diffText.concat("_");
+                            }
+                        }
+                    }
+
+                    for (int i=0; i<levelThree.size(); i++) {
+                        if (levelThree.get(i).keyCodecCfgDiff != null) {
+                            Iterator<String> itr = levelThree.get(i).keyCodecCfgDiff.iterator();
+                            for (int k=0; itr.hasNext(); k++) {
+                                levelThree.get(i).diffText = levelThree.get(i).diffText.concat(getCodecParamString(itr.next().getBytes()));
+                                levelThree.get(i).diffText = levelThree.get(i).diffText.concat("_");
+                            }
+                        }
+                    }
+
+                    //Concat and update the Description
+                    int startIdx = 0;
+                    int children = 0;
+                    for (int i=0; i<levelTwo.size(); i++) {
+                        startIdx = startIdx+ children;
+                        children = children + levelTwo.get(i).numSubGroups;
+                        for (int j=startIdx; j<startIdx+levelTwo.get(i).numSubGroups||j<levelThree.size(); j++) {
+                            levelThree.get(j).description = levelTwo.get(i).diffText +     levelThree.get(j).diffText;
+                        }
+                    }
+                }
+
+                void updateUniquenessForLevelTwo(int numNodes) {
+                    log("updateUniquenessForLevelTwo: ENTER");
+                    Set<String> uniqueCodecIds = new LinkedHashSet<String>();
+                    Set<String> uniqueCsfs = new LinkedHashSet<String>();
+                    Set<String> uniqueMetadatas = new LinkedHashSet<String>();
+
+                    log("updateUniquenessForLevelTwo");
+
+                    int startIdx = 0;
+                    int children = 0;
+                    for (int i=0; i<levelTwo.size(); i++) {
+                        //levelTwo.get(i).print();
+                        if (!levelTwo.get(i).isCodecIdUnknown()) {
+                            log("add codecId of subg: " + i);
+                            String s = new String(levelTwo.get(i).codecId);
+                            uniqueCodecIds.add(s);
+                        }
+
+                        if (levelTwo.get(i).codecConfigLength != 0) {
+                            log("add codecConfig of subg: " + i);
+                            String s = new String(levelTwo.get(i).codecConfigInfo);
+                            uniqueCsfs.add(s);
+                        }
+
+                        if (levelTwo.get(i).metaDataLength != 0) {
+                            String s = new String(levelTwo.get(i).metaData);
+                            log("add metadata of subg: " + i);
+                            uniqueMetadatas.add(s);
+                        }
+                        startIdx = startIdx+ children;
+                        children = children + levelTwo.get(i).numSubGroups;
+
+                        updateUniquenessForLevelThree(i, startIdx, levelTwo.get(i).numSubGroups);
+                    }
+
+                    Set<String> uniqueCodecParams = new LinkedHashSet<String>();
+                    Set<String> uniqueMetadataParams = new LinkedHashSet<String>();
+
+                    if (uniqueCodecIds.size() > 0) log("LevelTwo: UniqueCodecIds");
+
+                    if (uniqueCsfs.size() > 0) {
+                        log("LevelTwo: uniqueCsfs");
+                        //uniqueCodecParams =
+                        Iterator<String> itr = uniqueCsfs.iterator();
+                        for (int i=0; itr.hasNext(); i++) {
+                            byte[] ltvEntries = itr.next().getBytes();
+
+                            int k = 0;
+                            byte length = ltvEntries[k++];
+                            byte[] ltv = new byte[length+1];
+                            ltv[0] = length;
+                            System.arraycopy(ltvEntries, k, ltv, 1, length);
+                            //This should ensure Duplicate entries at this level
+                            String s = new String(ltvEntries);
+                            uniqueCodecParams.add(s);
+                        }
+                    }
+                    if (uniqueMetadatas.size() > 0) {
+                        log("LevelTwo: uniqueMetadatas");
+                        //uniqueMetadataParams = new LinkedHashSet<String>();
+                        Iterator<String> itr = uniqueMetadatas.iterator();
+                        for (int i=0; itr.hasNext(); i++) {
+                            byte[] ltvEntries = itr.next().getBytes();
+
+                            int k = 0;
+                            byte length = ltvEntries[k++];
+                            byte[] ltv = new byte[length+1];
+                            ltv[0] = length;
+                            System.arraycopy(ltvEntries, k, ltv, 1, length);
+                            //This should ensure Duplicate entries at this level
+                            String s = new String(ltvEntries);
+                            uniqueMetadataParams.add(s);
+                        }
+                    }
+
+                    //run though the nodes and update KEY differentiating factors
+                    if (uniqueCodecParams != null) {
+                        Iterator<String> itr = uniqueCodecParams.iterator();
+                        int i = 0;
+                        for (int k=0; itr.hasNext(); k++) {
+                            levelTwo.get(i).keyCodecCfgDiff.add(itr.next());
+                            i = (i+1)%(numNodes);
+                        }
+                    }
+
+                    //run though the nodes and update KEY differentiating factors
+                    if (uniqueMetadataParams != null) {
+                        Iterator<String> itr = uniqueMetadataParams.iterator();
+                        int i = 0;
+                        for (int k=0; itr.hasNext(); k++) {
+                            levelTwo.get(i).keyMetadataDiff.add(itr.next());
+                            i = (i+1)%(numNodes);
+                        }
+                    }
+
+                    /*log("Level2: Uniqueness among subgroups");
+                    if (uniqueCodecParams != null) {
+                        Iterator<String> itr = uniqueCodecParams.iterator();
+                        for (int k=0; itr.hasNext(); k++) {
+                            log("UniqueCodecParams:[" + k + "]" + Arrays.toString(itr.next().getBytes()));
+                        }
+                    }
+                    if (uniqueMetadataParams != null) {
+                        Iterator<String>  itr = uniqueMetadataParams.iterator();
+                        for (int k=0; itr.hasNext(); k++) {
+                            log("uniqueMetadataParams:["+ k + "]" + Arrays.toString(itr.next().getBytes()));
+                        }
+                    }
+                    log("END: Level2: Uniqueness among subgroups");
+                    */
+                }
+                void updateUniquenessForLevelThree(int parentSubgroup, int startIdx, int numNodes) {
+                    //Set<String> uniqueCodecIds = new LinkedHashSet<String>();
+                    Set<String> uniqueCsfs = new LinkedHashSet<String>();
+                    //Set<String> uniqueMetadatas = new LinkedHashSet<String>();
+
+                    log("updateUniquenessForLevelThree: startIdx" + startIdx + "numNodes" + numNodes);
+                    for (int i=startIdx; i<startIdx+numNodes||i<levelThree.size(); i++) {
+                        if (levelThree.get(i).codecConfigLength != 0) {
+                            String s = new String(levelThree.get(i).codecConfigInfo);
+                            uniqueCsfs.add(s);
+                            log("LEVEL3: add unique CSFs:");
+                        }
+                    }
+
+                    Set<String> uniqueCodecParams = new LinkedHashSet<String>();
+                    if (uniqueCsfs.size() > 0) {
+                        log("LevelThree: uniqueCsfs");
+                        //uniqueCodecParams =
+                        Iterator<String> itr = uniqueCsfs.iterator();
+                        for (int i=0; itr.hasNext(); i++) {
+                            byte[] ltvEntries = itr.next().getBytes();
+
+                            int k = 0;
+                            byte length = ltvEntries[k++];
+                            byte[] ltv = new byte[length+1];
+                            ltv[0] = length;
+                            System.arraycopy(ltvEntries, k, ltv, 1, length);
+                            //This should ensure Duplicate entries at this level
+                            String s = new String(ltvEntries);
+                            uniqueCodecParams.add(s);
+                        }
+                    }
+                    //run though the nodes and update KEY differentiating factors
+                    if (uniqueCodecParams != null) {
+                        Iterator<String> itr = uniqueCodecParams.iterator();
+                        int i = startIdx;
+                        for (int k=0; itr.hasNext(); k++) {
+                            levelThree.get(i).keyCodecCfgDiff.add(itr.next());
+                            i = (i+1)%(startIdx+numNodes);
+                        }
+                    }
+                    /*
+                    log("Level3: Uniqueness among children of " + parentSubgroup + "th Subgroup");
+                    if (uniqueCodecParams != null) {
+                        Iterator<String> itr = uniqueCodecParams.iterator();
+                        for (int k=0; itr.hasNext(); k++) {
+                            log("UniqueCodecParams:[" + k + "]" + Arrays.toString(itr.next().getBytes()));
+
+                        }
+                    }
+                    log("END: Level3: Uniqueness among children of " + parentSubgroup + "th Subgroup");
+                    */
+             }
+
+             void print() {
+                levelOne.print();
+                log("----- Level TWO BASE ----");
+                for (int i=0; i<levelTwo.size(); i++) {
+                    levelTwo.get(i).print();
+                }
+                log("----- Level THREE BASE ----");
+                for (int i=0; i<levelThree.size(); i++) {
+                    levelThree.get(i).print();
+                }
+            }
+
+            void printConsolidated() {
+                log("----- printConsolidated ----");
+                for (int i=0; i<levelThree.size(); i++) {
+                    levelThree.get(i).printConsolidated();
+                }
+            }
+
+            static void log(String msg) {
+                if (BassClientStateMachine.BASS_DBG) {
+                   Log.d(TAG, msg);
+            }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BassClientStateMachine.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BassClientStateMachine.java
new file mode 100644
index 0000000..b35cfa9
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BassClientStateMachine.java
@@ -0,0 +1,2388 @@
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ *
+ * 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.
+ */
+
+/**
+ * Bluetooth Bassclient StateMachine. There is one instance per remote device.
+ *  - "Disconnected" and "Connected" are steady states.
+ *  - "Connecting" and "Disconnecting" are transient states until the
+ *     connection / disconnection is completed.
+ *  - "ConnectedProcessing" is an intermediate state to ensure, there is only
+ *    one Gatt transaction from the profile at any point of time
+ *
+ *
+ *                        (Disconnected)
+ *                           |       ^
+ *                   CONNECT |       | DISCONNECTED
+ *                           V       |
+ *                 (Connecting)<--->(Disconnecting)
+ *                           |       ^
+ *                 CONNECTED |       | DISCONNECT
+ *                           V       |
+ *                          (Connected)
+ *                           |       ^
+ *                 GATT_TXN  |       | GATT_TXN_DONE/GATT_TXN_TIMEOUT
+ *                           V       |
+ *                          (ConnectedProcessing)
+ * NOTES:
+ *  - If state machine is in "Connecting" state and the remote device sends
+ *    DISCONNECT request, the state machine transitions to "Disconnecting" state.
+ *  - Similarly, if the state machine is in "Disconnecting" state and the remote device
+ *    sends CONNECT request, the state machine transitions to "Connecting" state.
+ *  - Whenever there is any Gatt Write/read, State machine will moved "ConnectedProcessing" and
+ *    all other requests (add, update, remove source) operations will be deferred in "ConnectedProcessing" state
+ *  - Once the gatt transaction is done (or after a specified timeout of no response), State machine will
+ *    move back to "Connected" and try to process the deferred requests as needed
+ *
+ *                    DISCONNECT
+ *    (Connecting) ---------------> (Disconnecting)
+ *                 <---------------
+ *                      CONNECT
+ *
+ */
+
+package com.android.bluetooth.bc;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothSyncHelper;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BleBroadcastSourceChannel;
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+import android.bluetooth.IBleBroadcastAudioScanAssistCallback;
+import android.bluetooth.BleBroadcastAudioScanAssistCallback;
+import com.android.bluetooth.Utils;
+
+//CSIP related imports
+///*_CSIP
+import com.android.bluetooth.groupclient.GroupService;
+import android.bluetooth.BluetoothGroupCallback;
+import android.bluetooth.DeviceGroup;
+//_CSIP*/
+
+///*_VCP
+import android.bluetooth.BluetoothVcp;
+import com.android.bluetooth.vcp.VcpController;
+//_VCP*/
+
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.PeriodicAdvertisingCallback;
+import android.bluetooth.le.PeriodicAdvertisingManager;
+import android.bluetooth.le.PeriodicAdvertisingReport;
+
+import android.bluetooth.IBluetoothManager;
+import android.os.ServiceManager;
+import android.os.IBinder;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import android.content.Intent;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import java.util.UUID;
+import java.util.Collection;
+import android.os.UserHandle;
+import java.lang.IllegalArgumentException;
+
+import com.android.bluetooth.btservice.ProfileService;
+/*_PACS
+import com.android.bluetooth.pacsclient.PacsClientService;
+_PACS*/
+
+import com.android.bluetooth.btservice.ServiceFactory;
+///*_BMS
+import com.android.bluetooth.broadcast.BroadcastService;
+//_BMS*/
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Scanner;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.lang.String;
+import java.lang.StringBuffer;
+import java.lang.Integer;
+
+import java.nio.ByteBuffer;
+import java.lang.Byte;
+import java.util.stream.IntStream;
+import android.os.SystemProperties;
+import android.os.ParcelUuid;
+
+
+final class BassClientStateMachine extends StateMachine {
+    private static final String TAG = "BassClientStateMachine";
+    public static final boolean BASS_DBG = true;
+    //public static final boolean BASS_DBG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private boolean mIsWhitelist = false;
+
+    static final int BCAST_RECEIVER_STATE_LENGTH = 15;
+    static final int CONNECT = 1;
+    static final int DISCONNECT = 2;
+    static final int CONNECTION_STATE_CHANGED = 3;
+    static final int GATT_TXN_PROCESSED = 4;
+    static final int READ_BASS_CHARACTERISTICS= 5;
+    static final int START_SCAN_OFFLOAD = 6;
+    static final int STOP_SCAN_OFFLOAD = 7;
+    static final int SELECT_BCAST_SOURCE = 8;
+    static final int ADD_BCAST_SOURCE = 9;
+    static final int UPDATE_BCAST_SOURCE = 10;
+    static final int SET_BCAST_CODE = 11;
+    static final int REMOVE_BCAST_SOURCE = 12;
+    static final int GATT_TXN_TIMEOUT = 13;
+    static final int PSYNC_ACTIVE_TIMEOUT = 14;
+    public static final int CSIP_CONNECTION_STATE_CHANGED = 15;
+    static final int CONNECT_TIMEOUT = 16;
+
+    //30 secs time out for all gatt writes
+    static final int GATT_TXN_TIMEOUT_MS = 30000;
+
+    //3 min time out for keeping PSYNC active
+    static final int PSYNC_ACTIVE_TIMEOUT_MS = 3*60000;
+    //2 secs time out achieving psync
+    static final int PSYNC_TIMEOUT = 200;
+
+    int NUM_OF_BROADCAST_RECEIVER_STATES = 0;
+
+    private final Disconnected mDisconnected;
+    private final Connected mConnected;
+    private final Connecting mConnecting;
+    private final Disconnecting mDisconnecting;
+    private final ConnectedProcessing mConnectedProcessing;
+    private int mLastConnectionState = -1;
+    private static int mConnectTimeoutMs = 30000;
+    private boolean mMTUChangeRequested = false;
+    private boolean mDiscoveryInitiated = false;
+
+    private BCService mService;
+    private final BluetoothDevice mDevice;
+    private BluetoothGatt mBluetoothGatt = null;
+
+    //BASS Characteristics UUID
+    public static final UUID BASS_UUID = UUID.fromString("0000184F-0000-1000-8000-00805F9B34FB");
+    private static final UUID BASS_BCAST_AUDIO_SCAN_CTRL_POINT = UUID.fromString("00002BC7-0000-1000-8000-00805F9B34FB");
+    private static final UUID BASS_BCAST_RECEIVER_STATE = UUID.fromString("00002BC8-0000-1000-8000-00805F9B34FB");
+    private static final UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString(
+             "00002902-0000-1000-8000-00805f9b34fb");
+    private List<BluetoothGattCharacteristic> mBroadcastReceiverStates;
+    private BluetoothGattCharacteristic mBroadcastScanControlPoint;
+    /*key is combination of sourceId, Address and advSid for this hashmap*/
+    private final Map<Integer, BleBroadcastSourceInfo> mBleBroadcastSourceInfos;
+    private boolean mFirstTimeBisDiscovery = false;
+    private int mPASyncRetryCounter = 0;
+    private ScanResult mScanRes = null;
+
+    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+    private ServiceFactory mFactory = new ServiceFactory();
+    ///*_BMS
+    private BroadcastService mBAService = null;
+    //_BMS*/
+
+    private final byte[] REMOTE_SCAN_STOP = {00};
+    private final byte[] REMOTE_SCAN_START = {01};
+    private byte BASS_ADD_SOURCE_OPCODE = 0x02;
+    private byte BASS_UPDATE_SOURCE_OPCODE = 0x03;
+    private byte BASS_SET_BCAST_PIN_OPCODE = 0x04;
+    private byte BASS_REMOVE_SOURCE_OPCODE = 0x05;
+
+    private static int num_of_recever_states = 0;
+    private static int PIN_CODE_CMD_LEN = 18;
+    private final int BASS_MAX_BYTES = 100;
+    private int mPendingOperation = -1;
+    private byte mPendingSourceId = -1;
+    public static byte INVALID_SRC_ID = -1;
+    private int  GATT_TXN_TOUT_ERROR = -1;
+    private BleBroadcastSourceInfo mSetBroadcastPINSrcInfo = null;
+    private boolean mSetBroadcastCodePending = false;
+
+    //types of command for  set broadcast PIN operation
+    public int FRESH = 1;
+    private int QUEUED = 2;
+
+    //types of command for select and add Broadcast source operations
+    public int AUTO = 1;
+    public int USER = 2;
+
+    //types of operation for Select source to determine
+    //if psync achieved on behalf of single device or multiple devices
+    public int GROUP_OP = 1;
+    public int NON_GROUP_OP = 0;
+
+    public static int BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC = 0;
+
+    //Service data Octet0
+    private static int BROADCAST_ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS = 0x00000001;
+    private static int BROADCAST_ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS = 0x00000002;
+
+    //Psync and PAST interfaces
+    private PeriodicAdvertisingManager mPeriodicAdvManager;
+    private static UUID BASIC_AUDIO_UUID = UUID.fromString("00001851-0000-1000-8000-00805F9B34FB");
+    private boolean mAutoAssist = false;
+    private boolean mNoReverse = false;
+    private boolean mAutoTriggerred = false;
+    private boolean mSyncingOnBehalfOfGroup = false;
+    private boolean mNoStopScanOffload = false;
+
+    //CSET interfaces
+    ///*_CSIP
+    private GroupService mSetCoordinator = GroupService.getGroupService();
+    private boolean mCsipConnected = false;
+    //_CSIP*/
+
+    private boolean mPacsAvail = false;
+    private boolean mDefNoPAS = false;
+    private boolean mNoPast = false;
+    private boolean mNoCSIPReconn = false;
+    private boolean mPublicAddrForcSrc = false;
+    private boolean mForceSB = false;
+    private boolean mVcpForBroadcast = false;
+
+    private int BROADCAST_SOURCE_ID_LENGTH = 3;
+    private byte mTempSourceId = 0;
+    //broadcast receiver state indicies
+    private static final int BCAST_RCVR_STATE_SRC_ID_IDX = 0;
+    private static final int BCAST_RCVR_STATE_SRC_ADDR_TYPE_IDX = 1;
+    private static final int BCAST_RCVR_STATE_SRC_ADDR_START_IDX = 2;
+    private static final int BCAST_RCVR_STATE_SRC_BCAST_ID_START_IDX = 9;
+    private static final int BCAST_RCVR_STATE_SRC_ADDR_SIZE = 6;
+    private static final int BCAST_RCVR_STATE_SRC_ADV_SID_IDX = 8;
+    private static final int BCAST_RCVR_STATE_PA_SYNC_IDX = 12;
+    private static final int BCAST_RCVR_STATE_ENC_STATUS_IDX = 13;
+    private static final int BCAST_RCVR_STATE_BADCODE_START_IDX = 14;
+    private static final int BCAST_RCVR_STATE_BADCODE_SIZE = 16;
+
+
+    private static final int BCAST_RCVR_STATE_BIS_SYNC_START_IDX = 10;
+    private static final int BCAST_RCVR_STATE_BIS_SYNC_SIZE = 4;
+    private static final int BCAST_RCVR_STATE_METADATA_LENGTH_IDX = 15;
+    private static final int BCAST_RCVR_STATE_METADATA_START_IDX = 16;
+    BassClientStateMachine(BluetoothDevice device, BCService svc,
+            Looper looper) {
+        super(TAG + "(" + device.toString() + ")", looper);
+        mDevice = device;
+        mService = svc;
+
+        mDisconnected = new Disconnected();
+        mDisconnecting = new Disconnecting();
+        mConnected = new Connected();
+        mConnecting = new Connecting();
+        mConnectedProcessing = new  ConnectedProcessing();
+
+        addState(mDisconnected);
+        addState(mDisconnecting);
+        addState(mConnected);
+        addState(mConnecting);
+        addState(mConnectedProcessing);
+
+        setInitialState(mDisconnected);
+        mBroadcastReceiverStates = new ArrayList<BluetoothGattCharacteristic>();
+        mBleBroadcastSourceInfos = new HashMap<Integer, BleBroadcastSourceInfo>();
+
+        //PSYNC and PAST instances
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mBluetoothAdapter != null) {
+            mPeriodicAdvManager = mBluetoothAdapter.getPeriodicAdvertisingManager();
+        }
+        ///*_BMS
+        mBAService = BroadcastService.getBroadcastService();
+        //_BMS*/
+
+        mNoReverse = SystemProperties.getBoolean("persist.vendor.service.bt.nReverse", false);
+        mAutoAssist = SystemProperties.getBoolean("persist.vendor.service.bt.autoassist", false);
+        mIsWhitelist = SystemProperties.getBoolean("persist.vendor.service.bt.wl", true);
+        mDefNoPAS = SystemProperties.getBoolean("persist.vendor.service.bt.defNoPAS", false);
+        mNoPast = SystemProperties.getBoolean("persist.vendor.service.bt.noPast", false);
+        mNoCSIPReconn = SystemProperties.getBoolean("persist.vendor.service.bt.noCsipRec", false);
+        mPublicAddrForcSrc = SystemProperties.getBoolean("persist.vendor.service.bt.pAddrForcSource", true);
+        mForceSB = SystemProperties.getBoolean("persist.vendor.service.bt.forceSB", false);
+        mVcpForBroadcast = SystemProperties.getBoolean("persist.vendor.service.bt.vcpForBroadcast", true);
+    }
+
+    static BassClientStateMachine make(BluetoothDevice device, BCService svc,
+            Looper looper) {
+        Log.d(TAG, "make for device " + device);
+        BassClientStateMachine BassclientSm = new BassClientStateMachine(device, svc,
+                looper);
+        BassclientSm.start();
+        return BassclientSm;
+    }
+
+    public void doQuit() {
+        log("doQuit for device " + mDevice);
+        quitNow();
+    }
+
+    public void cleanup() {
+        log("cleanup for device " + mDevice);
+        clearCharsCache();
+
+        if (mBluetoothGatt != null) {
+            log("disconnect gatt");
+            mBluetoothGatt.disconnect();
+            mBluetoothGatt.close();
+            mBluetoothGatt = null;
+        }
+        mPendingOperation = -1;
+        mPendingSourceId = -1;
+    }
+
+    BleBroadcastSourceInfo getBroadcastSourceInfoForSourceDevice (BluetoothDevice srcDevice) {
+        List<BleBroadcastSourceInfo> currentSourceInfos =
+                getAllBroadcastSourceInformation();
+        BleBroadcastSourceInfo srcInfo = null;
+        for (int i=0; i<currentSourceInfos.size(); i++) {
+            BluetoothDevice device = currentSourceInfos.get(i).getSourceDevice();
+            if (device != null && device.equals(srcDevice)) {
+                srcInfo = currentSourceInfos.get(i);
+                Log.e(TAG, "getBroadcastSourceInfoForSourceDevice: returns for: " + srcDevice + "&srcInfo" + srcInfo);
+                return srcInfo;
+            }
+        }
+        return null;
+     }
+
+    BleBroadcastSourceInfo getBroadcastSourceInfoForSourceId (int srcId) {
+        List<BleBroadcastSourceInfo> currentSourceInfos =
+                getAllBroadcastSourceInformation();
+        BleBroadcastSourceInfo srcInfo = null;
+        for (int i=0; i<currentSourceInfos.size(); i++) {
+            int sId = currentSourceInfos.get(i).getSourceId();
+            if (srcId == sId) {
+                srcInfo = currentSourceInfos.get(i);
+                Log.e(TAG, "getBroadcastSourceInfoForSourceId: returns for: " + srcId + "&srcInfo" + srcInfo);
+                return srcInfo;
+            }
+        }
+        return null;
+     }
+
+    void parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData) {
+        log("parseBaseData" + Arrays.toString(serviceData));
+        BaseData base_ = new BaseData(serviceData);
+        if (base_ != null) {
+            mService.updateBASE(syncHandle, base_);
+            base_.print();
+            base_.printConsolidated();
+            if (mAutoTriggerred == false) {
+                mService.sendBroadcastSourceSelectedCallback(device, base_.getBroadcastChannels(),
+                  BleBroadcastAudioScanAssistCallback.BASS_STATUS_SUCCESS);
+            } else {
+                //successfull auto periodic synchrnization with source
+                log("auto triggered assist");
+                mAutoTriggerred = false;
+                //perform PAST with this device
+                BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
+                if (srcDevice != null) {
+                    BleBroadcastSourceInfo srcInfo = getBroadcastSourceInfoForSourceDevice(srcDevice);
+                    processPASyncState(srcInfo);
+                } else {
+                    Log.w(TAG, "Autoassist: no matching device");
+                }
+            }
+        } else {
+            //
+            Log.e(TAG, "Seems BASE is not in parsable format");
+            if (mAutoTriggerred == false) {
+                mService.sendBroadcastSourceSelectedCallback(device, base_.getBroadcastChannels(),
+                          BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_SELECTED);
+                BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
+                cancelActiveSync(srcDevice);
+            } else {
+                mAutoTriggerred = false;
+            }
+        }
+    }
+
+    void parseScanRecord(int syncHandle, ScanRecord record) {
+        log("parseScanRecord" + record);
+        BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
+        Map<ParcelUuid, byte[]> bmsAdvDataMap = record.getServiceData();
+        if (bmsAdvDataMap != null) {
+            for (Map.Entry<ParcelUuid,byte[]> entry : bmsAdvDataMap.entrySet()) {
+                log("ParcelUUid = " + entry.getKey() +
+                             ", Value = " + entry.getValue());
+            }
+        } else {
+           log("bmsAdvDataMap is null");
+           if (mAutoTriggerred == false) {
+               mService.sendBroadcastSourceSelectedCallback(mDevice, null,
+                  BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_SELECTED);
+               cancelActiveSync(srcDevice);
+           } else {
+               mAutoTriggerred = false;
+           }
+        }
+        ParcelUuid basicAudioUuid = new ParcelUuid(BASIC_AUDIO_UUID);
+        byte[] bmsAdvData = record.getServiceData(basicAudioUuid);
+        if (bmsAdvData != null) {
+            //ByteBuffer bb = ByteBuffer.wrap(bmsAdvData);
+            parseBaseData(mDevice, syncHandle, bmsAdvData);
+
+        } else {
+            Log.e(TAG, "No service data in Scan record");
+            if (mAutoTriggerred == false) {
+                mService.sendBroadcastSourceSelectedCallback(mDevice, null,
+                  BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_SELECTED);
+                cancelActiveSync(srcDevice);
+            } else {
+                mAutoTriggerred = false;
+            }
+        }
+    }
+
+    /*Local Public address based check
+    Use this prior to addition of Broadcast source*/
+    boolean isLocalBroadcastSource (BluetoothDevice device)
+    {
+        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+        boolean ret = btAdapter.getAddress().equals(device.getAddress());
+
+        log("isLocalBroadcastSource returns" +ret);
+        return ret;
+    }
+
+    private boolean isValidBroadcastSourceInfo(BleBroadcastSourceInfo srcInfo) {
+        boolean ret = true;
+        List<BleBroadcastSourceInfo> currentSourceInfos =
+            getAllBroadcastSourceInformation();
+        Log.i(TAG, "input srcInfo: " + srcInfo);
+        for (int i=0; i<currentSourceInfos.size(); i++) {
+            Log.i(TAG, "srcInfo:  [" + i + "]" + currentSourceInfos.get(i));
+            if (srcInfo.matches(currentSourceInfos.get(i))) {
+                ret = false;
+                break;
+            }
+        }
+
+        log("isValidBroadcastSourceInfo returns: " + ret);
+        return ret;
+    }
+
+    public boolean selectBroadcastSource (ScanResult scanRes, boolean groupOp, boolean autoTriggerred) {
+        Log.i(TAG, "selectBroadcastSource for " + "isGroupOp:" + groupOp);
+        Log.i(TAG, "ScanResult " + scanRes);
+        int broadcastId = BCService.INVALID_BROADCAST_ID;
+        if (isLocalBroadcastSource(scanRes.getDevice()) != true) {
+            mAutoTriggerred = autoTriggerred;
+            mFirstTimeBisDiscovery = true;
+            mPASyncRetryCounter = 1;
+            //Cache Scan res for Retrys
+            mScanRes = scanRes;
+            /*This is an override case
+              if Previous sync is still active, cancel It
+              But dont stop the Scan offload as we still trying to assist remote*/
+            mNoStopScanOffload = true;
+            cancelActiveSync(null);
+            mService.getBassUtils().leScanControl(true);
+            try {
+               mPeriodicAdvManager.registerSync(scanRes, 0,
+                                            PSYNC_TIMEOUT, mPeriodicAdvCallback);
+               mSyncingOnBehalfOfGroup = groupOp;
+            } catch (IllegalArgumentException ex) {
+                Log.w(TAG, "registerSync:IllegalArguementException");
+                mService.sendBroadcastSourceSelectedCallback(mDevice, null,
+                    BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_SELECTED);
+                mService.stopScanOffloadInternal(mDevice, false);
+                return false;
+            }
+            //updating mainly for Address type and PA Interval here
+            //extract BroadcastId from ScanResult
+            ScanRecord scanRecord = scanRes.getScanRecord();
+            if (scanRecord != null) {
+                Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData();
+                if (listOfUuids != null) {
+                    if(listOfUuids.containsKey(ParcelUuid.fromString(BassUtils.BAAS_UUID))) {
+                        byte[] bId = listOfUuids.get(ParcelUuid.fromString(BassUtils.BAAS_UUID));
+                        broadcastId = (0x00FF0000 & (bId[0] << 16));
+                        broadcastId |= (0x0000FF00 & (bId[1] << 8));
+                        broadcastId |= (0x000000FF & bId[2]);
+                }
+            }
+            mService.updatePAResultsMap(scanRes.getDevice(), scanRes.getAddressType(),
+                          BCService.INVALID_SYNC_HANDLE, BCService.INVALID_ADV_SID,
+                          scanRes.getPeriodicAdvertisingInterval(),
+                          broadcastId);
+                }
+        }
+        else {
+            log("colocated case");
+            if (autoTriggerred) {
+                log("should never happen!!!");
+                //ignore
+                mAutoTriggerred = false;
+            }
+            ///*_BMS
+            if (mBAService == null || mBAService.isBroadcastActive() != true) {
+                Log.e(TAG, "colocated source handle unavailable OR not in streaming");
+                mService.sendBroadcastSourceSelectedCallback(mDevice, null,
+                    BleBroadcastAudioScanAssistCallback.BASS_STATUS_COLOCATED_SRC_UNAVAILABLE);
+                mService.stopScanOffloadInternal(mDevice, false);
+                return false;
+            }
+            String colocatedAddress = null;
+            int colocatedAddressType;
+            if (mPublicAddrForcSrc == true) {
+                colocatedAddress = BluetoothAdapter.getDefaultAdapter().getAddress();
+                colocatedAddressType = BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC;
+            } else {
+                colocatedAddress = mBAService.BroadcastGetAdvAddress();
+                colocatedAddressType = mBAService.BroadcastGetAdvAddrType();
+            }
+            int paInterval = 0x0000FFFF;
+            paInterval = mBAService.BroadcastGetAdvInterval();
+
+            if (colocatedAddress == null) {
+                log("colocatedAddress is null");
+                mService.sendBroadcastSourceSelectedCallback(mDevice, null,
+                    BleBroadcastAudioScanAssistCallback.BASS_STATUS_COLOCATED_SRC_UNAVAILABLE);
+                mService.stopScanOffloadInternal(mDevice, false);
+                return false;
+            }
+            BluetoothDevice colocatedSrcDevice =
+                                BluetoothAdapter.getDefaultAdapter().getRemoteDevice(colocatedAddress);
+            log("caching local Broacast details: " + colocatedSrcDevice);
+
+            //advSid is same as advHandle for collocated case
+            byte[] broadcast_id = mBAService.getBroadcastId();
+            broadcastId = (0x00FF0000 & (broadcast_id[2] << 16));
+            broadcastId |= (0x0000FF00 & (broadcast_id[1] << 8));
+            broadcastId |= (0x000000FF & broadcast_id[0]);
+
+            mService.updatePAResultsMap(colocatedSrcDevice, colocatedAddressType,
+                mBAService.BroadcatGetAdvHandle(),
+                mBAService.BroadcatGetAdvHandle(),
+                paInterval,
+                broadcastId);
+                BaseData localBase = new BaseData(mBAService.getNumSubGroups(),
+                                 mBAService.BroadcastGetBisInfo(),
+                                 mBAService.BroadcastGetMetaInfo());
+                localBase.printConsolidated();
+                //Use advHandle to cahce Base
+                mService.updateBASE(mBAService.BroadcatGetAdvHandle(), localBase);
+                mService.sendBroadcastSourceSelectedCallback(mDevice, localBase.getBroadcastChannels(),
+                    BleBroadcastAudioScanAssistCallback.BASS_STATUS_SUCCESS);
+            //_BMS*/
+        }
+        return true;
+    }
+
+    private void cancelActiveSync(BluetoothDevice sourceDev) {
+        log("cancelActiveSync");
+        boolean isCancelSyncNeeded = false;
+        BluetoothDevice activeSyncedSrc = mService.getActiveSyncedSource(mDevice);
+        if (activeSyncedSrc != null ) {
+            if (sourceDev == null) {
+                isCancelSyncNeeded = true;
+            } else if(activeSyncedSrc.equals(sourceDev)) {
+                isCancelSyncNeeded = true;
+            }
+        }
+        if (isCancelSyncNeeded) {
+            removeMessages(PSYNC_ACTIVE_TIMEOUT);
+            try {
+                log("calling unregisterSync");
+                mPeriodicAdvManager.unregisterSync(mPeriodicAdvCallback);
+            } catch (IllegalArgumentException ex) {
+                Log.w(TAG, "unregisterSync:IllegalArguementException");
+                //ignore
+            }
+            mService.clearPAResults(activeSyncedSrc);
+            mService.setActiveSyncedSource(mDevice, null);
+            if (mNoStopScanOffload != true) {
+                //trigger scan stop here
+                mService.stopScanOffloadInternal(mDevice, false);
+            }
+        }
+        mNoStopScanOffload = false;
+    }
+
+    /* Internal periodc Advertising manager callback
+      *
+      */
+     private PeriodicAdvertisingCallback mPeriodicAdvCallback = new PeriodicAdvertisingCallback() {
+           @Override
+           public void onSyncEstablished(int syncHandle, BluetoothDevice device,
+             int advertisingSid, int skip, int timeout,
+             int status) {
+               log ("onSyncEstablished" + "syncHandle" + syncHandle +
+                          "device" + device + "advertisingSid" + advertisingSid +
+                          "skip" + skip + "timeout" + timeout + "status" +
+                          status);
+                //turn off the LeScan once sync estd
+                mService.getBassUtils().leScanControl(false);
+                if (status == BluetoothGatt.GATT_SUCCESS) {
+                    //upates syncHandle, advSid
+                    mService.updatePAResultsMap(device,
+                         BCService.INVALID_ADV_ADDRESS_TYPE,
+                         syncHandle, advertisingSid,
+                         BCService.INVALID_ADV_INTERVAL,
+                         BCService.INVALID_BROADCAST_ID);
+                    sendMessageDelayed(PSYNC_ACTIVE_TIMEOUT, PSYNC_ACTIVE_TIMEOUT_MS);
+                    mService.setActiveSyncedSource(mDevice, device);
+                } else {
+                    log("failed to sync to PA" + mPASyncRetryCounter);
+                       mScanRes = null;
+                       if (mAutoTriggerred == false) {
+                           mService.sendBroadcastSourceSelectedCallback(mDevice, null,
+                                BleBroadcastAudioScanAssistCallback.BASS_STATUS_SOURCE_UNAVAILABLE);
+                           mService.stopScanOffloadInternal(mDevice, false);
+                       }
+                    mAutoTriggerred = false;
+                }
+           }
+           @Override
+           public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {
+               log( "onPeriodicAdvertisingReport");
+               //Parse the BIS indicies from report's service data
+               if (mFirstTimeBisDiscovery) {
+                   parseScanRecord(report.getSyncHandle(),report.getData());
+                   mFirstTimeBisDiscovery = false;
+               }
+           }
+           @Override
+           public void onSyncLost(int syncHandle) {
+               log( "OnSyncLost" + syncHandle);
+               BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
+               cancelActiveSync(srcDevice);
+           }
+
+           public void onSyncTransfered(BluetoothDevice device, int status) {
+                 log("sync transferred:" + device + " : " + status);
+           }
+       };
+
+    private void broadcastReceiverState(BleBroadcastSourceInfo state, int index, int max_num_srcInfos) {
+        log("broadcastReceiverState: " + mDevice);
+
+        Intent intent = new Intent(BleBroadcastAudioScanAssistManager.ACTION_BROADCAST_SOURCE_INFO);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+        intent.putExtra(BleBroadcastSourceInfo.EXTRA_SOURCE_INFO, state);
+        intent.putExtra(BleBroadcastSourceInfo.EXTRA_SOURCE_INFO_INDEX, index);
+        intent.putExtra(BleBroadcastSourceInfo.EXTRA_MAX_NUM_SOURCE_INFOS, max_num_srcInfos);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mService.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT,
+              Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    private static boolean isEmpty(final byte[] data){
+        return IntStream.range(0, data.length).parallel().allMatch(i -> data[i] == 0);
+    }
+
+   private void processPASyncState(BleBroadcastSourceInfo srcInfo) {
+       log("processPASyncState" + srcInfo);
+       int serviceData = 0;
+       if (srcInfo == null) {
+           Log.e(TAG, "processPASyncState: srcInfo is null");
+           return;
+       }
+       if (srcInfo.getMetadataSyncState() == BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_SYNCINFO_REQ) {
+           log("Initiate PAST procedure");
+            BCService.PAResults res = mService.getPAResults(srcInfo.getSourceDevice());
+            if (isAddedBroadcastSourceIsLocal(srcInfo.getSourceDevice()) &&
+                mService.isLocalBroadcasting()) {
+                if (res == null) {
+                    log("Populate colocated PA and initiate PAST");
+
+                    int colocatedAddressType;
+                    if (mPublicAddrForcSrc == true) {
+                        colocatedAddressType = BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC;
+                    } else {
+                        colocatedAddressType = mBAService.BroadcastGetAdvAddrType();
+                    }
+                    int broadcastId;
+                    byte[] broadcast_id = mBAService.getBroadcastId();
+                    broadcastId = (0x00FF0000 & (broadcast_id[2] << 16));
+                    broadcastId |= (0x0000FF00 & (broadcast_id[1] << 8));
+                    broadcastId |= (0x000000FF & broadcast_id[0]);
+                    mService.updatePAResultsMap(srcInfo.getSourceDevice(), colocatedAddressType,
+                                                mBAService.BroadcatGetAdvHandle(),
+                                                mBAService.BroadcatGetAdvHandle(),
+                                                mBAService.BroadcastGetAdvInterval(),
+                                                broadcastId);
+                }
+                res = mService.getPAResults(srcInfo.getSourceDevice());
+            }
+            if (res != null) {
+                int syncHandle = res.mSyncHandle;
+                log("processPASyncState: syncHandle" + res.mSyncHandle);
+                if (mNoPast == false && syncHandle != BCService.INVALID_SYNC_HANDLE) {
+                    if (isAddedBroadcastSourceIsLocal(srcInfo.getSourceDevice())) {
+                        log("Collocated Case Initiate PAST for :" + mDevice + "syncHandle:" +  syncHandle +
+                            "serviceData" + serviceData);
+                        serviceData = 0x000000FF & srcInfo.getSourceId();
+                        serviceData = serviceData << 8;
+                        //advA matches EXT_ADV_ADDRESS
+                        //but not matches source address (as we would have written pAddr)
+                        serviceData = serviceData & (~BROADCAST_ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS);
+                        serviceData = serviceData | (BROADCAST_ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS);
+                        try {
+                            mPeriodicAdvManager.transferSetInfo(mDevice, serviceData, syncHandle,mPeriodicAdvCallback);
+                        } catch (IllegalArgumentException ex) {
+                            Log.w(TAG, "transferSetInfo: IllegalArgumentException : PAST failure");
+                            //ignore
+                        }
+                     } else {
+                        serviceData = 0x000000FF & srcInfo.getSourceId();
+                        serviceData = serviceData << 8;
+                        //advA matches EXT_ADV_ADDRESS
+                        //also matches source address (as we would have written)
+                        serviceData = serviceData & (~BROADCAST_ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS);
+                        serviceData = serviceData & (~BROADCAST_ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS);
+                        log("Initiate PAST for :" + mDevice + "syncHandle:" +  syncHandle +
+                            "serviceData" + serviceData);
+                        mPeriodicAdvManager.transferSync(mDevice, serviceData, syncHandle);
+                    }
+                }
+            } else {
+                Log.e(TAG, "There is no valid sync handle for this Source");
+                if (mAutoAssist) {
+                    //initiate Auto Assist procedure for this device
+                    mService.getBassUtils().triggerAutoAssist (srcInfo);
+                }
+            }
+       }
+        else if (srcInfo.getAudioSyncState() == BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED ||
+            srcInfo.getMetadataSyncState() == BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_NO_PAST) {
+           //Cancel the existing sync and Invalidate the sync handle
+           if (isAddedBroadcastSourceIsLocal(srcInfo.getSourceDevice()) == false) {
+               if (mSyncingOnBehalfOfGroup == false) {
+                   //Cancel the Sync only If it is NOT syced on behalf of group.
+                   //group based sync will be kept active PSYNC_ACTIVE_TIMEOUT seconds so that
+                   //all group members can get back in sync
+                   log("Unregister sync as It is non colocated");
+                   cancelActiveSync(srcInfo.getSourceDevice());
+               }
+           } else {
+               //trigger scan stop here
+               mService.stopScanOffloadInternal(mDevice, false);
+           }
+       }
+   }
+
+    /*Actual OTA advertising address based check
+    Use this after the addition of Broadcast source*/
+    private boolean isAddedBroadcastSourceIsLocal (BluetoothDevice device)
+    {
+        if (device  == null) {
+            Log.e(TAG, "device handle is null");
+            return false;
+        }
+        String localBroadcasterAddr = null;
+        ///*_BMS
+        if (mPublicAddrForcSrc) {
+            localBroadcasterAddr = BluetoothAdapter.getDefaultAdapter().getAddress();
+        } else {
+            if (mBAService == null) {
+                mBAService = BroadcastService.getBroadcastService();
+            }
+            if (mBAService == null || mBAService.isBroadcastActive() != true) {
+                Log.e(TAG, "isAddedBroadcastSourceIsLocal: colocated source handle is unavailable");
+                return false;
+            }
+            localBroadcasterAddr = mBAService.BroadcastGetAdvAddress();
+        }
+        //_BMS*/
+        boolean ret = false;
+        if (localBroadcasterAddr == null) {
+            Log.e(TAG, "isAddedBroadcastSourceIsLocal: localBroadcasterAddr is null");
+            ret = false;
+        } else {
+            ret = localBroadcasterAddr.equals(device.getAddress());
+        }
+        log("isAddedBroadcastSourceIsLocal returns" +ret);
+        return ret;
+    }
+
+    private void checkAndUpdateBroadcastCode(BleBroadcastSourceInfo srcInfo) {
+        if (isAddedBroadcastSourceIsLocal(srcInfo.getSourceDevice())) {
+            if (mForceSB == true ||
+                srcInfo.getEncryptionStatus() == BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED) {
+                //query the Encryption Key from BMS and update
+                ///*_BMS
+                byte[] colocatedBcastCode = mBAService.GetEncryptionKey(null);
+                if (mBAService.isBroadcastStreamingEncrypted() == false) {
+                    Log.e(TAG, "seem to be Unencrypted colocated broadcast");
+                    //do nothing
+                    return;
+                }
+                log("colocatedBcastCode is " + colocatedBcastCode);
+                //queue a fresh command to update the
+                Message m = obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
+                m.obj = srcInfo;
+                m.arg1 = FRESH;
+                log("checkAndUpdateBroadcastCode: src device: " + srcInfo.getSourceDevice());
+                sendMessage(m);
+                //_BMS*/
+            }
+        } else {
+            log("checkAndUpdateBroadcastCode");
+            //non colocated case, Broadcast PIN should have been updated from lyaer
+            //If there is pending one process it Now
+            if (srcInfo.getEncryptionStatus() == BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED &&
+                mSetBroadcastCodePending == true) {
+                //Make a QUEUED command
+                log("Update the Broadcast now");
+                Message m = obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
+                m.obj = mSetBroadcastPINSrcInfo;
+                m.arg1 = QUEUED;
+
+                sendMessage(m);
+                mSetBroadcastCodePending = false;
+                mSetBroadcastPINSrcInfo = null;
+            }
+           }
+   }
+
+   private List<BleBroadcastSourceChannel> getListOfBisIndicies(int bisIndicies, int subGroupId, byte[] metaData) {
+       List<BleBroadcastSourceChannel> bcastIndicies = new ArrayList<BleBroadcastSourceChannel>();
+        int index =0;
+        log("getListOfBisIndicies:" + bisIndicies);
+        while (bisIndicies != 0) {
+            if ((bisIndicies & 0x00000001) == 0x00000001) {
+                BleBroadcastSourceChannel bI =
+                    new BleBroadcastSourceChannel(index, Integer.toString(index), true, subGroupId, metaData);
+                bcastIndicies.add(bI);
+                log("Adding BIS index for :" + index);
+            }
+            bisIndicies = bisIndicies>>1;
+            index++;
+        }
+        return bcastIndicies;
+
+    }
+
+    private void processBroadcastReceiverState (byte[] receiverState, BluetoothGattCharacteristic characteristic) {
+        int index = -1;
+        boolean isEmptyEntry = false;
+        BleBroadcastSourceInfo srcInfo = null;
+
+        log("processBroadcastReceiverState:: characteristic:" + characteristic);
+
+        byte sourceId = 0;
+        if (receiverState.length > 0)
+            sourceId = receiverState[BCAST_RCVR_STATE_SRC_ID_IDX];
+        log("processBroadcastReceiverState: receiverState length: " + receiverState.length);
+        if (receiverState.length == 0 ||
+            isEmpty(Arrays.copyOfRange(receiverState, 1, receiverState.length-1))) {
+            log("This is an Empty Entry");
+            if (mPendingOperation == REMOVE_BCAST_SOURCE) {
+                srcInfo = new BleBroadcastSourceInfo(mPendingSourceId);
+            } else if (receiverState.length == 0) {
+                if (mBleBroadcastSourceInfos != null) {
+                    mTempSourceId = (byte)mBleBroadcastSourceInfos.size();
+                }
+                if (mTempSourceId < NUM_OF_BROADCAST_RECEIVER_STATES) {
+                    mTempSourceId++;
+                    srcInfo = new BleBroadcastSourceInfo(mTempSourceId);
+                } else {
+                    Log.e(TAG, "reached the remote supported max SourceInfos");
+                    return;
+                }
+            }
+            isEmptyEntry = true;
+            //just create an Empty entry
+           if (srcInfo.isEmptyEntry()) {
+                log("An empty entry has been created");
+        }
+        }
+        else {
+            byte sourceAddressType = receiverState[BCAST_RCVR_STATE_SRC_ADDR_TYPE_IDX];
+            byte[] sourceAddress = new byte[BCAST_RCVR_STATE_SRC_ADDR_SIZE];
+            System.arraycopy(receiverState, BCAST_RCVR_STATE_SRC_ADDR_START_IDX, sourceAddress, 0, BCAST_RCVR_STATE_SRC_ADDR_SIZE);
+            byte sourceAdvSid = receiverState[BCAST_RCVR_STATE_SRC_ADV_SID_IDX];
+
+            byte[] broadcastIdBytes = new byte[BROADCAST_SOURCE_ID_LENGTH];
+            System.arraycopy(receiverState, BCAST_RCVR_STATE_SRC_BCAST_ID_START_IDX, broadcastIdBytes, 0, BROADCAST_SOURCE_ID_LENGTH);
+            int broadcastId = (0x00FF0000 & (broadcastIdBytes[2] << 16));
+            broadcastId |= (0x0000FF00 & (broadcastIdBytes[1] << 8));
+            broadcastId |= (0x000000FF & broadcastIdBytes[0]);
+
+            BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+            BluetoothDevice  device = btAdapter.getRemoteDevice(reverseBytes(sourceAddress));
+            byte metaDataSyncState = receiverState[BCAST_RCVR_STATE_PA_SYNC_IDX];
+
+
+            byte encyptionStatus = receiverState[BCAST_RCVR_STATE_ENC_STATUS_IDX];
+            byte[] badBroadcastCode = null;
+            byte badBroadcastCodeLen = 0;
+            byte numSubGroups = 0;
+            byte[] metadataLength = null;
+            byte[] metaData = null;
+            if (encyptionStatus == BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_BADCODE) {
+                badBroadcastCode = new byte[BCAST_RCVR_STATE_BADCODE_SIZE];
+                System.arraycopy(receiverState, BCAST_RCVR_STATE_BADCODE_START_IDX, badBroadcastCode, 0, BCAST_RCVR_STATE_BADCODE_SIZE);
+                badBroadcastCode = reverseBytes(badBroadcastCode);
+                badBroadcastCodeLen = BCAST_RCVR_STATE_BADCODE_SIZE;
+            }
+            numSubGroups = receiverState[BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen];
+            int offset = BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen + 1;
+            //Map of Bis Status <subGroupId, List_OF_Broadcast_channel>
+            Map<Integer, List<BleBroadcastSourceChannel>> bisIndexList = new HashMap<Integer, List<BleBroadcastSourceChannel>>();
+            //Map for Metada <subGroupId, Metadata>
+            Map<Integer, byte[]> metadataList = new HashMap<Integer, byte[]>();
+            metadataLength = new byte[numSubGroups];
+            byte audioSyncState = 0;
+            for (int i = 0; i < numSubGroups; i++) {
+                byte[] audioSyncIndex = new byte[BCAST_RCVR_STATE_BIS_SYNC_SIZE];
+                System.arraycopy(receiverState, offset, audioSyncIndex, 0, BCAST_RCVR_STATE_BIS_SYNC_SIZE);
+                offset = offset + BCAST_RCVR_STATE_BIS_SYNC_SIZE;
+                log("BIS index byte array: ");
+                BassUtils.printByteArray(audioSyncIndex);
+                ByteBuffer wrapped = ByteBuffer.wrap(reverseBytes(audioSyncIndex));
+                int audioBisIndex = wrapped.getInt();
+                if (audioBisIndex == 0xFFFFFFFF) {
+                    log("Remote failed to sync to BIS");
+                    audioSyncState = 0x00;
+                    audioBisIndex = 0;
+                } else {
+                    //Bits (0-30)=> (1-31)
+                    audioBisIndex = audioBisIndex << 1;
+                    log("BIS index converted: " + audioBisIndex);
+                    if (audioBisIndex != 0){
+                        //If any BIS is in sync, Set Audio state as ON
+                        audioSyncState = 0x01;
+                    }
+                }
+
+                metadataLength[i] =  receiverState[offset++];
+                if (metadataLength[i] != 0) {
+                    log("metadata of length: " + metadataLength[i] + "is avaialble");
+                    metaData = new byte[metadataLength[i]];
+                    System.arraycopy(receiverState, offset, metaData, 0, metadataLength[i]);
+                    offset = offset + metadataLength[i];
+                    metaData = reverseBytes(metaData);
+                    metadataList.put(i, metaData);
+                }
+                bisIndexList.put(i, getListOfBisIndicies(audioBisIndex, i, metaData));
+            }
+            srcInfo = new BleBroadcastSourceInfo(device,
+                                             sourceId,
+                                             sourceAdvSid,
+                                             broadcastId,
+                                             (int)sourceAddressType,
+                                             (int)metaDataSyncState,
+                                             (int)encyptionStatus,
+                                             badBroadcastCode,
+                                             numSubGroups,
+                                             (int)audioSyncState,
+                                             bisIndexList,
+                                             metadataList
+                                             );
+        }
+        BleBroadcastSourceInfo oldSourceInfo = mBleBroadcastSourceInfos.get(characteristic.getInstanceId());
+        if (oldSourceInfo == null) {
+            log("Initial Read and Populating values");
+            if (mBleBroadcastSourceInfos.size() == NUM_OF_BROADCAST_RECEIVER_STATES) {
+                Log.e(TAG, "reached the Max SourceInfos");
+                return;
+            }
+            mBleBroadcastSourceInfos.put(characteristic.getInstanceId(), srcInfo);
+            checkAndUpdateBroadcastCode(srcInfo);
+            processPASyncState(srcInfo);
+        } else {
+            log("old sourceInfo: " + oldSourceInfo);
+            log("new sourceInfo: " + srcInfo);
+            mBleBroadcastSourceInfos.replace(characteristic.getInstanceId(), srcInfo);
+            if (oldSourceInfo.isEmptyEntry() == true) {
+                log("New Source Addition");
+                sendPendingCallbacks(ADD_BCAST_SOURCE,
+                          srcInfo.getSourceId(), BluetoothGatt.GATT_SUCCESS);
+                checkAndUpdateBroadcastCode(srcInfo);
+                processPASyncState(srcInfo);
+            } else {
+                if (isEmptyEntry) {
+                    BluetoothDevice removedDevice = oldSourceInfo.getSourceDevice();
+                    log("sourceInfo removal" + removedDevice);
+                    cancelActiveSync(removedDevice);
+                    sendPendingCallbacks(REMOVE_BCAST_SOURCE,
+                          srcInfo.getSourceId(), BluetoothGatt.GATT_SUCCESS);
+                } else {
+                    log("update to an existing srcInfo");
+                    sendPendingCallbacks(UPDATE_BCAST_SOURCE,
+                          srcInfo.getSourceId(),BluetoothGatt.GATT_SUCCESS);
+                    processPASyncState(srcInfo);
+                    checkAndUpdateBroadcastCode(srcInfo);
+                }
+            }
+        }
+        index = srcInfo.getSourceId();
+        if (index == -1) {
+            log("processBroadcastReceiverState: invalid index");
+            return;
+        }
+        broadcastReceiverState(srcInfo, index, NUM_OF_BROADCAST_RECEIVER_STATES);
+    }
+    // Implements callback methods for GATT events that the app cares about.  For example,
+    // connection change and services discovered.
+    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
+        @Override
+        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+           boolean isStateChanged = false;
+           log( "onConnectionStateChange : Status=" + status + "newState" + newState);
+           if (newState == BluetoothProfile.STATE_CONNECTED && getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+              isStateChanged = true;
+               Log.w(TAG, "Bassclient Connected from Disconnected state: " + mDevice);
+               if (mService.okToConnect(mDevice)) {
+                   log("Bassclient Connected to: " + mDevice);
+                   if (mBluetoothGatt != null) {
+                       log( "Attempting to start service discovery:" +
+                        mBluetoothGatt.discoverServices());
+                       mDiscoveryInitiated = true;
+                   }
+               } else {
+                   if (mBluetoothGatt != null) {
+                       // Reject the connection
+                      Log.w(TAG, "Bassclient Connect request rejected: " + mDevice);
+                      mBluetoothGatt.disconnect();
+                      mBluetoothGatt.close();
+                      mBluetoothGatt = null;
+                      //force move to disconnected
+                      newState = BluetoothProfile.STATE_DISCONNECTED;
+                   }
+               }
+           } else if (newState == BluetoothProfile.STATE_DISCONNECTED &&
+                      getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
+                isStateChanged = true;
+                log( "Disconnected from Bass GATT server.");
+           }
+           if (isStateChanged) {
+               Message m = obtainMessage(CONNECTION_STATE_CHANGED);
+               m.obj = newState;
+               sendMessage(m);
+           }
+        }
+        @Override
+        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+            log("onServicesDiscovered:" + status);
+            if (mDiscoveryInitiated == true) {
+                mDiscoveryInitiated = false;
+                if (status == BluetoothGatt.GATT_SUCCESS && mBluetoothGatt != null) {
+                    mBluetoothGatt.requestMtu(BASS_MAX_BYTES);
+                    mMTUChangeRequested = true;
+                } else {
+                    Log.w(TAG, "onServicesDiscovered received: " + status
+                        + "mBluetoothGatt" + mBluetoothGatt);
+                }
+            } else {
+                log("remote initiated callback");
+                //do nothing
+            }
+        }
+        @Override
+        public void onCharacteristicRead(BluetoothGatt gatt,
+                                     BluetoothGattCharacteristic characteristic,
+                                     int status) {
+            log( "onCharacteristicRead:: status: " + status + "char:" + characteristic);
+
+            if (status == BluetoothGatt.GATT_SUCCESS &&
+                characteristic.getUuid().equals(BASS_BCAST_RECEIVER_STATE)) {
+                log( "onCharacteristicRead: BASS_BCAST_RECEIVER_STATE: status" + status);
+                logByteArray("Received ", characteristic.getValue(), 0,
+                     characteristic.getValue().length);
+                if (characteristic.getValue() == null) {
+                    Log.e(TAG, "Remote receiver state is NULL");
+                    return;
+                }
+                processBroadcastReceiverState(characteristic.getValue(), characteristic);
+            }
+            // switch to receiving notifications after initial characteristic read
+            BluetoothGattDescriptor desc = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
+            if (mBluetoothGatt != null && desc != null) {
+                log("Setting the value for Desc");
+                mBluetoothGatt.setCharacteristicNotification(characteristic, true);
+                desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+                mBluetoothGatt.writeDescriptor(desc);
+            } else {
+                Log.w(TAG, "CCC for " + characteristic + "seem to be not present");
+                //atleast move the SM to stable state
+                Message m = obtainMessage(GATT_TXN_PROCESSED);
+                m.arg1 = status;
+                sendMessage(m);
+            }
+        }
+
+        @Override
+        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+             int status) {
+             log("onDescriptorWrite");
+             if (status == BluetoothGatt.GATT_SUCCESS &&
+                 descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIG)) {
+                 log("CCC write resp");
+            }
+
+             //Move the SM to connected so further reads happens
+             Message m = obtainMessage(GATT_TXN_PROCESSED);
+             m.arg1 = status;
+             sendMessage(m);
+        }
+
+        @Override
+        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status)
+        {
+            log("onMtuChanged: mtu:" + mtu);
+            if (mMTUChangeRequested == true && mBluetoothGatt != null) {
+                acquireAllBassChars();
+                mMTUChangeRequested = false;
+            } else {
+                log("onMtuChanged is remote initiated trigger, mBluetoothGatt:" + mBluetoothGatt);
+                //Do nothing
+            }
+        }
+
+        @Override
+        public void onCharacteristicChanged(BluetoothGatt gatt,
+                                        BluetoothGattCharacteristic characteristic) {
+            log( "onCharacteristicChanged :: "
+                        + characteristic.getUuid().toString());
+            if (characteristic.getUuid().equals(BASS_BCAST_RECEIVER_STATE)) {
+                log( "onCharacteristicChanged is rcvr State :: "
+                        + characteristic.getUuid().toString());
+                if (characteristic.getValue() == null) {
+                    Log.e(TAG, "Remote receiver state is NULL");
+                    return;
+                }
+                logByteArray("onCharacteristicChanged: Received ", characteristic.getValue(), 0,
+                     characteristic.getValue().length);
+                processBroadcastReceiverState(characteristic.getValue(), characteristic);
+            }
+        }
+
+        @Override
+        public void onCharacteristicWrite(BluetoothGatt gatt,
+                BluetoothGattCharacteristic characteristic, int status) {
+            log( "onCharacteristicWrite: "
+                        + characteristic.getUuid().toString()
+                        + "status:" + status);
+            if (status == 0 &&
+                    characteristic.getUuid().equals(BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) {
+                log( "BASS_BCAST_AUDIO_SCAN_CTRL_POINT is written successfully");
+            }
+            Message m = obtainMessage(GATT_TXN_PROCESSED);
+            m.arg1 = status;
+            sendMessage(m);
+        }
+    };
+
+    public List<BleBroadcastSourceInfo> getAllBroadcastSourceInformation() {
+        log( "getAllBroadcastSourceInformation");
+        List list = new ArrayList(mBleBroadcastSourceInfos.values());
+        return list;
+    }
+
+    void acquireAllBassChars() {
+        clearCharsCache();
+        BluetoothGattService service = null;
+        if (mBluetoothGatt != null) {
+            log("getting Bass Service handle");
+            service = mBluetoothGatt.getService(BASS_UUID);
+        }
+        if (service != null) {
+            log( "found BASS_SERVICE");
+            List<BluetoothGattCharacteristic> allChars = service.getCharacteristics();
+            int numOfChars = allChars.size();
+            NUM_OF_BROADCAST_RECEIVER_STATES = numOfChars-1;
+            log( "Total number of chars" + numOfChars);
+            //static var to keep track of read callbacks
+            num_of_recever_states = NUM_OF_BROADCAST_RECEIVER_STATES;
+            for (int i = 0; i < allChars.size(); i++) {
+                if (allChars.get(i).getUuid().equals(BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) {
+                    mBroadcastScanControlPoint = allChars.get(i);
+                    log( "Index of ScanCtrlPoint:" + i);
+                } else {
+                    log( "Reading " + i + "th ReceiverState");
+                    mBroadcastReceiverStates.add(allChars.get(i));
+                    Message m = obtainMessage(READ_BASS_CHARACTERISTICS);
+                    m.obj = allChars.get(i);
+                    sendMessage(m);
+                }
+            }
+        } else {
+            Log.e(TAG, "acquireAllBassChars: BASS service not found");
+        }
+     }
+
+     void clearCharsCache() {
+         if (mBroadcastReceiverStates != null) {
+            mBroadcastReceiverStates.clear();
+        }
+        if (mBroadcastScanControlPoint != null) {
+            mBroadcastScanControlPoint = null;
+        }
+        num_of_recever_states = 0;
+        if (mBleBroadcastSourceInfos != null) {
+            mBleBroadcastSourceInfos.clear();
+        }
+        mPendingOperation = -1;
+     }
+
+    @VisibleForTesting
+    class Disconnected extends State {
+        @Override
+        public void enter() {
+            log( "Enter Disconnected(" + mDevice + "): " + messageWhatToString(
+                    getCurrentMessage().what));
+            clearCharsCache();
+            mTempSourceId = 0;
+            removeDeferredMessages(DISCONNECT);
+
+            if (mLastConnectionState == -1) {
+                log( "no Broadcast of initial profile state ");
+            } else {
+                if (mPacsAvail == true) {
+                    /*_PACS
+                    PacsClientService mPacsClientService = PacsClientService.getPacsClientService();
+                    if (mPacsClientService != null) {
+                        log("trigger disconnect to Pacs");
+                        mPacsClientService.disconnect(mDevice);
+                    } else {
+                       Log.e(TAG, "PACs interface is null");
+                    }
+                    _PACS*/
+                }
+
+                ///*_VCP
+                if (mVcpForBroadcast) {
+                    VcpController vcpController = VcpController.getVcpController();
+                    if (vcpController != null) {
+                        log("trigger disconnect to Vcp Renderer");
+                        if (!vcpController.disconnect(mDevice, BluetoothVcp.MODE_BROADCAST)) {
+                            log("Disconnect Vcp failed");
+                        }
+                    } else {
+                        Log.e(TAG, "VcpController interface is null");
+                    }
+                }
+                //_VCP*/
+                 broadcastConnectionState(mDevice, mLastConnectionState,
+                              BluetoothProfile.STATE_DISCONNECTED);
+            }
+        }
+
+        @Override
+        public void exit() {
+            log("Exit Disconnected(" + mDevice + "): " + messageWhatToString(
+                    getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Disconnected process message(" + mDevice + "): " + messageWhatToString(
+                    message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    log("Connecting to " + mDevice);
+                    if (mBluetoothGatt != null) {
+                        Log.d(TAG, "clear off, pending wl connection");
+                        mBluetoothGatt.disconnect();
+                        mBluetoothGatt.close();
+                        mBluetoothGatt = null;
+                    }
+
+                    if ((mBluetoothGatt = mDevice.connectGatt(mService, mIsWhitelist, mGattCallback,
+                            BluetoothDevice.TRANSPORT_LE, false, (BluetoothDevice.PHY_LE_1M_MASK |
+                            BluetoothDevice.PHY_LE_2M_MASK | BluetoothDevice.PHY_LE_CODED_MASK),
+                            null, true)) == null) {
+                        Log.e(TAG, "Disconnected: error connecting to " + mDevice);
+                        break;
+                    } else {
+                        transitionTo(mConnecting);
+                    }
+                    break;
+                case DISCONNECT:
+                    Log.w(TAG, "Disconnected: DISCONNECT ignored: " + mDevice);
+                    break;
+                case CONNECTION_STATE_CHANGED:
+                    int state = (int)message.obj;
+                    Log.w(TAG, "connection state changed:" + state);
+                    if (state == BluetoothProfile.STATE_CONNECTED) {
+                        log("remote/wl connection, ensure csip is up as well");
+                        if (mNoCSIPReconn == false && mService != null &&
+                           mService.isLockSupportAvailable(mDevice)) {
+                            /////*_CSIP
+                            mCsipConnected = false;
+                            mSetCoordinator.connect(mService.mCsipAppId, mDevice);
+                            transitionTo(mConnecting);
+                            break;
+                            ////_CSIP*/
+                        } else {
+                            transitionTo(mConnected);
+                        }
+                    } else {
+                        Log.w(TAG, "Disconected: Connection failed to " + mDevice);
+                    }
+                    break;
+                case PSYNC_ACTIVE_TIMEOUT:
+                    cancelActiveSync(null);
+                    break;
+                default:
+                    log("DISCONNECTED: not handled message:" + message.what);
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+    @VisibleForTesting
+     class Connecting extends State {
+         @Override
+         public void enter() {
+             log( "Enter Connecting(" + mDevice + "): " + messageWhatToString(
+                     getCurrentMessage().what));
+             sendMessageDelayed(CONNECT_TIMEOUT, mDevice, mConnectTimeoutMs);
+             broadcastConnectionState(mDevice, mLastConnectionState,
+                               BluetoothProfile.STATE_CONNECTING);
+         }
+
+         @Override
+         public void exit() {
+             log("Exit Connecting(" + mDevice + "): " + messageWhatToString(
+                     getCurrentMessage().what));
+             mLastConnectionState = BluetoothProfile.STATE_CONNECTING;
+             removeMessages(CONNECT_TIMEOUT);
+         }
+
+         @Override
+         public boolean processMessage(Message message) {
+             log("Connecting process message(" + mDevice + "): " + messageWhatToString(
+                     message.what));
+
+             switch (message.what) {
+                 case CONNECT:
+                     log("Already Connecting to " + mDevice);
+                     log("Ignore this connection request " + mDevice);
+                     break;
+                 case DISCONNECT:
+                     Log.w(TAG, "Connecting: DISCONNECT deferred: " + mDevice);
+                     deferMessage(message);
+                     break;
+                 case READ_BASS_CHARACTERISTICS:
+                     Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice);
+                     deferMessage(message);
+                     break;
+                 case CSIP_CONNECTION_STATE_CHANGED:
+                     int state = (int)message.obj;
+                    if (state == BluetoothProfile.STATE_CONNECTED) {
+                        ///*_CSIP
+                        if (mCsipConnected == true) {
+                            Log.e(TAG,  "CSIP is already up, ignore this DUP event");
+                            break;
+                        }
+                        mCsipConnected = true;
+                        Log.d(TAG, "Csip connected");
+                        transitionTo(mConnected);
+                    } else {
+                        Log.w(TAG, "CSIP Connection failed to " + mDevice);
+                        if (mBluetoothGatt != null) {
+                            //disc bass
+                            mBluetoothGatt.disconnect();
+                            mBluetoothGatt.close();
+                            mBluetoothGatt = null;
+                        }
+                        transitionTo(mDisconnected);
+                    }
+
+                    break;
+                 case CONNECTION_STATE_CHANGED:
+                     state = (int)message.obj;
+                     Log.w(TAG, "Connecting: connection state changed:" + state);
+                     if (state == BluetoothProfile.STATE_CONNECTED) {
+                         if (mService != null &&
+                             mService.isLockSupportAvailable(mDevice)) {
+                             ///*_CSIP
+                             //If Lock support available & connect to csip
+                             mCsipConnected = false;
+                             mSetCoordinator.connect(mService.mCsipAppId, mDevice);
+                             break;
+                             //_CSIP*/
+                         } else {
+                             transitionTo(mConnected);
+                         }
+                     } else {
+                         Log.w(TAG, "Connection failed to " + mDevice);
+                         transitionTo(mDisconnected);
+                     }
+                     break;
+                 case CONNECT_TIMEOUT:
+                    Log.w(TAG, "CONNECT_TIMEOUT");
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    if (!mDevice.equals(device)) {
+                         Log.e(TAG, "Unknown device timeout " + device);
+                         break;
+                    }
+                    transitionTo(mDisconnected);
+                    break;
+                 case PSYNC_ACTIVE_TIMEOUT:
+                    deferMessage(message);
+                    break;
+                 default:
+                      log("CONNECTING: not handled message:" + message.what);
+                     return NOT_HANDLED;
+             }
+             return HANDLED;
+         }
+     }
+
+    private byte[] reverseBytes(byte[] a) {
+        if (mNoReverse) {
+               log("no reverse is enabled>");
+               return a;
+        }
+        for(int i=0; i<a.length/2; i++){
+          byte tmp = a[i];
+          a[i] = a[a.length -i -1];
+          a[a.length -i -1] = tmp;
+        }
+
+        return a;
+    }
+
+    private byte[] BluetoothAddressToBytes (String s) {
+        log("BluetoothAddressToBytes: input string:" + s);
+        String[] splits = s.split(":");
+
+        byte[] addressBytes = new byte[6];
+        for (int i=0; i<6; i++) {
+            int hexValue = Integer.parseInt(splits[i], 16);
+            log("hexValue:" + hexValue);
+            addressBytes[i] = (byte)hexValue;
+        }
+
+        return addressBytes;
+
+    }
+
+    private int convertBisIndiciesToIntegerValue(List<BleBroadcastSourceChannel> bisIndicies, int subGroupId) {
+        int audioBisIndex = 0;
+        if (bisIndicies != null) {
+            for (int i=0; i<bisIndicies.size(); i++) {
+                if (bisIndicies.get(i).getStatus() == true && bisIndicies.get(i).getSubGroupId() == subGroupId) {
+                    audioBisIndex = audioBisIndex | 1<<(bisIndicies.get(i).getIndex()-1);
+                    log( "set the bit" + bisIndicies.get(i).getIndex() + "after:" + audioBisIndex);
+                }
+            }
+        } else {
+            log("bisIndicies Channels are null");
+            audioBisIndex = 0xFFFFFFFF;
+
+        }
+        log( "audioBisIndex" + audioBisIndex);
+
+        return audioBisIndex;
+    }
+
+    private byte[] convertSourceInfoToAddSourceByteArray(BleBroadcastSourceInfo srcInfo) {
+        byte[] res;
+        /*fixed length for add source op*/
+        int addSourceFixedLength = 16;
+        byte[] metaDataLength = null;
+        BluetoothDevice broadcastSource = null;
+        String localBcastAddr = null;
+        BCService.PAResults paRes = null;
+        log("Get PAresults for :" + srcInfo.getSourceDevice());
+        broadcastSource = srcInfo.getSourceDevice();
+
+        ///*_BMS
+        if (mPublicAddrForcSrc == false) {
+            if (isLocalBroadcastSource(broadcastSource)){
+                //update broadcastSource if it is colocated
+                if (mBAService != null) {
+                    localBcastAddr = mBAService.BroadcastGetAdvAddress();
+                }
+                if (localBcastAddr == null) {
+                    Log.w(TAG, "convertSourceInfoToAddSourceByteArray: localBCast not avaiable");
+                    sendPendingCallbacks(ADD_BCAST_SOURCE,INVALID_SRC_ID,
+                       BleBroadcastAudioScanAssistCallback.BASS_STATUS_SOURCE_UNAVAILABLE);
+                    return null;
+                } else {
+                   broadcastSource =
+                       BluetoothAdapter.getDefaultAdapter().getRemoteDevice(localBcastAddr);
+                       log("convertSourceInfoToAddSourceByteArray: colocated case: " + broadcastSource);
+                }
+            }
+        }
+        //_BMS*/
+        paRes = mService.getPAResults(broadcastSource);
+        if (paRes == null) {
+            Log.e(TAG, "No mathcing psync, scan res for this addition");
+            sendPendingCallbacks(ADD_BCAST_SOURCE,INVALID_SRC_ID,
+                       BleBroadcastAudioScanAssistCallback.BASS_STATUS_SOURCE_UNAVAILABLE);
+            return null;
+        }
+
+        //populate metadata from BASE levelOne
+        BaseData base_ = mService.getBASE(paRes.mSyncHandle);
+        if (base_ == null) {
+            Log.e(TAG, "No valid base data populated for this device");
+            sendPendingCallbacks(ADD_BCAST_SOURCE,INVALID_SRC_ID,
+                       BleBroadcastAudioScanAssistCallback.BASS_STATUS_SOURCE_UNAVAILABLE);
+            return null;
+        }
+        int numSubGroups = base_.getNumberOfSubgroupsofBIG();
+        metaDataLength = new byte[numSubGroups];
+        int totalMetadataLength = 0;
+        for (int i=0; i<numSubGroups; i++) {
+            if (base_.getMetadata(i) == null) {
+                Log.w(TAG, "no valid metadata from BASE");
+                metaDataLength[i] = 0;
+            } else {
+                metaDataLength[i] = (byte)base_.getMetadata(i).length;
+                log("metaDataLength updated:" + metaDataLength[i]);
+            }
+            totalMetadataLength = totalMetadataLength + metaDataLength[i];
+        }
+        res = new byte [addSourceFixedLength + numSubGroups*5 + totalMetadataLength];
+        srcInfo.setSourceDevice(broadcastSource);
+        srcInfo.setAdvAddressType((byte)paRes.mAddressType);
+        srcInfo.setAdvertisingSid((byte)paRes.mAdvSid);
+        srcInfo.setBroadcasterId(paRes.mBroadcastId);
+
+        if (isValidBroadcastSourceInfo(srcInfo) == false) {
+            log("Discarding this Add Broadcast source If It is DUP");
+            sendPendingCallbacks(ADD_BCAST_SOURCE,INVALID_SRC_ID,
+                            BleBroadcastAudioScanAssistCallback.BASS_STATUS_DUPLICATE_ADDITION);
+            return null;
+        }
+
+        res[0] = BASS_ADD_SOURCE_OPCODE;
+        res[1] = (byte)paRes.mAddressType;
+        String address = broadcastSource.getAddress();
+        byte[] addrByteVal = BluetoothAddressToBytes(address);
+        log("Address bytes: " + Arrays.toString(addrByteVal));
+        byte[] revAddress= reverseBytes(addrByteVal);
+        log("reverse Address bytes: " + Arrays.toString(revAddress));
+        System.arraycopy(revAddress, 0, res, 2, 6);
+        res[8] = (byte)paRes.mAdvSid;
+
+        //System.arraycopy(paRes.mBroadcastId, 0, res, 9, BROADCAST_SOURCE_ID_LENGTH);
+        log("mBroadcastId: " + paRes.mBroadcastId);
+        res[9] = (byte)(paRes.mBroadcastId & 0x00000000000000FF);
+        res[10] = (byte)((paRes.mBroadcastId & 0x000000000000FF00) >>> 8);
+        res[11] = (byte)((paRes.mBroadcastId & 0x0000000000FF0000) >>> 16);
+        if (mDefNoPAS == false &&
+                srcInfo.getMetadataSyncState() == BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC) {
+            res[12] = (byte)(0x01);
+        } else {
+            log("setting PA sync to ZERO");
+            res[12] = (byte)0x00;
+        }
+
+        res[13] = (byte)(paRes.mPAInterval & 0x00000000000000FF);
+        res[14] = (byte)((paRes.mPAInterval & 0x000000000000FF00)>>>8);
+
+        res[15] = base_.getNumberOfSubgroupsofBIG();
+
+        int offset = 16;
+        for (int i=0; i<base_.getNumberOfSubgroupsofBIG(); i++) {
+
+            //Select based on PACs?
+            //int bisIndexValue = convertBisIndiciesToIntegerValue(srcInfo.getBroadcastChannelsSyncStatus());
+            int bisIndexValue = convertBisIndiciesToIntegerValue(mService.getBassUtils().selectBises(mDevice, srcInfo, base_), i);
+
+            res[offset++] = (byte)(bisIndexValue & 0x00000000000000FF);
+            res[offset++] = (byte)((bisIndexValue & 0x000000000000FF00)>>>8);
+            res[offset++] = (byte)((bisIndexValue & 0x0000000000FF0000)>>>16);
+            res[offset++] = (byte)((bisIndexValue & 0x00000000FF000000)>>>24);
+
+            res[offset++] = metaDataLength[i];
+            if (metaDataLength[i] != 0) {
+                if (isLocalBroadcastSource(broadcastSource) == false) {
+                    byte[] revMetadata = reverseBytes(base_.getMetadata(i));
+                    System.arraycopy(revMetadata, 0, res, offset, metaDataLength[i]);
+                } else {
+                    System.arraycopy(base_.getMetadata(i), 0, res, offset, metaDataLength[i]);
+                }
+            }
+            offset = offset + metaDataLength[i];
+        }
+
+        log("ADD_BCAST_SOURCE in Bytes");
+        BassUtils.printByteArray(res);
+        return res;
+    }
+
+    private byte[] convertSourceInfoToUpdateSourceByteArray(BleBroadcastSourceInfo srcInfo) {
+        byte[] res;
+        int updateSourceFixedLength = 6;
+        BCService.PAResults paRes = null;
+        BleBroadcastSourceInfo existingSI = getBroadcastSourceInfoForSourceId(srcInfo.getSourceId());
+        if (existingSI == null) {
+            log("no existing SI for update source op");
+            return null;
+        }
+
+        byte numSubGroups = existingSI.getNumberOfSubGroups();
+        //on Modify source, dont update any Metadata
+        byte metaDataLength = 0;
+        res = new byte [updateSourceFixedLength + numSubGroups*5];
+
+        res[0] = BASS_UPDATE_SOURCE_OPCODE;
+        res[1] = srcInfo.getSourceId();
+
+        if (srcInfo.getMetadataSyncState() == BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC) {
+            res[2] = (byte)(0x01);
+        } else {
+            res[2] = (byte)0x00;
+        }
+        //update these from existing SI
+        BluetoothDevice existingSrcDevice = existingSI.getSourceDevice();
+        if (isAddedBroadcastSourceIsLocal(existingSrcDevice)) {
+            int paInterval = 0x0000FFFF;
+            paInterval = mBAService.BroadcastGetAdvInterval();
+            res[4] = (byte)((paInterval & 0x000000000000FF00)>>>8);
+            res[3] = (byte)(paInterval & 0x00000000000000FF);
+        } else {
+            //for non-c mmodify op, set PA Interval as UNKNOWN
+            res[4] = (byte)0xFF;
+            res[3] = (byte)0xFF;
+        }
+        //For modify op, just set number of Subgroups as UNKNOWN
+        //ZERO is treated as UNKNOWN
+        res[5] = numSubGroups;
+
+        int offset = 6;
+        int bisIndexValue = 0;
+        Map<Integer, Integer> bisIndexList =  existingSI.getBisIndexList();
+        if (srcInfo.getAudioSyncState() == BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED) {
+            //Force BIS index value to NO_PREF for modify SRC
+            bisIndexValue = 0xFFFFFFFF;
+        } else {
+            bisIndexValue = 0x00000000;
+        }
+        log("UPDATE_BCAST_SOURCE b4: bisIndexValue : " + bisIndexValue);
+        //If there is an empty List, set NO pref all subgroups
+        if (bisIndexList == null || bisIndexList.size() == 0) {
+            bisIndexValue = 0xFFFFFFFF;
+        }
+        for (int i=0; i<numSubGroups; i++) {
+
+            //Select based on PACs?
+            //int bisIndexValue = convertBisIndiciesToIntegerValue(srcInfo.getBroadcastChannelsSyncStatus());
+            if (bisIndexValue != 0xFFFFFFFF && bisIndexValue != 0) {
+                bisIndexValue = bisIndexList.get(i);
+            }
+            log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue);
+
+            res[offset++] = (byte)(bisIndexValue & 0x00000000000000FF);
+            res[offset++] = (byte)((bisIndexValue & 0x000000000000FF00)>>>8);
+            res[offset++] = (byte)((bisIndexValue & 0x0000000000FF0000)>>>16);
+            res[offset++] = (byte)((bisIndexValue & 0x00000000FF000000)>>>24);
+
+            res[offset++] = metaDataLength;
+        }
+        log("UPDATE_BCAST_SOURCE in Bytes");
+        BassUtils.printByteArray(res);
+        return res;
+    }
+
+    private byte[] convertAsciitoValues (byte[] val) {
+        byte[] ret = new byte[val.length];
+        for (int i=0; i< val.length; i++) {
+            ret[i] = (byte)(val[i] - (byte)'0');
+        }
+        log("convertAsciitoValues: returns:" + Arrays.toString(val));
+        return ret;
+    }
+
+    private byte[] convertSourceInfoToSetBroadcastCodeByteArray(BleBroadcastSourceInfo srcInfo) {
+
+        byte[] res = new byte[PIN_CODE_CMD_LEN];
+        res[0] = BASS_SET_BCAST_PIN_OPCODE;
+        res[1] = srcInfo.getSourceId();
+        log("convertSourceInfoToSetBroadcastCodeByteArray: Source device : " + srcInfo.getSourceDevice());
+        byte[] actualPIN = null;
+        //srcInfo.getSourceDevice() will be NULL if this request coming from SDK
+        // srcInfo.getSourceDevice() will have valid Source device only If this is
+        //collocated device
+        if (srcInfo.getSourceDevice() != null &&
+               isAddedBroadcastSourceIsLocal(srcInfo.getSourceDevice())) {
+            //colocated Source addition
+            //query the Encryption Key from BMS and update
+            ///*_BMS
+            actualPIN = mBAService.GetEncryptionKey(null);
+            //_BMS*/
+            log("colocatedBcastCode is " + Arrays.toString(actualPIN));
+        } else {
+            //Can Keep as ASCII as is
+            String reversePIN = new StringBuffer(srcInfo.getBroadcastCode()).reverse().toString();
+            actualPIN = reversePIN.getBytes();
+        }
+        if (actualPIN == null) {
+            Log.e(TAG, "actual PIN is null");
+            return null;
+        } else {
+            log( "byte array broadcast Code:" + Arrays.toString(actualPIN));
+            log( "pinLength:" + actualPIN.length);
+
+            //Fill the PIN code in the Last Position
+            System.arraycopy(actualPIN, 0, res, ((PIN_CODE_CMD_LEN)-actualPIN.length), actualPIN.length);
+
+            log("SET_BCAST_PIN in Bytes");
+            BassUtils.printByteArray(res);
+        }
+        return res;
+    }
+
+    private boolean IsItRightTimeToUpdateBroadcastPIN(byte srcId) {
+        Collection<BleBroadcastSourceInfo> srcInfos = mBleBroadcastSourceInfos.values();
+        Iterator<BleBroadcastSourceInfo> iterator = srcInfos.iterator();
+        boolean ret = false;
+        if (mForceSB) {
+            log("force SB is set");
+            return true;
+        }
+        while (iterator.hasNext()) {
+            BleBroadcastSourceInfo sI = iterator.next();
+            if (sI == null) {
+                log("src Info is null");
+                continue;
+            }
+            if (srcId == sI.getSourceId() &&
+                sI.getEncryptionStatus() == BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED) {
+                ret = true;
+                break;
+            }
+        }
+        log("IsItRightTimeToUpdateBroadcastPIN returning:" + ret);
+        return ret;
+    }
+
+    @VisibleForTesting
+    class Connected extends State {
+        @Override
+        public void enter() {
+            log( "Enter Connected(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+
+            removeDeferredMessages(CONNECT);
+            if (mLastConnectionState == BluetoothProfile.STATE_CONNECTED) {
+                log("CONNECTED->CONNTECTED: Ignore");
+             } else {
+                broadcastConnectionState(mDevice, mLastConnectionState,
+                                              BluetoothProfile.STATE_CONNECTED);
+                //initialize PACs for this devices
+                if (mPacsAvail == true) {
+                    /*
+                    PacsClientService mPacsClientService = PacsClientService.getPacsClientService();
+                    if (mPacsClientService != null) {
+                        log("trigger connect to Pacs");
+                        mPacsClientService.connect(mDevice);
+                    } else {
+                        Log.e(TAG, "PACs interface is null");
+                    }
+                    */
+                 }
+
+                ///*_VCP
+                if (mVcpForBroadcast) {
+                    VcpController vcpController = VcpController.getVcpController();
+                    if (vcpController != null) {
+                        log("trigger connect to Vcp Renderer");
+                        if (!vcpController.connect(mDevice, BluetoothVcp.MODE_BROADCAST)) {
+                            log("Connect vcp failed");
+                        }
+                    } else {
+                        Log.e(TAG, "VcpController interface is null");
+                    }
+                }
+                //_VCP*/
+             }
+        }
+
+        @Override
+        public void exit() {
+            log("Exit Connected(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_CONNECTED;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Connected process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+            BleBroadcastSourceInfo srcInfo;
+            switch (message.what) {
+                case CONNECT:
+                    Log.w(TAG, "Connected: CONNECT ignored: " + mDevice);
+                    break;
+                case DISCONNECT:
+                    log("Disconnecting from " + mDevice);
+                    if (mBluetoothGatt != null) {
+                        mBluetoothGatt.disconnect();
+                        mBluetoothGatt.close();
+                        mBluetoothGatt = null;
+                        //transitionTo(mDisconnecting);
+                        cancelActiveSync(null);
+                        //Trigger the CSip disconnection, dont worry about pass/failure
+                        if (mCsipConnected && mSetCoordinator != null) {
+                             mSetCoordinator.disconnect(mService.mCsipAppId, mDevice);
+                             mCsipConnected = false;
+                        }
+                        transitionTo(mDisconnected);
+                    } else {
+                        log("mBluetoothGatt is null");
+                    }
+                    break;
+                case CONNECTION_STATE_CHANGED:
+                    int state = (int)message.obj;
+                    Log.w(TAG, "Connected:connection state changed:" + state);
+                    if (state == BluetoothProfile.STATE_CONNECTED) {
+                        Log.w(TAG, "device is already connected to Bass" + mDevice);
+                    } else {
+                        Log.w(TAG, "unexpected disconnected from " + mDevice);
+                        cancelActiveSync(null);
+                        ///*_CSIP
+                        //Trigger the CSip disconnection, dont worry about pass/failure
+                        if (mCsipConnected) {
+                              mSetCoordinator.disconnect(mService.mCsipAppId, mDevice);
+                              mCsipConnected = false;
+                        }
+                        //_CSIP*/
+                        transitionTo(mDisconnected);
+                    }
+                    break;
+                case READ_BASS_CHARACTERISTICS:
+                    BluetoothGattCharacteristic characteristic = (BluetoothGattCharacteristic)message.obj;
+                    if (mBluetoothGatt != null) {
+                        mBluetoothGatt.readCharacteristic(characteristic);
+                        transitionTo(mConnectedProcessing);
+                    } else {
+                        Log.e(TAG, "READ_BASS_CHARACTERISTICS is ignored, Gatt handle is null");
+                    }
+                    break;
+                case START_SCAN_OFFLOAD:
+                    if (mBluetoothGatt != null &&
+                         mBroadcastScanControlPoint != null) {
+                        mBroadcastScanControlPoint.setValue(REMOTE_SCAN_START);
+                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
+                        mPendingOperation = message.what;
+                        transitionTo(mConnectedProcessing);
+                    } else {
+                        log("no Bluetooth Gatt handle, may need to fetch write");
+                    }
+                    break;
+                case STOP_SCAN_OFFLOAD:
+                    if (mBluetoothGatt != null &&
+                          mBroadcastScanControlPoint != null) {
+                        mBroadcastScanControlPoint.setValue(REMOTE_SCAN_STOP);
+                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
+                        mPendingOperation = message.what;
+                        transitionTo(mConnectedProcessing);
+                    } else {
+                        log("no Bluetooth Gatt handle, may need to fetch write");
+                    }
+                    break;
+                case SELECT_BCAST_SOURCE:
+                    ScanResult scanRes = (ScanResult)message.obj;
+                    boolean auto = ((int) message.arg1) == AUTO;
+                    boolean isGroupOp = ((int) message.arg2) == GROUP_OP;
+                    selectBroadcastSource(scanRes, isGroupOp, auto);
+                    break;
+                case ADD_BCAST_SOURCE:
+                    srcInfo = (BleBroadcastSourceInfo)message.obj;
+                    log("Adding Broadcast source" + srcInfo);
+                    byte[] addSourceInfo =  convertSourceInfoToAddSourceByteArray(srcInfo);
+                    if (addSourceInfo == null) {
+                        Log.e(TAG, "add source: source Info is NULL");
+                        break;
+                    }
+                    if (mBluetoothGatt != null &&
+                          mBroadcastScanControlPoint != null) {
+                        mBroadcastScanControlPoint.setValue(addSourceInfo);
+                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
+                        mPendingOperation = message.what;
+                        transitionTo(mConnectedProcessing);
+                        sendMessageDelayed(GATT_TXN_TIMEOUT, GATT_TXN_TIMEOUT_MS);
+                    } else {
+                        Log.e(TAG, "ADD_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
+                        sendPendingCallbacks(ADD_BCAST_SOURCE,INVALID_SRC_ID,
+                            BleBroadcastAudioScanAssistCallback.BASS_STATUS_FATAL);
+                    }
+                    break;
+                case UPDATE_BCAST_SOURCE:
+                    srcInfo = (BleBroadcastSourceInfo)message.obj;
+                    mAutoTriggerred = ((int) message.arg1) == AUTO;
+                    log("Updating Broadcast source" + srcInfo);
+                    byte[] updateSourceInfo =  convertSourceInfoToUpdateSourceByteArray(srcInfo);
+                    if (updateSourceInfo == null) {
+                        Log.e(TAG, "update source: source Info is NULL");
+                        break;
+                    }
+                    if (mBluetoothGatt != null &&
+                          mBroadcastScanControlPoint != null) {
+                        mBroadcastScanControlPoint.setValue(updateSourceInfo);
+                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
+                        mPendingOperation = message.what;
+                        mPendingSourceId = srcInfo.getSourceId();
+                        transitionTo(mConnectedProcessing);
+                        sendMessageDelayed(GATT_TXN_TIMEOUT, GATT_TXN_TIMEOUT_MS);
+                    } else {
+                        Log.e(TAG, "UPDATE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
+                        sendPendingCallbacks(UPDATE_BCAST_SOURCE,INVALID_SRC_ID,
+                            BleBroadcastAudioScanAssistCallback.BASS_STATUS_FATAL);
+                    }
+                    break;
+                case SET_BCAST_CODE:
+                    srcInfo = (BleBroadcastSourceInfo)message.obj;
+                    int cmdType = message.arg1;
+                    log("SET_BCAST_CODE srcInfo: " + srcInfo);
+
+                    if (cmdType != QUEUED &&
+                        IsItRightTimeToUpdateBroadcastPIN(srcInfo.getSourceId()) != true) {
+                        mSetBroadcastCodePending = true;
+                        mSetBroadcastPINSrcInfo = srcInfo;
+                        log("Ignore SET_BCAST now, but store it for later");
+                        //notify so that lock release happens as SET_BCAST_CODE
+                        //queued for future
+                        mService.notifyOperationCompletion(mDevice,SET_BCAST_CODE);
+                    } else {
+                        byte[] setBroadcastPINcmd =  convertSourceInfoToSetBroadcastCodeByteArray(srcInfo);
+                        if (setBroadcastPINcmd == null) {
+                            Log.e(TAG, "SET_BCAST_CODE: Broadcast code is NULL");
+                            break;
+                        }
+                        if (mBluetoothGatt != null &&
+                          mBroadcastScanControlPoint != null) {
+                            mBroadcastScanControlPoint.setValue(setBroadcastPINcmd);
+                            mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
+                            mPendingOperation = message.what;
+                            mPendingSourceId = srcInfo.getSourceId();
+                            transitionTo(mConnectedProcessing);
+                            sendMessageDelayed(GATT_TXN_TIMEOUT, GATT_TXN_TIMEOUT_MS);
+                        } else {
+                            Log.e(TAG, "SET_BCAST_CODE: no Bluetooth Gatt handle, Fatal");
+                            sendPendingCallbacks(SET_BCAST_CODE,INVALID_SRC_ID,
+                                BleBroadcastAudioScanAssistCallback.BASS_STATUS_FATAL);
+
+                        }
+                    }
+                    break;
+                case REMOVE_BCAST_SOURCE:
+                    byte sourceId = (byte)message.arg1;
+                    BluetoothDevice audioSrc = (BluetoothDevice)message.obj;
+                    log("Removing Broadcast source: audioSource:" + audioSrc + "sourceId:" + sourceId);
+                    byte[] removeSourceInfo = new byte [2];
+                    removeSourceInfo[0] = BASS_REMOVE_SOURCE_OPCODE;
+                    removeSourceInfo[1] = sourceId;
+                    if (mBluetoothGatt != null &&
+                          mBroadcastScanControlPoint != null) {
+                        mBroadcastScanControlPoint.setValue(removeSourceInfo);
+                        mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
+                        mPendingOperation = message.what;
+                        mPendingSourceId = sourceId;
+                        transitionTo(mConnectedProcessing);
+                        sendMessageDelayed(GATT_TXN_TIMEOUT, GATT_TXN_TIMEOUT_MS);
+                    } else {
+                        Log.e(TAG, "REMOVE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
+                        sendPendingCallbacks(REMOVE_BCAST_SOURCE,INVALID_SRC_ID,
+                            BleBroadcastAudioScanAssistCallback.BASS_STATUS_FATAL);
+
+                    }
+                    break;
+                case PSYNC_ACTIVE_TIMEOUT:
+                    cancelActiveSync(null);
+                    break;
+                default:
+                    log("CONNECTED: not handled message:" + message.what);
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+
+    void sendPendingCallbacks(int pendingOp, byte sourceId, int status) {
+        if (status != 0) {
+            //Notify service only In case of failure cases
+            //Success case would have been notified through State machine anyways
+            mService.notifyOperationCompletion(mDevice, pendingOp);
+        }
+        switch (pendingOp) {
+            case START_SCAN_OFFLOAD:
+                if (status != 0) {
+                    if (mAutoTriggerred == false) {
+                        log("notify the app only if start Scan offload fails");
+                        //shouldnt happen in general
+                        mService.sendBroadcastSourceSelectedCallback(mDevice, null, status);
+                        cancelActiveSync(null);
+                    } else {
+                        mAutoTriggerred = false;
+                    }
+                }
+                break;
+            case ADD_BCAST_SOURCE:
+                if (status != 0) {
+                    sourceId = INVALID_SRC_ID;
+                    cancelActiveSync(null);
+                    //stop Scan offload for colocated case
+                    mService.stopScanOffloadInternal(mDevice, false);
+                }
+                mService.sendAddBroadcastSourceCallback(mDevice, sourceId, status);
+                break;
+            case UPDATE_BCAST_SOURCE:
+                if (mAutoTriggerred == false) {
+                    mService.sendUpdateBroadcastSourceCallback(mDevice, sourceId, status);
+                } else {
+                    mAutoTriggerred = false;
+                }
+                break;
+            case REMOVE_BCAST_SOURCE:
+                mService.sendRemoveBroadcastSourceCallback(mDevice, sourceId, status);
+                break;
+            case SET_BCAST_CODE:
+                mService.sendSetBroadcastPINupdatedCallback(mDevice, sourceId, status);
+                break;
+            default:
+                    {
+                        log("sendPendingCallbacks: unhandled case");
+                    }
+            }
+    }
+    @VisibleForTesting
+    class ConnectedProcessing extends State {
+         @Override
+         public void enter() {
+             log( "Enter ConnectedProcessing(" + mDevice + "): " + messageWhatToString(
+                     getCurrentMessage().what));
+         }
+
+         @Override
+         public void exit() {
+             log("Exit ConnectedProcessing(" + mDevice + "): " + messageWhatToString(
+                     getCurrentMessage().what));
+         }
+         @Override
+         public boolean processMessage(Message message) {
+             log("ConnectedProcessing process message(" + mDevice + "): " + messageWhatToString(
+                     message.what));
+             switch (message.what) {
+                 case CONNECT:
+                     Log.w(TAG, "CONNECT request is ignored" + mDevice);
+                     break;
+                 case DISCONNECT:
+                     Log.w(TAG, "DISCONNECT requested!: " + mDevice);
+                     if (mBluetoothGatt != null) {
+                        mBluetoothGatt.disconnect();
+                        mBluetoothGatt.close();
+                        mBluetoothGatt = null;
+                        cancelActiveSync(null);
+                        //Trigger the CSIP disconnection, dont worry about pass/failure
+                        if (mCsipConnected && mSetCoordinator != null) {
+                             mSetCoordinator.disconnect(mService.mCsipAppId, mDevice);
+                             mCsipConnected = false;
+                        }
+                        transitionTo(mDisconnected);
+                    } else {
+                        log("mBluetoothGatt is null");
+                    }
+                     break;
+                 case READ_BASS_CHARACTERISTICS:
+                       Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice);
+                      deferMessage(message);
+                      break;
+                 case CONNECTION_STATE_CHANGED:
+                     int state = (int)message.obj;
+                     Log.w(TAG, "ConnectedProcessing: connection state changed:" + state);
+                     if (state == BluetoothProfile.STATE_CONNECTED) {
+                         Log.w(TAG, "should never happen from this state");
+                     } else {
+                         Log.w(TAG, "Unexpected disconnection " + mDevice);
+                         transitionTo(mDisconnected);
+                     }
+                     break;
+                case GATT_TXN_PROCESSED:
+                    removeMessages(GATT_TXN_TIMEOUT);
+                    int status = (int)message.arg1;
+                    log( "GATT transaction processed for" + mDevice);
+                    mService.notifyOperationCompletion(mDevice, mPendingOperation);
+                    if (status == BluetoothGatt.GATT_SUCCESS) {
+                        if (mPendingOperation == SET_BCAST_CODE) {
+                            //If Pending operation is SET_BCAST_CODE
+                            //send callback to notify BCAST is updated
+                            //This is needed only for SET_BCAST operation
+                           sendPendingCallbacks(mPendingOperation,
+                                  mPendingSourceId, BleBroadcastAudioScanAssistCallback.BASS_STATUS_SUCCESS);
+                        }
+                    } else {
+                        //any failure to write operation
+                        //will be converted to corresponding
+                        //callback with failure status
+                        sendPendingCallbacks(mPendingOperation,
+                                  mPendingSourceId, BleBroadcastAudioScanAssistCallback.BASS_STATUS_FAILURE);
+                    }
+                    transitionTo(mConnected);
+                    break;
+                case GATT_TXN_TIMEOUT:
+                    log( "GATT transaction timedout for" + mDevice);
+                    mService.notifyOperationCompletion(mDevice, mPendingOperation);
+                    sendPendingCallbacks(mPendingOperation,
+                                 mPendingSourceId, BleBroadcastAudioScanAssistCallback.BASS_STATUS_TXN_TIMEOUT);
+                    mPendingOperation = -1;
+                    transitionTo(mConnected);
+                    mPendingSourceId = -1;
+               break;
+                case START_SCAN_OFFLOAD:
+                case STOP_SCAN_OFFLOAD:
+                case SELECT_BCAST_SOURCE:
+                case ADD_BCAST_SOURCE:
+                case SET_BCAST_CODE:
+                case REMOVE_BCAST_SOURCE:
+                case PSYNC_ACTIVE_TIMEOUT:
+                    log("defer the message:" + message.what + "so that it will be processed later");
+                    deferMessage(message);
+                       break;
+                 default:
+                     log("CONNECTEDPROCESSING: not handled message:" + message.what);
+                    return NOT_HANDLED;
+             }
+             return HANDLED;
+         }
+     }
+
+
+    @VisibleForTesting
+     class Disconnecting extends State {
+         @Override
+         public void enter() {
+             log( "Enter Disconnecting(" + mDevice + "): " + messageWhatToString(
+                     getCurrentMessage().what));
+             sendMessageDelayed(CONNECT_TIMEOUT, mDevice, mConnectTimeoutMs);
+             broadcastConnectionState(mDevice, mLastConnectionState,
+                               BluetoothProfile.STATE_DISCONNECTING);
+         }
+         @Override
+         public void exit() {
+             log("Exit Disconnecting(" + mDevice + "): " + messageWhatToString(
+                     getCurrentMessage().what));
+             removeMessages(CONNECT_TIMEOUT);
+             mLastConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+         }
+         @Override
+         public boolean processMessage(Message message) {
+             log("Disconnecting process message(" + mDevice + "): " + messageWhatToString(
+                     message.what));
+             switch (message.what) {
+                 case CONNECT:
+                     log("Disconnecting to " + mDevice);
+                     log("deferring this connection request " + mDevice);
+                     deferMessage(message);
+                     break;
+                 case DISCONNECT:
+                     Log.w(TAG, "Already disconnecting: DISCONNECT ignored: " + mDevice);
+                     break;
+                 case CONNECTION_STATE_CHANGED:
+                     int state = (int)message.obj;
+                     Log.w(TAG, "Disconnecting: connection state changed:" + state);
+                     if (state == BluetoothProfile.STATE_CONNECTED) {
+                         Log.e(TAG, "should never happen from this state");
+                         transitionTo(mConnected);
+                     } else {
+                         Log.w(TAG, "disconnection successfull to " + mDevice);
+                         cancelActiveSync(null);
+                         transitionTo(mDisconnected);
+                         ///*_CSIP
+                         //Trigger the CSip disconnection, dont worry about pass/failure
+                         if (mCsipConnected) {
+                             mSetCoordinator.disconnect(mService.mCsipAppId, mDevice);
+                             mCsipConnected = false;
+                         }
+                         //_CSIP*/
+                     }
+                     break;
+                 case CONNECT_TIMEOUT:
+                     Log.w(TAG, "CONNECT_TIMEOUT");
+
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    if (!mDevice.equals(device)) {
+                         Log.e(TAG, "Unknown device timeout " + device);
+                         break;
+                    }
+                    transitionTo(mDisconnected);
+            break;
+                 default:
+                     return NOT_HANDLED;
+             }
+             return HANDLED;
+         }
+     }
+
+
+    void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) {
+         log( "broadcastConnectionState " + device + ": " + fromState + "->" + toState);
+         if (fromState == BluetoothProfile.STATE_CONNECTED &&
+             toState == BluetoothProfile.STATE_CONNECTED) {
+             log("CONNECTED->CONNTECTED: Ignore");
+            return;
+         }
+         Intent intent = new Intent(BluetoothSyncHelper.ACTION_CONNECTION_STATE_CHANGED);
+         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
+         intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
+         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+         mService.sendBroadcastAsUser(intent, UserHandle.ALL,
+                 BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    int getConnectionState() {
+        String currentState = "Unknown";
+        if (getCurrentState() != null) {
+            currentState = getCurrentState().getName();
+        }
+        switch (currentState) {
+            case "Disconnected":
+                log("Disconnected");
+                return BluetoothProfile.STATE_DISCONNECTED;
+            case "Disconnecting":
+                log("Disconnecting");
+                return BluetoothProfile.STATE_DISCONNECTING;
+            case "Connecting":
+                log("Connecting");
+                return BluetoothProfile.STATE_CONNECTING;
+            case "Connected":
+            case "ConnectedProcessing":
+                log("connected");
+                return BluetoothProfile.STATE_CONNECTED;
+            default:
+                Log.e(TAG, "Bad currentState: " + currentState);
+                return BluetoothProfile.STATE_DISCONNECTED;
+        }
+    }
+
+    BluetoothDevice getDevice() {
+        return mDevice;
+    }
+
+    synchronized boolean isConnected() {
+        return getCurrentState() == mConnected;
+    }
+
+    public static String messageWhatToString(int what) {
+        switch (what) {
+            case CONNECT:
+                return "CONNECT";
+            case DISCONNECT:
+                return "DISCONNECT";
+            case CONNECTION_STATE_CHANGED:
+                return "CONNECTION_STATE_CHANGED";
+            case GATT_TXN_PROCESSED:
+                return "GATT_TXN_PROCESSED";
+            case READ_BASS_CHARACTERISTICS:
+                return "READ_BASS_CHARACTERISTICS";
+            case START_SCAN_OFFLOAD:
+                return "START_SCAN_OFFLOAD";
+            case STOP_SCAN_OFFLOAD:
+                return "STOP_SCAN_OFFLOAD";
+            case ADD_BCAST_SOURCE:
+                return "ADD_BCAST_SOURCE";
+            case SELECT_BCAST_SOURCE:
+                return "SELECT_BCAST_SOURCE";
+            case UPDATE_BCAST_SOURCE:
+                return "UPDATE_BCAST_SOURCE";
+            case SET_BCAST_CODE:
+                return "SET_BCAST_CODE";
+            case REMOVE_BCAST_SOURCE:
+                return "REMOVE_BCAST_SOURCE";
+            case PSYNC_ACTIVE_TIMEOUT:
+                return "PSYNC_ACTIVE_TIMEOUT";
+            case CSIP_CONNECTION_STATE_CHANGED:
+                return "CSIP_CONNECTION_STATE_CHANGED";
+            case CONNECT_TIMEOUT:
+                return "CONNECT_TIMEOUT";
+            default:
+                break;
+        }
+        return Integer.toString(what);
+    }
+
+    private static String profileStateToString(int state) {
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return "DISCONNECTED";
+            case BluetoothProfile.STATE_CONNECTING:
+                return "CONNECTING";
+            case BluetoothProfile.STATE_CONNECTED:
+                return "CONNECTED";
+            case BluetoothProfile.STATE_DISCONNECTING:
+                return "DISCONNECTING";
+            default:
+                break;
+        }
+        return Integer.toString(state);
+    }
+
+    public void dump(StringBuilder sb) {
+        ProfileService.println(sb, "mDevice: " + mDevice);
+        ProfileService.println(sb, "  StateMachine: " + this);
+        // Dump the state machine logs
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+        super.dump(new FileDescriptor(), printWriter, new String[]{});
+        printWriter.flush();
+        stringWriter.flush();
+        ProfileService.println(sb, "  StateMachineLog:");
+        Scanner scanner = new Scanner(stringWriter.toString());
+        while (scanner.hasNextLine()) {
+            String line = scanner.nextLine();
+            ProfileService.println(sb, "    " + line);
+        }
+        scanner.close();
+    }
+
+    @Override
+    protected void log( String msg) {
+        if (BASS_DBG) {
+            super.log(msg);
+        }
+    }
+
+    private static void logByteArray(String prefix, byte[] value, int offset, int count) {
+        StringBuilder builder = new StringBuilder(prefix);
+        for (int i = offset; i < count; i++) {
+            builder.append(String.format("0x%02X", value[i]));
+            if (i != value.length - 1) {
+                builder.append(", ");
+            }
+        }
+        Log.d(TAG, builder.toString());
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BassCsetManager.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BassCsetManager.java
new file mode 100644
index 0000000..fd2be7c
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BassCsetManager.java
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ *
+ * 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.
+ */
+
+/**
+ * Bass CSET managet StateMachine. There is one instance per Coordinated "set Id".
+ *  - "Idle" and "Locked" are steady states.
+ *  - "Locking" is a transient states until the
+ *     Locking confirmation comes from upper layers.
+ *  - Once lock is acquired, profile dont try to unlock
+ *
+ *                             (Idle)
+ *                           |       ^
+ *                   LOCK    |       | UNLOCK
+ *                           V       |
+ *                      (Locking)<->(Unlocking)
+ *                           |       ^
+ *                 ON_LOCK   |       | ON_UNLOCK
+ *                           V       |
+ *                          (Locked)
+ *
+ *
+ */
+
+package com.android.bluetooth.bc;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothSyncHelper;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BleBroadcastSourceChannel;
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+import android.bluetooth.IBleBroadcastAudioScanAssistCallback;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.PeriodicAdvertisingCallback;
+import android.bluetooth.le.PeriodicAdvertisingManager;
+import android.bluetooth.le.PeriodicAdvertisingReport;
+
+///*_CSIP
+//CSET
+import android.bluetooth.BluetoothDeviceGroup;
+import com.android.bluetooth.groupclient.GroupService;
+//_CSIP*/
+
+import android.bluetooth.IBluetoothManager;
+import android.os.ServiceManager;
+import android.os.IBinder;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import android.content.Intent;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import java.util.UUID;
+import java.util.Collection;
+import android.os.UserHandle;
+
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.ServiceFactory;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Scanner;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.lang.String;
+import java.lang.StringBuffer;
+import java.lang.Integer;
+
+import java.nio.ByteBuffer;
+import java.lang.Byte;
+import java.util.stream.IntStream;
+import android.os.SystemProperties;
+import android.os.ParcelUuid;
+
+final class BassCsetManager extends StateMachine {
+    private static final String TAG = "BassCsetManager";
+
+    //Considered as Coordinated ops
+    static final int BASS_GRP_START_SCAN_OFFLOAD = 6;
+    static final int BASS_GRP_STOP_SCAN_OFFLOAD = 7;
+    static final int BASS_GRP_ADD_BCAST_SOURCE = 9;
+    static final int BASS_GRP_UPDATE_BCAST_SOURCE = 10;
+    static final int BASS_GRP_SET_BCAST_CODE = 11;
+    static final int BASS_GRP_REMOVE_BCAST_SOURCE = 12;
+
+    static final int LOCK = 17;
+    static final int UNLOCK = 18;
+    static final int LOCK_STATE_CHANGED = 3;
+    static final int LOCK_TIMEOUT = 4;
+    static final int ON_CSIP_CONNECTED = 5;
+
+    //10 secs time out for all gatt writes
+    static final int LOCK_TIMEOUT_MS = 10000;
+
+
+    @VisibleForTesting
+    private static final int CONNECT_TIMEOUT = 201;
+
+    private final Idle mIdle;
+    private final Locking mLocking;
+    private final Locked mLocked;
+    private final LockedProcessing mLockedProcessing;
+    private final Unlocking mUnlocking;
+
+    private BCService mBCService;
+    private final BluetoothDevice mDevice;
+    private final int mSetId;
+    private List<BluetoothDevice> mMemberDevices = null;
+
+    ///*_CSIP
+    //CSIP Locking Interfaces
+    private GroupService mSetCoordinator = GroupService.getGroupService();
+    //_CSIP*/
+
+    BassCsetManager(int setId, BluetoothDevice masterDevice, BCService svc,
+            Looper looper) {
+        super(TAG, looper);
+        mSetId = setId;
+        mBCService = svc;
+
+        mIdle = new Idle();
+        mLocked = new Locked();
+        mLockedProcessing = new LockedProcessing();
+        mLocking = new Locking();
+        mUnlocking = new Unlocking();
+
+        addState(mIdle);
+        addState(mLocking);
+        addState(mLocked);
+        addState(mLockedProcessing);
+        addState(mUnlocking);
+
+        setInitialState(mIdle);
+        mDevice = masterDevice;
+        mMemberDevices = new ArrayList<BluetoothDevice>();
+
+    }
+
+    static BassCsetManager make(int setId, BluetoothDevice masterDevice, BCService svc,
+            Looper looper) {
+        Log.d(TAG, "make for setId, setId " + setId + ": masterDevice" + masterDevice);
+        BassCsetManager BassclientSm = new BassCsetManager(setId, masterDevice, svc,
+                looper);
+        BassclientSm.start();
+        return BassclientSm;
+    }
+
+    public void doQuit() {
+        log("doQuit for device " + mDevice);
+        quitNow();
+    }
+
+    public void cleanup() {
+        log("cleanup for device " + mDevice);
+    }
+
+    @VisibleForTesting
+    class Idle extends State {
+        @Override
+        public void enter() {
+            log( "Enter Idle(" + mSetId + "): " + messageWhatToString(
+                    getCurrentMessage().what));
+            mMemberDevices = null;
+
+        }
+
+        @Override
+        public void exit() {
+            log("Exit Idle(" + mSetId + "): " + messageWhatToString(
+                    getCurrentMessage().what));
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Idle process message(" + mSetId + "): " + messageWhatToString(
+                    message.what));
+
+            switch (message.what) {
+                case BASS_GRP_START_SCAN_OFFLOAD:
+                case BASS_GRP_STOP_SCAN_OFFLOAD:
+                case BASS_GRP_ADD_BCAST_SOURCE:
+                case BASS_GRP_UPDATE_BCAST_SOURCE:
+                case BASS_GRP_SET_BCAST_CODE:
+                case BASS_GRP_REMOVE_BCAST_SOURCE:
+                    //defer the meesage and move to Locked
+                    deferMessage(message);
+                    //Intentional miss of break
+                case LOCK:
+                    //treat Connect & Lock as same request
+                    log("Locking to " + mSetId);
+                    //get CSIP connection status for BluetoothDevice
+                    //if CSIP disconnected: start Connect Procedure (mostly hpns only at first time)
+                    //if CSIP connected: start Lock Procedure
+                    ///*_CSIP
+                    mSetCoordinator.setLockValue(mBCService.mCsipAppId, mSetId, null, BluetoothDeviceGroup.ACCESS_GRANTED);
+                    //_CSIP*/
+                    transitionTo(mLocking);
+
+                    //transitionTo(mLocked);
+                    break;
+                case UNLOCK:
+                    Log.w(TAG, "Idle: UNLOCK ignored: " + mSetId);
+                    break;
+                case LOCK_STATE_CHANGED:
+                    //This most likely not happen
+                    ///*_CSIP
+                    int value = (int)message.arg1;
+                    List<BluetoothDevice> devices = (List<BluetoothDevice>)message.obj;
+                    Log.w(TAG, "Lock state changed:" + value);
+                    if (value == BluetoothDeviceGroup.ACCESS_GRANTED) {
+                        transitionTo(mLocked);
+                    } else {
+                        Log.w(TAG, "Idle: Lock failed to " + mSetId);
+                    }
+                    //_CSIP*/
+                    break;
+                case ON_CSIP_CONNECTED:
+                //starts the Lock procedure
+                //Only reason why we Connect is to Lock
+                //
+                //Dont transition the state
+                default:
+                    log("Idle: not handled message:" + message.what);
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+    @VisibleForTesting
+     class Locking extends State {
+         @Override
+         public void enter() {
+             log( "Enter Locking(" + mSetId + "): " + messageWhatToString(
+                     getCurrentMessage().what));
+         }
+
+         @Override
+         public void exit() {
+             log("Exit Locking(" + mSetId + "): " + messageWhatToString(
+                     getCurrentMessage().what));
+         }
+
+         @Override
+         public boolean processMessage(Message message) {
+             log("Locking process message(" + mSetId + "): " + messageWhatToString(
+                     message.what));
+
+             switch (message.what) {
+
+                 case BASS_GRP_START_SCAN_OFFLOAD:
+                 case BASS_GRP_STOP_SCAN_OFFLOAD:
+                 case BASS_GRP_ADD_BCAST_SOURCE:
+                 case BASS_GRP_UPDATE_BCAST_SOURCE:
+                 case BASS_GRP_SET_BCAST_CODE:
+                 case BASS_GRP_REMOVE_BCAST_SOURCE:
+                     //defer the meesage and move to Locked
+                     deferMessage(message);
+                     break;
+                 case LOCK:
+                     log("Already Locking to " + mSetId);
+                     log("Ignore this Lock request " + mSetId);
+                     break;
+                 case UNLOCK:
+                     Log.w(TAG, "Locking: UNLOCK deferred: " + mSetId);
+                     deferMessage(message);
+                     break;
+                 case LOCK_STATE_CHANGED:
+                     ///*_CSIP
+                     int value = (int)message.arg1;
+                     Log.w(TAG, "Lock state changed:" + value);
+                     if (value == BluetoothDeviceGroup.ACCESS_GRANTED) {
+                          List<BluetoothDevice> devices = (List<BluetoothDevice>)message.obj;
+                         mMemberDevices = devices;
+                         transitionTo(mLocked);
+                     } else {
+                         Log.w(TAG, "Locking: Unlocked to " + mSetId);
+                         transitionTo(mIdle);
+                     }
+                     //_CSIP*/
+                     break;
+                 case ON_CSIP_CONNECTED:
+                     //starts the Lock procedure
+                     //Only reason why we Connect is to Lock
+                     //
+                     //Dont transition the state
+                     break;
+                 default:
+                      log("LOCKING: not handled message:" + message.what);
+                     return NOT_HANDLED;
+             }
+             return HANDLED;
+         }
+     }
+
+    @VisibleForTesting
+    class Locked extends State {
+        @Override
+        public void enter() {
+            log( "Enter Locked(" + mSetId + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+
+            removeDeferredMessages(LOCK);
+
+        }
+
+        @Override
+        public void exit() {
+            log("Exit Locked(" + mSetId + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Locked process message(" + mSetId + "): "
+                    + messageWhatToString(message.what));
+            BleBroadcastSourceInfo srcInfo;
+            switch (message.what) {
+                case LOCK:
+                    Log.w(TAG, "Locked: Lock ignored: " + mSetId);
+                    break;
+                case UNLOCK:
+                    log("Unlocking from " + mDevice);
+                    //trigger unlock procedure
+                    ///*_CSIP
+                    mSetCoordinator.setLockValue(mBCService.mCsipAppId, mSetId, null, BluetoothDeviceGroup.ACCESS_RELEASED);
+                    transitionTo(mUnlocking);
+                    //_CSIP*/
+
+                    //transitionTo(mIdle);
+                    break;
+                case LOCK_STATE_CHANGED:
+                    ///*_CSIP
+                    int value = (int)message.arg1;
+                    List<BluetoothDevice> devices = (List<BluetoothDevice>)message.obj;
+                    Log.w(TAG, "Lock state changed:" + value);
+                    if (value == BluetoothDeviceGroup.ACCESS_GRANTED) {
+                        transitionTo(mLocked);
+                    } else {
+                        Log.w(TAG, "Locking: Unlocked to " + mSetId);
+                        transitionTo(mIdle);
+                    }
+                    //_CSIP*/
+                    break;
+                case BASS_GRP_START_SCAN_OFFLOAD:
+                    if (mBCService != null) {
+                        log("START_SCAN_OFFLOAD: " + mMemberDevices);
+                        mBCService.startScanOffload(mDevice, mMemberDevices);
+                        transitionTo(mLockedProcessing);
+                    } else {
+                        log("no Bassclient service handle");
+                    }
+                    break;
+                case BASS_GRP_STOP_SCAN_OFFLOAD:
+                    if (mBCService != null) {
+                        log("STOP_SCAN_OFFLOAD: " + mMemberDevices);
+                        mBCService.stopScanOffload(mDevice, mMemberDevices);
+                        transitionTo(mLockedProcessing);
+                    } else {
+                        log("no Bassclient service handle");
+                    }
+                    break;
+                case BASS_GRP_ADD_BCAST_SOURCE:
+                    srcInfo = (BleBroadcastSourceInfo)message.obj;
+                    if (mBCService != null) {
+                        mBCService.addBroadcastSource(mDevice, mMemberDevices, srcInfo);
+                        transitionTo(mLockedProcessing);
+                    } else {
+                        log("no Bassclient service handle");
+                    }
+                    break;
+                case BASS_GRP_UPDATE_BCAST_SOURCE:
+                    srcInfo = (BleBroadcastSourceInfo)message.obj;
+                    if (mBCService != null) {
+                        mBCService.updateBroadcastSource(mDevice, mMemberDevices, srcInfo);
+                        transitionTo(mLockedProcessing);
+                    } else {
+                        log("no Bassclient service handle");
+                    }
+                    break;
+                case BASS_GRP_SET_BCAST_CODE:
+                    srcInfo = (BleBroadcastSourceInfo)message.obj;
+                    if (mBCService != null) {
+                        mBCService.setBroadcastCode(mDevice, mMemberDevices, srcInfo);
+                        transitionTo(mLockedProcessing);
+                    } else {
+                        log("no Bassclient service handle");
+                    }
+                    break;
+                case BASS_GRP_REMOVE_BCAST_SOURCE:
+                    byte sourceId = (byte)message.arg1;
+                    if (mBCService != null) {
+                        mBCService.removeBroadcastSource(mDevice, mMemberDevices, sourceId);
+                        transitionTo(mLockedProcessing);
+                    } else {
+                        log("no Bassclient service handle");
+                    }
+                    break;
+                default:
+                    log("Locked: not handled message:" + message.what);
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+    @VisibleForTesting
+     class LockedProcessing extends State {
+         @Override
+         public void enter() {
+             log( "Enter LockedProcessing(" + mSetId + "): "
+                     + messageWhatToString(getCurrentMessage().what));
+         }
+
+         @Override
+         public void exit() {
+             log("Exit LockedProcessing(" + mSetId + "): "
+                     + messageWhatToString(getCurrentMessage().what));
+         }
+
+         @Override
+         public boolean processMessage(Message message) {
+             log("LockedProcessing process message(" + mSetId + "): "
+                     + messageWhatToString(message.what));
+             BleBroadcastSourceInfo srcInfo;
+             switch (message.what) {
+                 case UNLOCK:
+                     log("LockedProcessing: UNLOCK defer " + mDevice);
+                     deferMessage(message);
+                     transitionTo(mLocked);
+                     break;
+                 case LOCK_STATE_CHANGED:
+                     int value = (int)message.arg1;
+                     Log.w(TAG, "Locking state changed:" + value);
+                     //Should never happen
+                     break;
+                 case LOCK:
+                      log("LockedProcessing: LOCK ignore " + mDevice);
+                     break;
+                 case BASS_GRP_START_SCAN_OFFLOAD:
+                 case BASS_GRP_STOP_SCAN_OFFLOAD:
+                 case BASS_GRP_ADD_BCAST_SOURCE:
+                 case BASS_GRP_UPDATE_BCAST_SOURCE:
+                 case BASS_GRP_SET_BCAST_CODE:
+                 case BASS_GRP_REMOVE_BCAST_SOURCE:
+                     //defer the meesage and move to Locked
+                     if (hasDeferredMessages(UNLOCK)) {
+                         //If Unlock is in pending list, remove it
+                         //Override the UNLOCK with this new operation
+                         log("removing the unlock message, as there is another req");
+                         removeDeferredMessages(UNLOCK);
+                     }
+                     deferMessage(message);
+                     break;
+                 default:
+                     log("LockedProcessing: not handled message:" + message.what);
+                     return NOT_HANDLED;
+             }
+             return HANDLED;
+         }
+     }
+
+
+    @VisibleForTesting
+    class Unlocking extends State {
+        @Override
+        public void enter() {
+            log( "Enter Unlocking(" + mSetId + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+
+            //removeDeferredMessages(LOCK);
+
+        }
+
+        @Override
+        public void exit() {
+            log("Exit Unlocking(" + mSetId + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Locked process message(" + mSetId + "): "
+                    + messageWhatToString(message.what));
+            BleBroadcastSourceInfo srcInfo;
+            switch (message.what) {
+                case UNLOCK:
+                    log("Unlocking: UNLOCK ignored from " + mDevice);
+                    break;
+                case LOCK_STATE_CHANGED:
+                    ///*_CSIP
+                    int value = (int)message.arg1;
+                    Log.w(TAG, "Locking state changed:" + value);
+                    if (value == BluetoothDeviceGroup.ACCESS_RELEASED) {
+                         mMemberDevices = null;
+                         transitionTo(mIdle);
+                     } else {
+                         Log.w(TAG, "UnLocking: failed to " + mSetId);
+                         //keep that back in Locked?
+                         transitionTo(mLocked);
+                         //
+                     }
+                    //_CSIP*/
+                    break;
+                case LOCK:
+                case BASS_GRP_START_SCAN_OFFLOAD:
+                case BASS_GRP_STOP_SCAN_OFFLOAD:
+                case BASS_GRP_ADD_BCAST_SOURCE:
+                case BASS_GRP_UPDATE_BCAST_SOURCE:
+                case BASS_GRP_SET_BCAST_CODE:
+                case BASS_GRP_REMOVE_BCAST_SOURCE:
+                    //defer the meesage and move to Locked
+                    deferMessage(message);
+                    break;
+                default:
+                    log("Locked: not handled message:" + message.what);
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+
+    private static String messageWhatToString(int what) {
+        switch (what) {
+            case LOCK:
+                return "LOCK";
+            case UNLOCK:
+                return "UNLOCK";
+            case LOCK_STATE_CHANGED:
+                return "LOCK_STATE_CHANGED";
+            case BASS_GRP_START_SCAN_OFFLOAD:
+                return "BASS_GRP_START_SCAN_OFFLOAD";
+            case BASS_GRP_STOP_SCAN_OFFLOAD:
+                return "BASS_GRP_STOP_SCAN_OFFLOAD";
+            case BASS_GRP_ADD_BCAST_SOURCE:
+                return "BASS_GRP_ADD_BCAST_SOURCE";
+            case BASS_GRP_UPDATE_BCAST_SOURCE:
+                return "BASS_GRP_UPDATE_BCAST_SOURCE";
+            case BASS_GRP_SET_BCAST_CODE:
+                return "BASS_GRP_SET_BCAST_CODE";
+            case BASS_GRP_REMOVE_BCAST_SOURCE:
+                return "BASS_GRP_REMOVE_BCAST_SOURCE";
+            default:
+                break;
+        }
+        return Integer.toString(what);
+    }
+
+    @Override
+    protected void log( String msg) {
+        if (BassClientStateMachine.BASS_DBG) {
+            super.log(msg);
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BassUtils.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BassUtils.java
new file mode 100644
index 0000000..9289198
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/bassclient/BassUtils.java
@@ -0,0 +1,506 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package com.android.bluetooth.bc;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import java.util.UUID;
+import java.util.Collection;
+import android.os.UserHandle;
+
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import java.nio.charset.StandardCharsets;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Scanner;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.lang.String;
+import java.lang.StringBuffer;
+import java.lang.Integer;
+
+import java.nio.ByteBuffer;
+import java.lang.Byte;
+import java.util.stream.IntStream;
+import java.util.NoSuchElementException;
+
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.BluetoothLeScanner;
+import java.util.UUID;
+import android.os.Handler;
+import android.os.ParcelUuid;
+import android.os.SystemProperties;
+import android.os.RemoteException;
+
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BleBroadcastSourceChannel;
+//import android.bluetooth.BluetoothBroadcast;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import com.android.bluetooth.btservice.ServiceFactory;
+///*_BMS
+import com.android.bluetooth.broadcast.BroadcastService;
+//_BMS*/
+import android.bluetooth.BluetoothCodecConfig;
+/*_PACS
+import com.android.bluetooth.pacsclient.PacsClientService;
+_PACS*/
+import android.bluetooth.IBleBroadcastAudioScanAssistCallback;
+
+/**
+ * Bass Utility functions
+ */
+
+final class BassUtils {
+        private static final String TAG = "BassUtils";
+        /*LE Scan related members*/
+        private boolean mBroadcastersAround = false;
+        private BluetoothAdapter mBluetoothAdapter = null;
+        private BluetoothLeScanner mLeScanner = null;
+        private BCService mBCService = null;
+
+        ///*_BMS
+        private BroadcastService mBAService = null;
+        //_BMS*/
+        public static final String BAAS_UUID = "00001852-0000-1000-8000-00805F9B34FB";
+        private boolean mIsLocalBMSNotified = false;
+        private ServiceFactory mFactory = new ServiceFactory();
+        //Using ArrayList as KEY to hashmap. May be not risk
+        //in this case as It is used to track the callback to cancel Scanning later
+        private final Map<ArrayList<IBleBroadcastAudioScanAssistCallback>, ScanCallback> mLeAudioSourceScanCallbacks;
+        private final Map<BluetoothDevice, ScanCallback> mBassAutoAssist;
+
+        private static final int AA_START_SCAN = 1;
+        private static final int AA_SCAN_SUCCESS = 2;
+        private static final int AA_SCAN_FAILURE = 3;
+        private static final int AA_SCAN_TIMEOUT = 4;
+        //timeout for internal scan
+        private static final int AA_SCAN_TIMEOUT_MS = 1000;
+
+        /**
+         * Stanadard Codec param types
+         */
+        static final  int LOCATION = 3;
+        //sample rate
+        static final int SAMPLE_RATE = 1;
+        //frame duration
+        static final int FRAME_DURATION = 2;
+        //Octets per frame
+        static final int OCTETS_PER_FRAME = 8;
+        /*_PACS
+        private PacsClientService mPacsClientService = PacsClientService.getPacsClientService();
+        _PACS*/
+        BassUtils (BCService service) {
+            mBCService = service;
+            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+            mLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
+            mLeAudioSourceScanCallbacks = new HashMap<ArrayList<IBleBroadcastAudioScanAssistCallback>, ScanCallback>();
+            mBassAutoAssist = new HashMap<BluetoothDevice, ScanCallback>();
+            ///*_BMS
+            mBAService = BroadcastService.getBroadcastService();
+            //_BMS*/
+        }
+
+        private ScanCallback mPaSyncScanCallback = new ScanCallback() {
+            @Override
+            public void onScanResult(int callbackType, ScanResult result) {
+                log( "onScanResult:" + result);
+            }
+        };
+
+        void cleanUp () {
+
+              if (mLeAudioSourceScanCallbacks != null) {
+                  mLeAudioSourceScanCallbacks.clear();
+              }
+
+              if (mBassAutoAssist != null) {
+                  mBassAutoAssist.clear();
+              }
+        }
+
+        boolean leScanControl(boolean on) {
+            log("leScanControl:" + on);
+            mLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
+            if (mLeScanner == null) {
+                Log.e(TAG, "LeScan handle not available");
+                return false;
+            }
+
+            if (on) {
+                mLeScanner.startScan(mPaSyncScanCallback);
+            } else {
+                mLeScanner.stopScan(mPaSyncScanCallback);
+            }
+
+            return true;
+         }
+
+        /* private helper to check if the Local BLE Broadcast happening Or not */
+         public boolean isLocalLEAudioBroadcasting() {
+             boolean ret = false;
+             /*String localLeABroadcast = SystemProperties.get("persist.vendor.btstack.isLocalLeAB");
+             if (!localLeABroadcast.isEmpty() && "true".equals(localLeABroadcast)) {
+                 ret = true;
+             }
+             log("property isLocalLEAudioBroadcasting returning " + ret);*/
+             ///*_Broadcast
+             if (mBAService == null) {
+                 mBAService = BroadcastService.getBroadcastService();
+             }
+             if (mBAService != null) {
+                 ret = mBAService.isBroadcastActive();
+                 //ret = mBAService.isBroadcastStreaming();
+                log("local broadcast streaming:" + ret);
+             } else {
+                log("BroadcastService is Null");
+             }
+             //_Broadcast*/
+             log("isLocalLEAudioBroadcasting returning " + ret);
+             return ret;
+         }
+
+        Handler mAutoAssistScanHandler = new Handler() {
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+                switch (msg.what) {
+                    case AA_START_SCAN:
+                        BluetoothDevice dev = (BluetoothDevice) msg.obj;
+                        Message m = obtainMessage(AA_SCAN_TIMEOUT);
+                        m.obj = dev;
+                        sendMessageDelayed(m, AA_SCAN_TIMEOUT_MS);
+                        searchforLeAudioBroadcasters(dev, null);
+                        break;
+                    case AA_SCAN_SUCCESS:
+                        //Able to find to desired desired Source Device
+                        ScanResult scanRes = (ScanResult) msg.obj;
+                        dev = scanRes.getDevice();
+                        stopSearchforLeAudioBroadcasters(dev,null);
+                        mBCService.selectBroadcastSource(dev, scanRes, false, true);
+                        break;
+                    case AA_SCAN_FAILURE:
+                        //Not able to find the given source
+                        //ignore
+                        break;
+                    case AA_SCAN_TIMEOUT:
+                        dev = (BluetoothDevice)msg.obj;
+                        stopSearchforLeAudioBroadcasters(dev, null);
+                        break;
+                }
+            }
+        };
+        private void notifyLocalBroadcastSourceFound(ArrayList<IBleBroadcastAudioScanAssistCallback> cbs) {
+            BluetoothDevice localDev =
+                BluetoothAdapter.getDefaultAdapter().getRemoteDevice(mBluetoothAdapter.getAddress());
+            String localName = BluetoothAdapter.getDefaultAdapter().getName();
+            ScanRecord record = null;
+            if (localName != null) {
+                byte name_len = (byte)localName.length();
+                byte[] bd_name = localName.getBytes(StandardCharsets.US_ASCII);
+                byte[] name_key = new byte[] {++name_len, 0x09 }; //0x09 TYPE:Name
+                byte[] scan_r = new byte[name_key.length + bd_name.length];
+                System.arraycopy(name_key, 0, scan_r, 0, name_key.length);
+                System.arraycopy(bd_name, 0, scan_r, name_key.length, bd_name.length);
+                record = ScanRecord.parseFromBytes(scan_r);
+                log ("Local name populated in fake Scan res:" + record.getDeviceName());
+            }
+            ScanResult scanRes = new ScanResult(localDev,
+                1, 1, 1,2, 0, 0, 0, record, 0);
+            if (cbs != null) {
+                for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
+                    try {
+                          cb.onBleBroadcastSourceFound(scanRes);
+                    } catch (RemoteException e)  {
+                          Log.e(TAG, "Exception while calling onBleBroadcastSourceFound");
+                    }
+                }
+            }
+        }
+        public boolean searchforLeAudioBroadcasters (BluetoothDevice srcDevice,
+                                       ArrayList<IBleBroadcastAudioScanAssistCallback> cbs
+                                       ) {
+           log( "searchforLeAudioBroadcasters: ");
+           BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+           mIsLocalBMSNotified = false;
+           if (scanner == null) {
+                Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
+                return false;
+           }
+           synchronized (mLeAudioSourceScanCallbacks) {
+                if (mLeAudioSourceScanCallbacks.containsKey(cbs)) {
+                    Log.e(TAG, "LE Scan has already started");
+                    return false;
+                }
+                ScanCallback scanCallback = new ScanCallback() {
+                   @Override
+                    public void onScanResult(int callbackType, ScanResult result) {
+                        log( "onScanResult:" + result);
+                        if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
+                            // Should not happen.
+                            Log.e(TAG, "LE Scan has already started");
+                            return;
+                        }
+                        ScanRecord scanRecord = result.getScanRecord();
+                        //int pInterval = result.getPeriodicAdvertisingInterval();
+                        if (scanRecord != null) {
+                            Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData();
+                            if (listOfUuids != null) {
+                                //ParcelUuid bmsUuid = new ParcelUuid(BroadcastService.BAAS_UUID);
+                                //boolean isBroadcastSource = listOfUuids.containsKey(bmsUuid);
+                                boolean isBroadcastSource = listOfUuids.containsKey(ParcelUuid.fromString(BAAS_UUID));
+                                log( "isBroadcastSource:" + isBroadcastSource);
+                                if (isBroadcastSource) {
+                                    log( "Broadcast Source Found:" + result.getDevice());
+                                    if (cbs != null) {
+                                        for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
+                                           try {
+                                               cb.onBleBroadcastSourceFound(result);
+                                           } catch (RemoteException e)  {
+                                               Log.e(TAG, "Exception while calling onBleBroadcastSourceFound");
+                                           }
+                                        }
+                                    } else {
+                                        if (srcDevice.equals(result.getDevice())) {
+                                            log("matching src Device found");
+                                            Message msg = mAutoAssistScanHandler.obtainMessage(AA_SCAN_SUCCESS);
+                                            msg.obj = result;
+                                            mAutoAssistScanHandler.sendMessage(msg);
+                                        }
+                                    }
+                                } else {
+                                    log( "Broadcast Source UUID not preset, ignore");
+                                }
+                            } else {
+                                Log.e(TAG, "Ignore no UUID");
+                                return;
+                            }
+                        } else {
+                            Log.e(TAG, "Scan record is null, ignoring this Scan res");
+                            return;
+                        }
+                        //Before starting LE Scan, Call local APIs to find out if the local device
+                        //is Broadcaster, then generate callback for Local device
+                        if (!mIsLocalBMSNotified && isLocalLEAudioBroadcasting()) {
+                        //Create a DUMMY scan result for colocated case
+                            notifyLocalBroadcastSourceFound(cbs);
+                            mIsLocalBMSNotified = true;
+                        }
+                       }
+
+                     public void onScanFailed(int errorCode) {
+                         Log.e(TAG, "Scan Failure:" + errorCode);
+                     }
+                };
+         if (mBluetoothAdapter != null) {
+             if (cbs != null) {
+                 mLeAudioSourceScanCallbacks.put(cbs, scanCallback);
+             } else {
+                 //internal auto assist trigger remember it
+                 //based on device
+                 mBassAutoAssist.put(srcDevice, scanCallback);
+             }
+
+             ScanSettings settings = new ScanSettings.Builder().setCallbackType(
+                 ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
+                 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+                 .setLegacy(false)
+                 .build();
+             ScanFilter.Builder filterBuilder = new ScanFilter.Builder();
+                 ScanFilter srcFilter = filterBuilder.setServiceUuid(
+                     ParcelUuid.fromString(BAAS_UUID)).build();
+                     List<ScanFilter> filters = new ArrayList<ScanFilter>();
+                 if (!mIsLocalBMSNotified && isLocalLEAudioBroadcasting()) {
+                    //Create a DUMMY scan result for colocated case
+                    notifyLocalBroadcastSourceFound(cbs);
+                    mIsLocalBMSNotified = true;
+                 }
+                 scanner.startScan(filters, settings, scanCallback);
+                 return true;
+             } else {
+                 Log.e(TAG, "searchforLeAudioBroadcasters: Adapter is NULL");
+                 return false;
+             }
+         }
+    }
+    public boolean stopSearchforLeAudioBroadcasters(BluetoothDevice srcDev,
+                                                     ArrayList<IBleBroadcastAudioScanAssistCallback> cbs) {
+        log( "stopSearchforLeAudioBroadcasters()");
+        BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+        if (scanner == null) {
+            return false;
+        }
+        ScanCallback scanCallback = null;
+        if (cbs != null) {
+            scanCallback = mLeAudioSourceScanCallbacks.remove(cbs);
+        } else {
+            scanCallback = mLeAudioSourceScanCallbacks.remove(srcDev);
+        }
+
+        if (scanCallback == null) {
+            log( "scan not started yet");
+            return false;
+        }
+        scanner.stopScan(scanCallback);
+        return true;
+    }
+
+    private int convertConfigurationSRToCapabilitySR(byte sampleRate) {
+        int ret = BluetoothCodecConfig.SAMPLE_RATE_NONE;
+        switch (sampleRate) {
+            case 1:
+                ret = BluetoothCodecConfig.SAMPLE_RATE_NONE; break;
+            case 2:
+                ret = BluetoothCodecConfig.SAMPLE_RATE_NONE; break;
+            case 3:
+                ret = BluetoothCodecConfig.SAMPLE_RATE_NONE; break;
+            case 4:
+                //ret = BluetoothCodecConfig.SAMPLE_RATE_32000; break;
+            case 5:
+                ret = BluetoothCodecConfig.SAMPLE_RATE_44100; break;
+            case 6:
+                ret = BluetoothCodecConfig.SAMPLE_RATE_48000; break;
+            }
+        log("convertConfigurationSRToCapabilitySR returns:" + ret);
+        return ret;
+    }
+
+    private boolean isSampleRateSupported(BluetoothDevice device, byte sampleRate) {
+        boolean ret = false;
+        /*_PACS
+        BluetoothCodecConfig[]  supportedConfigs = mPacsClientService.getSinkPacs(device);
+        int actualSampleRate = convertConfigurationSRToCapabilitySR(sampleRate);
+
+        if (actualSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) {
+            return false;
+        }
+
+        for (int i=0; i<supportedConfigs.length; i++) {
+            if (actualSampleRate == supportedConfigs[i].getSampleRate()) {
+                ret = true;
+            }
+        }
+
+        log("isSampleRateSupported returns:" + ret);
+        _PACS*/
+        return ret;
+    }
+    public List<BleBroadcastSourceChannel> selectBises(BluetoothDevice device,
+                                                 BleBroadcastSourceInfo srcInfo, BaseData base)  {
+        boolean noPref = SystemProperties.getBoolean("persist.vendor.service.bt.bass_no_pref", false);
+        if (noPref) {
+            log("No pref selected");
+            return null;
+        } else {
+        /*_PACS
+        mPacsClientService = PacsClientService.getPacsClientService();
+        List<BleBroadcastSourceChannel> bChannels = new ArrayList<BleBroadcastSourceChannel>();
+        //if (mPacsClientService == null) {
+            log("selectBises: Pacs Service is null, pick BISes apropriately");
+            //Pacs not available
+            if (base != null) {
+                bChannels = base.pickAllBroadcastChannels();
+            } else {
+                bChannels = null;
+            }
+            return bChannels;
+        //}
+        if (mPacsClientService != null) {
+            int supportedLocations = 1/*mPacsClientService.getSinkLocations(device);
+            ArrayList<BaseData.BaseInformation> broadcastedCodecInfo = base.getBISIndexInfos();
+            if (broadcastedCodecInfo != null) {
+                for (int i=0; i<broadcastedCodecInfo.size(); i++) {
+                    HashMap<Integer, String> consolidatedUniqueCodecInfo = broadcastedCodecInfo.get(i).consolidatedUniqueCodecInfo;
+                    byte index = broadcastedCodecInfo.get(i).index;
+                    if (consolidatedUniqueCodecInfo != null) {
+
+
+                        byte[] bisChannelLocation = consolidatedUniqueCodecInfo.get(LOCATION).getBytes();
+                        byte[] locationValue = new byte[4];
+                        System.arraycopy(bisChannelLocation, 2, locationValue, 0, 4);
+                        log ("locationValue>>> ");
+                        printByteArray(locationValue);
+                        ByteBuffer wrapped = ByteBuffer.wrap(locationValue);
+                        int bisLocation = wrapped.getInt();
+                        log("bisLocation: " + bisLocation);
+                        int reversebisLoc = Integer.reverseBytes(bisLocation);
+                        log("reversebisLoc: " + reversebisLoc);
+
+                        byte[] bisSampleRate = consolidatedUniqueCodecInfo.get(SAMPLE_RATE).getBytes();
+                        byte bisSR = bisSampleRate[2];
+
+                        //using bitwise operand as Location can be bitmask
+                        if (isSampleRateSupported(device, bisSR) && (reversebisLoc & supportedLocations) == supportedLocations) {
+                             log("matching location: bisLocation " + reversebisLoc + ":: " + supportedLocations);
+                             BleBroadcastSourceChannel bc = new BleBroadcastSourceChannel(index, String.valueOf(index), true);
+                             bChannels.add(bc);
+                        }
+                     }
+                }
+            }
+        }
+
+        if (bChannels != null && bChannels.size() == 0) {
+            log("selectBises: no channel are selected");
+            bChannels = null;
+
+        }
+        return bChannels;
+        _PACS*/
+      }
+      return null;
+    }
+
+    public void triggerAutoAssist (BleBroadcastSourceInfo srcInfo) {
+        //searchforLeAudioBroadcasters (srcInfo.getSourceDevice(), null, AUTO_ASSIST_SCAN_TIMEOUT);
+        BluetoothDevice dev = srcInfo.getSourceDevice();
+
+        Message msg = mAutoAssistScanHandler.obtainMessage(AA_START_SCAN);
+        msg.obj = srcInfo.getSourceDevice();
+        mAutoAssistScanHandler.sendMessage(msg);
+    }
+
+    static void log(String msg) {
+        if (BassClientStateMachine.BASS_DBG) {
+           Log.d(TAG, msg);
+        }
+    }
+
+    static void printByteArray(byte[] array) {
+        log("Entire byte Array as string: " + Arrays.toString(array));
+        log("printitng byte by bte");
+        for (int i=0; i<array.length; i++) {
+             log( "array[" + i + "] :" + Byte.toUnsignedInt(array[i]));
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/broadcast/BroadcastNativeInterface.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/broadcast/BroadcastNativeInterface.java
new file mode 100644
index 0000000..31340b6
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/broadcast/BroadcastNativeInterface.java
@@ -0,0 +1,253 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package com.android.bluetooth.broadcast;
+
+import android.bluetooth.BluetoothBroadcast;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.util.Log;
+import java.util.List;
+
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import java.util.Arrays;
+
+/**
+ * Broadcast Native Interface to/from JNI.
+ */
+
+public class BroadcastNativeInterface {
+    private static final String TAG = "BroadcastNativeInterface";
+    private static final boolean DBG = true;
+    private BluetoothAdapter mAdapter;
+    @GuardedBy("INSTANCE_LOCK")
+    private static BroadcastNativeInterface sInstance;
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static {
+        classInitNative();
+    }
+
+    @VisibleForTesting
+    private BroadcastNativeInterface() {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mAdapter == null) {
+            Log.wtfStack(TAG, "No Bluetooth Adapter Available");
+        }
+    }
+
+    /**
+     * Get singleton instance.
+     */
+    public static BroadcastNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new BroadcastNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+    /**
+     * Initializes the native interface.
+     *
+     * @param maxConnectedAudioDevices maximum number of A2DP Sink devices that can be connected
+     * simultaneously
+     * @param codecConfigPriorities an array with the codec configuration
+     * priorities to configure.
+     */
+    public void init(int maxBroadcast, BluetoothCodecConfig codecConfig, int mode) {
+        initNative(maxBroadcast, codecConfig, mode);
+    }
+
+    /**
+     * Cleanup the native interface.
+     */
+    public void cleanup() {
+        cleanupNative();
+    }
+
+    /**
+     * Sets a connected A2DP remote device as active.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    public boolean enableBroadcast(BluetoothCodecConfig mCodecConfig) {
+        Log.d(TAG, "enableBroadcast");
+        return enableBroadcastNative(mCodecConfig);
+    }
+    //Move SM to IDLE
+    public boolean disableBroadcast(int adv_id) {
+        Log.d(TAG, "disableBroadcast");
+        return disableBroadcastNative(adv_id);
+    }
+    //Remove ISO Data path for reconfiguration
+    public boolean SetupAudioPath(boolean enable, int adv_id, int BIG_handle, int num_bises, int[] bises) {
+        Log.d(TAG, "SetupAudioPath for BIG Handle: " + BIG_handle);
+        return setupAudioPathNative(enable, adv_id, BIG_handle, num_bises, bises);
+    }
+    //Star/End Session
+    public boolean setActiveDevice(boolean enable, int advID) {
+        Log.d(TAG, "SetActiveDevice");
+        return setActiveDeviceNative(enable, advID);
+    }
+    //Retrieve stored encryption key
+    public String GetEncryptionKey() {
+        Log.d(TAG, "GetEncryptionKey");
+        return getEncryptionKeyNative();
+    }
+    //Set new encyption key
+    public boolean SetEncryptionKey(boolean enabled, int length) {
+        Log.d(TAG, "SetEncryptionKey");
+        return setEncryptionKeyNative(enabled, length);
+    }
+    /**
+     * Sets the codec configuration preferences.
+     *
+     * @param device the remote Bluetooth device
+     * @param codecConfigArray an array with the codec configurations to
+     * configure.
+     * @return true on success, otherwise false.
+     */
+    //Restart session with new codec config
+    public boolean setCodecConfigPreference(int adv_handle, BluetoothCodecConfig codecConfig) {
+        Log.d(TAG, "setCodecConfigPreference");
+        return setCodecConfigPreferenceNative(adv_handle, codecConfig);
+    }
+    private int translate_state_to_app(int event, int state) {
+        if (event == BroadcastStackEvent.EVENT_TYPE_BROADCAST_STATE_CHANGED) {
+            switch(state) {
+                 case BroadcastStackEvent.STATE_IDLE:
+                     return BluetoothBroadcast.STATE_DISABLED;
+                 case BroadcastStackEvent.STATE_CONFIGURED:
+                     return BluetoothBroadcast.STATE_ENABLED;
+                 case BroadcastStackEvent.STATE_STREAMING:
+                     return BluetoothBroadcast.STATE_STREAMING;
+                 default:
+                    return BluetoothBroadcast.STATE_DISABLED;
+            }
+        } else if (event == BroadcastStackEvent.EVENT_TYPE_BROADCAST_AUDIO_STATE_CHANGED) {
+             switch(state) {
+                 case BroadcastStackEvent.STATE_STOPPED:
+                     return BluetoothBroadcast.STATE_NOT_PLAYING;
+                 case BroadcastStackEvent.STATE_STARTED:
+                     return BluetoothBroadcast.STATE_PLAYING;
+                 default:
+                     return BluetoothBroadcast.STATE_NOT_PLAYING;
+             }
+        }
+        return BluetoothBroadcast.STATE_DISABLED;
+    }
+    private void sendMessageToService(BroadcastStackEvent event) {
+        BroadcastService service = BroadcastService.getBroadcastService();
+        if (service != null) {
+            service.messageFromNative(event);
+        } else {
+            Log.w(TAG, "Event ignored, service not available: " + event);
+        }
+    }
+
+    private void onBroadcastStateChanged(int adv_handle, int state) {
+        BroadcastStackEvent event =
+             new BroadcastStackEvent(BroadcastStackEvent.EVENT_TYPE_BROADCAST_STATE_CHANGED);
+        event.valueInt = translate_state_to_app(BroadcastStackEvent.EVENT_TYPE_BROADCAST_STATE_CHANGED,state);
+        event.advHandle = adv_handle;
+        if (DBG) {
+            Log.d(TAG, "onBroadcastStateChanged: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void onAudioStateChanged(int adv_handle, int state) {
+        BroadcastStackEvent event =
+             new BroadcastStackEvent(BroadcastStackEvent.EVENT_TYPE_BROADCAST_AUDIO_STATE_CHANGED);
+        event.valueInt = translate_state_to_app(BroadcastStackEvent.EVENT_TYPE_BROADCAST_AUDIO_STATE_CHANGED,state);
+        event.advHandle = adv_handle;
+        if (DBG) {
+            Log.d(TAG, "onAudioStateChanged: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void onEncryptionKeyGenerated(String key) {
+        BroadcastStackEvent event =
+            new BroadcastStackEvent(BroadcastStackEvent.EVENT_TYPE_ENC_KEY_GENERATED);
+        event.key = key;
+        if (DBG) {
+            Log.d(TAG, "onEncryptionKeyGenerated: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void onCodecConfigChanged(int adv_handle, BluetoothCodecConfig newCodecConfig,
+                                                 BluetoothCodecConfig[] codecCapabilities) {
+        BroadcastStackEvent event =
+               new BroadcastStackEvent(BroadcastStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED);
+        event.codecStatus = new BluetoothCodecStatus(newCodecConfig, codecCapabilities, codecCapabilities);
+        event.advHandle = adv_handle;
+        if (DBG) {
+            Log.d(TAG, "onCodecConfigChanged: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void onSetupBIG(int setup, int adv_id, int big_handle, int num_bises, char[] bis_handles) {
+        BroadcastStackEvent event = new BroadcastStackEvent(BroadcastStackEvent.EVENT_TYPE_SETUP_BIG);
+        event.valueInt = setup;
+        event.advHandle = adv_id;
+        event.bigHandle = big_handle;
+        event.NumBises = num_bises;
+        if (DBG) {
+            Log.d(TAG, "onSetupBIG: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void onBroadcastIdGenerated(byte[] broadcast_id) {
+        BroadcastStackEvent event =
+            new BroadcastStackEvent(BroadcastStackEvent.EVENT_TYPE_BROADCAST_ID_GENERATED);
+        Log.d(TAG,"onBroadcastIdGenerated");
+        for(int i = 0; i < 3; i++) {
+            event.BroadcastId[i] = broadcast_id[i];
+            Log.d(TAG, "BroadcastID ["+i+"]" + " = " + event.BroadcastId[i]);
+        }
+        if (DBG) {
+            Log.d(TAG, "onBroadcastIdGenerated: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    // Native methods that call into the JNI interface
+    private static native void classInitNative();
+    private native void initNative(int maxBroadcast, BluetoothCodecConfig codecConfig, int mode);
+    private native void cleanupNative();
+    private native boolean setActiveDeviceNative(boolean enable, int adv_id);
+    private native boolean enableBroadcastNative(BluetoothCodecConfig codecConfig);
+    private native boolean disableBroadcastNative(int adv_id);
+    private native boolean setupAudioPathNative(boolean enable, int adv_id, int big_handle,
+                                                         int num_bises, int[] bises);
+    private native String getEncryptionKeyNative();
+    private native boolean setEncryptionKeyNative(boolean enabled, int length);
+    private native boolean setCodecConfigPreferenceNative(int adv_id, BluetoothCodecConfig codecConfig);
+
+}
+
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/broadcast/BroadcastService.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/broadcast/BroadcastService.java
new file mode 100644
index 0000000..96ccd06
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/broadcast/BroadcastService.java
@@ -0,0 +1,1960 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package com.android.bluetooth.broadcast;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothBroadcast;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.IBluetoothBroadcast;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.util.StatsLog;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+
+import com.android.bluetooth.BluetoothMetricsProto;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.avrcp.Avrcp;
+import com.android.bluetooth.avrcp.Avrcp_ext;
+import com.android.bluetooth.avrcp.AvrcpTargetService;
+import com.android.bluetooth.a2dp.A2dpService;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.MetricsLogger;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.bluetooth.ba.BATService;
+import com.android.bluetooth.gatt.GattService;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.bluetooth.hfp.HeadsetService;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.AdvertisingSet;
+import android.bluetooth.le.AdvertisingSetCallback;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
+import android.media.MediaMetadata;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.UUID;
+import java.util.HashMap;
+import android.os.ParcelUuid;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+import com.android.bluetooth.apm.ActiveDeviceManagerService;
+import com.android.bluetooth.apm.ApmConst;
+import com.android.bluetooth.apm.DeviceProfileMap;
+import com.android.bluetooth.apm.MediaAudio;
+
+/**
+ * Provides Bluetooth Broadcast profile, as a service in the Bluetooth application.
+ * @hide
+ */
+public class BroadcastService extends ProfileService {
+    private static final boolean DBG = true;
+    private static final boolean VDBG = true;
+    private static final String TAG = "BroadcastService";
+    private final Object mBroadcastLock = new Object();
+    private static BroadcastService sBroadcastService;
+    private AdapterService mAdapterService;
+    @VisibleForTesting
+    BroadcastNativeInterface mBroadcastNativeInterface;
+    @VisibleForTesting
+    ServiceFactory mFactory = new ServiceFactory();
+    private AudioManager mAudioManager;
+    int mBroadcastState = BluetoothBroadcast.STATE_DISABLED;
+    int mBroadcastAudioState = BluetoothBroadcast.STATE_NOT_PLAYING;
+    private String mEncryptionString;
+    private byte[] mEncKey = new byte[16];
+    private byte [] BigBroadcastCode = new byte [16];
+    private byte [] mBroadcastID = new byte[3];
+    private final int mBroadcastIdLength = 3;
+    private boolean mEncryptionEnabled = true;
+    private boolean mPartialSimulcast = false;//dual quality simulcast
+    private boolean mEncKeyRefreshed = false;
+    private int mEncryptionLength =16;
+    private int mDefaultEncryptionLength = 16;
+    private int [] bis_handles;
+    private int mBIGHandle = -1;
+    private int mNumBises = -1;
+    private int mNumSubGrps = 1;
+    private int mPD = 0;
+    private boolean goingDown = false;
+    private boolean mIsAdvertising = false;
+    private BroadcastMessageHandler mHandler;
+    private AdvertisingSetCallback mCallback;
+    private AdvertisingSet mAdvertisingSet;
+    List <BisInfo> mBisInfo;
+    Map<Integer, MetadataLtv>mMetaInfo = Collections.synchronizedMap(new HashMap<>());;
+    private String mAdvAddress;
+    private int mAdvAddressType;
+    private BluetoothLeAdvertiser mAdvertiser;
+    private BluetoothCodecStatus mCodecStatus;
+    private BluetoothCodecConfig mCodecConfig;
+    private BluetoothCodecConfig mHapCodecConfig;
+    private BroadcastCodecConfig mBroadcastCodecConfig;
+    private BroadcastAdvertiser mBroadcastAdvertiser;
+    private int mBroadcastConfigSettings;
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothDevice mBroadcastDevice = null;
+    private boolean mBroadcastDeviceIsActive = false;
+    TrackMetadata mTrackMetadata;
+    private String mBroadcastAddress = "FA:CE:FA:CE:FA:CE";
+    ActiveDeviceManagerService mActiveDeviceManager;
+    public static UUID BROADCAST_AUDIO_UUID = UUID.fromString("00001852-0000-1000-8000-00805F9B34FB");
+    public static UUID BASIC_AUDIO_UUID = UUID.fromString("00001851-0000-1000-8000-00805F9B34FB");
+    private BroadcastBase mBroadcastBase;
+    private MediaAudio mMediaAudio;
+    private boolean new_codec_id = false;
+    private static int mSecPhy = 1;
+    private static int mTxPowerLevel = 1;
+    private static int mPaInt;
+    private boolean mNewVersion = false;
+    List <String> broadcast_supported_config = new ArrayList<String>(List.of("16_2", "24_2", "48_1", "48_2", "48_3", "48_4", "48_5", "48_6"));
+    private static final int MSG_ENABLE_BROADCAST = 1;
+    private static final int MSG_DISABLE_BROADCAST = 2;
+    private static final int MSG_SET_ENCRYPTION_KEY = 3;
+    private static final int MSG_GET_ENCRYPTION_KEY = 4;
+    private static final int MSG_SET_BROADCAST_ACTIVE = 5;
+    private static final int MSG_UPDATE_BROADCAST_ADV_SET = 6;
+    private static final int MSG_ADV_DATA_SET = 7;
+    private static final int MSG_SET_AUDIO_PATH = 8;
+    private static final int MSG_RESET_ENCRYPTION_FLAG_TIMEOUT = 9;
+    private static final int MSG_FROM_NATIVE_CODEC_STATE = 10;
+    private static final int MSG_FROM_NATIVE_BROADCAST_STATE = 11;
+    private static final int MSG_FROM_NATIVE_ENCRYPTION_KEY = 12;
+    private static final int MSG_FROM_NATIVE_BROADCAST_AUDIO_STATE = 13;
+    private static final int MSG_FROM_NATIVE_SETUP_BIG = 14;
+    private static final int MSG_UPDATE_BROADCAST_STATE = 15;
+    private static final int MSG_FROM_NATIVE_BROADCAST_ID = 16;
+    @Override
+    protected IProfileServiceBinder initBinder() {
+        return new BluetoothBroadcastBinder(this);
+    }
+
+    @Override
+    protected void create() {
+        Log.i(TAG, "create()");
+    }
+
+    @Override
+    protected boolean start() {
+        Log.i(TAG, "start()");
+        if (sBroadcastService != null) {
+            Log.w(TAG, "Broadcastervice is already running");
+            return true;
+        }
+        if (mHandler != null)
+            mHandler = null;
+        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+            "AdapterService cannot be null when A2dpService starts");
+
+        mBroadcastNativeInterface = Objects.requireNonNull(mBroadcastNativeInterface.getInstance(),
+            "BroadcastNativeInterface cannot be null when BroadcastService starts");
+        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+            Objects.requireNonNull(mAudioManager,
+            "AudioManager cannot be null when A2dpService starts");
+        HandlerThread thread = new HandlerThread("BroadcastHandler");
+        mBroadcastConfigSettings = SystemProperties.getInt("persist.vendor.btstack.bap_ba_setting", 4);
+        mBroadcastCodecConfig = new BroadcastCodecConfig();
+        String PartialSimulcast = SystemProperties.get("persist.vendor.btstack.partial_simulcast");
+        if (!PartialSimulcast.isEmpty() && "true".equals(PartialSimulcast)) {
+            mPartialSimulcast = true;
+            mNumSubGrps = 2;
+            mNumBises = 4;
+            //mHapCodecConfig = new BroadcastCodecConfig(mPartialSimulcast);
+        }
+        String mNewCodecId = SystemProperties.get("persist.vendor.btstack.new_lc3_id");
+        if (mNewCodecId.isEmpty() || "true".equals(mNewCodecId) ||
+            "6".equals(mNewCodecId)) {
+            new_codec_id = true;
+        }
+        /* Property to set seconday advertising phy to 1M or 2M. 2M is selected by default
+         * if propety is not set
+         */
+        mSecPhy = SystemProperties.getInt("persist.vendor.btstack.secphy", 2);
+        mTxPowerLevel = SystemProperties.getInt("persist.vendor.service.bt.txpower", 9);
+        mPD = SystemProperties.getInt("persist.vendor.service.bt.presentation_delay", 40);
+        mPaInt = SystemProperties.getInt("persist.vendor.btstack.pa_interval", 360);
+        mNewVersion = SystemProperties.getBoolean("persist.vendor.service.bt.new_ba_version", true);
+        int offload_mode = 1; //offload
+        mBroadcastNativeInterface.init(1, mCodecConfig,offload_mode);
+        thread.start();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        mAdapterService.registerReceiver(mBroadcastReceiver, filter);
+        Looper looper = thread.getLooper();
+        mHandler = new BroadcastMessageHandler(looper);
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        mBroadcastBase = new BroadcastBase();
+        mBisInfo = new ArrayList<>();
+        //mBroadcastAdvertiser = new BroadcastAdvertiser();
+        setBroadcastService(this);
+        mBroadcastDevice = mAdapter.getRemoteDevice(mBroadcastAddress);
+        mTrackMetadata = new TrackMetadata(null);
+
+        mActiveDeviceManager = ActiveDeviceManagerService.get(this);
+        DeviceProfileMap dpm = DeviceProfileMap.getDeviceProfileMapInstance();
+        dpm.profileConnectionUpdate(mBroadcastDevice, ApmConst.AudioFeatures.BROADCAST_AUDIO, ApmConst.AudioProfiles.BROADCAST_LE, true);
+
+        //Get current codec and call native init
+        return true;
+    }
+    private void initialize_advertiser() {
+        Log.d(TAG,"initalize_advertiser");
+        mBroadcastAdvertiser = new BroadcastAdvertiser();
+        GetEncryptionKeyFromNative();
+    }
+    private void startAdvTest() {
+        //Log.d(TAG,"startAdvTest!!!");
+        boolean ba_test = SystemProperties.getBoolean("persist.vendor.btstack.batest",false);
+        if (ba_test) {
+            Log.d(TAG,"startAdvTest!!!");
+            EnableBroadcast(null);
+        }
+    }
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+               int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.ERROR);
+               Log.d(TAG,"action: " + action + " state: " + state);
+               if (state == BluetoothAdapter.STATE_ON) {
+                   initialize_advertiser();
+                   startAdvTest();
+               } else if (state == BluetoothAdapter.STATE_TURNING_OFF) {
+                   if (sBroadcastService != null)
+                       cleanup_broadcast();
+               }
+            }
+        }
+    };
+    @Override
+    protected boolean stop() {
+        Log.i(TAG, "stop()");
+        if (sBroadcastService == null) {
+            Log.w(TAG, "stop() called before start()");
+            return true;
+        }
+        notifyBroadcastEnabled(false);
+        if (mIsAdvertising) {
+           mBroadcastAdvertiser.stopBroadcastAdvertising();
+        }
+        mAdapterService = null;
+        mBroadcastNativeInterface = null;
+        mAudioManager = null;
+        mIsAdvertising = false;
+        Looper looper = mHandler.getLooper();
+        if (looper != null) {
+            looper.quit();
+        }
+        setBroadcastService(null);
+        return true;
+    }
+
+    @Override
+    protected void cleanup() {
+        Log.i(TAG, "cleanup()");
+    }
+    public static synchronized BroadcastService getBroadcastService() {
+        if (sBroadcastService == null) {
+            Log.w(TAG, "getBroadcastService(): service is null");
+            return null;
+        }
+        if (!sBroadcastService.isAvailable()) {
+            Log.w(TAG, "getBroadcastService(): service is not available");
+            return null;
+        }
+        return sBroadcastService;
+    }
+
+    /** Handles Broadcast messages. */
+    private final class BroadcastMessageHandler extends Handler {
+        private BroadcastMessageHandler(Looper looper) {
+            super(looper);
+        }
+        @Override
+        public void handleMessage(Message msg) {
+            Log.v(TAG, "BroadcastMessageHandler: received message=" + msg.what);
+            int prev_state;
+            switch (msg.what) {
+                case MSG_ENABLE_BROADCAST:
+                    //int prev_state;
+                    synchronized (mBroadcastLock) {
+                        if (VDBG) {
+                            Log.i(TAG, "Setting broadcast state to ENABLING");
+                        }
+                        prev_state = mBroadcastState;
+                        mBroadcastState = BluetoothBroadcast.STATE_ENABLING;
+                    }
+                    broadcastState(mBroadcastState, prev_state);
+                    mBroadcastNativeInterface.enableBroadcast(mCodecConfig);
+                  break;
+                case MSG_DISABLE_BROADCAST:
+                    //int prev_state;
+                    goingDown = true;
+                    if (!mIsAdvertising) {
+                        Log.e(TAG, "Broadcast is not advertising");
+                        break;
+                    }
+                    synchronized(mBroadcastLock) {
+                        if (VDBG) {
+                            Log.i(TAG,"Disabling broadcast, setting state to DISABLING");
+                        }
+                        prev_state = mBroadcastState;
+                        mBroadcastState = BluetoothBroadcast.STATE_DISABLING;
+                    }
+                    broadcastState(mBroadcastState, prev_state);
+                    mBroadcastNativeInterface.disableBroadcast(mAdvertisingSet.getAdvertiserId());
+                    //mBroadcastAdvertiser.stopBroadcastAdvertising();
+                  break;
+                case MSG_SET_ENCRYPTION_KEY:
+                    //int length = msg.arg1;
+                    mBroadcastNativeInterface.SetEncryptionKey(mEncryptionEnabled, mEncryptionLength);
+                    if (mEncryptionLength == 0) {
+                        for(int i = 0; i < mDefaultEncryptionLength; i++) {
+                            BigBroadcastCode[i] = 0x00;
+                        }
+                        broadcastEncryptionkeySet();
+                    }
+                  break;
+                case MSG_GET_ENCRYPTION_KEY: {
+                    mEncryptionString = mBroadcastNativeInterface.GetEncryptionKey();
+                    if (mEncryptionString == null) {
+                        Log.e(TAG,"MSG_GET_ENCRYPTION_KEY: mEncryptionString null");
+                        for (int i = 0; i < mDefaultEncryptionLength; i++) {
+                             BigBroadcastCode[i] = 0x00;
+                        }
+                        break;
+                    }
+                    mEncKey= mEncryptionString.getBytes();
+                    Log.i(TAG, "mEncryptionString: " + mEncryptionString);
+                    System.arraycopy(mEncKey, 0, BigBroadcastCode, 0, mEncKey.length);
+                    if (mEncKey.length < mDefaultEncryptionLength) {
+                        for (int i = mEncKey.length; i < mDefaultEncryptionLength; i++) {
+                            BigBroadcastCode[i] = 0x00;
+                        }
+                    }
+                    for (int i = 0;i < mDefaultEncryptionLength/2; i++) {
+                        byte temp = BigBroadcastCode[i];
+                        BigBroadcastCode[i] = BigBroadcastCode[(mDefaultEncryptionLength -1) - i];
+                        BigBroadcastCode[(mDefaultEncryptionLength -1) - i] = temp;
+                    }
+                    for (int i = 0; i < 16; i++) {
+                        Log.i(TAG,"BigBroadcastCode["+ i + "] = " + BigBroadcastCode[i]);
+                    }
+                    //TODO: Stub to test encryption key creation, to be removed
+                    //Log.i(TAG,"calling setencryptionkey");
+                    //mBroadcastNativeInterface.SetEncryptionKey(4);
+                    broadcastEncryptionkeySet();
+                  }
+                  break;
+                case MSG_UPDATE_BROADCAST_ADV_SET:
+                  break;
+                case MSG_SET_BROADCAST_ACTIVE:
+                  // Call native layer to set broadcast active
+                    //mBroadcastNativeInterface.setActiveDevice(true, mAdvertisingSet.getAdvertiserId());
+                    //setActiveDevice(mBroadcastDevice);
+                    notifyBroadcastEnabled(true);
+                  break;
+                case MSG_RESET_ENCRYPTION_FLAG_TIMEOUT:
+                    Log.i(TAG,"Setting mEncKeyRefreshed to false");
+                    mEncKeyRefreshed = false;
+                  break;
+                case MSG_FROM_NATIVE_BROADCAST_STATE:
+                    synchronized(mBroadcastLock) {
+                          prev_state = mBroadcastState;
+                          mBroadcastState = msg.arg1;
+                          if (VDBG) {
+                              Log.i(TAG,"New broadcast state: " + mBroadcastState);
+                          }
+                    }
+                    if (mBroadcastState == BluetoothBroadcast.STATE_DISABLED) {
+                        if (goingDown) {
+                            notifyBroadcastEnabled(false);
+                        }
+                        mBIGHandle = -1;
+                        mBroadcastAdvertiser.stopBroadcastAdvertising();
+                        break;
+                    }
+                    if (prev_state != mBroadcastState)
+                        broadcastState(mBroadcastState, prev_state);
+                  break;
+                case MSG_ADV_DATA_SET:
+                    synchronized (mBroadcastLock) {
+                        if (VDBG) {
+                            Log.i(TAG, "Setting broadcast state to ENABLING");
+                        }
+                        prev_state = mBroadcastState;
+                        mBroadcastState = BluetoothBroadcast.STATE_ENABLED;
+                    }
+                    broadcastState(mBroadcastState, prev_state);
+                  break;
+                case MSG_SET_AUDIO_PATH:
+                  //mBroadcastNativeInterface.SetupAudioPath(true,mAdvertisingSet.getAdvertiserId(),mBIGHandle,mNumBises,bis_handles);
+                  break;
+                case MSG_FROM_NATIVE_CODEC_STATE:
+                    mCodecStatus = (BluetoothCodecStatus)msg.obj;
+                    if (IsCodecConfigChanged(mCodecStatus.getCodecConfig())) {
+                        mBroadcastCodecConfig.updateBroadcastCodecConfig(mCodecStatus.getCodecConfig());
+                        mBroadcastBase.populateBase();
+                        mBroadcastAdvertiser.updatePAwithBase();
+                    }
+                    broadcastCodecConfig(mCodecStatus);
+                    mMediaAudio = MediaAudio.get();
+                    mMediaAudio.onCodecConfigChange(mBroadcastDevice, mCodecStatus, ApmConst.AudioProfiles.BROADCAST_LE);
+                  break;
+                case MSG_FROM_NATIVE_ENCRYPTION_KEY: {
+                    mEncryptionString = (String)msg.obj;
+                    Log.d(TAG,"mEncryptionString: " + mEncryptionString);
+                    mEncKey= mEncryptionString.getBytes();
+                    System.arraycopy(mEncKey, 0, BigBroadcastCode, 0, mEncKey.length);
+                    if (mEncKey.length < mDefaultEncryptionLength) {
+                        for (int i = mEncKey.length; i < mDefaultEncryptionLength; i++) {
+                            BigBroadcastCode[i] = 0x00;
+                        }
+                    }
+                    for (int i = 0; i < mEncKey.length; i++) {
+                        Log.d(TAG,"mEnc[" + i +"] = " + mEncKey[i]);
+                    }
+                    for (int i = 0;i < mDefaultEncryptionLength/2; i++) {
+                        byte temp = BigBroadcastCode[i];
+                        BigBroadcastCode[i] = BigBroadcastCode[(mDefaultEncryptionLength - 1) - i];
+                        BigBroadcastCode[(mDefaultEncryptionLength - 1) - i] = temp;
+                    }
+                    //Broadcast encyption key set
+                    broadcastEncryptionkeySet();
+                  }
+                  break;
+                case MSG_FROM_NATIVE_SETUP_BIG:
+                    int setup = msg.arg1;
+                    boolean set = (setup == 1);
+                    if (set) {
+                        Log.d(TAG, "BIG created: " + mBIGHandle + "with no of bises: " + mNumBises);
+                        mNumBises = mNumBises * mNumSubGrps;
+                        mBroadcastBase.populateBase();
+                        mBroadcastAdvertiser.updatePAwithBase();
+                    } else {
+                        Log.d(TAG, "BIG terminated");
+                        mBIGHandle = -1;
+                        //Clean up mBisInfo List
+                        mBisInfo.clear();
+                        mMetaInfo.clear();
+                    }
+                    break;
+                case MSG_FROM_NATIVE_BROADCAST_AUDIO_STATE:
+                    int prevState = mBroadcastAudioState;
+                    mBroadcastAudioState = msg.arg1;
+                    if (prevState != mBroadcastAudioState)
+                        broadcastAudioState(mBroadcastAudioState, prevState);
+                    break;
+                case MSG_FROM_NATIVE_BROADCAST_ID:
+                   if (mBroadcastAdvertiser != null) {
+                       mBroadcastAdvertiser.startBroadcastAdvertising();
+                   } else {
+                       Log.e(TAG,"Did not receive adatper state change intent, turning off Broadcast");
+                       prev_state = mBroadcastState;
+                       mBroadcastState = BluetoothBroadcast.STATE_DISABLED;
+                       broadcastState(mBroadcastState, prev_state);
+                   }
+                   break;
+                case MSG_UPDATE_BROADCAST_STATE:
+                    prev_state = msg.arg1;
+                    mBroadcastState = BluetoothBroadcast.STATE_DISABLED;
+                    Log.d(TAG,"MSG_UPDATE_BROADCAST_STATE");
+                    broadcastState(mBroadcastState, prev_state);
+                    break;
+                default:
+                  Log.e(TAG,"unknown message msg.what = " + msg.what);
+                  break;
+            }
+            Log.d(TAG,"Exit handleMessage");
+        }
+    }
+
+    private void updateBroadcastStateToHfp(int state) {
+        if (DBG) {
+            Log.d(TAG,"updateBroadcastStateToHfp");
+        }
+        HeadsetService hfpService = HeadsetService.getHeadsetService();
+        if (hfpService != null) {
+            hfpService.updateBroadcastState(state);
+        }
+    }
+    private void broadcastState(int state, int prev_state) {
+        if (DBG) {
+            Log.d(TAG, "Broadcasting broadcastState: " + state);
+        }
+        Intent intent = new Intent(BluetoothBroadcast.ACTION_BROADCAST_STATE_CHANGED);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prev_state);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
+        sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
+        updateBroadcastStateToHfp(state);
+    }
+    private void broadcastCodecConfig(BluetoothCodecStatus codecStatus) {
+        if (DBG) {
+            Log.d(TAG, "Broacasting broadcastCodecConfig" + codecStatus);
+        }
+        Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
+        intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, codecStatus);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBroadcastDevice);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        //sendBroadcast(intent, BLUETOOTH_CONNECT);
+    }
+
+    private void broadcastEncryptionkeySet() {
+        if (DBG) {
+            Log.d(TAG, "broadcastEncryptionkeySet");
+        }
+        Intent intent = new Intent(BluetoothBroadcast.ACTION_BROADCAST_ENCRYPTION_KEY_GENERATED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    private void broadcastAudioState(int newState, int prevState) {
+        Log.d(TAG, "broadcastAudioState: State:" + audioStateToString(prevState)
+                + "->" + audioStateToString(newState));
+        Intent intent = new Intent(BluetoothBroadcast.ACTION_BROADCAST_AUDIO_STATE_CHANGED);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    private static String audioStateToString(int state) {
+        switch (state) {
+            case BluetoothBroadcast.STATE_PLAYING:
+                return "PLAYING";
+            case BluetoothBroadcast.STATE_NOT_PLAYING:
+                return "NOT_PLAYING";
+            default:
+                break;
+        }
+        return Integer.toString(state);
+    }
+    private boolean IsCodecConfigChanged(BluetoothCodecConfig config) {
+        return (mCodecConfig.getSampleRate() != config.getSampleRate() ||
+                mCodecConfig.getChannelMode() != config.getChannelMode() ||
+                mCodecConfig.getCodecSpecific1() != config.getCodecSpecific1() ||
+                mCodecConfig.getCodecSpecific2() != config.getCodecSpecific2());
+    }
+    private boolean isCodecValid(BluetoothCodecConfig mCodecConfig) {
+        if (mCodecConfig.getCodecType() != BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean isCodecConfigValid(String config_id) {
+        if (broadcast_supported_config.contains(config_id)) {
+            Log.d(TAG,"isCodecConfigValid: config supported");
+            return true;
+        }
+        Log.d(TAG,"isCodecConfigValid: config not supported");
+        return false;
+    }
+
+    private boolean isEncrytionLengthValid(int enc_length) {
+        if (enc_length == 4 || enc_length == 16) {
+            return true;
+        }
+        return false;
+    }
+
+    private BluetoothCodecConfig buildCodecConfig(String config_id, int channel) {
+        //BluetoothCodecConfig cc;
+        int index = broadcast_supported_config.indexOf(config_id);
+        int sr;
+        long codecspecific1, codecspecific2;
+        String isMono = SystemProperties.get("persist.vendor.btstack.enable.broadcast_mono");
+        Log.d(TAG,"buildCodecConfig:" + config_id + " index: " + index);
+        switch(index) {
+            case 0: //16_2
+                sr = BluetoothCodecConfig.SAMPLE_RATE_16000;
+                //ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                codecspecific1 = 1001;//32kbps
+                codecspecific2 = 1;
+                break;
+            case 1: //24_2
+                sr = BluetoothCodecConfig.SAMPLE_RATE_24000;
+                //ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                codecspecific1 = 1002;//48kbps
+                codecspecific2 = 1;
+                break;
+            case 2: //48_1
+                sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                //ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                codecspecific1 = 1004;//80kbps
+                codecspecific2 = 0;
+                break;
+            case 3: //48_2
+                sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                //ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                codecspecific1 = 1004;//80kbps
+                codecspecific2 = 1;
+                break;
+            case 4: //48_3
+                sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                //ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                codecspecific1 = 1006;//96kbps
+                codecspecific2 = 0;
+                break;
+            case 5: //48_4
+                sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                //ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                codecspecific1 = 1006;//96kbps
+                codecspecific2 = 1;
+                break;
+            case 6: //48_5
+                sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                //ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                codecspecific1 = 1007;//124kbps
+                codecspecific2 = 0;
+                break;
+            case 7: //48_6
+                sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                //ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                codecspecific1 = 1007;//124kbps
+                codecspecific2 = 1;
+                break;
+
+            default:
+                sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                //ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                codecspecific1 = 1007;//80kbps
+                codecspecific2 = 1;
+                break;
+            }
+        //if (isMono.isEmpty() || isMono.equals("mono")) {
+        //    ch_mode = BluetoothCodecConfig.CHANNEL_MODE_MONO;
+        //}
+        BluetoothCodecConfig cc = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+                                      BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                      sr, BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                      channel, codecspecific1, codecspecific2, 0, 0);
+        return cc;
+    }
+    private static synchronized void setBroadcastService(BroadcastService instance) {
+        if (DBG) {
+            Log.d(TAG, "setBroadcastService(): set to: " + instance);
+        }
+        sBroadcastService = instance;
+    }
+
+    private void cleanup_broadcast() {
+        if (DBG) Log.d (TAG, "cleanup_broadcast");
+        synchronized (mBroadcastLock) {
+            if (mIsAdvertising) {
+                if (mBroadcastNativeInterface != null)
+                    mBroadcastNativeInterface.disableBroadcast(mAdvertisingSet.getAdvertiserId());
+                mBroadcastAdvertiser.stopBroadcastAdvertising();
+                int prev_state = mBroadcastState;
+                mBroadcastState = BluetoothBroadcast.STATE_DISABLED;
+                broadcastState(mBroadcastState, prev_state);
+            }
+        }
+    }
+    public boolean EnableBroadcast(String packageName) {
+        if (DBG) Log.d (TAG, "EnableBroadcast");
+
+        if (mBroadcastState != BluetoothBroadcast.STATE_DISABLED) {
+            return false;
+        }
+        Message msg = mHandler.obtainMessage(MSG_ENABLE_BROADCAST);
+        mHandler.sendMessage(msg);
+        return true;
+    }
+    public boolean DisableBroadcast(String packageName) {
+        if (DBG) Log.d (TAG, "DisableBroadcast: state " + mBroadcastState);
+
+        if (mBroadcastState == BluetoothBroadcast.STATE_DISABLING ||
+            mBroadcastState == BluetoothBroadcast.STATE_DISABLED) {
+            return true;
+        } else if (mBroadcastState != BluetoothBroadcast.STATE_ENABLED &&
+            mBroadcastState != BluetoothBroadcast.STATE_STREAMING) {
+            Log.d(TAG,"Broadcast is not enabled yet");
+            return false;
+        }
+        Message msg = mHandler.obtainMessage(MSG_DISABLE_BROADCAST);
+        mHandler.sendMessage(msg);
+        return true;
+    }
+    public boolean SetEncryption(boolean enable, int enc_len,
+                           boolean use_existing, String packageName) {
+        if (DBG) Log.d (TAG,"SetEncryption");
+
+        mEncryptionEnabled = enable;
+        if (enable) {
+            if (!isEncrytionLengthValid(enc_len)) {
+                if (DBG) Log.d (TAG,"SetEncryption: invalid encrytion length requested");
+                return false;
+            }
+        } else {
+            Log.d(TAG,"Selected unencrypted");
+            enc_len = 0;
+        }
+        if (!use_existing) {
+            Log.d (TAG,"Generate new ecrytpion key of lenght = " + enc_len);
+            mEncryptionLength = enc_len;
+            if (mBroadcastState == BluetoothBroadcast.STATE_ENABLED ||
+                mBroadcastState == BluetoothBroadcast.STATE_STREAMING) {
+                mEncKeyRefreshed = true;
+                Message msg = mHandler.obtainMessage(MSG_RESET_ENCRYPTION_FLAG_TIMEOUT);
+                mHandler.sendMessageDelayed(msg, 1000);
+            }
+            Message msg = mHandler.obtainMessage(MSG_SET_ENCRYPTION_KEY);
+            mHandler.sendMessage(msg);
+        }
+        return true;
+    }
+
+    public byte[] GetEncryptionKey(String packageName) {
+        if (DBG) Log.d (TAG,"GetBroadcastEncryptionKey: package name = " + packageName);
+
+        return BigBroadcastCode;
+    }
+
+    public int GetBroadcastStatus(String packageName) {
+        if (DBG) Log.d (TAG,"GetBroadcastStatus: state = " + mBroadcastState + " package name = " + packageName);
+        return mBroadcastState;
+    }
+
+    public boolean isBroadcastActive() {
+        if (mBroadcastDeviceIsActive == false) {
+            Log.d (TAG,"isBroadcastActive: Broadcast is turned to off");
+            return false;
+        }
+        if (DBG) Log.d (TAG,"isBroadcastActive");
+        return ((mBroadcastState == BluetoothBroadcast.STATE_ENABLED) ||
+               (mBroadcastState == BluetoothBroadcast.STATE_STREAMING));
+    }
+
+    public BluetoothDevice getBroadcastDevice() {
+        if (DBG) Log.d (TAG,"getBroadcastDevice");
+        return mBroadcastDevice;
+    }
+
+    public String getBroadcastAddress() {
+        if (DBG) Log.d (TAG,"getBroadcastAddress");
+        return mBroadcastAddress;
+    }
+
+    public byte[] getBroadcastId() {
+        Log.d(TAG,"getBroadcastId: " + mBroadcastID);
+        return mBroadcastID;
+    }
+
+    public boolean isBroadcastStreamingEncrypted() {
+        return mEncryptionEnabled;
+    }
+
+    public boolean isBroadcastStreaming() {
+        return (mBroadcastState == BluetoothBroadcast.STATE_STREAMING);
+    }
+
+    public String BroadcastGetAdvAddress() {
+        if (DBG) Log.d (TAG,"BroadcastGetAdvAddress: " + mAdvAddress);
+        return mAdvAddress;
+    }
+
+    public int getNumSubGroups() {
+        if (DBG) Log.d (TAG,"getNumSubGroups: " + mNumSubGrps);
+        return mNumSubGrps;
+    }
+
+    public int BroadcastGetAdvAddrType() {
+        return mAdvAddressType;
+    }
+
+    public int BroadcatGetAdvHandle() {
+        //check if advertising
+        return mAdvertisingSet.getAdvertiserId();
+    }
+
+    public int BroadcastGetAdvInterval() {
+        return mPaInt;
+    }
+    public List<BisInfo> BroadcastGetBisInfo() {
+        if (isBroadcastStreaming()) {
+            return mBisInfo;
+        }
+        Log.d(TAG,"BroadcastGetBisInfo: Broadcast is not active");
+        return mBisInfo;
+    }
+
+    public Map<Integer, MetadataLtv> BroadcastGetMetaInfo() {
+        if (isBroadcastStreaming()) {
+            return mMetaInfo;
+        }
+        Log.d(TAG,"BroadcastGetMetaInfo: Broadcast is not active");
+        return mMetaInfo;
+    }
+    public byte[] BroadcastGetMetadata() {
+        if (isBroadcastStreaming()) {
+            return mBroadcastBase.getMetadataContext();
+        }
+        Log.d(TAG,"BroadcastGetMetadata: Broadcast is not active");
+        return mBroadcastBase.getMetadataContext();
+    }
+    public void setCodecPreference(String config_id, int ch_mode) {
+        if (isCodecConfigValid(config_id)) {
+            setCodecPreference(buildCodecConfig(config_id, ch_mode));
+        }
+    }
+    public void setCodecPreference(BluetoothCodecConfig newConfig) {
+        if (DBG) Log.d (TAG, "setCodecPreference");
+        if (newConfig.getCodecType() != BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) {
+            Log.e(TAG, "setCodecPreference: Invalid codec for broadcast mode: " + newConfig.getCodecType());
+            return;
+        }
+        //mBroadcastCodecConfig.updateCodecConfig(newConfig);
+        if (mBroadcastState != BluetoothBroadcast.STATE_DISABLED)
+            mBroadcastNativeInterface.setCodecConfigPreference(mAdvertisingSet.getAdvertiserId(),newConfig);
+    }
+
+    public void GetEncryptionKeyFromNative() {
+        Log.e(TAG,"GetEncryptionKeyFromNative");
+        Message msg = mHandler.obtainMessage(MSG_GET_ENCRYPTION_KEY);
+        mHandler.sendMessage(msg);
+    }
+    private void setup_isodatapath(int adv_id, int big_handle,int num_bises, int[] bises) {
+    }
+    /* LE HAP broadcast hooks */
+    public boolean startHAPBroadcast() {
+        if (isBroadcastActive()) {
+        //TODO: update codec config with HAP HQ mode
+        //Terminate BIG if created
+        //Notify codec config change to stack
+        //Create BIG and update BASE
+        } else {
+        //TODO: update codec config with HAP HQ mode
+        //Start Adv
+        //Existing encryption key will be used for HAP as only music streaming is supported
+        //Announcement content type will not be covered
+        }
+        return true;
+    }
+    public boolean stopHAPBroadcast() {
+        //TODO: DisableAudioPath
+        //Terminate BIG
+        //update state to disabling
+        //stop Adv
+        //reset codec config to default config
+        return true;
+    }
+    public void removeActiveDevice() {
+        if (DBG) Log.d (TAG,"removeActiveDevice");
+        //int [] bis_handles = {-1, -1};
+        if (mBroadcastDeviceIsActive == false) {
+            Log.d (TAG,"removeActiveDevice: mBADeviceIsActive is false, already removed");
+            return;
+        }
+        mBroadcastDeviceIsActive = false;
+        synchronized (mBroadcastLock) {
+            if (mIsAdvertising &&
+               (mBroadcastState == BluetoothBroadcast.STATE_ENABLED ||
+                mBroadcastState == BluetoothBroadcast.STATE_STREAMING)) {
+                mBroadcastNativeInterface.disableBroadcast(mAdvertisingSet.getAdvertiserId());
+                //mBroadcastAdvertiser.stopBroadcastAdvertising();
+            }
+            if (!mBroadcastNativeInterface.setActiveDevice(false, mAdvertisingSet.getAdvertiserId())) {
+                Log.d(TAG,"SetActiveNative failed");
+            }
+        }
+        //notifyBroadcastEnabled(false);
+    }
+
+    public BluetoothCodecStatus getCodecStatus() {
+        if (DBG) Log.d (TAG,"getCodecStatus");
+        BluetoothCodecConfig[] mBroadcastCodecConfig = {mCodecConfig};
+        return (new BluetoothCodecStatus(mCodecConfig, mBroadcastCodecConfig, mBroadcastCodecConfig));
+    }
+    public int setActiveDevice(BluetoothDevice device) {
+        if (DBG) Log.d (TAG,"setActiveDevice");
+        if (device == null) {
+            removeActiveDevice();
+            return ActiveDeviceManagerService.SHO_SUCCESS;
+        }
+        if (!Objects.equals(device, mBroadcastDevice)) {
+            Log.d(TAG,"setActiveDevice: Not a Broadcast device");
+            return ActiveDeviceManagerService.SHO_FAILED;
+        }
+        if (!mBroadcastNativeInterface.setActiveDevice(true, mAdvertisingSet.getAdvertiserId())) {
+            Log.d(TAG,"SetActiveNative failed");
+            return ActiveDeviceManagerService.SHO_FAILED;
+        }
+        mBroadcastDeviceIsActive = true;
+
+        return ActiveDeviceManagerService.SHO_SUCCESS;
+    }
+
+    public void notifyBroadcastEnabled(boolean enabled) {
+        if (DBG) Log.d (TAG,"notifyBroadcastEnabled: " + enabled);
+        ActiveDeviceManagerService activeDeviceManager = ActiveDeviceManagerService.get();
+        if(activeDeviceManager == null) {
+            Log.e(TAG,"ActiveDeviceManagerService not started. Return");
+            return;
+        }
+        if (enabled)
+            activeDeviceManager.enableBroadcast(mBroadcastDevice);
+        else
+            activeDeviceManager.disableBroadcast();
+    }
+
+    public void updateMetadataFromAvrcp(MediaMetadata data) {
+        if (DBG) Log.d (TAG,"updateMetadataFromAvrcp");
+        mTrackMetadata = new TrackMetadata(data);
+    }
+    public void messageFromNative(BroadcastStackEvent event) {
+        if (DBG) Log.d (TAG,"messageFromNative: event " + event);
+        switch(event.type) {
+            case BroadcastStackEvent.EVENT_TYPE_BROADCAST_STATE_CHANGED:
+                {
+                    Message msg =
+                        mHandler.obtainMessage(MSG_FROM_NATIVE_BROADCAST_STATE,
+                                               event.valueInt, event.advHandle);
+                    mHandler.sendMessage(msg);
+                }
+                break;
+            case BroadcastStackEvent.EVENT_TYPE_BROADCAST_AUDIO_STATE_CHANGED:
+                {
+                    Message msg =
+                        mHandler.obtainMessage(MSG_FROM_NATIVE_BROADCAST_AUDIO_STATE,
+                                               event.valueInt, event.advHandle);
+                    mHandler.sendMessage(msg);
+                }
+                break;
+            case BroadcastStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED:
+                {
+                    Message msg =
+                        mHandler.obtainMessage(MSG_FROM_NATIVE_CODEC_STATE);
+                    msg.obj = event.codecStatus;
+                    mHandler.sendMessage(msg);
+                }
+                break;
+            case BroadcastStackEvent.EVENT_TYPE_ENC_KEY_GENERATED:
+                {
+                    Message msg =
+                        mHandler.obtainMessage(MSG_FROM_NATIVE_ENCRYPTION_KEY);
+                    msg.obj = event.key;
+                    mHandler.sendMessage(msg);
+                }
+                break;
+            case BroadcastStackEvent.EVENT_TYPE_SETUP_BIG:
+                {
+                    mBIGHandle =  event.bigHandle;
+                    if (event.valueInt == 1)
+                        mNumBises = event.NumBises;
+                    Message msg =
+                        mHandler.obtainMessage(MSG_FROM_NATIVE_SETUP_BIG,event.valueInt, event.advHandle);
+                    mHandler.sendMessage(msg);
+                }
+                break;
+            case BroadcastStackEvent.EVENT_TYPE_BROADCAST_ID_GENERATED:
+                {
+                    Message msg =
+                        mHandler.obtainMessage(MSG_FROM_NATIVE_BROADCAST_ID);
+                    for (int i = 0; i < mBroadcastIdLength; i++) {
+                         mBroadcastID[i] = (byte)event.BroadcastId[i];
+                         Log.d(TAG,"mBroadcastID["+i+"]" + " = " + mBroadcastID[i]);
+                    }
+                    mHandler.sendMessage(msg);
+                }
+                break;
+            default:
+              Log.e (TAG,"messageFromNative: Invalid");
+        }
+    }
+    class TrackMetadata {
+     private String title;
+     private String artistName;
+     private String albumName;
+     private String genre;
+     private long playingTimeMs;
+
+         public TrackMetadata(MediaMetadata data) {
+             if (data == null) return;
+             artistName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ARTIST));
+             albumName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ALBUM));
+             title = data.getString(MediaMetadata.METADATA_KEY_TITLE);
+             genre = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_GENRE));
+             playingTimeMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION);
+         }
+         private String stringOrBlank(String s) {
+            return s == null ? new String() : s;
+         }
+    }
+    class BroadcastAdvertiser {
+      public BroadcastAdvertiser() {
+          Log.i(TAG,"BroadcastAdvertiser");
+          mCallback = new BroadcastAdvertiserCallback();
+          mAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
+          if (mAdvertiser == null) {
+              Log.e(TAG, "BroadcastAdvertiser: mAdvertiser is null");
+          }
+      }
+      public void startBroadcastAdvertising() {
+          Log.i(TAG,"startBroadcastAdvertising");
+          if (mAdvertiser == null) {
+              Log.e(TAG,"startBroadcastAdvertising: Advertiser is null");
+              int prev_state = mBroadcastState;
+              mBroadcastState = BluetoothBroadcast.STATE_DISABLED;
+              broadcastState(mBroadcastState, prev_state);
+              return;
+          }
+          AdvertisingSetParameters.Builder adv_param =
+                         new AdvertisingSetParameters.Builder();
+          adv_param.setLegacyMode(false);
+          adv_param.setConnectable(false);
+          adv_param.setScannable(false);
+          adv_param.setInterval(AdvertisingSetParameters.INTERVAL_MIN); //100msec
+          adv_param.setTxPowerLevel(mTxPowerLevel);
+          adv_param.setPrimaryPhy(1);
+          adv_param.setSecondaryPhy(mSecPhy);
+          AdvertiseData AdvData = new AdvertiseData.Builder()
+                                      .setIncludeDeviceName(true)
+                                      .addServiceData(new ParcelUuid(BROADCAST_AUDIO_UUID), mBroadcastID).build();
+          PeriodicAdvertisingParameters.Builder periodic_param = new PeriodicAdvertisingParameters.Builder();
+          periodic_param.setIncludeTxPower(true);
+          periodic_param.setInterval(mPaInt);
+          AdvertiseData PeriodicData = new AdvertiseData.Builder().addServiceData(new ParcelUuid(BASIC_AUDIO_UUID), new byte[0]).build();
+          Log.i(TAG,"Calling startAdvertisingSet");
+          mAdvertiser.startAdvertisingSet(adv_param.build(), AdvData, null, periodic_param.build(), PeriodicData, 0, 0, mCallback);
+      }
+      public void stopBroadcastAdvertising() {
+          Log.i(TAG,"stopBroadcastAdvertising");
+          if (mAdvertiser != null)
+              mAdvertiser.stopAdvertisingSet(mCallback);
+      }
+
+      public void updatePAwithBase() {
+          Log.i(TAG,"updatePAwithBase");
+          AdvertiseData PeriodicData = new AdvertiseData.Builder().addServiceData(new ParcelUuid(BASIC_AUDIO_UUID), mBroadcastBase.getBroadcastBaseInfo()).build();
+          mAdvertisingSet.setPeriodicAdvertisingData(PeriodicData);
+      }
+    }
+
+    private class BroadcastAdvertiserCallback extends AdvertisingSetCallback {
+        @Override
+        public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower,
+                                         int status) {
+            Log.i(TAG, "onAdvertisingSetStarted status " + status
+                 + " advertisingSet: " + advertisingSet + " txPower " + txPower);
+            if (status != BluetoothGatt.GATT_SUCCESS) {
+                Log.e(TAG,"Failed to start Broadcast Advertisement");
+                int prev_state = mBroadcastState;
+                mBroadcastState = BluetoothBroadcast.STATE_DISABLED;
+                broadcastState(mBroadcastState,prev_state);
+            }
+            if (status == BluetoothGatt.GATT_SUCCESS) {
+                mAdvertisingSet = advertisingSet;
+                mIsAdvertising = true;
+                int prev_state = mBroadcastState;
+                mBroadcastState = BluetoothBroadcast.STATE_ENABLED;
+                Log.i(TAG,"onAdvertisingSetStarted: adv_id = " + advertisingSet.getAdvertiserId() + "copied id = " + mAdvertisingSet.getAdvertiserId());
+                broadcastState(mBroadcastState,prev_state);
+                if (mHandler.hasMessages(MSG_RESET_ENCRYPTION_FLAG_TIMEOUT)) {
+                    Message msg =
+                        mHandler.obtainMessage(MSG_SET_BROADCAST_ACTIVE);
+                    mHandler.sendMessageDelayed(msg,600);
+                } else {
+                    notifyBroadcastEnabled(true);
+                }
+                int mChMode = mCodecConfig.getChannelMode();
+                switch (mChMode) {
+                    case BluetoothCodecConfig.CHANNEL_MODE_MONO:
+                    case BluetoothCodecConfig.CHANNEL_MODE_JOINT_STEREO:
+                        mNumBises = 1 * mNumSubGrps;
+                        break;
+                    case BluetoothCodecConfig.CHANNEL_MODE_STEREO:
+                        mNumBises = 2 * mNumSubGrps;
+                        break;
+                    default:
+                        Log.e(TAG,"channel mode unknown");
+                }
+                mBroadcastBase.populateBase();
+                mBroadcastAdvertiser.updatePAwithBase();
+                mAdvertisingSet.getOwnAddress();
+            }
+        }
+
+        @Override
+        public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
+            Log.i(TAG, "onAdvertisingSetStopped advertisingSet: " + advertisingSet);
+            mIsAdvertising = false;
+            int prev_state = mBroadcastState;
+            if (!goingDown && mBroadcastDeviceIsActive) {
+                Log.d(TAG,"onAdvertisingSetStopped: Unexpected Broadcast turn off");
+                notifyBroadcastEnabled(false);
+            }
+            if (goingDown) {
+                Message msg = mHandler.obtainMessage(MSG_UPDATE_BROADCAST_STATE,
+                               BluetoothBroadcast.STATE_DISABLING);
+                mHandler.sendMessageDelayed(msg,500);
+                goingDown = false;
+            } else {
+                mBroadcastState = BluetoothBroadcast.STATE_DISABLED;
+                broadcastState(mBroadcastState, prev_state);
+            }
+        }
+
+        @Override
+        public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable,
+                                         int status) {
+            Log.i(TAG, "onAdvertisingEnabled advertisingSet: " + advertisingSet
+                    + " status " + status + " enable: " + enable);
+        }
+
+        @Override
+        public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {
+            Log.i(TAG, "onAdvertisingDataSet advertisingSet: " + advertisingSet
+                    + " status " + status);
+            if (status == BluetoothGatt.GATT_SUCCESS) {
+                Log.i(TAG, "onAdvertisingDataSet: Base Info updated");
+            }
+        }
+        @Override
+        public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet,
+                                                   int txPower, int status) {
+            Log.i(TAG, "onAdvertisingParametersUpdated  advertisingSet: " + advertisingSet
+                    + " status " + status  + " txPower " + txPower);
+        }
+
+        @Override
+        public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType,
+                                     String address) {
+            Log.i(TAG, "onOwnAddressRead advertisingSet: " + advertisingSet
+                    + " address " + address + " addressType " + addressType);
+            mAdvAddress = address;
+            mAdvAddressType = addressType;
+        }
+
+    }
+    class BroadcastBase {
+        private final int LC3_SAMPLE_RATE_8000 = 0x01;
+        private final int LC3_SAMPLE_RATE_16000 = 0x02;
+        private final int LC3_SAMPLE_RATE_24000 = 0x03;
+        private final int LC3_SAMPLE_RATE_32000 = 0x04;
+        private final int LC3_SAMPLE_RATE_44100 = 0x05;
+        private final int LC3_SAMPLE_RATE_48000 = 0x06;
+
+        int presentationDelay = 0x009C40;
+        byte [] mPresentationDelay = new byte[3];
+        byte [] mCodecId = new byte[5];
+        byte [] mCodecSpecificLength = new byte[1];
+        byte [] mCodecSpecificSampleRate = new byte[3];
+        byte [] mCodecSpecificFrameDuration = new byte[3];
+        byte [] mCodecSpecificAudioLocation = new byte[6];
+        byte [] mCodecSpecificOctetsPerFrame = new byte[3];
+        byte [] mCodecSpecificBlocksPerSdu = new byte[3];
+        byte [] mCodecSpecificLengthL2 = new byte[1];
+        byte [] mCodecSpecificSampleRateL2 = new byte[3];
+        byte [] mCodecSpecificFrameDurationL2 = new byte[3];
+        byte [] mCodecSpecificAudioLocationL2 = new byte[6];
+        byte [] mCodecSpecificOctetsPerFrameL2 = new byte[3];
+        byte [] mCodecSpecificBlocksPerSduL2 = new byte[3];
+        byte [] mMetadataLength = new byte[1];
+        byte [] mMetadataContext = new byte[3];
+        byte [] mNumSubgroups = new byte[1];
+        byte [] mL2CodecID = new byte[1];
+        byte [] mL2CodecSpecificLength = new byte[1];
+        byte [] mL2mMetadataLength = new byte[1];
+        byte [] mL2NumBises = new byte[1];
+        byte [] mL2BisIndices = new byte[2];
+        byte [] mL3BisIndex = new byte[1];
+        byte [] mL3CodecSpecificLength = new byte[1];
+        byte [] mL3CodecSpecificAudioLocation = new byte[6];
+        byte mSampleRateLength = 2;
+        byte mSampleRateType = 0x01;
+        byte mFrameDurationLength = 2;
+        byte mFrameDurationType = 0x02;
+        byte mFrameDuration_7_5 = 0x00;//7.5 msec
+        byte mFrameDuration_10 = 0x01;//10msec
+        byte mAudioLocationLength = 5;
+        byte mAudioLocationType = 0x03;
+        byte mAudioLocationLeft = 0x01;
+        byte mAudioLocationRight = 0x02;
+        byte mAudioLocationCentre = 0x04;
+        byte mOctetsPerFrameLength = 3;
+        byte mOctestPerFrameType = 0x04;
+        byte mBlocksPerSduLength = 2;
+        byte mBlocksPerSduType = 0x05;
+        long LC3_CODEC_ID_OLD = 0x0000000001;
+        long LC3_CODEC_ID = 0x0000000006;
+        byte mCodecConfigLength = 0x10; //to be changed
+        byte mMediaContextType = 0x10;
+        byte [] BroadcastBaseArray = null;
+        //Metadata AD type
+        //Metadata
+      public BroadcastBase() {
+          //mccid = 0;
+          //int presentationDelay = 0x000014;
+          if (mPD == 20) {
+              Log.d(TAG,"Presentation Delay is set to 20msec");
+              presentationDelay = 0x004E20;
+          }
+          if (mNewVersion) {
+              mPresentationDelay = intTobyteArray(presentationDelay, 3);
+              mNumSubgroups[0] = (byte)mNumSubGrps;
+          } else {
+              mPresentationDelay = intTobyteArray(presentationDelay, 3);
+              if (new_codec_id) {
+                  mCodecId = longTobyteArray(LC3_CODEC_ID,5);
+              } else {
+                  mCodecId = longTobyteArray(LC3_CODEC_ID_OLD,5);
+              }
+              mCodecSpecificLength[0] = mCodecConfigLength;
+              mCodecSpecificSampleRate = updateSampleRate();
+              mCodecSpecificFrameDuration = updateFrameDuration();
+              mCodecSpecificAudioLocation = updateAudioLocation(0);
+              mCodecSpecificOctetsPerFrame = updateOctetsPerFrame();
+              mMetadataLength[0] = (byte)0x03;
+              int index = 0;
+              mMetadataContext[index++] = (byte)0x02; //length
+              mMetadataContext[index++] = (byte)mMediaContextType; //Type
+              mMetadataContext[index++] = (byte)0x01; //Value Music
+              mNumSubgroups[0] = (byte)mNumSubGrps; // only one set of broadcast is supported.
+          }
+      }
+      public byte [] getBroadcastBaseInfo() {
+          return BroadcastBaseArray;
+      }
+      public void updateBIGhandle(int handle) {
+          mBIGHandle = handle;
+      }
+
+      public byte[] getMetadataContext() {
+          return mMetadataContext;
+      }
+
+      public int getNumSubGroups() {
+          return mNumSubgroups[0];
+      }
+      public byte [] updateSampleRate() {
+          int SR = mCodecConfig.getSampleRate();
+          byte bytevalue;
+          switch (SR) {
+              case BluetoothCodecConfig.SAMPLE_RATE_48000:
+                if (mNewVersion) {
+                    bytevalue = (byte)0x08;
+                } else {
+                    bytevalue = (byte)0x06;
+                }
+                break;
+              case BluetoothCodecConfig.SAMPLE_RATE_44100:
+                if (mNewVersion) {
+                    bytevalue = (byte)0x07;
+                } else {
+                    bytevalue = (byte)0x05;
+                }
+                break;
+              case BluetoothCodecConfig.SAMPLE_RATE_32000:
+                if (mNewVersion) {
+                    bytevalue = (byte)0x06;
+                } else {
+                    bytevalue = (byte)0x04;
+                }
+                break;
+              case BluetoothCodecConfig.SAMPLE_RATE_24000:
+                if (mNewVersion) {
+                    bytevalue = (byte)0x05;
+                } else {
+                    bytevalue = (byte)0x03;
+                }
+                break;
+              case BluetoothCodecConfig.SAMPLE_RATE_16000:
+                if (mNewVersion) {
+                    bytevalue = (byte)0x03;
+                } else {
+                    bytevalue = (byte)0x02;
+                }
+                break;
+              case BluetoothCodecConfig.SAMPLE_RATE_8000:
+                bytevalue = (byte)0x01;
+                break;
+              default:
+                if (mNewVersion) {
+                    bytevalue = (byte)0x08;
+                } else {
+                    bytevalue = (byte)0x06;
+                }
+          }
+          byte [] ltv = {mSampleRateLength, mSampleRateType, bytevalue};
+          return ltv;
+      }
+      public byte[] updateOctetsPerFrame() {
+          long bitrate = (int) mCodecConfig.getCodecSpecific1();
+          long frameDuration = (int) mCodecConfig.getCodecSpecific2();
+          byte bytevalue;
+          //Update OctetsPerFrame based on frame duration
+          switch ((int)bitrate) {
+              case 1001:
+                if (frameDuration == 0) { //7.5msec
+                   bytevalue = (byte)30;
+                } else { //10msec
+                   bytevalue = (byte)40;
+                }
+                break;
+              case 1002:
+                if (frameDuration == 0) {
+                    bytevalue = (byte)45;
+                } else {
+                    bytevalue = (byte)60;
+                }
+                break;
+              case 1004:
+                if (frameDuration == 0) {
+                    bytevalue = (byte)75;
+                } else {
+                    bytevalue = (byte)100;
+                }
+                break;
+              case 1006:
+                if (frameDuration == 0) {
+                    bytevalue = (byte)90;
+                } else {
+                    bytevalue = (byte)120;
+                }
+                break;
+              case 1007:
+                if (frameDuration == 0) {
+                    bytevalue = (byte)117;
+                } else {
+                    bytevalue = (byte)155;
+                }
+                break;
+              default:
+                bytevalue = (byte)100;
+          }
+          Log.d(TAG,"updateOctetsPerFrame: " + bytevalue);
+          byte [] ltv = {mOctetsPerFrameLength, mOctestPerFrameType, bytevalue, 0x00};
+          return ltv;
+      }
+      private byte[] updateBlocksPerSdu() {
+          byte[] ltv = {mBlocksPerSduLength, mBlocksPerSduType,0x01};
+          return ltv;
+      }
+
+      public byte [] updateHAPSampleRate() {
+          int SR = mCodecConfig.getSampleRate();
+          byte bytevalue;
+          switch (SR) {
+              case BluetoothCodecConfig.SAMPLE_RATE_16000:
+                bytevalue = (byte)0x02;
+                break;
+              case BluetoothCodecConfig.SAMPLE_RATE_24000:
+                bytevalue = (byte)0x03;
+              default:
+                bytevalue = (byte)0x02;
+          }
+          byte[] ltv = {mSampleRateLength, mSampleRateType, bytevalue};
+          return ltv;
+      }
+      public byte [] updateHapOctetsPerFrame() {
+          long bitrate = mCodecConfig.getCodecSpecific1();
+          long frameDuration = (int) mCodecConfig.getCodecSpecific2();
+          byte bytevalue;
+          //Update OctetsPerFrame based on frame duration
+          switch((int)bitrate) {
+              case 1001:
+                if (frameDuration == 0) { //7.5msec
+                   bytevalue = (byte)30;
+                } else { //10msec
+                   bytevalue = (byte)40;
+                }
+                break;
+              case 1002:
+                if (frameDuration == 0) {
+                    bytevalue = (byte)45;
+                } else {
+                    bytevalue = (byte)60;
+                }
+                break;
+              default:
+                bytevalue = (byte)40;
+          }
+          byte [] ltv = {mOctetsPerFrameLength, mOctestPerFrameType, bytevalue, 0x00};
+          return ltv;
+      }
+      public byte [] updateAudioLocation(int bis_index) {
+          int ch_mode = mCodecConfig.getChannelMode();
+          byte ch = 0;
+          if (bis_index == 0) {
+              // stereo
+              if (ch_mode == BluetoothCodecConfig.CHANNEL_MODE_STEREO ||
+                  ch_mode == BluetoothCodecConfig.CHANNEL_MODE_JOINT_STEREO)
+                  ch = (byte)0x03;
+              else if (ch_mode == BluetoothCodecConfig.CHANNEL_MODE_MONO)
+                  ch = (byte)0x00;
+          } else {
+              if (ch_mode == BluetoothCodecConfig.CHANNEL_MODE_STEREO) {
+                  int bises = (mNumBises/((int)mNumSubgroups[0]));
+                  ch = (byte)(mAudioLocationRight - (bis_index % bises));
+              } else if (ch_mode == BluetoothCodecConfig.CHANNEL_MODE_JOINT_STEREO) {
+                  ch = (byte)0x03;
+              } else if (ch_mode == BluetoothCodecConfig.CHANNEL_MODE_MONO) {
+                  ch = (byte)0x00;
+              }
+          }
+          byte [] loc = {mAudioLocationLength, mAudioLocationType, ch, 0x00, 0x00, 0x00};
+          return loc;
+      }
+      public byte[] updateFrameDuration() {
+          byte mFD = mFrameDuration_10;
+          if (mCodecConfig.getCodecSpecific2() == 0) {
+              Log.d(TAG,"updateFrameDuration: 7.5msec");
+              mFD = mFrameDuration_7_5;
+          } else {
+              Log.d(TAG,"updateFrameDuration: 10 msec");
+          }
+          byte[] ltv = {mFrameDurationLength,mFrameDurationType,mFD};
+          return ltv;
+      }
+      public byte[] intTobyteArray(int intValue, int bytelen) {
+          byte [] val = new byte[bytelen];
+          for (int i = 0; i < bytelen; i++) {
+              val[(bytelen - 1) -i] = (byte)((intValue >> (8 *(bytelen - (i + 1)))) & 0x000000FF);
+          }
+          return val;
+      }
+      public byte [] longTobyteArray(long longValue, int bytelen) {
+          byte [] val = new byte[bytelen];
+          for (int i = 0; i < bytelen; i++) {
+              val[(bytelen - 1) -i] = (byte)((longValue >> (8 *(bytelen - (i + 1)))) & 0x00000000000000FF);
+          }
+          return val;
+      }
+      public int calculateBisPerGroup() {
+          int mChMode = mCodecConfig.getChannelMode();
+          int numbis = 2;
+          switch (mChMode) {
+              case BluetoothCodecConfig.CHANNEL_MODE_MONO:
+              case BluetoothCodecConfig.CHANNEL_MODE_JOINT_STEREO:
+                  Log.d(TAG,"BisPerGroup is 1");
+                  numbis = 1;
+                  break;
+              case BluetoothCodecConfig.CHANNEL_MODE_STEREO:
+                  Log.d(TAG,"BisPerGroup is 2");
+                  numbis = 2;
+                  break;
+              default:
+                  Log.e(TAG,"channel mode unknown");
+          }
+          return numbis;
+      }
+      public void populateBase() {
+          if (DBG) Log.d(TAG,"populateBase");
+          byte [] baseL1 = populate_level1_base();
+          byte [] baseL2 = populate_level2_base();
+          ByteArrayOutputStream ByteStr = new ByteArrayOutputStream();
+          ByteStr.write(baseL1, 0, baseL1.length);
+          ByteStr.write(baseL2, 0, baseL2.length);
+          if (!mNewVersion) {
+              byte [] baseL3 = populate_level3_base();
+              ByteStr.write(baseL3, 0, baseL3.length);
+          }
+          BroadcastBaseArray = ByteStr.toByteArray();
+      }
+      private byte [] populate_level1_base() {
+          ByteArrayOutputStream ByteStr = new ByteArrayOutputStream();
+          if (mNewVersion) {
+              mPresentationDelay = intTobyteArray(presentationDelay, 3);
+              mNumSubgroups[0] = (byte)mNumSubGrps;//calculate based on num bises and channel mode
+              ByteStr.write(mPresentationDelay, 0, mPresentationDelay.length);
+              ByteStr.write(mNumSubgroups, 0, mNumSubgroups.length);
+          } else {
+              mPresentationDelay = intTobyteArray(presentationDelay, 3);
+              if (new_codec_id) {
+                  mCodecId = longTobyteArray(LC3_CODEC_ID,5);
+              } else {
+                  mCodecId = longTobyteArray(LC3_CODEC_ID_OLD,5);
+              }
+              mCodecSpecificLength[0] = mCodecConfigLength;
+              mCodecSpecificSampleRate = updateSampleRate();
+              mCodecSpecificFrameDuration = updateFrameDuration();
+              mCodecSpecificAudioLocation = updateAudioLocation(0);
+              mCodecSpecificOctetsPerFrame = updateOctetsPerFrame();
+              mMetadataLength[0] = (byte)0x03;
+              byte [] mediacontext = {2, mMediaContextType, (byte)0x01};
+              mNumSubgroups[0] = (byte)mNumSubGrps;//calculate based on num bises and channel mode
+
+              ByteStr.write(mPresentationDelay, 0, mPresentationDelay.length);
+              ByteStr.write(mCodecId, 0, mCodecId.length);
+              ByteStr.write(mCodecSpecificLength, 0, mCodecSpecificLength.length);
+              ByteStr.write(mCodecSpecificSampleRate, 0, mCodecSpecificSampleRate.length);
+              ByteStr.write(mCodecSpecificFrameDuration, 0, mCodecSpecificFrameDuration.length);
+              ByteStr.write(mCodecSpecificAudioLocation, 0, mCodecSpecificAudioLocation.length);
+              ByteStr.write(mCodecSpecificOctetsPerFrame, 0, mCodecSpecificOctetsPerFrame.length);
+              ByteStr.write(mMetadataLength, 0, mMetadataLength.length);
+              ByteStr.write(mMetadataContext, 0, mMetadataContext.length);
+              ByteStr.write(mNumSubgroups, 0, mNumSubgroups.length);
+          }
+          return ByteStr.toByteArray();
+      }
+      private byte [] populate_level2_base() {
+          Log.d(TAG,"populate_level2_base, subgroup = " + mNumSubgroups[0]);
+          ByteArrayOutputStream ByteStr = new ByteArrayOutputStream();
+          byte [] metalength = new byte[1];
+          int bisPerGroup = calculateBisPerGroup();//mNumBises/mNumSubGrps;
+          byte [] numBises = new byte[1];
+          numBises = intTobyteArray(bisPerGroup,1);
+          byte [] bisInd = new byte[bisPerGroup];
+          if (mNewVersion) {
+              byte[] mcid = new byte[1];
+              if (new_codec_id) {
+                  mcid = longTobyteArray(LC3_CODEC_ID,5);
+              } else {
+                  mcid = longTobyteArray(LC3_CODEC_ID_OLD,5);
+              }
+              mMetadataLength[0] = (byte)0x04;
+              byte [] mediacontext = {3, 2, (byte)0x04, (byte)0x00};
+              int codecConfigLength = 0x13;
+              mCodecSpecificLength = intTobyteArray(codecConfigLength, 1);
+              for (int i = 0; i < mNumSubgroups[0]; i++) {
+                  if (mPartialSimulcast) {
+                      if (i < (mNumSubgroups[0] / 2)) {
+                      //High quality
+                          ByteStr.write(numBises, 0, numBises.length);
+                          ByteStr.write(mcid, 0, mcid.length);
+                          mCodecSpecificSampleRate = updateSampleRate();
+                          mCodecSpecificFrameDuration = updateFrameDuration();
+                          mCodecSpecificAudioLocation = updateAudioLocation(0);
+                          mCodecSpecificOctetsPerFrame = updateOctetsPerFrame();
+                          mCodecSpecificBlocksPerSdu= updateBlocksPerSdu();
+                          ByteStr.write(mCodecSpecificLength, 0, mCodecSpecificLength.length);
+                          ByteStr.write(mCodecSpecificSampleRate, 0, mCodecSpecificSampleRate.length);
+                          ByteStr.write(mCodecSpecificFrameDuration, 0, mCodecSpecificFrameDuration.length);
+                          ByteStr.write(mCodecSpecificAudioLocation, 0, mCodecSpecificAudioLocation.length);
+                          ByteStr.write(mCodecSpecificOctetsPerFrame, 0, mCodecSpecificOctetsPerFrame.length);
+                          ByteStr.write(mCodecSpecificBlocksPerSdu, 0, mCodecSpecificBlocksPerSdu.length);
+                          ByteStr.write(mMetadataLength, 0, mMetadataLength.length);
+                          ByteStr.write(mediacontext, 0, mediacontext.length);
+                          byte[] level3 = populate_level3_new_base(i, mcid, mCodecSpecificSampleRate,
+                                                                   mCodecSpecificFrameDuration,
+                                                                   mCodecSpecificOctetsPerFrame,
+                                                                   mCodecSpecificBlocksPerSdu,
+                                                                   mediacontext);
+                          ByteStr.write(level3, 0, level3.length);
+                          mMetaInfo.put(i,new MetadataLtv(mediacontext));
+                      } else {
+                      //Low quality
+                          ByteStr.write(numBises, 0, numBises.length);
+                          ByteStr.write(mcid, 0, mcid.length);
+                          mCodecSpecificSampleRateL2= updateHAPSampleRate();
+                          mCodecSpecificFrameDurationL2= updateFrameDuration();
+                          mCodecSpecificAudioLocationL2= updateAudioLocation(0);
+                          mCodecSpecificOctetsPerFrameL2= updateHapOctetsPerFrame();
+                          mCodecSpecificBlocksPerSduL2= updateBlocksPerSdu();
+                          ByteStr.write(mCodecSpecificLength, 0, mCodecSpecificLength.length);
+                          ByteStr.write(mCodecSpecificSampleRateL2, 0, mCodecSpecificSampleRateL2.length);
+                          ByteStr.write(mCodecSpecificFrameDurationL2, 0, mCodecSpecificFrameDurationL2.length);
+                          ByteStr.write(mCodecSpecificAudioLocationL2, 0, mCodecSpecificAudioLocationL2.length);
+                          ByteStr.write(mCodecSpecificOctetsPerFrameL2, 0, mCodecSpecificOctetsPerFrameL2.length);
+                          ByteStr.write(mCodecSpecificBlocksPerSduL2, 0, mCodecSpecificBlocksPerSduL2.length);
+                          ByteStr.write(mMetadataLength, 0, mMetadataLength.length);
+                          ByteStr.write(mediacontext, 0, mediacontext.length);
+                          byte[] level3 = populate_level3_new_base(i, mcid, mCodecSpecificSampleRateL2,
+                                                                   mCodecSpecificFrameDurationL2,
+                                                                   mCodecSpecificOctetsPerFrameL2,
+                                                                   mCodecSpecificBlocksPerSduL2,
+                                                                   mediacontext);
+                          ByteStr.write(level3, 0, level3.length);
+                          mMetaInfo.put(i,new MetadataLtv(mediacontext));
+                      }
+                  } else {
+                      ByteStr.write(numBises, 0, numBises.length);
+                      ByteStr.write(mcid, 0, mcid.length);
+                      mCodecSpecificLengthL2 = intTobyteArray(codecConfigLength, 1);
+                      mCodecSpecificSampleRateL2 = updateSampleRate();
+                      mCodecSpecificFrameDurationL2 = updateFrameDuration();
+                      mCodecSpecificAudioLocationL2 = updateAudioLocation(0);
+                      mCodecSpecificOctetsPerFrameL2 = updateOctetsPerFrame();
+                      mCodecSpecificBlocksPerSduL2 = updateBlocksPerSdu();
+
+                      ByteStr.write(mCodecSpecificLengthL2, 0, mCodecSpecificLengthL2.length);
+                      ByteStr.write(mCodecSpecificSampleRateL2, 0, mCodecSpecificSampleRateL2.length);
+                      ByteStr.write(mCodecSpecificFrameDurationL2, 0, mCodecSpecificFrameDurationL2.length);
+                      ByteStr.write(mCodecSpecificAudioLocationL2, 0, mCodecSpecificAudioLocationL2.length);
+                      ByteStr.write(mCodecSpecificOctetsPerFrameL2, 0, mCodecSpecificOctetsPerFrameL2.length);
+                      ByteStr.write(mCodecSpecificBlocksPerSduL2, 0, mCodecSpecificBlocksPerSduL2.length);
+                      ByteStr.write(mMetadataLength, 0, mMetadataLength.length);
+                      ByteStr.write(mediacontext, 0, mediacontext.length);
+                      byte[] level3 = populate_level3_new_base(0, mcid, mCodecSpecificSampleRateL2,
+                                                               mCodecSpecificFrameDurationL2,
+                                                               mCodecSpecificOctetsPerFrameL2,
+                                                               mCodecSpecificBlocksPerSduL2,
+                                                               mediacontext);
+                      ByteStr.write(level3, 0, level3.length);
+                      mMetaInfo.put(i,new MetadataLtv(mediacontext));
+                  }
+              }
+          } else {
+              for (int i = 0; i < mNumSubgroups[0]; i++) {
+                  if (mPartialSimulcast) {
+                      if (i < (mNumSubgroups[0] / 2)) {
+                          //High quality
+                          byte[] mcid = new byte[1];
+                          mcid = intTobyteArray(0xFE,1);
+                          mL2CodecSpecificLength = intTobyteArray(0,1);//(byte) 0;
+                          ByteStr.write(mcid, 0, mcid.length);
+                          ByteStr.write(mL2CodecSpecificLength, 0, mL2CodecSpecificLength.length);
+                      } else {
+                          //Low quality
+                          byte[] mcid = new byte[5];
+                          if (new_codec_id) {
+                              mcid = longTobyteArray(LC3_CODEC_ID,5);
+                          } else {
+                              mcid = longTobyteArray(LC3_CODEC_ID_OLD,5);
+                          }
+                          mCodecSpecificLengthL2 = intTobyteArray(mCodecConfigLength, 1);
+                          mCodecSpecificSampleRateL2 = updateHAPSampleRate();
+                          mCodecSpecificFrameDurationL2 = updateFrameDuration();
+                          mCodecSpecificAudioLocationL2 = updateAudioLocation(0);
+                          mCodecSpecificOctetsPerFrameL2 = updateHapOctetsPerFrame();
+                          ByteStr.write(mcid, 0, mcid.length);
+                          ByteStr.write(mL2CodecSpecificLength, 0, mL2CodecSpecificLength.length);
+                          ByteStr.write(mCodecSpecificSampleRateL2, 0, mCodecSpecificSampleRateL2.length);
+                          ByteStr.write(mCodecSpecificFrameDurationL2, 0, mCodecSpecificFrameDurationL2.length);
+                          ByteStr.write(mCodecSpecificAudioLocationL2, 0, mCodecSpecificAudioLocationL2.length);
+                          ByteStr.write(mCodecSpecificOctetsPerFrameL2, 0, mCodecSpecificOctetsPerFrameL2.length);
+                      }
+                      metalength = intTobyteArray(0, 1);//(byte)0;
+                      for (int j = 0; j < bisPerGroup;j++) {
+                          bisInd[j] = (byte)(1 + (bisPerGroup * i) + j);
+                      }
+                      ByteStr.write(metalength, 0, metalength.length);
+                      ByteStr.write(numBises, 0, numBises.length);
+                      ByteStr.write(bisInd, 0, bisInd.length);
+                  } else {
+                      byte [] mcid = new byte[1];
+                      mcid = intTobyteArray(0xFE,1);//(byte)0xFE;
+                      mL2CodecSpecificLength = intTobyteArray(0,1);//(byte)0;
+                      metalength = intTobyteArray(0,1);//(byte)0;
+                      for (int j = 0; j < bisPerGroup;j++) {
+                          bisInd[j] = (byte)(1 + (bisPerGroup * i) + j);
+                      }
+                      ByteStr.write(mcid, 0, mcid.length);
+                      ByteStr.write(mL2CodecSpecificLength, 0, mL2CodecSpecificLength.length);
+                      ByteStr.write(metalength, 0, metalength.length);
+                      ByteStr.write(numBises, 0, numBises.length);
+                      ByteStr.write(bisInd, 0, bisInd.length);
+                  }
+              }
+          }
+          return ByteStr.toByteArray();
+      }
+      private byte[] populate_level3_base() {
+          ByteArrayOutputStream ByteStr = new ByteArrayOutputStream();
+          for (int i = 0; i < mNumBises; i++) {
+              byte[] index = new byte[1];
+              byte [] configlength = new byte[1];
+              index[0] = (byte)(1 + i); //fetch from mAdvertisingSet
+              configlength[0] = (byte)6;
+              byte [] config = updateAudioLocation(i+1);
+              ByteStr.write(index, 0, index.length);
+              ByteStr.write(configlength,0, configlength.length);
+              ByteStr.write(config, 0, config.length);
+              mBisInfo.add(new BisInfo((int)index[0], mCodecId, mCodecSpecificSampleRate, mCodecSpecificFrameDuration,
+                              config, mCodecSpecificOctetsPerFrame, mMetadataContext));
+          }
+          return ByteStr.toByteArray();
+      }
+      private byte[] populate_level3_new_base(int subGroupId, byte[] codecId, byte[] SampleRate,
+                                               byte[] frameDuration, byte[] octetsPerFrame, byte[] BlocksPerSdu,
+                                               byte[] mMetadata) {
+          ByteArrayOutputStream ByteStr = new ByteArrayOutputStream();
+          int bisPerGroup = calculateBisPerGroup();
+          for (int i = 0; i < bisPerGroup; i++) {
+              byte[] index = new byte[1];
+              byte [] configlength = new byte[1];
+              index[0] = (byte)(1 + i + (bisPerGroup * subGroupId));
+              configlength[0] = (byte)6;
+              byte [] config = updateAudioLocation(i+1);
+              ByteStr.write(index, 0, index.length);
+              ByteStr.write(configlength,0, configlength.length);
+              ByteStr.write(config, 0, config.length);
+              mBisInfo.add(new BisInfo((int)index[0], codecId, SampleRate, frameDuration, config,
+                           octetsPerFrame, BlocksPerSdu, mMetadata, subGroupId));
+          }
+          return ByteStr.toByteArray();
+      }
+    }
+    public class BisInfo {
+        public int BisIndex;
+        public byte [] mCodecId = new byte[5];
+        public CodecConfigLtv BisCodecConfig;
+        public MetadataLtv BisMetadata;
+        public int mSubGroupId;
+        public BisInfo(int index, byte[] codecId, byte[] CodecSpecificSampleRate, byte[] CodecSpecificFrameDuration,
+                       byte[] CodecSpecificAudioLocation, byte[] CodecSpecificOctetsPerFrame, byte[] AudioContext) {
+            BisIndex = index;
+            mCodecId = codecId;
+            BisCodecConfig = new CodecConfigLtv(CodecSpecificSampleRate, CodecSpecificFrameDuration,
+                                            CodecSpecificAudioLocation, CodecSpecificOctetsPerFrame);
+            BisMetadata = new MetadataLtv(AudioContext);
+            mSubGroupId = -1;
+        }
+        public BisInfo (int index, byte[] codecId, byte[] CodecSpecificSampleRate, byte[] CodecSpecificFrameDuration,
+                        byte[] CodecSpecificAudioLocation, byte[] CodecSpecificOctetsPerFrame,
+                        byte[] CodecSpecificBlocksPerSdu, byte[] AudioContext, int subGroupId) {
+             BisIndex = index;
+             mCodecId = codecId;
+             BisCodecConfig = new CodecConfigLtv(CodecSpecificSampleRate, CodecSpecificFrameDuration,
+                                             CodecSpecificAudioLocation, CodecSpecificBlocksPerSdu,
+                                             CodecSpecificOctetsPerFrame);
+             BisMetadata = new MetadataLtv(AudioContext);
+             mSubGroupId = subGroupId;
+        }
+    }
+    public class CodecConfigLtv{
+        byte [] mCodecSpecificSampleRate;
+        byte [] mCodecSpecificFrameDuration;
+        byte [] mCodecSpecificAudioLocation;
+        byte [] mCodecSpecificOctetsPerFrame;
+        byte [] mCodecSpecificBlocksPerSdu;
+        public CodecConfigLtv(byte[] CodecSpecificSampleRate,
+                              byte[] CodecSpecificFrameDuration,
+                              byte[] CodecSpecificAudioLocation,
+                              byte[] CodecSpecificOctetsPerFrame) {
+            mCodecSpecificSampleRate = CodecSpecificSampleRate;
+            mCodecSpecificFrameDuration = CodecSpecificFrameDuration;
+            mCodecSpecificAudioLocation = CodecSpecificAudioLocation;
+            mCodecSpecificOctetsPerFrame = CodecSpecificOctetsPerFrame;
+        }
+        public CodecConfigLtv(byte[] CodecSpecificSampleRate,
+                              byte[] CodecSpecificFrameDuration,
+                              byte[] CodecSpecificAudioLocation,
+                              byte[] CodecSpecificOctetsPerFrame,
+                              byte [] CodecSpecificBlocksPerSdu) {
+            mCodecSpecificSampleRate = CodecSpecificSampleRate;
+            mCodecSpecificFrameDuration = CodecSpecificFrameDuration;
+            mCodecSpecificAudioLocation = CodecSpecificAudioLocation;
+            mCodecSpecificOctetsPerFrame = CodecSpecificOctetsPerFrame;
+            mCodecSpecificBlocksPerSdu = CodecSpecificBlocksPerSdu;
+        }
+        public byte[] getByteArray() {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            try {
+                outputStream.write(mCodecSpecificSampleRate);
+                outputStream.write(mCodecSpecificFrameDuration);
+                outputStream.write(mCodecSpecificAudioLocation);
+                outputStream.write(mCodecSpecificOctetsPerFrame);
+                if (mNewVersion) {
+                    outputStream.write(mCodecSpecificBlocksPerSdu);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "getBytes: ioexception caught!" + e);
+                return null;
+            }
+            return outputStream.toByteArray( );
+        }
+    }
+    public class MetadataLtv {
+        byte[] mAudioContext;
+        public MetadataLtv(byte[] audiocontext) {
+            mAudioContext = audiocontext;
+        }
+        public byte[] getByteArray() {
+            return mAudioContext;
+        }
+    }
+    class BroadcastCodecConfig {
+        public BroadcastCodecConfig() {
+        //Default configuration
+            int sr, ch_mode;
+            long codecspecific1;
+            switch(mBroadcastConfigSettings) {
+                case 1:
+                    sr = BluetoothCodecConfig.SAMPLE_RATE_16000;
+                    ch_mode = BluetoothCodecConfig.CHANNEL_MODE_MONO;
+                    codecspecific1 = 1001;//32kbps
+                    break;
+                case 2:
+                    sr = BluetoothCodecConfig.SAMPLE_RATE_16000;
+                    ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                    codecspecific1 = 1001;//32kbps
+                    break;
+                case 3:
+                    sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                    ch_mode = BluetoothCodecConfig.CHANNEL_MODE_MONO;
+                    codecspecific1 = 1004;//80kbps
+                    break;
+                case 4:
+                    sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                    ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                    codecspecific1 = 1004;//80kbps
+                    break;
+                case 5:
+                    sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                    ch_mode = BluetoothCodecConfig.CHANNEL_MODE_MONO;
+                    codecspecific1 = 1006;//96kbps
+                    break;
+                case 6:
+                    sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                    ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                    codecspecific1 = 1006;//96
+                    break;
+                case 7:
+                    sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                    ch_mode = BluetoothCodecConfig.CHANNEL_MODE_MONO;
+                    codecspecific1 = 1007;//124
+                    break;
+                case 8:
+                default:
+                    sr = BluetoothCodecConfig.SAMPLE_RATE_48000;
+                    ch_mode = BluetoothCodecConfig.CHANNEL_MODE_STEREO;
+                    codecspecific1 = 1007;
+                    break;
+
+            }
+            mCodecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+                                                    BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                                    sr, BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                                    ch_mode, codecspecific1, 1, 0, 0);
+            if (mPartialSimulcast) {
+                mHapCodecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+                                                        BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+                                                        BluetoothCodecConfig.SAMPLE_RATE_16000,
+                                                        BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                                        BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                                        1000, 1, 0, 0);
+
+            }
+        }
+        public void updateBroadcastCodecConfig(BluetoothCodecConfig newConfig) {
+            if (DBG) Log.d(TAG, "updateBroadcastCodecConfig: " + newConfig);
+            mCodecConfig = newConfig;
+            int mChMode = mCodecConfig.getChannelMode();
+            switch (mChMode) {
+                case BluetoothCodecConfig.CHANNEL_MODE_MONO:
+                case BluetoothCodecConfig.CHANNEL_MODE_JOINT_STEREO:
+                    mNumBises = 1 * mNumSubGrps;
+                    break;
+                case BluetoothCodecConfig.CHANNEL_MODE_STEREO:
+                    mNumBises = 2 * mNumSubGrps;
+                    break;
+                default:
+                    Log.e(TAG,"channel mode unknown");
+            }
+        }
+    }
+
+    /**
+     * Binder object: must be a static class or memory leak may occur.
+     */
+    @VisibleForTesting
+    static class BluetoothBroadcastBinder extends IBluetoothBroadcast.Stub
+            implements IProfileServiceBinder {
+        private BroadcastService mService;
+
+        private BroadcastService getService() {
+            if (!Utils.checkCallerIsSystemOrActiveUser(TAG)) {
+                return null;
+            }
+
+            if (mService != null && mService.isAvailable()) {
+                return mService;
+            }
+            return null;
+        }
+
+        BluetoothBroadcastBinder(BroadcastService svc) {
+            mService = svc;
+        }
+
+        @Override
+        public void cleanup() {
+            mService = null;
+        }
+        @Override
+        public boolean SetBroadcast(boolean enable, String packageName) {
+            BroadcastService service = getService();
+            if (service == null) {
+                return false;
+            }
+            if (enable) {
+                return service.EnableBroadcast(packageName);
+            }
+            else {
+                return service.DisableBroadcast(packageName);
+            }
+            //return false;
+        }
+
+        @Override
+        public boolean SetEncryption(boolean enable, int enc_len, boolean use_existing,
+                         String packageName) {
+            BroadcastService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.SetEncryption(enable, enc_len, use_existing, packageName);
+        }
+
+        @Override
+        public byte[] GetEncryptionKey(String packageName) {
+            BroadcastService service = getService();
+            if (service == null) {
+                return null;
+            }
+            return service.GetEncryptionKey(packageName);
+        }
+        @Override
+        public int GetBroadcastStatus(String packageName) {
+            BroadcastService service = getService();
+            if (service == null) {
+                return BluetoothBroadcast.STATE_DISABLED;
+            }
+            return service.GetBroadcastStatus(packageName);
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/broadcast/BroadcastStackEvent.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/broadcast/BroadcastStackEvent.java
new file mode 100644
index 0000000..a1b5596
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/broadcast/BroadcastStackEvent.java
@@ -0,0 +1,120 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package com.android.bluetooth.broadcast;
+
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothBroadcast;
+/**
+ * Stack event sent via a callback from JNI to Java, or generated.
+ */
+public class BroadcastStackEvent {
+    // Event types for STACK_EVENT message (coming from native)
+    private static final int EVENT_TYPE_NONE = 0;
+    public static final int EVENT_TYPE_BROADCAST_STATE_CHANGED = 1;
+    public static final int EVENT_TYPE_BROADCAST_AUDIO_STATE_CHANGED = 2;
+    public static final int EVENT_TYPE_ENC_KEY_GENERATED = 3;
+    public static final int EVENT_TYPE_CODEC_CONFIG_CHANGED = 4;
+    public static final int EVENT_TYPE_SETUP_BIG = 5;
+    public static final int EVENT_TYPE_BROADCAST_ID_GENERATED = 6;
+
+    public static final int STATE_IDLE = 0;
+    public static final int STATE_CONFIGURED = 1;
+    public static final int STATE_STREAMING = 2;
+
+    public static final int STATE_STOPPED = 0;
+    public static final int STATE_STARTED = 1;
+
+    public int type = EVENT_TYPE_NONE;
+    public int advHandle = 0;
+    public int valueInt = 0;
+    public int bigHandle = 0;
+    public int NumBises = 0;
+    public int[] BisHandles;
+    public byte[] BroadcastId = new byte[3];
+    public String key;
+    public BluetoothCodecStatus codecStatus;
+
+    BroadcastStackEvent(int type) {
+        this.type = type;
+    }
+
+    @Override
+    public String toString() {
+        // event dump
+        StringBuilder result = new StringBuilder();
+        result.append("BroadcastStackEvent {type:" + eventTypeToString(type));
+        result.append(", value1:" + eventTypeValueIntToString(type, valueInt));
+        if (codecStatus != null) {
+            result.append(", codecStatus:" + codecStatus);
+        }
+        result.append("}");
+        return result.toString();
+    }
+
+    private static String eventTypeToString(int type) {
+        switch (type) {
+            case EVENT_TYPE_NONE:
+                return "EVENT_TYPE_NONE";
+            case EVENT_TYPE_BROADCAST_STATE_CHANGED:
+                return "EVENT_TYPE_BROADCAST_STATE_CHANGED";
+            case EVENT_TYPE_BROADCAST_AUDIO_STATE_CHANGED:
+                return "EVENT_TYPE_BROADCAST_AUDIO_STATE_CHANGED";
+            case EVENT_TYPE_ENC_KEY_GENERATED:
+                return "EVENT_TYPE_ENC_KEY_GENERATED";
+            case EVENT_TYPE_CODEC_CONFIG_CHANGED:
+                return "EVENT_TYPE_CODEC_CONFIG_CHANGED";
+            case EVENT_TYPE_SETUP_BIG:
+                return "EVENT_TYPE_SETUP_BIG";
+            default:
+                return "EVENT_TYPE_UNKNOWN:" + type;
+        }
+    }
+
+    private static String eventTypeValueIntToString(int type, int value) {
+        switch (type) {
+            case EVENT_TYPE_BROADCAST_STATE_CHANGED:
+                switch (value) {
+                    case BluetoothBroadcast.STATE_DISABLED:
+                        return "DISABLED";
+                    case BluetoothBroadcast.STATE_ENABLING:
+                        return "ENABLING";
+                    case BluetoothBroadcast.STATE_ENABLED:
+                        return "CONFIGURED";
+                    case BluetoothBroadcast.STATE_STREAMING:
+                        return "STREAMING";
+                    default:
+                        break;
+                }
+                break;
+            case EVENT_TYPE_BROADCAST_AUDIO_STATE_CHANGED:
+                switch(value) {
+                  case BluetoothBroadcast.STATE_PLAYING:
+                      return "PLAYING";
+                  case BluetoothBroadcast.STATE_NOT_PLAYING:
+                      return "NOT PLAYING";
+                  default:
+                      break;
+                }
+            default:
+                break;
+        }
+        return Integer.toString(value);
+    }
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CCHalConstants.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CCHalConstants.java
new file mode 100644
index 0000000..eb4df0b
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CCHalConstants.java
@@ -0,0 +1,100 @@
+/*
+ *Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.cc;
+
+/*
+ * @hide
+ */
+public final class CCHalConstants {
+    static final int NETWORK_STATE_NOT_AVAILABLE = 0;
+    static final int NETWORK_STATE_AVAILABLE = 1;
+
+    static final int SERVICE_TYPE_HOME = 0;
+    static final int SERVICE_TYPE_ROAMING = 1;
+
+    static final int CALL_STATE_ACTIVE = 0;
+    static final int CALL_STATE_HELD = 1;
+    static final int CALL_STATE_DIALING = 2;
+    static final int CALL_STATE_ALERTING = 3;
+    static final int CALL_STATE_INCOMING = 4;
+    static final int CALL_STATE_WAITING = 5;
+    static final int CALL_STATE_IDLE = 6;
+    static final int CALL_STATE_DISCONNECTED = 7;
+
+    //Call State as expected by Stack/CC
+    static final int CCS_STATE_INCOMING = 0x00;
+    static final int CCS_STATE_DIALING = 0x01;
+    static final int CCS_STATE_ALERTING = 0x02;
+    static final int CCS_STATE_ACTIVE = 0x03;
+    static final int CCS_STATE_LOCAL_HELD= 0x04;
+    static final int CCS_STATE_REMOTELY_HELD= 0x05;
+    static final int CCS_STATE_LOCAL_REMOTE_HELD= 0x06;
+    static final int CCS_STATE_DISCONNECTED = 0x07;
+
+    static final int BTCC_OP_ACCEPT = 0;
+    static final int BTCC_OP_TERMINATE = 1;
+    static final int BTCC_OP_LOCAL_HLD = 2;
+    static final int BTCC_OP_LOCAL_RETRIEVE = 3;
+    static final int BTCC_OP_ORIGINATE = 4;
+    static final int BTCC_OP_JOIN = 5;
+
+    static final int BTCC_OP_SUCCESS = 0x00;
+    static final int BTCC_OP_NOT_POSSIBLE = 0x02;
+
+    //default call index for failures
+    static final int BTCC_DEF_INDEX_FOR_FAILURES = 0;
+
+    static int getCCsCallState(int telephonyCallState) {
+        int ret = 0xFF;
+        switch(telephonyCallState) {
+            case CALL_STATE_ACTIVE: ret = CCS_STATE_ACTIVE; break;
+            case CALL_STATE_HELD: ret = CCS_STATE_LOCAL_HELD; break;
+            case CALL_STATE_DIALING: ret = CCS_STATE_DIALING; break;
+            case CALL_STATE_ALERTING: ret = CCS_STATE_ALERTING; break;
+            case CALL_STATE_INCOMING: ret = CCS_STATE_INCOMING; break;
+            case CALL_STATE_DISCONNECTED: ret = CCS_STATE_DISCONNECTED; break;
+            //this means second Incoming call is waiting
+            case CALL_STATE_WAITING: ret = CCS_STATE_INCOMING; break;
+            default: break;
+        }
+        return ret;
+    }
+
+    public static String operationToString(int what) {
+        switch (what) {
+            case BTCC_OP_ACCEPT :
+                return "BTCC_OP_ACCEPT";
+            case BTCC_OP_TERMINATE :
+                return "BTCC_OP_TERMINATE";
+            case BTCC_OP_LOCAL_HLD :
+                return "BTCC_OP_LOCAL_HLD";
+            case BTCC_OP_LOCAL_RETRIEVE :
+                return "BTCC_OP_LOCAL_RETRIEVE";
+            case BTCC_OP_ORIGINATE :
+                return "BTCC_OP_ORIGINATE";
+            case BTCC_OP_JOIN :
+                return "BTCC_OP_JOIN";
+            default:
+                break;
+        }
+        return Integer.toString(what);
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CCNativeInterface.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CCNativeInterface.java
new file mode 100644
index 0000000..1e93c62
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CCNativeInterface.java
@@ -0,0 +1,255 @@
+/*
+ *Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * Defines the native interface that is used by state machine/service to
+ * send or receive messages from the native stack. This file is registered
+ * for the native methods in the corresponding JNI C++ file.
+ */
+package com.android.bluetooth.cc;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Ccp Native Interface to/from JNI.
+ */
+public class CCNativeInterface {
+    private static final String TAG = "CCNativeInterface";
+    private static final boolean DBG = true;
+    private BluetoothAdapter mAdapter;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static CCNativeInterface sInstance;
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static {
+        classInitNative();
+    }
+
+    private CCNativeInterface() {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mAdapter == null) {
+            Log.w(TAG, "No Bluetooth Adapter Available");
+        }
+    }
+
+     /**
+     * This class is a singleton because native library should only be loaded once
+     *
+     * @return default instance
+     */
+    public static CCNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new CCNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Initialize native stack
+     *
+     * @param ccsClients maximum number of CCS clients that can be connected simultaneously
+     * @param inbandRingingEnabled whether in-band ringing is enabled on this AG
+     */
+    @VisibleForTesting
+    public void init(int maxCcsClients, boolean inbandRingingEnabled) {
+        initializeNative("00008fd1-0000-1000-8000-00805F9B34FB", maxCcsClients, inbandRingingEnabled);
+    }
+
+    /**
+     * Cleanup the native interface.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void cleanup() {
+        cleanupNative();
+    }
+
+    /**
+     * Disconnects Call control from a remote device.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean disconnect(BluetoothDevice device) {
+        return disconnectNative(getByteAddress(device));
+    }
+    /**
+     * update CC optional supported feature
+     * @param feature
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean callControlOptionalFeatures(int feature) {
+        return callControlPointOpcodeSupportedNative(feature);
+    }
+
+  /**
+     * Sets the CC call state
+     * @param state
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean callState(ArrayList<CallControlState> callList) {
+        int len = callList.size();
+        byte[] cStateListBytes = new byte[len*3];
+        for (int i=0; i<len; i++) {
+            cStateListBytes[3*i+0] = (byte) callList.get(i).mIndex;
+            cStateListBytes[3*i+1] = (byte) CCHalConstants.getCCsCallState(callList.get(i).mState);
+            cStateListBytes[3*i+2] = (byte) callList.get(i).mFlags;
+        }
+        return callStateNative(len, cStateListBytes);
+    }
+
+  /**
+     * update CC Bearer name
+     * @param Bearer name
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean updateBearerProviderName(String name) {
+        return updateBearerNameNative(name);
+    }
+
+    /**
+     * update CC Bearer  technology type
+     * @param Bearer technology
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean updateBearerTechnology(int tech_type) {
+         return updateBearerTechnologyNative(tech_type);
+
+    }
+
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean updateStatusFlags(int value) {
+        return updateStatusFlagsNative(value);
+    }
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean updateSignalStrength(int signal_value) {
+        return updateSignalStatusNative(signal_value);
+    }
+
+
+   @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+   public boolean updateIncomingCall(int index, String uri) {
+      updateIncomingCallNative(index, uri);
+       return true;
+   }
+
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+   public boolean updateSupportedBearerList(String supportedBearers) {
+      return  updateSupportedBearerListNative(supportedBearers);
+   }
+
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean callControlResponse(int op, int index, int status, BluetoothDevice device) {
+        return callControlResponseNative(op, index, status,  getByteAddress(device));
+    }
+
+  /**
+     * update active device
+     * @param device
+       * @param setId
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean setActiveDevice(BluetoothDevice device, int setId) {
+        return setActiveDeviceNative(setId, getByteAddress(device));
+    }
+    /**
+     * Sets call content control id
+     * @param ccid
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean contentControlId(int ccid) {
+
+        return contentControlIdNative(ccid);
+    }
+
+    private BluetoothDevice getDevice(byte[] address) {
+        return mAdapter.getRemoteDevice(address);
+    }
+
+    private byte[] getByteAddress(BluetoothDevice device) {
+        if (device == null) {
+            return Utils.getBytesFromAddress("00:00:00:00:00:00");
+        }
+        return Utils.getBytesFromAddress(device.getAddress());
+    }
+
+    // Callbacks from the native stack back into the Java framework.
+    private void callControlInitializedCallback(int state) {
+        if (DBG) {
+            Log.d(TAG, "CallControlInitializedCallback: " + state);
+        }
+
+        CCService service = CCService.getCCService();
+        if (service != null) {
+            service.onCallControlInitialized(state);
+        }
+    }
+
+    private void onConnectionStateChanged(int state, byte[] address) {
+        if (DBG) {
+            Log.d(TAG, "OnConnectionStateChanged: " + state);
+        }
+        BluetoothDevice device = getDevice(address);
+
+        CCService service = CCService.getCCService();
+        if (service != null)
+            service.onConnectionStateChanged(device, state);
+    }
+
+    private void callControlPointChangedRequest(int op, int[]call_indices, int count, byte[] dialNumber, byte[] address) {
+        BluetoothDevice device = getDevice(address);
+        String dialUri = new String(dialNumber, StandardCharsets.UTF_8);
+        if (DBG) {
+            Log.d(TAG, "CallControlPointChangedRequest: " + op + "dialNumber: " + dialUri);
+        }
+        CCService service = CCService.getCCService();
+        if (service != null)
+            service.onCallControlPointChangedRequest(op, call_indices, count, dialUri, device);
+    }
+
+    // Native methods that call into the JNI interface
+    private static native void classInitNative();
+    private native void initializeNative(String uuid, int max_ccs_clients, boolean inbandRingingEnabled);
+    private native void cleanupNative();
+    private native boolean callControlPointOpcodeSupportedNative(int feature);
+    private native boolean callStateNative(int len, byte[] callStateList);
+    private native boolean updateBearerNameNative(String providerName);
+    private native boolean updateBearerTechnologyNative(int tech_type);
+    private native boolean updateSignalStatusNative(int signal);
+    private native boolean updateStatusFlagsNative(int value);
+    private native boolean setActiveDeviceNative(int setId, byte[] address);
+    private native boolean contentControlIdNative(int ccid);
+    private native boolean disconnectNative(byte[] address);
+    private native boolean callControlResponseNative(int op, int index, int status, byte[] address);
+    private native boolean updateSupportedBearerListNative(String supportedBearers);
+    private native boolean updateIncomingCallNative(int index, String uri);
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CCService.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CCService.java
new file mode 100644
index 0000000..723bc2e
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CCService.java
@@ -0,0 +1,864 @@
+/*
+ *Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.cc;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.telephony.PhoneStateListener;
+import android.content.SharedPreferences;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Queue;
+import java.util.LinkedList;
+import java.util.HashMap;
+import java.util.Map;
+import android.os.Message;
+import android.os.Binder;
+import android.os.IBinder;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.apm.ApmConst;
+import com.android.bluetooth.apm.CallAudio;
+import com.android.bluetooth.apm.CallControl;
+import com.android.bluetooth.apm.ActiveDeviceManagerService;
+import java.util.Objects;
+
+/**
+ * Provides Bluetooth CC profile as a service in the Bluetooth application.
+ * @hide
+ */
+public class CCService extends ProfileService {
+
+    private static final String TAG = "CCService";
+    private static final String DISABLE_INBAND_RINGING_PROPERTY =
+             "persist.bluetooth.disableinbandringing";
+    private static final boolean DBG = true;
+    private static CCService sCCService;
+    private BroadcastReceiver mBondStateChangedReceiver;
+
+    private int mCCId = 0xFF;
+    private BluetoothDevice mActiveDevice;
+    private BluetoothDevice mCallOriginatedDevice = null;
+    private AdapterService mAdapterService;
+    private CCNativeInterface mNativeInterface;
+    private CallAudio mCallAudio = null;
+    private ActiveDeviceManagerService mActiveDevMgrService = null;
+    private Context mContext  = null;;
+    private CcsMessageHandler mHandler;
+    private int mMaxConnectedAudioDevices = 1;
+    private boolean  InBandRingtoneSupport = false;
+    private boolean mVirtualCallStarted = false;
+    private boolean mStarted;
+    private boolean mCreated;
+    private static int mLatestActiveCallIndex = 0;
+    private static int mLatestHeldCallIndex = 0;
+    private CallControlState mPrevTelephonyState = null;
+    private HashMap<Integer, CallControlState> mCallStateList = null;
+    private HashMap<Integer, CallControlState> mPrevCallStateList = null;
+    private Queue<Integer> mLccTobeQueued = null;
+    private Queue<Integer> mLccWaitForResponseQ = null;
+
+    private static final int FLAGS_DIRECTION_BIT = 0x0001;
+    private static final int CC_SIGNAL_STRENGTH_FACTOR = 20;
+
+    private static final int CC_CONTENT_CONTROL_ID = 77;
+    private static final int CC_OPTIONAL_LOCAL_HOLD_FEAT = 0x01;
+    private static final int CC_OPTIONAL_JOIN_FEAT = 0x02;
+    private static final int CALL_CONTROL_OPTIONAL_FEATURES = CC_OPTIONAL_LOCAL_HOLD_FEAT|CC_OPTIONAL_JOIN_FEAT;
+    //native event
+    static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
+    static final int EVENT_TYPE_CALL_CONTROL_POINT_CHANGED = 2;
+    //CC to JNI update
+    static final int UPDATE_BEARER_NAME = 3;
+    static final int UPDATE_BEARER_TECH = 4;
+    static final int UPDATE_STATUS_FLAGS = 5;
+    static final int UPDATE_SIGNAL_STRENGTH = 6;
+    static final int UPDATE_BEARERLIST_SUPPORTED = 7;
+    static final int UPDATE_CONTENT_CONTROL_ID = 8;
+    static final int UPDATE_CALL_STATE = 9;
+    static final int UPDATE_CALL_CONTROL_OPCODES_SUPPORTED = 10;
+    static final int UPDATE_CALL_CONTROL_RESPONSE = 11;
+    static final int UPDATE_INCOMING_CALL = 12;
+    static final int PROCESS_CALL_STATE = 13;
+    static final int PROCESS_PHONE_STATE_CHANGED = 14;
+    static final int ACTIVE_DEVICE_CHANGED = 15;
+
+    @Override
+    protected IProfileServiceBinder initBinder() {
+        return new CcBinder(this);
+    }
+
+    @Override
+    protected void create() {
+        Log.i(TAG, "create()");
+        if (mCreated) {
+            throw new IllegalStateException("create() called twice");
+        }
+        mCreated = true;
+    }
+
+    @Override
+    protected void cleanup() {
+        Log.i(TAG, "cleanup()");
+        if (mNativeInterface != null) {
+           mNativeInterface.cleanup();
+        }
+    }
+
+    @Override
+    protected boolean start() {
+        Log.i(TAG, "start()");
+        if (sCCService != null) {
+            Log.w(TAG, "CCService is already running");
+            return true;
+        }
+        if (DBG) {
+            Log.d(TAG, "Create CCService Instance");
+        }
+
+        mContext = this;
+        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+                "AdapterService cannot be null when CCService starts");
+        mNativeInterface = Objects.requireNonNull(CCNativeInterface.getInstance(),
+                "CcNativeInterface cannot be null when CcService starts");
+        // Step 2: Get maximum number of connected audio devices
+        mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
+        Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices);
+
+        if (mHandler != null) {
+           mHandler = null;
+        }
+        HandlerThread thread = new HandlerThread("BluetoothCCSHandler");
+        thread.start();
+        Looper looper = thread.getLooper();
+        mHandler = new CcsMessageHandler(looper);
+        //APM's CallControl and CallAudio initialization
+        CallControl.init(mContext);
+        mCallAudio = CallAudio.init(mContext);
+        mNativeInterface.init(mMaxConnectedAudioDevices,InBandRingtoneSupport);
+        Log.d(TAG, "cc native init done");
+        IntentFilter filter = new IntentFilter();
+        //mSystemInterface = HeadsetService.getSystemInterfaceObj();
+        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mBondStateChangedReceiver = new BondStateChangedReceiver();
+        mContext.registerReceiver(mBondStateChangedReceiver, filter);
+        mCallStateList = new HashMap<> ();
+        mPrevCallStateList = new HashMap<> ();
+        mLccWaitForResponseQ = new LinkedList<> ();
+        mLccTobeQueued = new LinkedList<> ();
+        mActiveDevMgrService = ActiveDeviceManagerService.get();
+        setCCService(this);
+        return true;
+    }
+
+    @Override
+    protected boolean stop() {
+        Log.i(TAG, "stop()");
+        if (sCCService == null) {
+           Log.w(TAG, "stop() called before start()");
+           return true;
+        }
+        // Step 8: Mark service as stopped
+        setCCService(null);
+        // Cleanup native interface
+        mNativeInterface.cleanup();
+        mNativeInterface = null;
+        mContext.unregisterReceiver(mBondStateChangedReceiver);
+        // Clear AdapterService
+        mAdapterService = null;
+        mMaxConnectedAudioDevices = 1;
+        mCallOriginatedDevice = null;
+        CallControl.listenForPhoneState(PhoneStateListener.LISTEN_NONE);
+        return true;
+    }
+
+    private static synchronized void setCCService(CCService instance) {
+        if (DBG) {
+            Log.d(TAG, "setCCService(): set to: " + instance);
+        }
+        sCCService = instance;
+    }
+
+    public static synchronized CCService getCCService() {
+        if (sCCService == null) {
+            Log.w(TAG, "getCCService(): service is null");
+            return null;
+        }
+        return sCCService;
+    }
+
+    public boolean updateBearerProviderName(String name) {
+       Message msg = mHandler.obtainMessage();
+       msg.what = UPDATE_BEARER_NAME;
+       msg.obj = name;
+       mHandler.sendMessage(msg);
+       return true;
+    }
+    public boolean updateBearerProviderTechnology (int  tech_type)  {
+      Message msg = mHandler.obtainMessage();
+      msg.what = UPDATE_BEARER_TECH;
+      msg.arg1 = tech_type;
+      mHandler.sendMessage(msg);
+      return true;
+    }
+
+    public boolean updateSignalStrength(int signal) {
+       Message msg = mHandler.obtainMessage();
+       msg.what = UPDATE_SIGNAL_STRENGTH;
+       msg.arg1 = signal*CC_SIGNAL_STRENGTH_FACTOR;
+       mHandler.sendMessage(msg);
+       return true;
+    }
+
+    public boolean updateSupportedBearerList(String supportedBearers) {
+       Message msg = mHandler.obtainMessage();
+       msg.what = UPDATE_BEARERLIST_SUPPORTED ;
+       msg.obj = supportedBearers;
+       mHandler.sendMessage(msg);
+       return true;
+    }
+
+    public void updateOriginateResult(BluetoothDevice device, int event, int res) {
+        if (mCallOriginatedDevice == null || device != mCallOriginatedDevice) {
+            Log.e(TAG, "Originate resp ignored, as there is no Orginate req");
+            return;
+        }
+        if (res != 1) {
+            mCallOriginatedDevice = null;
+            updateCallControlResponse(CCHalConstants.BTCC_OP_ORIGINATE,
+                                      CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES,
+                                      CCHalConstants.BTCC_OP_NOT_POSSIBLE, device);
+        }
+    }
+
+    public boolean updateContentControlID(int ccid) {
+       Message msg = mHandler.obtainMessage();
+       msg.what = UPDATE_CONTENT_CONTROL_ID;
+       msg.arg1 = ccid;
+       mHandler.sendMessage(msg);
+       mCCId = ccid;
+       return true;
+    }
+
+    public boolean updateStatusFlags(int statusFlags) {
+       Message msg = mHandler.obtainMessage();
+       msg.what = UPDATE_STATUS_FLAGS;
+       msg.arg1 = statusFlags;
+       mHandler.sendMessage(msg);
+       return true;
+    }
+
+    public boolean updateCallControlOptionalFeatures(int feature) {
+       Message msg = mHandler.obtainMessage();
+       msg.what = UPDATE_CALL_CONTROL_OPCODES_SUPPORTED;
+       msg.arg1 = feature;
+       mHandler.sendMessage(msg);
+       return true;
+    }
+
+    public boolean updateCallControlResponse(int op, int index, int status, BluetoothDevice device) {
+       Message msg = mHandler.obtainMessage();
+       msg.what = UPDATE_CALL_CONTROL_RESPONSE ;
+       msg.arg1 = op;
+       msg.arg2 = index;
+       msg.obj = status;
+       mHandler.sendMessage(msg);
+       return true;
+    }
+
+    private boolean updateIncomingCall(int index, String uri)  {
+       Message msg = mHandler.obtainMessage();
+       msg.what = UPDATE_INCOMING_CALL ;
+       msg.arg1 = index;
+       msg.obj = uri;
+       mHandler.sendMessage(msg);
+       return true;
+    }
+
+    boolean isVirtualCallStarted() {
+
+        return mVirtualCallStarted;
+    }
+
+   public void setVirtualCallActive(boolean state) {
+      Log.i(TAG, "setVirtualCallActive: " + state);
+      if (state == true) {
+        startScoUsingVirtualVoiceCall();
+      } else {
+          stopScoUsingVirtualVoiceCall();
+      }
+    }
+
+    private void disaptchFakeCallState (CallControlState state) {
+        if (state != null) {
+            mCallStateList.put(state.mIndex, state);
+        }
+        Message msg = mHandler.obtainMessage();
+        msg.what = PROCESS_CALL_STATE;
+        Collection<CallControlState> values = mCallStateList.values();
+        ArrayList<CallControlState> listOfValues = new ArrayList<>(values);
+        msg.obj = listOfValues;
+        mHandler.sendMessage(msg);
+    }
+
+    boolean startScoUsingVirtualVoiceCall() {
+
+        Log.i(TAG, "startScoUsingVirtualVoiceCall: " + Utils.getUidPidString());
+        mVirtualCallStarted = true;
+        // Send fake call states to mimic outgoing calls
+
+        mCallStateList.clear();
+        CallControlState alertingState = new CallControlState(1,CCHalConstants.CALL_STATE_ALERTING,FLAGS_DIRECTION_BIT);
+        disaptchFakeCallState(alertingState);
+        CallControlState activeState = new CallControlState(1,CCHalConstants.CALL_STATE_ACTIVE,FLAGS_DIRECTION_BIT);
+        disaptchFakeCallState(activeState);
+        return true;
+   }
+
+   boolean stopScoUsingVirtualVoiceCall() {
+
+        Log.i(TAG, "stopScoUsingVirtualVoiceCall: " + Utils.getUidPidString());
+            // 1. Check if virtual call has already started
+         if (!mVirtualCallStarted) {
+            Log.w(TAG, "stopScoUsingVirtualVoiceCall: virtual call not started");
+             return false;
+         }
+         mVirtualCallStarted = false;
+            // 2. Send fake call states to mimic it ias outgoing calls
+
+         mCallStateList.clear();
+         CallControlState disConnectedState = new CallControlState(1,CCHalConstants.CCS_STATE_DISCONNECTED,FLAGS_DIRECTION_BIT);
+         disaptchFakeCallState(disConnectedState);
+         return true;
+   }
+
+   private void updateCallState(ArrayList<CallControlState> listOfValues) {
+       Log.d(TAG, "updateCallState");
+       Message msg = mHandler.obtainMessage();
+       msg.what = UPDATE_CALL_STATE;
+       msg.obj = listOfValues;
+       mHandler.sendMessage(msg);
+   }
+
+   public void processAndUpdateCallState(ArrayList<CallControlState> listOfValues) {
+      int flags = 0;
+
+      for (CallControlState state : listOfValues) {
+          Log.i(TAG, "processAndUpdateCallState: direction" + state.mDirection);
+          if (state.mDirection == 1) {
+              //Incoming call: off the direction bit
+             flags = (flags & (~FLAGS_DIRECTION_BIT));
+          } else {
+             //Outgoing call: on the direction bit
+             flags = (flags | FLAGS_DIRECTION_BIT);
+          }
+          state.mFlags = flags;
+          String uri = "";
+          String uri_str = "tel:";
+          Log.i(TAG, "processAndUpdateCallState: index = " + state.mIndex);
+          if (state.mState == CCHalConstants.CALL_STATE_ACTIVE) {
+              mLatestActiveCallIndex = state.mIndex;
+          } else if (state.mState == CCHalConstants.CALL_STATE_HELD) {
+              mLatestHeldCallIndex = state.mIndex;
+          }
+          if (state.mState == CCHalConstants.CALL_STATE_INCOMING) {
+              if (state.mNumber != null) {
+                  uri = uri_str.concat(state.mNumber);
+              }
+              Log.i(TAG, "processAndUpdateCallState: inc uri = " + uri);
+              updateIncomingCall(state.mIndex, uri);
+          }
+      }
+      updateCallState(listOfValues);
+   }
+
+   private void compareAndUpdateWithPrevCallList (HashMap<Integer, CallControlState> currentCallStateList) {
+        Log.d(TAG, "compareAndUpdateWithPrevCallList");
+        for (Integer key: mPrevCallStateList.keySet()) {
+            if (currentCallStateList.containsKey(key) == false) {
+            //create a fake disconnected for that index
+                if (mPrevCallStateList.get(key).mState != CCHalConstants.CALL_STATE_DISCONNECTED) {
+                    Log.d(TAG, "inserting DISC state fake!");
+                    CallControlState fakeDiscForDisappeared =
+                        new CallControlState(key,CCHalConstants.CALL_STATE_DISCONNECTED, mPrevCallStateList.get(key).mFlags);
+                        mCallStateList.put(key, fakeDiscForDisappeared);
+                }
+            }
+        }
+        mPrevCallStateList.putAll(mCallStateList);
+   }
+
+   public void clccResponse(int index, int direction, int call_status, int mode, boolean mpty,
+                 String number, int type) {
+        Log.d(TAG, "clccResponse");
+        if (index != 0) {
+            CallControlState state = new CallControlState(index, direction, call_status, number);
+            mCallStateList.put(index, state);
+        } else {
+            //update the call state to stack as 0 indicates end of call list
+            compareAndUpdateWithPrevCallList(mCallStateList);
+            Message msg = mHandler.obtainMessage();
+            msg.what = PROCESS_CALL_STATE;
+            Collection<CallControlState> values = mCallStateList.values();
+              ArrayList<CallControlState> listOfValues = new ArrayList<>(values);
+            msg.obj = listOfValues;
+            mHandler.sendMessage(msg);
+            if (!mLccWaitForResponseQ.isEmpty()) {
+                mLccWaitForResponseQ.remove();
+            }
+            if (!mLccTobeQueued.isEmpty()) {
+                mLccTobeQueued.remove();
+                getBlcc();
+            }
+        }
+    }
+
+    private void getBlcc() {
+        Log.d(TAG, "getBlcc");
+        if (mLccTobeQueued.isEmpty()) {
+            if (CallControl.listCurrentCalls() == true) {
+                mLccWaitForResponseQ.add(1);
+                Log.d(TAG, "getBlcc: successfully sent");
+                //telephony should always respond with clccresponse
+                mCallStateList.clear();
+            }
+        } else {
+            mLccTobeQueued.add(1);
+        }
+    }
+
+    private boolean processCallStateChange(CallControlState state) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = PROCESS_PHONE_STATE_CHANGED;
+        msg.obj = state;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    boolean isInbandRingingEnabled() {
+        boolean returnVal;
+
+        returnVal = BluetoothHeadset.isInbandRingingSupported(this) && !SystemProperties.getBoolean(
+                DISABLE_INBAND_RINGING_PROPERTY, true);
+        Log.d(TAG, "isInbandRingingEnabled returning: " + returnVal);
+        return returnVal;
+    }
+
+     boolean isCallAudioNeeded(CallControlState state) {
+        boolean ret = false;
+        if (isInbandRingingEnabled() && state.mState == CCHalConstants.CALL_STATE_INCOMING) {
+            ret = true;
+        } else if (mCallAudio != null && mCallAudio.isAudioOn() == false &&
+                   (state.mState == CCHalConstants.CALL_STATE_ALERTING ||
+                   mPrevTelephonyState != null && mPrevTelephonyState.mNumActive == 0 &&
+                   state.mNumActive == 1)) {
+
+            ret = true;
+        }
+        return ret;
+    }
+
+    public  boolean  phoneStateChanged(int numActive, int numHeld, int callState, String number, int type,
+                       String name, boolean isVirtualCall)    {
+      Log.d(TAG, "phoneStateChanged: " +
+                 "callState: " + callState +
+                 "number:" + number +
+                 "numActive:" + numActive +
+                 "isVirtualCall:" + isVirtualCall);
+      CallControlState currentTelephonyState = new CallControlState(numActive, numHeld,callState, number, type, name);
+
+      if (isCallAudioNeeded(currentTelephonyState)) {
+          if (mCallAudio != null) {
+            mCallAudio.connectAudio();
+        } else {
+            Log.e(TAG, "no CallAudio handle");
+        }
+      }
+
+      if (mPrevTelephonyState != null && mPrevTelephonyState.mNumActive == 1
+            && currentTelephonyState.mNumActive == 0 && currentTelephonyState.mNumHeld == 0) {
+            if (mPrevTelephonyState.mNumHeld == 0 && currentTelephonyState.mNumHeld == 1) {
+             Log.d(TAG, "special case where Active call moved to HOLD");
+          } else {
+             if (mCallAudio != null) {
+                 mCallAudio.disconnectAudio();
+             } else {
+                 Log.e(TAG, "no CallAudio handle for disc Call handling");
+             }
+          }
+      }
+
+      if (callState == CCHalConstants.CALL_STATE_DIALING) {
+          //ignore this as it is fake Telephony event
+          return true;
+      }
+
+      // Should stop all other audio mode in this case
+      if ((numActive + numHeld) > 0 || callState != CCHalConstants.CALL_STATE_IDLE) {
+          if (!isVirtualCall && mVirtualCallStarted) {
+              // stop virtual voice call if there is an incoming Telecom call update
+              stopScoUsingVirtualVoiceCall();
+          }
+          processCallStateChange(currentTelephonyState);
+          mPrevTelephonyState = currentTelephonyState;
+       } else {
+          // ignore CS non-call state update when virtual call started
+          if (!isVirtualCall && mVirtualCallStarted) {
+            Log.i(TAG, "Ignore CS non-call state update");
+            return true;
+          }
+       }
+       return true;
+   }
+
+    public BluetoothDevice getActiveDevice() {
+        return mActiveDevice;
+    }
+
+    public int getContentControlID() {
+        return mCCId;
+    }
+
+    public boolean setActiveDevice(BluetoothDevice device) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = ACTIVE_DEVICE_CHANGED;
+        msg.obj = device;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private boolean setActiveDeviceRemoteTrigger(BluetoothDevice device) {
+        boolean ret = false;
+        if (mActiveDevMgrService != null) {
+            ret = mActiveDevMgrService.setActiveDeviceBlocking(device, ApmConst.AudioFeatures.CALL_AUDIO);
+        }
+        Log.d(TAG, "setActiveDevice returns" + ret);
+        return ret;
+    }
+
+    private boolean isActiveDevice(BluetoothDevice device) {
+        boolean ret = false;
+        if (mActiveDevMgrService != null) {
+            ret = (device == mActiveDevMgrService.getActiveDevice(ApmConst.AudioFeatures.CALL_AUDIO));
+        }
+        Log.d(TAG, "isActiveDevice returns" + ret);
+        return ret;
+    }
+
+    public boolean onCallControlPointChangedRequest(int op, int[] call_indices, int count, String dialNumber, BluetoothDevice device ) {
+       Log.d(TAG, " onCallControlPointChangedRequest opcode  : " + CCHalConstants.operationToString(op)) ;
+       switch(op) {
+         case CCHalConstants.BTCC_OP_ACCEPT: {
+            setActiveDeviceRemoteTrigger (device);
+            CallControl.answerCall(device);
+            break;
+         }
+         case CCHalConstants.BTCC_OP_TERMINATE: {
+            int callIndex = call_indices[0];
+            Log.d(TAG, "callIndex: " + callIndex);
+            CallControl.terminateCall(device, callIndex);
+            break;
+         }
+         case CCHalConstants.BTCC_OP_LOCAL_HLD:{
+            int callIndex = call_indices[0];
+            int res;
+            int idx = CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES;
+            Log.d(TAG, "callIndex: " + callIndex);
+            if (CallControl.holdCall(device, callIndex) == true) {
+                res = CCHalConstants.BTCC_OP_SUCCESS;
+                idx = callIndex;
+            } else {
+                res = CCHalConstants.BTCC_OP_NOT_POSSIBLE;
+                idx = CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES;
+            }
+            updateCallControlResponse(op, idx, res, device);
+            break;
+         }
+         case CCHalConstants.BTCC_OP_LOCAL_RETRIEVE: {
+             //Analogus to SWAP as stack would have
+             //already validated the input index is in HELD state
+            int chld = 2;
+            int res;
+            int idx = CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES;
+            if (CallControl.processChld(device, chld) == true) {
+                res = CCHalConstants.BTCC_OP_SUCCESS;
+                idx = call_indices[0];
+            } else {
+                res = CCHalConstants.BTCC_OP_NOT_POSSIBLE;
+                idx = CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES;
+            }
+            updateCallControlResponse(op, idx, res, device);
+            break;
+         }
+         case CCHalConstants.BTCC_OP_ORIGINATE: {
+            Log.d(TAG, "Orignate: from Device: " + device + "dialString: " + dialNumber);
+            if (dialNumber == null) {
+                Log.e(TAG, "null dial string");
+                break;
+            }
+            if (mCallOriginatedDevice != null) {
+                Log.d(TAG, "Originate is pending from device: " + mCallOriginatedDevice);
+                updateCallControlResponse(op, CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES,
+                                          CCHalConstants.BTCC_OP_NOT_POSSIBLE, device);
+                break;
+            } else {
+                setActiveDeviceRemoteTrigger (device);
+                String[] result = dialNumber.split(":");
+                if (CallControl.dialOutgoingCall(device, result[1]) == true) {
+                    mCallOriginatedDevice = device;
+                } else {
+                    updateCallControlResponse(op, CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES,
+                                          CCHalConstants.BTCC_OP_NOT_POSSIBLE, device);
+                }
+            }
+            break;
+         }
+         case CCHalConstants.BTCC_OP_JOIN: {
+             //Stack would have validate to ensure the input indicies
+             //are valid candidates for JOIN op
+            int chld = 3;
+            int res;
+            int idx = CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES;
+            if (CallControl.processChld(device, chld) == true) {
+                res = CCHalConstants.BTCC_OP_SUCCESS;
+                idx = call_indices[0];
+            } else {
+                res = CCHalConstants.BTCC_OP_NOT_POSSIBLE;
+                idx = CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES;
+            }
+            updateCallControlResponse(op, idx, res, device);
+            break;
+         }
+        }
+        return true;
+    }
+
+    public void onCallControlInitialized(int status) {
+       Log.v(TAG, "CallControlInitializedCallback: status=" + status);
+       if (status == 0)  {
+           //Initialize Telephony and APM related Initialization
+           CallControl.listenForPhoneState(PhoneStateListener.LISTEN_SERVICE_STATE|PhoneStateListener.LISTEN_SERVICE_STATE);
+           updateContentControlID(CC_CONTENT_CONTROL_ID);
+           updateSupportedBearerList("tel");
+           updateCallControlOptionalFeatures(CALL_CONTROL_OPTIONAL_FEATURES);
+       }
+    }
+
+
+    public void onConnectionStateChanged(BluetoothDevice device, int status) {
+       Log.v(TAG, "onConnectionStateChanged: address=" + device.toString());
+    }
+
+    private class BondStateChangedReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
+                return;
+            }
+            int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                                           BluetoothDevice.ERROR);
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+            Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
+            bondStateChanged(device, state);
+        }
+    }
+
+    void bondStateChanged(BluetoothDevice device, int bondState) {
+        if (DBG) {
+            Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState);
+        }
+        // Remove state machine if the bonding for a device is removed
+        if (bondState != BluetoothDevice.BOND_NONE) {
+            return;
+        }
+
+    }
+
+    private boolean callListContainsDialingCall(ArrayList<CallControlState> listOfValues) {
+        boolean ret = false;
+        for (CallControlState state : listOfValues) {
+            if (state.mState == CCHalConstants.CALL_STATE_DIALING
+                || state.mState == CCHalConstants.CALL_STATE_ALERTING) {
+                ret = true;
+                break;
+            }
+        }
+        return ret;
+    }
+
+     /** Handles CCS messages. */
+    private final class CcsMessageHandler extends Handler {
+        private CcsMessageHandler(Looper looper) {
+            super(looper);
+        }
+         @Override
+        public void handleMessage(Message msg) {
+           if (DBG) Log.v(TAG, "CcsMessageHandler: received message=" + messageWhatToString(msg.what));
+           ArrayList<CallControlState> listOfValues = null;
+           switch (msg.what) {
+               case UPDATE_BEARER_NAME:
+                   String bName = (String)msg.obj;
+                   mNativeInterface.updateBearerProviderName(bName);
+                   break;
+               case UPDATE_BEARER_TECH:
+                   int tech_type = (int)msg.arg1;
+                   mNativeInterface.updateBearerTechnology(tech_type);
+                   break;
+               case UPDATE_SIGNAL_STRENGTH:
+                   int signal = (int)msg.arg1;
+                   mNativeInterface.updateSignalStrength(signal);
+                   break;
+               case UPDATE_STATUS_FLAGS:
+                   int statusFlags = (int)msg.arg1;
+                   mNativeInterface.updateStatusFlags(statusFlags);
+                   break;
+               case UPDATE_BEARERLIST_SUPPORTED :
+                   String bSList = (String)msg.obj;
+                   mNativeInterface.updateSupportedBearerList(bSList);
+                   break;
+               case UPDATE_CONTENT_CONTROL_ID:
+                   int ccid = (int)msg.arg1;
+                   mNativeInterface.contentControlId(ccid);
+                   break;
+               case UPDATE_CALL_STATE:
+                   listOfValues = (ArrayList<CallControlState>)msg.obj;
+                   Log.d(TAG, "Call list size : " + listOfValues.size());
+                   boolean status = mNativeInterface.callState(listOfValues);
+                   if (mCallOriginatedDevice != null && callListContainsDialingCall(listOfValues)) {
+                       Log.e(TAG, "push the pending Originate response");
+                       //Stack will pick the right index
+                       updateCallControlResponse(CCHalConstants.BTCC_OP_ORIGINATE,
+                          CCHalConstants.BTCC_DEF_INDEX_FOR_FAILURES,
+                          CCHalConstants.BTCC_OP_SUCCESS, mCallOriginatedDevice);
+                       mCallOriginatedDevice = null;
+                   }
+                   break;
+               case UPDATE_CALL_CONTROL_OPCODES_SUPPORTED :
+                   int feature = (int)msg.arg1;
+                   mNativeInterface.callControlOptionalFeatures(feature);
+                   break;
+               case UPDATE_CALL_CONTROL_RESPONSE :
+                   int op = (int)msg.arg1;
+                   int ind = (int)msg.arg2;
+                   int st = (int)msg.obj;
+                   mNativeInterface.callControlResponse(op, ind, st, null);
+                   break;
+               case UPDATE_INCOMING_CALL :
+                   int index = (int)msg.arg1;
+                   String uri = (String)msg.obj;
+                   mNativeInterface.updateIncomingCall(index, uri);
+                   break;
+               case PROCESS_PHONE_STATE_CHANGED:
+                   getBlcc();
+                   break;
+               case PROCESS_CALL_STATE:
+                   listOfValues = (ArrayList<CallControlState>)msg.obj;
+                   processAndUpdateCallState(listOfValues);
+                   break;
+               case ACTIVE_DEVICE_CHANGED:
+                   BluetoothDevice device = (BluetoothDevice)msg.obj;
+                   mNativeInterface.setActiveDevice(device,-1);
+                   break;
+               case EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                   break;
+               default:
+                 Log.e(TAG, "unknown message! msg.what=" + messageWhatToString(msg.what));
+                 break;
+           }
+           Log.v(TAG, "Exit handleMessage");
+      }
+}
+
+    public static String messageWhatToString(int what) {
+        switch (what) {
+            case UPDATE_BEARER_NAME :
+                return "UPDATE_BEARER_NAME";
+            case UPDATE_BEARER_TECH :
+                return "UPDATE_BEARER_TECH";
+            case UPDATE_SIGNAL_STRENGTH :
+                return "UPDATE_SIGNAL_STRENGTH";
+            case UPDATE_BEARERLIST_SUPPORTED :
+                return "UPDATE_BEARERLIST_SUPPORTED";
+            case UPDATE_CONTENT_CONTROL_ID :
+                return "UPDATE_CONTENT_CONTROL_ID";
+            case UPDATE_CALL_STATE :
+                return "UPDATE_CALL_STATE";
+            case UPDATE_CALL_CONTROL_OPCODES_SUPPORTED :
+                return "UPDATE_CALL_CONTROL_OPCODES_SUPPORTED ";
+            case UPDATE_CALL_CONTROL_RESPONSE :
+                return "UPDATE_CALL_CONTROL_RESPONSE";
+            case UPDATE_INCOMING_CALL  :
+                return "UPDATE_INCOMING_CALL";
+            case PROCESS_CALL_STATE :
+                return "PROCESS_CALL_STATE";
+            case UPDATE_STATUS_FLAGS:
+                return "UPDATE_STATUS_FLAGS";
+            default:
+                break;
+        }
+        return Integer.toString(what);
+    }
+
+    /**
+     * Binder object: must be a static class or memory leak may occur.
+     */
+
+    static class CcBinder extends Binder implements IProfileServiceBinder {
+        private CCService mService;
+
+        private CCService getService() {
+            if (!Utils.checkCallerIsSystemOrActiveUser(TAG)) {
+                return null;
+            }
+
+            if (mService != null && mService.isAvailable()) {
+                return mService;
+            }
+            return null;
+        }
+
+        CcBinder(CCService svc) {
+            mService = svc;
+        }
+
+    @Override
+        public void cleanup() {
+            mService = null;
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CallControlState.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CallControlState.java
new file mode 100644
index 0000000..2feb65a
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/cc/CallControlState.java
@@ -0,0 +1,84 @@
+/*
+ *Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.cc;
+
+import java.util.Objects;
+import java.util.Arrays;
+
+/**
+ * A blob of data representing an overall call state on the phone
+ */
+class CallControlState {
+
+     int mIndex;
+    /**
+     * Number of active calls
+     */
+    int mNumActive;
+    /**
+     * Number of held calls
+     */
+    int mNumHeld;
+    /**
+     * Current call setup state
+     */
+    int mState;
+    /**
+     * Currently active call's phone number
+     */
+    String mNumber;
+    /**
+     * Phone number type
+     */
+    int mType;
+
+    /**
+     *  flags to define direction, information witheld by network or server.
+     */
+     int mFlags;
+
+    /**
+     * Caller display name
+     */
+    String mName;
+
+    int mDirection;
+
+    CallControlState(int numActive, int numHeld, int callState, String number, int type,
+            String name) {
+        mNumActive = numActive;
+        mNumHeld = numHeld;
+        mState = callState;
+        mNumber = number;
+        mType = type;
+        mName = name;
+    }
+    CallControlState(int index, int callState, int flags) {
+         mIndex  = index;
+         mState = callState;
+         mFlags = flags;
+    }
+    CallControlState(int index, int direction, int callState, String number) {
+         mIndex  = index;
+         mDirection = direction;
+         mState = callState;
+         mNumber = number;
+    }
+
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupAppMap.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupAppMap.java
new file mode 100644
index 0000000..13375e4
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupAppMap.java
@@ -0,0 +1,178 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package com.android.bluetooth.groupclient;
+
+import android.bluetooth.IBluetoothGroupCallback;
+import android.bluetooth.BluetoothGroupCallback;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.UUID;
+
+/* This class keeps track of registered GroupClient applications and
+ * managing callbacks to be given to appropriate app or module */
+
+public class GroupAppMap {
+
+    private static final String TAG = "BluetoothGroupAppMap";
+
+    class GroupClientApp {
+        /* The UUID of the application */
+        public UUID uuid;
+
+        /* The id of the application */
+        public int appId;
+
+        /* flag to determine if Bluetooth module has registered. */
+        public boolean isLocal;
+
+        /* Callbacks to be given to application */
+        public IBluetoothGroupCallback appCb;
+
+        /* Callbacks to be given to registered Bluetooth modules*/
+        public BluetoothGroupCallback  mCallback;
+
+        public boolean isRegistered;
+
+        /** Death receipient */
+        private IBinder.DeathRecipient mDeathRecipient;
+
+        GroupClientApp(UUID uuid, boolean isLocal, IBluetoothGroupCallback appCb,
+                BluetoothGroupCallback  localCallbacks) {
+            this.uuid = uuid;
+            this.isLocal = isLocal;
+            this.appCb = appCb;
+            this.mCallback = localCallbacks;
+            this.isRegistered = true;
+            appUuids.add(uuid);
+        }
+
+        /**
+         * To link death recipient
+         */
+        void linkToDeath(IBinder.DeathRecipient deathRecipient) {
+            try {
+                IBinder binder = ((IInterface) appCb).asBinder();
+                binder.linkToDeath(deathRecipient, 0);
+                mDeathRecipient = deathRecipient;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to link deathRecipient for appId: " + appId);
+            }
+        }
+
+    }
+
+    List<GroupClientApp> mApps = Collections.synchronizedList(new ArrayList<GroupClientApp>());
+
+    ArrayList<UUID> appUuids = new ArrayList<UUID>();
+
+    /**
+     * Add an entry to the application list.
+     */
+    GroupClientApp add(UUID uuid, boolean isLocal, IBluetoothGroupCallback appCb,
+            BluetoothGroupCallback  localCallback) {
+        synchronized (mApps) {
+            GroupClientApp app = new GroupClientApp(uuid, isLocal, appCb, localCallback);
+            mApps.add(app);
+            return app;
+        }
+    }
+
+    /**
+     * Remove the entry for a given UUID
+     */
+    void remove(UUID uuid) {
+        synchronized (mApps) {
+            Iterator<GroupClientApp> i = mApps.iterator();
+            while (i.hasNext()) {
+                GroupClientApp entry = i.next();
+                if (entry.uuid.equals(uuid)) {
+                    entry.isRegistered = false;
+                    i.remove();
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Remove the entry for a given application ID.
+     */
+    void remove(int appId) {
+        synchronized (mApps) {
+            Iterator<GroupClientApp> i = mApps.iterator();
+            while (i.hasNext()) {
+                GroupClientApp entry = i.next();
+                if (entry.appId == appId) {
+                    entry.isRegistered = false;
+                    i.remove();
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Get GroupClient application by UUID.
+     */
+    GroupClientApp getByUuid(UUID uuid) {
+        synchronized (mApps) {
+            Iterator<GroupClientApp> i = mApps.iterator();
+            while (i.hasNext()) {
+                GroupClientApp entry = i.next();
+                if (entry.uuid.equals(uuid)) {
+                    return entry;
+                }
+            }
+        }
+        Log.e(TAG, "App not found for UUID " + uuid);
+        return null;
+    }
+
+    /**
+     * Get a GroupClient application by appId.
+     */
+    GroupClientApp getById(int appId) {
+        synchronized (mApps) {
+            Iterator<GroupClientApp> i = mApps.iterator();
+            while (i.hasNext()) {
+                GroupClientApp entry = i.next();
+                if (entry.appId == appId) {
+                    return entry;
+                }
+            }
+        }
+        Log.e(TAG, "GroupClient App not found for appId " + appId);
+        return null;
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupClientNativeInterface.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupClientNativeInterface.java
new file mode 100644
index 0000000..ce1076f
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupClientNativeInterface.java
@@ -0,0 +1,251 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package com.android.bluetooth.groupclient;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import java.util.ArrayList;
+import java.util.List;
+import android.util.Log;
+import java.util.UUID;
+
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * CSIP Client Native Interface to/from JNI.
+ */
+public class GroupClientNativeInterface {
+    private static final String TAG = "BluetoothGroupNativeIntf";
+    private static final boolean DBG = true;
+    private BluetoothAdapter mAdapter;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static GroupClientNativeInterface sInstance;
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static {
+        classInitNative();
+    }
+
+    private GroupClientNativeInterface() {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mAdapter == null) {
+            Log.wtfStack(TAG, "No Bluetooth Adapter Available");
+        }
+    }
+
+    /**
+     * Get singleton instance.
+     */
+    public static GroupClientNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new GroupClientNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Initializes the native interface.
+     *
+     * priorities to configure.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void init() {
+        initNative();
+    }
+
+    /**
+     * Cleanup the native interface.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void cleanup() {
+        cleanupNative();
+    }
+
+    /**
+     * Register CSIP app with the stack code.
+     *
+     * @param appUuidLsb lsb of app uuid.
+     * @param appUuidMsb msb of app uuid.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void registerCsipApp(long appUuidLsb, long appUuidMsb) {
+       registerCsipAppNative(appUuidLsb, appUuidMsb);
+    }
+
+    /**
+     * Register CSIP app with the stack code.
+     *
+     * @param appId ID of the application to be unregistered.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void unregisterCsipApp(int appId) {
+       unregisterCsipAppNative(appId);
+    }
+
+    /**
+     * Change lock value of the coordinated set member
+     *
+     * @param appId ID of the application which is requesting change in lock status
+     * @param setId Identifier of the set
+     * @param devices List of bluetooth devices for whick lock status change is required
+     * @param value Lock/Unlock value
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void setLockValue(int appId, int setId, List<BluetoothDevice> devices,
+             int value) {
+       int i = 0;
+       int size = ((devices != null) ? devices.size() : 0);
+       String[] devicesList = new String[size];
+       if (size > 0) {
+           for (BluetoothDevice device: devices) {
+                devicesList[i++] = device.toString();
+           }
+       }
+       setLockValueNative(appId, setId, value, devicesList);
+    }
+
+    /**
+     * Initiates Csip connection to a remote device.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean connectSetDevice(int appId, BluetoothDevice device) {
+        return connectSetDeviceNative(appId, getByteAddress(device));
+    }
+
+    /**
+     * Disconnects Csip from a remote device.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean disconnectSetDevice(int appId, BluetoothDevice device) {
+        return disconnectSetDeviceNative(appId, getByteAddress(device));
+    }
+
+
+    private BluetoothDevice getDevice(byte[] address) {
+        return mAdapter.getRemoteDevice(address);
+    }
+
+    private byte[] getByteAddress(BluetoothDevice device) {
+        if (device == null) {
+            return Utils.getBytesFromAddress("00:00:00:00:00:00");
+        }
+        return Utils.getBytesFromAddress(device.getAddress());
+    }
+
+    private void onCsipAppRegistered (int status, int appId,
+            long uuidLsb, long uuidMsb) {
+        UUID uuid = new UUID(uuidMsb, uuidLsb);
+
+        GroupService service = GroupService.getGroupService();
+        if (service != null) {
+            service.onCsipAppRegistered(status, appId, uuid);
+        }
+    }
+
+    private void onConnectionStateChanged(int appId, String bdAddr,
+            int state, int status) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter()
+                                                 .getRemoteDevice(bdAddr);
+
+        GroupService service = GroupService.getGroupService();
+        if (service != null) {
+            service.onConnectionStateChanged (appId, device, state, status);
+        }
+    }
+
+    private void onNewSetFound (int setId, String bdAddr, int size, byte[] sirk,
+            long uuidLsb, long uuidMsb, boolean lockSupport) {
+        UUID uuid = new UUID(uuidMsb, uuidLsb);
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter()
+                                                 .getRemoteDevice(bdAddr);
+
+        GroupService service = GroupService.getGroupService();
+        if (service != null) {
+            service.onNewSetFound(setId, device, size, sirk, uuid, lockSupport);
+        }
+    }
+
+    private void onNewSetMemberFound (int setId, String bdAddr) {
+        BluetoothDevice device =
+                BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdAddr);
+
+        GroupService service = GroupService.getGroupService();
+        if (service != null) {
+            service.onNewSetMemberFound(setId, device);
+        }
+    }
+
+    private void onLockStatusChanged (int appId, int setId, int value,
+            int status, String[] bdAddr) {
+        List<BluetoothDevice> lockMembers = new ArrayList<BluetoothDevice>();
+        for (String address: bdAddr) {
+            lockMembers.add(mAdapter.getRemoteDevice(address.toUpperCase()));
+        }
+
+        GroupService service = GroupService.getGroupService();
+        if (service != null) {
+            service.onLockStatusChanged(appId, setId, value, status, lockMembers);
+        }
+    }
+
+    private void onLockAvailable (int appId, int setId, String address) {
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
+        GroupService service = GroupService.getGroupService();
+        if (service != null) {
+            service.onLockAvailable(appId, setId, device);
+        }
+    }
+
+    private void onSetSizeChanged (int setId, int size, String address) {
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
+        GroupService service = GroupService.getGroupService();
+        if (service != null) {
+            service.onSetSizeChanged(setId, size, device);
+        }
+    }
+
+    private void onSetSirkChanged(int setId, byte[] sirk, String address) {
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
+        GroupService service = GroupService.getGroupService();
+        if (service != null) {
+            service.onSetSirkChanged(setId, sirk, device);
+        }
+    }
+
+    // Native methods that call JNI interface
+    private static native void classInitNative();
+    private native void initNative();
+    private native void cleanupNative();
+    private native void registerCsipAppNative(long appUuidLsb, long appUuidMsb);
+    private native void unregisterCsipAppNative(int appId);
+    private native void setLockValueNative(int appId, int setId, int value, String[] devicesList);
+    private native boolean connectSetDeviceNative(int appId, byte[] address);
+    private native boolean disconnectSetDeviceNative(int appId, byte[] address);
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupScanner.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupScanner.java
new file mode 100644
index 0000000..bc192b2
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupScanner.java
@@ -0,0 +1,529 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.groupclient;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDeviceGroup;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.SystemProperties;
+
+import android.util.Log;
+
+import com.android.bluetooth.btservice.AdapterService;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Class that handles Bluetooth LE Scan results for Set member discovery.
+ * It performs scan result resolution for set identification.
+ *
+ * @hide
+ */
+public class GroupScanner {
+    private static final boolean DBG = true;
+    private static final boolean VDBG = GroupService.VDBG;
+    private static final String TAG = "BluetoothGroupScanner";
+
+    // messages for handling filtered PSRI Scan results
+    private static final int MSG_HANDLE_LE_SCAN_RESULT = 0;
+
+    // message for starting coordinated set discovery
+    private static final int MSG_START_SET_DISCOVERY = 1;
+
+    // message to stop coordinated set discovery
+    private static final int MSG_STOP_SET_DISCOVERY = 2;
+
+    // message when set member discovery timeout happens
+    private static final int MSG_SET_MEMBER_DISC_TIMEOUT = 3;
+
+    // message to handle PSRI from EIR packet
+    private static final int MSG_HANDLE_EIR_RESPONSE = 4;
+
+    // PSRI Service AD Type
+    private final ParcelUuid PSRI_SERVICE_ADTYPE_UUID
+            = BluetoothUuid.parseUuidFrom(new byte[]{0x2E, 0x00});
+
+    private static final int PSRI_LEN = 6;
+    private static final int PSRI_SPLIT_LEN = 3; // 24 bits
+    private static final int AES_128_IO_LEN = 16;
+
+    // Set Member Discovery timeout
+    private static final int SET_MEMBER_DISCOVERY_TIMEOUT = 10000; // 10 sec
+
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothDevice mCurrentDevice;
+    private BluetoothLeScanner mScanner;
+    private GroupService mGroupService;
+    private volatile CsipHandler mHandler;
+    private Handler mainHandler;
+    private boolean mScanResolution;
+    private int mDiscoveryStoppedReason;
+    private CsipLeScanCallback mCsipScanCallback;
+
+    // parameters for set discovery
+    private int mSetId;
+    private byte[] mSirk;
+    private int mTransport;
+    private int mSetSize;
+    private int mTotalDiscovered;
+
+    private int mScanType = 1;
+
+    // filter out duplicate scans
+    ArrayList<BluetoothDevice> scannedDevices = new ArrayList<BluetoothDevice>();
+
+    GroupScanner(GroupService service) {
+        mGroupService = service;
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        // register receiver for Bluetooth State change
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        mGroupService.registerReceiver(mReceiver, filter);
+
+        mainHandler = new Handler(mGroupService.getMainLooper());
+        HandlerThread thread = new HandlerThread("CsipScanHandlerThread");
+        thread.start();
+        mHandler = new CsipHandler(thread.getLooper());
+        mCsipScanCallback = new CsipLeScanCallback();
+        ScanRecord.DATA_TYPE_GROUP_AD_TYPE = 0x2E;
+        /* Testing: Property used for deciding scan and filter type. To be removed */
+        mScanType = SystemProperties.getInt(
+                     "persist.vendor.service.bt.csip.scantype", 1);
+    }
+
+    // Handler for CSIP scan operations and set member resolution.
+    private class CsipHandler extends Handler {
+        CsipHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (VDBG) Log.v(TAG, "msg.what = " + msg.what);
+
+            switch (msg.what) {
+                case MSG_HANDLE_LE_SCAN_RESULT:
+                    // start processing scan result
+                    int callBackType = msg.arg1;
+                    ScanResult result = (ScanResult) msg.obj;
+                    mCurrentDevice = result.getDevice();
+
+                    /* In case of DUMO device if advertisement is coming from other RPA */
+                    if (mCurrentDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
+                        return;
+                    }
+
+                    // skip scanresult if already processed for this device
+                    if (scannedDevices.contains(mCurrentDevice)) {
+                        if (VDBG) {
+                            Log.w(TAG, "duplicate scanned result or Device"
+                                    + mCurrentDevice + " Group info already resolved. Ignore");
+                         }
+                        return;
+                    }
+
+                    scannedDevices.add(mCurrentDevice);
+                    ScanRecord record =  result.getScanRecord();
+
+                    // get required service data with PSRI AD Type
+                    byte[] srvcData = null;
+                    /* for debugging purpose */
+                    if (mScanType == 2) {
+                        srvcData = record.getServiceData(PSRI_SERVICE_ADTYPE_UUID);
+                    } else {
+                        srvcData = record.getGroupIdentifierData();
+                    }
+                    if (srvcData == null || srvcData.length != PSRI_LEN) {
+                        Log.e(TAG, "Group info with incorrect length found "
+                                + "in advertisement of " + mCurrentDevice);
+                        return;
+                    }
+
+                    startPsriResolution(srvcData);
+                    break;
+
+                case MSG_HANDLE_EIR_RESPONSE:
+                    EirData eirData = (EirData)msg.obj;
+                    mCurrentDevice = eirData.curDevice;
+                    byte[] eirGroupData = eirData.groupData;
+
+                    // skip eir if already processed for this device
+                    if (scannedDevices.contains(mCurrentDevice)) {
+                        if (VDBG) {
+                            Log.w(TAG, "duplicate eir or Device"
+                                    + mCurrentDevice + " PSRI already resolved. Ignore");
+                         }
+                        return;
+                    }
+
+                    scannedDevices.add(mCurrentDevice);
+                    if (eirGroupData == null || eirGroupData.length != PSRI_LEN) {
+                        Log.e(TAG, "PSRI data with incorrect length found "
+                                + "in EIR of " + mCurrentDevice);
+                        return;
+                    }
+                    startPsriResolution(eirGroupData);
+                    break;
+
+                // High priority msg received in front of the message queue
+                case MSG_SET_MEMBER_DISC_TIMEOUT:
+                    mDiscoveryStoppedReason = BluetoothDeviceGroup.DISCOVERY_STOPPED_BY_TIMEOUT;
+                case MSG_STOP_SET_DISCOVERY:
+                    handleStopSetDiscovery();
+                    break;
+
+                // High priority msg received in front of the message queue
+                case MSG_START_SET_DISCOVERY:
+                    handleStartSetDiscovery();
+                    break;
+
+                default:
+                    Log.e(TAG, "Unknown message : " + msg.what);
+            }
+        }
+    }
+
+    /* BroadcastReceiver for BT ON State intent for registering BLE Scanner */
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == null) {
+                Log.e(TAG, "Received intent with null action");
+                return;
+            }
+
+            switch (action) {
+                case BluetoothAdapter.ACTION_STATE_CHANGED:
+                    mScanner = mBluetoothAdapter.getBluetoothLeScanner();
+                break;
+            }
+
+        }
+    };
+
+    /* Scan results callback */
+    private class CsipLeScanCallback extends ScanCallback {
+        @Override
+        public void onScanResult(int callBackType, ScanResult result) {
+            if (VDBG) Log.v(TAG, "onScanResult callBackType : " + callBackType);
+            if (mHandler != null && mScanResolution) {
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_HANDLE_LE_SCAN_RESULT,
+                        callBackType, 0, result));
+             } else {
+                if (VDBG) Log.e(TAG, "onScanResult mHandler is null" +
+                                     " or Scan Resolution is stopped");
+             }
+        }
+
+        public void onScanFailed(int errorCode) {
+            mScanResolution = false;
+            Log.e(TAG, "Scan failed. Error code: " + new Integer(errorCode).toString());
+        }
+    }
+
+    /* EIR Data */
+    private class EirData {
+        private BluetoothDevice curDevice;
+        private byte[] groupData;
+
+        EirData(BluetoothDevice device, byte[] data) {
+            curDevice = device;
+            groupData = data;
+        }
+    }
+
+    /* API that handles PSRI received from EIR */
+    public void handleEIRGroupData(BluetoothDevice device, byte[] data) {
+        if (VDBG) Log.v(TAG, "handleEirData: device: " + device);
+        if (mHandler != null && mScanResolution) {
+            EirData eirData = new EirData(device, data);
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_HANDLE_EIR_RESPONSE, eirData));
+         } else {
+            if (VDBG) Log.e(TAG, "handleEirData mHandler is null" +
+                                 " or Inquiry Scan Resolution is stopped");
+         }
+    }
+
+    /* API to start set discovery by starting either LE scan or BREDR Inquiry */
+    void startSetDiscovery(int setId, byte[] sirk, int transport,
+            int size, List<BluetoothDevice> setDevices) {
+        Log.d(TAG, "startGroupDiscovery: groupId: " + setId + ", group size = "
+                + size + ", Total discovered = " + setDevices.size()
+                + " Transport = " + transport);
+
+        // check if set discovery is already in progress
+        if (mScanResolution) {
+            Log.e(TAG, "Group discovery is already in progress for Group Id: " + mSetId
+                    + ". Ignore this request");
+            return;
+        }
+
+        // mark parameters of the set to be discovered
+        mSetId = setId;
+        mTransport = transport;
+        mSetSize = size;
+        mTotalDiscovered = setDevices.size();
+        mSirk = Arrays.copyOf(sirk, AES_128_IO_LEN);
+        reverseByteArray(mSirk);
+
+        // clear scanned arrayList and add already found set members to it
+        scannedDevices.clear();
+        scannedDevices.addAll(setDevices);
+
+        //post message in the front of message queue
+        mHandler.sendMessageAtFrontOfQueue(
+                mHandler.obtainMessage(MSG_START_SET_DISCOVERY));
+    }
+
+    /* API to start discovery with required settings and transport */
+    void handleStartSetDiscovery() {
+        Log.d(TAG, "handleStartGroupDiscovery");
+        mScanResolution = true;
+
+        if (mTransport == BluetoothDevice.DEVICE_TYPE_CLASSIC) {
+            // start BREDR inquiry (unfiltered)
+            mBluetoothAdapter.startDiscovery();
+        } else {
+            // Confiigure scan filter and start filtered scan for PSRI data
+            ScanSettings.Builder settingBuilder = new ScanSettings.Builder();
+            List<ScanFilter> filters = new ArrayList<ScanFilter>();
+            byte[] psri = {};
+
+            mScanType = SystemProperties.getInt(
+                     "persist.vendor.service.bt.csip.scantype", 1);
+
+            // for debugging purpose only
+            if (mScanType == 2) {
+                filters.add(new ScanFilter.Builder().setServiceData(
+                        PSRI_SERVICE_ADTYPE_UUID, psri).build());
+            } else if (mScanType == 1) {
+                filters.add(new ScanFilter.Builder().setGroupBasedFiltering(true)
+                                                    .build());
+            }
+            settingBuilder.setScanMode(ScanSettings.SCAN_MODE_BALANCED)
+                          .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
+                          .setLegacy(false);
+
+            // start BLE filtered Scan
+            if (mScanType != 0) {
+                Log.i(TAG, " filtered scan started");// debug
+                mScanner.startScan(filters, settingBuilder.build(), mCsipScanCallback);
+            } else {
+                Log.i(TAG, " Unfiltered scan started");// debug
+                mScanner.startScan(mCsipScanCallback);
+            }
+        }
+
+        // Start Set Member discovery timeout of 10 sec
+        mHandler.removeMessages(MSG_SET_MEMBER_DISC_TIMEOUT);
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(MSG_SET_MEMBER_DISC_TIMEOUT),
+                SET_MEMBER_DISCOVERY_TIMEOUT);
+    }
+
+    /* to stop set discorvey procedure - stop LE scan or BREDR inquiry */
+    void stopSetDiscovery(int setId, int reason) {
+        Log.d(TAG, "stopGroupDiscovery");
+
+        mDiscoveryStoppedReason = reason;
+
+        //post message in the front of message queue
+        mHandler.sendMessageAtFrontOfQueue(
+                mHandler.obtainMessage(MSG_STOP_SET_DISCOVERY));
+    }
+
+    /* handles actions to be taken once set discovery is needed to be stopped*/
+    void handleStopSetDiscovery() {
+        Log.d(TAG, "handleStopGroupDiscovery");
+        mScanResolution = false;
+
+        if (mTransport == BluetoothDevice.DEVICE_TYPE_LE ||
+                mTransport == BluetoothDevice.DEVICE_TYPE_DUAL) {
+            mScanner.stopScan(mCsipScanCallback);
+        } else {
+            mBluetoothAdapter.cancelDiscovery();
+        }
+
+        // remove all the queued scan results and set member discovery timeout message
+        mHandler.removeMessages(MSG_HANDLE_LE_SCAN_RESULT);
+        mHandler.removeMessages(MSG_SET_MEMBER_DISC_TIMEOUT);
+
+        // Give callback to service to route it to requesting application
+        mainHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mGroupService.onSetDiscoveryCompleted(
+                        mSetId, mTotalDiscovered, mDiscoveryStoppedReason);
+            }
+        });
+    }
+
+    /* Starts resolution of PSRI data received in scan results */
+    void startPsriResolution(byte[] psri) {
+        Log.d(TAG, "startGroupResolution");
+
+        if (VDBG) printByteArrayInHex(psri, "GroupInfo");
+        // obtain remote hash and random number
+        byte[] remoteHash = new byte[PSRI_SPLIT_LEN];
+        byte[] randomNumber = new byte[PSRI_SPLIT_LEN];
+
+        // Get remote hash from first 24 bits of PSRI
+        System.arraycopy(psri, 0, remoteHash, 0, PSRI_SPLIT_LEN);
+        // Get random number from last 24 bits of PSRI
+        System.arraycopy(psri, PSRI_SPLIT_LEN, randomNumber, 0, PSRI_SPLIT_LEN);
+
+        byte[] localHash = computeLocalHash(randomNumber);
+
+        if (VDBG) {
+            printByteArrayInHex(localHash, "localHash");
+            printByteArrayInHex(remoteHash, "remoteHash");
+        }
+
+        if (localHash != null) {
+            validateSetMember(localHash, remoteHash);
+        }
+    }
+
+    /* computes local hash from received random number and SIRK */
+    byte[] computeLocalHash(byte[] randomNumber) {
+        byte[] localHash = new byte[AES_128_IO_LEN];
+        byte[] randomNumber128 = new byte[AES_128_IO_LEN];
+        System.arraycopy(randomNumber, 0, randomNumber128, 0, PSRI_SPLIT_LEN);
+
+        reverseByteArray(randomNumber128);
+
+        if (VDBG) {
+            // for debugging
+            printByteArrayInHex(mSirk, "reversed GroupIRK");
+            printByteArrayInHex(randomNumber128, "reverse randomNumber");
+        }
+
+        try {
+            SecretKeySpec skeySpec = new SecretKeySpec(mSirk, "AES");
+            Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
+            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
+            localHash = cipher.doFinal(randomNumber128);
+            reverseByteArray(localHash);
+            if (VDBG) printByteArrayInHex(localHash, "after AES 128 encryption");
+            return Arrays.copyOfRange(localHash, 0, PSRI_SPLIT_LEN);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while generating local hash: " + e);
+        }
+        return null;
+    }
+
+    /* to validate that if remote belongs to a given coordinated set*/
+    void validateSetMember(byte[] localHash, byte[] remoteHash) {
+        if (!Arrays.equals(localHash, remoteHash)) {
+            return;
+        }
+        Log.d(TAG, "New Group device discovered: " + mCurrentDevice);
+        mTotalDiscovered++;
+
+        // give set member found callback on main thread
+        mainHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mGroupService.onSetMemberFound(mSetId, mCurrentDevice);
+            }
+        });
+
+        //check if all set members have been discovered
+        if (mSetSize > 0 && mTotalDiscovered >= mSetSize) {
+            // to immediatly ignore processing scan results after completion
+            mScanResolution = false;
+            mDiscoveryStoppedReason = BluetoothDeviceGroup.DISCOVERY_COMPLETED;
+            mHandler.sendMessageAtFrontOfQueue(
+                mHandler.obtainMessage(MSG_STOP_SET_DISCOVERY));
+        } else {
+            // restart set member discovery timeout
+            mHandler.removeMessages(MSG_SET_MEMBER_DISC_TIMEOUT);
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(MSG_SET_MEMBER_DISC_TIMEOUT),
+                    SET_MEMBER_DISCOVERY_TIMEOUT);
+        }
+
+    }
+
+    /* cleanup tasks on BT OFF*/
+    void cleanup() {
+        mGroupService.unregisterReceiver(mReceiver);
+    }
+
+    // returns reversed byte array
+    void reverseByteArray(byte[] byte_arr) {
+        int size = byte_arr.length;
+        for (int i = 0; i < size/2; i++) {
+            byte b = byte_arr[i];
+            byte_arr[i] = byte_arr[size - 1 - i];
+            byte_arr[size - 1 - i] = b;
+        }
+    }
+
+    public static byte[] hexStringToByteArray(String str) {
+        byte[] b = new byte[str.length() / 2];
+        for (int i = 0; i < b.length; i++) {
+          int index = i * 2;
+          int val = Integer.parseInt(str.substring(index, index + 2), 16);
+          b[i] = (byte) val;
+        }
+        return b;
+      }
+
+    // print byte array in hexadecimal format
+    void printByteArrayInHex(byte[] data, String name) {
+        final StringBuilder hex = new StringBuilder();
+        for(byte b : data) {
+            hex.append(String.format("%02x", b));
+        }
+        Log.i(TAG, name + ": " + hex);
+    }
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupService.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupService.java
new file mode 100644
index 0000000..5f8ba52
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/groupclient/GroupService.java
@@ -0,0 +1,1107 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.groupclient;
+
+import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
+import android.bluetooth.BluetoothDeviceGroup;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.DeviceGroup;
+import android.bluetooth.IBluetoothDeviceGroup;
+import android.bluetooth.IBluetoothGroupCallback;
+import android.bluetooth.BluetoothGroupCallback;
+import android.content.AttributionSource;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ParcelUuid;
+import android.os.SystemProperties;
+
+import android.util.Log;
+
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.Config;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.bluetooth.Utils;
+
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Provides Bluetooth CSIP Client profile, as a service in the Bluetooth application.
+ * @hide
+ */
+public class GroupService extends ProfileService {
+    private static final boolean DBG = true;
+    private static final String TAG = "BluetoothGroupService";
+    protected static final boolean VDBG = true;//Log.isLoggable(TAG, Log.VERBOSE);
+
+    private GroupScanner mGroupScanner;
+
+    private static GroupService sGroupService;
+
+    private AdapterService mAdapterService;
+
+    GroupClientNativeInterface mGroupNativeInterface;
+
+    GroupAppMap mAppMap = new GroupAppMap();
+
+    private static CopyOnWriteArrayList<DeviceGroup> mCoordinatedSets
+            = new CopyOnWriteArrayList<DeviceGroup>();
+
+    private static HashMap<Integer, byte[]> setSirkMap = new HashMap<Integer, byte[]>();
+
+    private static final int INVALID_APP_ID = 0x10;
+    private static final int INVALID_SET_ID = 0x10;
+    private static final UUID EMPTY_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000");
+
+    /* parameters to hold details for ongoing set discovery and pending set discovery */
+    private SetDiscoveryRequest mCurrentSetDisc = null;
+    private SetDiscoveryRequest mPendingSetDisc = null;
+
+    /* Constants for Coordinated set properties */
+    private static final String SET_ID = "SET_ID";
+    private static final String INCLUDING_SRVC = "INCLUDING_SRVC";
+    private static final String SIZE = "SIZE";
+    private static final String SIRK = "SIRK";
+    private static final String LOCK_SUPPORT = "LOCK_SUPPORT";
+
+    private class SetDiscoveryRequest {
+        private int mAppId = INVALID_APP_ID;
+        private int mSetId = INVALID_SET_ID;
+        private boolean mDiscInProgress = false;
+
+        SetDiscoveryRequest() {
+            mAppId = INVALID_APP_ID;
+            mSetId = INVALID_SET_ID;
+            mDiscInProgress = false;
+        }
+
+        SetDiscoveryRequest(int appId, int setId, boolean inProgress) {
+            mAppId = appId;
+            mSetId = setId;
+            mDiscInProgress = inProgress;
+        }
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == null) {
+                Log.e(TAG, "Received intent with null action");
+                return;
+            }
+
+            switch (action) {
+                case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
+                    BluetoothDevice device = intent.getParcelableExtra(
+                            BluetoothDevice.EXTRA_DEVICE);
+                    int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                        BluetoothDevice.ERROR);
+                    if (bondState == BluetoothDevice.BOND_NONE) {
+                        int setId = getRemoteDeviceGroupId(device, null);
+                        if (setId < BluetoothDeviceGroup.INVALID_GROUP_ID) {
+                            Log.i(TAG, " Group Device "+ device +
+                                    " unpaired. Group ID: " + setId);
+                            removeSetMemberFromCSet(setId, device);
+                        }
+                    }
+                    break;
+            }
+
+        }
+    };
+
+
+    @Override
+    protected IProfileServiceBinder initBinder() {
+        return new GroupBinder(this);
+    }
+
+    @Override
+    protected boolean start() {
+        if (DBG) {
+            Log.d(TAG, "start()");
+        }
+
+        mGroupNativeInterface = Objects.requireNonNull(GroupClientNativeInterface.getInstance(),
+                "GroupClientNativeInterface cannot be null when GroupService starts");
+
+        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+                "AdapterService cannot be null when GroupService starts");
+
+        mGroupScanner = new GroupScanner(this);
+
+        mGroupNativeInterface.init();
+        setGroupService(this);
+
+        // register receiver for Bluetooth State change
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        registerReceiver(mReceiver, filter);
+
+        return true;
+    }
+
+    private static synchronized void setGroupService(GroupService instance) {
+        if (DBG) {
+            Log.d(TAG, "setGroupService(): set to: " + instance);
+        }
+        sGroupService = instance;
+    }
+
+    protected boolean stop() {
+        if (DBG) {
+            Log.d(TAG, "stop()");
+        }
+
+        if (mGroupScanner != null) {
+            mGroupScanner.cleanup();
+        }
+
+        if (sGroupService == null) {
+            Log.w(TAG, "stop() called already..");
+            return true;
+        }
+
+        unregisterReceiver(mReceiver);
+
+        // Cleanup native interface
+        mGroupNativeInterface.cleanup();
+        mGroupNativeInterface = null;
+
+        // Mark service as stopped
+        setGroupService(null);
+
+        // cleanup initializations
+        mGroupScanner = null;
+        mAdapterService = null;
+
+        return true;
+    }
+
+    @Override
+    protected void cleanup() {
+        if (DBG) {
+            Log.d(TAG, "cleanup()");
+        }
+
+        // Cleanup native interface
+        if (mGroupNativeInterface != null) {
+            mGroupNativeInterface.cleanup();
+            mGroupNativeInterface = null;
+        }
+
+        // cleanup initializations
+        mGroupScanner = null;
+        mAdapterService = null;
+    }
+
+    /**
+     * Get the GroupService instance
+     * @return GroupService instance
+     */
+    public static synchronized GroupService getGroupService() {
+        if (sGroupService == null) {
+            Log.w(TAG, "getGroupService(): service is NULL");
+            return null;
+        }
+
+        if (!sGroupService.isAvailable()) {
+            Log.w(TAG, "getGroupService(): service is not available");
+            return null;
+        }
+
+        return sGroupService;
+    }
+
+    /* API to load coordinated set from bonded device on BT ON */
+    public static void loadDeviceGroupFromBondedDevice (
+            BluetoothDevice device, String setDetails) {
+        String[] csets = setDetails.split(" ");
+        if (VDBG) Log.v(TAG, " Device is part of " + csets.length + " device groups");
+
+        for (String setInfo: csets) {
+            String[] setProperties = setInfo.split("~");
+            int setId = INVALID_SET_ID, size = 0;
+            UUID inclSrvcUuid = UUID.fromString("00000000-0000-0000-0000-000000000000");
+            boolean lockSupport = false;
+
+            for (String property: setProperties) {
+                if (VDBG) Log.v(TAG, "Property = " + property);
+                String[] propSplit = property.split(":");
+                if (propSplit[0].equals(SET_ID)) {
+                    setId = Integer.parseInt(propSplit[1]);
+                } else if (propSplit[0].equals(INCLUDING_SRVC)) {
+                    inclSrvcUuid = UUID.fromString(propSplit[1]);
+                } else if (propSplit[0].equals(SIZE)) {
+                    size = Integer.parseInt(propSplit[1]);
+                } else if (propSplit[0].equals(SIRK) && setId != 16) {
+                    setSirkMap.put(setId, GroupScanner.hexStringToByteArray(propSplit[1]));
+                } else if (propSplit[0].equals(LOCK_SUPPORT)) {
+                    lockSupport = Boolean.parseBoolean(propSplit[1]);
+                }
+            }
+
+            DeviceGroup set = getCoordinatedSet(setId, false);
+            if (set == null) {
+                List<BluetoothDevice> members = new ArrayList<BluetoothDevice>();
+                members.add(device);
+                set = new DeviceGroup(setId, size, members,
+                        new ParcelUuid(inclSrvcUuid), lockSupport);
+                mCoordinatedSets.add(set);
+            } else {
+                if (!set.getDeviceGroupMembers().contains(device)) {
+                    set.getDeviceGroupMembers().add(device);
+                }
+            }
+            if (VDBG) Log.v(TAG, "Device " + device + " loaded in Group ("+ setId +")"
+                            + " Devices: " + set.getDeviceGroupMembers());
+        }
+    }
+
+   /* API to accept PSRI data from EIR packet */
+    public void handleEIRGroupData(BluetoothDevice device, String data) {
+        mGroupScanner.handleEIRGroupData(device, data.getBytes());
+    }
+
+    public static void setAdvanceAudioSupport() {
+        Log.d(TAG, "setAdvanceAudioSupport: Setting support from LEA Module");
+
+        if (SystemProperties.get("persist.vendor.service.bt.adv_audio_mask").isEmpty()) {
+          SystemProperties.set("persist.vendor.service.bt.adv_audio_mask",
+                  String.valueOf(Config.ADV_AUDIO_UNICAST_FEAT_MASK |
+                                 Config.ADV_AUDIO_BCA_FEAT_MASK |
+                                 Config.ADV_AUDIO_BCS_FEAT_MASK));
+        }
+    }
+
+    private static class GroupBinder
+            extends IBluetoothDeviceGroup.Stub implements IProfileServiceBinder {
+        private GroupService mService;
+
+        private GroupService getService() {
+            if (mService != null && mService.isAvailable()) {
+                return mService;
+            }
+            return null;
+        }
+
+        GroupBinder(GroupService service) {
+            if (DBG) {
+                Log.v(TAG, "GroupBinder()");
+            }
+            mService = service;
+        }
+
+        @Override
+        public void cleanup() {
+            mService = null;
+        }
+
+        @Override
+        public void connect(int appId, BluetoothDevice device, AttributionSource source) {
+            if (DBG) {
+                Log.d(TAG, "connect Device " + device);
+            }
+
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                    service, source, "connect")) {
+                return;
+            }
+            service.connect(appId, device);
+        }
+
+        @Override
+        public void disconnect(int appId, BluetoothDevice device, AttributionSource source) {
+            if (DBG) {
+                Log.d(TAG, "disconnect Device " + device);
+            }
+
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                    service, source, "disconnect")) {
+                return;
+            }
+            service.disconnect(appId, device);
+        }
+
+        @Override
+        public void registerGroupClientApp(ParcelUuid uuid,
+                IBluetoothGroupCallback callback, AttributionSource source) {
+            if (VDBG) {
+                Log.d(TAG, "registerGroupClientApp");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                    service, source, "registerGroupClientApp")) {
+                return;
+            }
+            service.registerGroupClientApp(uuid.getUuid(), callback, null);
+        }
+
+        @Override
+        public void unregisterGroupClientApp(int appId, AttributionSource source) {
+            if (VDBG) {
+                    Log.d(TAG, "unregisterGroupClientApp");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                    service, source, "unregisterGroupClientApp")) {
+                return;
+            }
+            service.unregisterGroupClientApp(appId);
+        }
+
+        @Override
+        public void setExclusiveAccess(int appId, int groupId, List<BluetoothDevice> devices,
+                   int value, AttributionSource source) {
+            if (VDBG) {
+                Log.d(TAG, "setExclusiveAccess");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                    service, source, "setExclusiveAccess")) {
+                return;
+            }
+            service.setLockValue(appId, groupId, devices, value);
+        }
+
+        @Override
+        public void startGroupDiscovery(int appId, int groupId
+                , AttributionSource source) throws RemoteException  {
+            if (VDBG) {
+                Log.d(TAG, "startGroupDiscovery");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(service,
+                    source, "startGroupDiscovery")) {
+                return;
+            }
+            service.startSetDiscovery(appId, groupId);
+        }
+
+        @Override
+        public void stopGroupDiscovery(int appId, int groupId, AttributionSource source) {
+            if (VDBG) {
+                Log.d(TAG, "stopGroupDiscovery");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                    service, source, "stopGroupDiscovery")) {
+                return;
+            }
+            enforceBluetoothPrivilegedPermission(service);
+            service.stopSetDiscovery(appId, groupId);
+        }
+
+        @Override
+        public void getExclusiveAccessStatus(int appId, int groupId,
+                List<BluetoothDevice> devices, AttributionSource source) {
+            if (DBG) {
+                Log.d(TAG, "getExclusiveAccessStatus");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                service, source, "getExclusiveAccessStatus")) {
+                return;
+            }
+            enforceBluetoothPrivilegedPermission(service);
+        }
+
+        @Override
+        public List<DeviceGroup> getDiscoveredGroups(boolean mPublicAddr
+                , AttributionSource source) {
+            if (DBG) {
+                Log.d(TAG, "getDiscoveredGroups");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                service, source, "getDiscoveredGroups")) {
+                return null;
+            }
+            return service.getDiscoveredCoordinatedSets(mPublicAddr);
+        }
+
+        @Override
+        public DeviceGroup getDeviceGroup(int setId, boolean mPublicAddr,
+                AttributionSource source) {
+            if (DBG) {
+                Log.d(TAG, "getDeviceGroup");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                service, source, "getDeviceGroup")) {
+                return null;
+            }
+            return (service.getCoordinatedSet(setId, mPublicAddr));
+        }
+
+        @Override
+        public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid,
+                boolean mPublicAddr, AttributionSource source) {
+            if (DBG) {
+                Log.d(TAG, "getRemoteDeviceGroupId");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkConnectPermissionForDataDelivery(
+                service, source, "getRemoteDeviceGroupId")) {
+                return INVALID_SET_ID;
+            }
+            return service.getRemoteDeviceGroupId(device, uuid, mPublicAddr);
+        }
+
+        @Override
+        public boolean isGroupDiscoveryInProgress (int setId, AttributionSource source) {
+            if (DBG) {
+                Log.d(TAG, "isGroupDiscoveryInProgress");
+            }
+            GroupService service = getService();
+            if (service == null || !Utils.checkScanPermissionForDataDelivery(
+                service, source, "isGroupDiscoveryInProgress")) {
+                return false;
+            }
+            return service.isSetDiscoveryInProgress(setId);
+        }
+    };
+
+    /**
+     * DeathReceipient handler to unregister applications those are
+     * disconnected ungracefully (ie. crash or forced close).
+     */
+    class GroupAppDeathRecipient implements IBinder.DeathRecipient {
+        int mAppId;
+
+        GroupAppDeathRecipient(int appId) {
+            mAppId = appId;
+            Log.i(TAG, "GroupAppDeathRecipient");
+        }
+
+        @Override
+        public void binderDied() {
+            if (DBG) {
+                Log.d(TAG, "Binder is dead - unregistering app (" + mAppId + ")!");
+            }
+
+            mAppMap.remove(mAppId);
+            unregisterGroupClientApp(mAppId);
+        }
+
+    }
+
+    /* for registration of other Bluetooth profile in Bluetooth App Space*/
+    public void registerGroupClientModule(BluetoothGroupCallback callback) {
+        Log.d(TAG, "registerGroupClientModule");
+
+        UUID uuid;
+
+        if (mGroupNativeInterface == null) return;
+        // Generate an unique UUID for Bluetooth Modules which is not used by others apps
+        do {
+            uuid = UUID.randomUUID();
+        } while(mAppMap.appUuids.contains(uuid));
+
+        registerGroupClientApp(uuid, null, callback);
+    }
+
+    /* Registers CSIP App or module with CSIP native layer */
+    public void registerGroupClientApp(UUID uuid, IBluetoothGroupCallback appCb,
+            BluetoothGroupCallback localCallback) {
+        if (DBG) {
+            Log.d(TAG, "registerGroupClientApp: UUID = " + uuid.toString());
+        }
+
+        boolean isLocal = false;
+        if (localCallback != null) {
+            isLocal = true;
+        }
+
+        mAppMap.add(uuid, isLocal, appCb, localCallback);
+        mGroupNativeInterface.registerCsipApp(uuid.getLeastSignificantBits(),
+                uuid.getMostSignificantBits());
+    }
+
+    /* Unregisters Bluetooth module (BT profile) with CSIP*/
+    public void unregisterGroupClientModule(int appId) {
+        unregisterGroupClientApp(appId);
+    }
+
+    /* Unregisters App/Module with CSIP*/
+    public void unregisterGroupClientApp(int appId) {
+        if (DBG) {
+            Log.d(TAG, "unregisterGroupClientApp: appId = " + appId);
+        }
+
+        if (mGroupNativeInterface == null) return;
+        mAppMap.remove(appId);
+        mGroupNativeInterface.unregisterCsipApp(appId);
+    }
+
+    /* API to request change in lock value */
+    public void setLockValue(int appId, int setId, List<BluetoothDevice> devices,
+                   int value) {
+        if (DBG) {
+            Log.d(TAG, "setExclusiveAccess: appId = " + appId + ", setId: " + setId +
+                    ", value = " + value + ", set Members = " + devices);
+        }
+
+        if (mGroupNativeInterface == null) return;
+        // appId and setId validation is done at stack layer
+        mGroupNativeInterface.setLockValue(appId, setId, devices, value);
+    }
+
+    /* Starts the set members discovery for the requested coordinated set */
+    public void startSetDiscovery(int appId, int setId) throws RemoteException {
+        if (DBG) {
+            Log.d(TAG, "startGroupDiscovery. setId = " + setId + " Initiating appId = " + appId);
+        }
+
+        // Get Apllication details
+        GroupAppMap.GroupClientApp app = mAppMap.getById(appId);
+        if (app == null) {
+            Log.e(TAG, "Application not found for appId: " + appId);
+            return;
+        }
+
+        DeviceGroup cSet = getCoordinatedSet(setId, true);
+        if (cSet == null || !setSirkMap.containsKey(setId)) {
+            Log.e(TAG, "Invalid Group Id: " + setId);
+            mCurrentSetDisc = null;
+            app.appCb.onGroupDiscoveryStatusChanged(setId, BluetoothDeviceGroup.GROUP_DISCOVERY_STOPPED,
+                    BluetoothDeviceGroup.DISCOVERY_NOT_STARTED_INVALID_PARAMS);
+            return;
+        }
+
+        /* check if all set members are already discovered */
+        int setSize = cSet.getDeviceGroupSize();
+        if (setSize != 0 && cSet.getTotalDiscoveredGroupDevices() >= setSize) {
+            app.appCb.onGroupDiscoveryStatusChanged(setId,
+                    BluetoothDeviceGroup.GROUP_DISCOVERY_STOPPED,
+                    BluetoothDeviceGroup.DISCOVERY_COMPLETED);
+            return;
+        }
+
+        if (mCurrentSetDisc != null && mCurrentSetDisc.mDiscInProgress) {
+            Log.e(TAG, "Group Discovery is already in Progress for Group: "
+                + mCurrentSetDisc.mSetId + " from AppId: " + mCurrentSetDisc.mAppId
+                + " Stop current Group discovery");
+            mPendingSetDisc = new SetDiscoveryRequest(appId, setId, false);
+            mGroupScanner.stopSetDiscovery(mCurrentSetDisc.mSetId, 0);
+            return;
+        } else if (mCurrentSetDisc == null) {
+            mCurrentSetDisc = new SetDiscoveryRequest(appId, setId, false);
+        }
+
+        int transport;
+        byte[] sirk;
+
+        sirk = setSirkMap.get(setId);
+
+        /*TODO: Optimize logic if device type is UNKNOWN */
+        try {
+            BluetoothDevice device = cSet.getDeviceGroupMembers().get(0);
+            transport = device.getType();
+        } catch (IndexOutOfBoundsException e) {
+            Log.e(TAG, "Invalid Group- No device found : " + e);
+            mCurrentSetDisc = null;
+            return;
+        }
+
+        mGroupScanner.startSetDiscovery(setId, sirk, transport,
+                cSet.getDeviceGroupSize(), cSet.getDeviceGroupMembers());
+        mCurrentSetDisc.mDiscInProgress = true;
+
+        try {
+            if (app.appCb != null) {
+                app.appCb.onGroupDiscoveryStatusChanged(setId,
+                        BluetoothDeviceGroup.GROUP_DISCOVERY_STARTED,
+                        BluetoothDeviceGroup.DISCOVERY_STARTED_BY_APPL);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception : " + e);
+        }
+    }
+
+    /* Stops the set members discovery for the requested coordinated set */
+    public void stopSetDiscovery(int appId, int setId) {
+        if (DBG) {
+            Log.d(TAG, "stopGroupDiscovery: appId = " + appId + " groupId = " + setId);
+        }
+
+        // Get Apllication details
+        GroupAppMap.GroupClientApp app = mAppMap.getById(appId);
+
+        if (app == null) {
+            Log.e(TAG, "Application not found for appId: " + appId);
+            return;
+        }
+
+        // check if requesting app is stopping the discovery
+        if (mCurrentSetDisc == null || mCurrentSetDisc.mAppId != appId) {
+            Log.e(TAG, " Either no discovery in progress or Stop Request from"
+                   + " App which has not started Group Discovery");
+            return;
+        }
+
+        mGroupScanner.stopSetDiscovery(setId, BluetoothDeviceGroup.DISCOVERY_STOPPED_BY_APPL);
+    }
+
+    /* Reading lock status of coordinated set for ordered access procedure */
+    public void getLockStatus(int setId, List<BluetoothDevice> devices) {
+        //TODO: Future enhancement
+    }
+
+    /* To connect to Coordinated Set Device */
+    public void connect (int appId, BluetoothDevice device) {
+        Log.d(TAG, "connect Device: " + device + ", appId: " + appId);
+        if (mGroupNativeInterface == null) return;
+        mGroupNativeInterface.connectSetDevice(appId, device);
+    }
+
+    /* To disconnect from Coordinated Set Device */
+    public void disconnect (int appId, BluetoothDevice device) {
+        Log.d(TAG, "disconnect Device: " + device + ", appId: " + appId);
+        if (mGroupNativeInterface == null) return;
+        mGroupNativeInterface.disconnectSetDevice(appId, device);
+    }
+
+    public List<DeviceGroup> getDiscoveredCoordinatedSets() {
+        return getDiscoveredCoordinatedSets(true);
+    }
+
+    /* returns all discovered coordinated sets  */
+    public List<DeviceGroup> getDiscoveredCoordinatedSets(boolean mPublicAddr) {
+        if (DBG) {
+            Log.d(TAG, "getDiscoveredGroups");
+        }
+
+        /* Add logic to replace random addresses to public addresses if requested */
+        // Iterate on coordinated sets
+        // check address type. Replace with public if requested
+        if (mPublicAddr) {
+            List<DeviceGroup> coordinatedSets = new ArrayList<DeviceGroup>();
+            AdapterService adapterService = Objects.requireNonNull(
+                    AdapterService.getAdapterService(),
+                    "AdapterService cannot be null");
+            for (DeviceGroup set: mCoordinatedSets) {
+                DeviceGroup cSet = new DeviceGroup(
+                            set.getDeviceGroupId(), set.getDeviceGroupSize(),
+                            new ArrayList<BluetoothDevice>(),
+                            set.getIncludingServiceUUID(), set.isExclusiveAccessSupported());
+                for (BluetoothDevice device: set.getDeviceGroupMembers()) {
+                    BluetoothDevice publicDevice = device;
+                    if (adapterService.isIgnoreDevice(device)) {
+                        publicDevice = adapterService.getIdentityAddress(device);
+                    }
+                    cSet.getDeviceGroupMembers().add(publicDevice);
+                }
+                coordinatedSets.add(cSet);
+            }
+            return coordinatedSets;
+        }
+
+        return mCoordinatedSets;
+    }
+
+    public static DeviceGroup getCoordinatedSet(int setId) {
+        return getCoordinatedSet(setId, true);
+    }
+
+    /* returns requested coordinated set */
+    public static DeviceGroup getCoordinatedSet(int setId, boolean mPublicAddr) {
+        if (DBG) {
+            Log.d(TAG, "getDeviceGroup : groupId = " + setId
+                    + " mPublicAddr: " + mPublicAddr);
+        }
+
+        AdapterService adapterService = Objects.requireNonNull(
+                AdapterService.getAdapterService(), "AdapterService cannot be null");
+
+        for (DeviceGroup cSet: mCoordinatedSets) {
+            if (cSet.getDeviceGroupId() == setId) {
+                if (!mPublicAddr) {
+                    return cSet;
+
+                // Public addresses are requested. Replace address with public addr
+                } else {
+                    DeviceGroup set = new DeviceGroup(
+                            cSet.getDeviceGroupId(), cSet.getDeviceGroupSize(),
+                            new ArrayList<BluetoothDevice>(),
+                            cSet.getIncludingServiceUUID(), cSet.isExclusiveAccessSupported());
+                    for (BluetoothDevice device: cSet.getDeviceGroupMembers()) {
+                        if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
+                            BluetoothDevice publicDevice = device;
+                            if (adapterService.isIgnoreDevice(device)) {
+                                publicDevice = adapterService.getIdentityAddress(device);
+                            }
+                            set.getDeviceGroupMembers().add(publicDevice);
+                        }
+                    }
+                    return set;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public boolean isSetDiscoveryInProgress (int setId) {
+        if (DBG) {
+            Log.d(TAG, "isGroupDiscoveryInProgress: groupId = " + setId);
+        }
+
+        if (mCurrentSetDisc != null && mCurrentSetDisc.mSetId == setId
+                && mCurrentSetDisc.mDiscInProgress)
+            return true;
+        return false;
+    }
+
+    public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid) {
+        return getRemoteDeviceGroupId(device, uuid, true);
+    }
+
+    public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid,
+            boolean mPublicAddr) {
+        if (DBG) {
+            Log.d(TAG, "getRemoteDeviceGroupId: device = " + device + " uuid = " + uuid
+                    + ", mPublicAddr = " + mPublicAddr);
+        }
+
+        if (mAdapterService == null) {
+            Log.e(TAG, "AdapterService instance is NULL. Return.");
+            return INVALID_SET_ID;
+        }
+
+        BluetoothDevice setDevice = null;
+        if (mPublicAddr && mAdapterService.isIgnoreDevice(device)) {
+            setDevice = mAdapterService.getIdentityAddress(device);
+        }
+
+        if (uuid == null) {
+            uuid = new ParcelUuid(EMPTY_UUID);
+        }
+
+        for (DeviceGroup cSet: mCoordinatedSets) {
+            if ((cSet.getDeviceGroupMembers().contains(device) ||
+                    cSet.getDeviceGroupMembers().contains(setDevice))
+                    && cSet.getIncludingServiceUUID().equals(uuid)) {
+                return cSet.getDeviceGroupId();
+            }
+        }
+
+        return INVALID_SET_ID;
+    }
+
+    /* This API is called when pairing with LE Audio capable set member fails or
+     * when set member is unpaired. Removing the set member from list gives option
+     * to user to rediscover it */
+    public void removeSetMemberFromCSet(int setId, BluetoothDevice device) {
+        Log.d(TAG, "removeDeviceFromDeviceGroup: setId = " + setId + ", Device: " + device);
+
+        DeviceGroup cSet = getCoordinatedSet(setId, false);
+        if (cSet != null) {
+            cSet.getDeviceGroupMembers().remove(device);
+            if (cSet.getDeviceGroupMembers().size() == 0) {
+                Log.i(TAG, "Last device unpaired. Removing Device Group from database");
+                mCoordinatedSets.remove(cSet);
+                return;
+            }
+        }
+
+        cSet = getCoordinatedSet(setId, true);
+        if (cSet != null) {
+            cSet.getDeviceGroupMembers().remove(device);
+            if (cSet.getDeviceGroupMembers().size() == 0) {
+                Log.i(TAG, "Last device unpaired. Removing Device Group from database");
+                mCoordinatedSets.remove(cSet);
+            }
+        }
+    }
+
+    public void printAllCoordinatedSets() {
+        if (VDBG) {
+            for (DeviceGroup set: mCoordinatedSets) {
+                Log.i(TAG, "GROUP_ID: " + set.getDeviceGroupId()
+                    + ", size = " + set.getDeviceGroupSize()
+                    + ", discovered = " + set.getTotalDiscoveredGroupDevices()
+                    + ", Including Srvc Uuid = "+ set.getIncludingServiceUUID()
+                    + ", devices = " + set.getDeviceGroupMembers());
+            }
+        }
+    }
+
+    /* Callback received from CSIP native layer when an APP/module has been registered */
+    protected void onCsipAppRegistered (int status, int appId, UUID uuid) {
+        Log.d(TAG, "onGroupClientAppRegistered: appId: " + appId + ", UUID: " + uuid.toString());
+
+        GroupAppMap.GroupClientApp app = mAppMap.getByUuid(uuid);
+
+        if (app == null) {
+            Log.e(TAG, "Application not found for UUID: " + uuid.toString());
+            return;
+        }
+
+        app.appId = appId;
+        // Give callback to the application that app has been registered
+        try {
+            if (app.isRegistered && app.isLocal) {
+                app.mCallback.onGroupClientAppRegistered(status, appId);
+            } else if (app.isRegistered && !app.isLocal) {
+                app.linkToDeath(new GroupAppDeathRecipient(appId));
+                app.appCb.onGroupClientAppRegistered(status, appId);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception : " + e);
+        }
+    }
+
+    /* When CSIP Profile connection state has been changed */
+    protected void onConnectionStateChanged(int appId, BluetoothDevice device,
+                int state, int status) {
+        Log.d(TAG, "onConnectionStateChanged: appId: " + appId + ", device: " + device
+                + ", State: " + state + ", Status: " + status);
+
+        GroupAppMap.GroupClientApp app = mAppMap.getById(appId);
+
+        if (app == null) {
+            Log.e(TAG, "Application not found for appId: " + appId);
+            return;
+        }
+
+        try {
+            if (app.isRegistered && app.isLocal) {
+                app.mCallback.onConnectionStateChanged(state, device);
+            } else if (app.isRegistered && !app.isLocal) {
+                app.appCb.onConnectionStateChanged(state, device);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception : " + e);
+        }
+    }
+
+    /* When a new set member is discovered as a part of Set Discovery procedure */
+    protected void onSetMemberFound (int setId, BluetoothDevice device) {
+        Log.d(TAG, "onGroupDeviceFound: groupId: " + setId + ", device: " + device);
+        for (DeviceGroup cSet: mCoordinatedSets) {
+            if (cSet.getDeviceGroupId() == setId
+                    && !cSet.getDeviceGroupMembers().contains(device)) {
+                cSet.getDeviceGroupMembers().add(device);
+                break;
+            }
+        }
+
+        // Give callback to adapterservice to initiate bonding if required
+        mAdapterService.processGroupMember(setId, device);
+
+        // Give callback to the application that started Set Discovery
+        GroupAppMap.GroupClientApp app = mAppMap.getById(mCurrentSetDisc.mAppId);
+
+        if (app == null) {
+            Log.e(TAG, "Application not found for appId: " + mCurrentSetDisc.mAppId);
+            return;
+        }
+
+        try {
+            if (app.appCb != null) {
+                app.appCb.onGroupDeviceFound(setId, device);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception : " + e);
+        }
+    }
+
+    /* When set discovery procedure has been completed */
+    protected void onSetDiscoveryCompleted (int setId,
+            int totalDiscovered, int reason) {
+        Log.d(TAG, "onGroupDiscoveryCompleted: groupId: " + setId + ", totalDiscovered = "
+                + totalDiscovered + "reason: " + reason);
+
+        // mark Set Discovery procedure as completed
+        mCurrentSetDisc.mDiscInProgress = false;
+        // Give callback to the application that Set Discovery has been completed
+        GroupAppMap.GroupClientApp app = mAppMap.getById(mCurrentSetDisc.mAppId);
+
+        if (app == null) {
+            Log.e(TAG, "Application not found for appId: " + mCurrentSetDisc.mAppId);
+            return;
+        }
+
+        try {
+            if (app.appCb != null) {
+                app.appCb.onGroupDiscoveryStatusChanged(setId,
+                        BluetoothDeviceGroup.GROUP_DISCOVERY_STOPPED, reason);
+            }
+
+            DeviceGroup cSet = getCoordinatedSet(setId, false);
+            if (VDBG && cSet != null) {
+                Log.i(TAG, "Device Group: groupId" + setId + ", devices: "
+                        + cSet.getDeviceGroupMembers());
+            }
+
+            if (mPendingSetDisc != null) {
+                mCurrentSetDisc = mPendingSetDisc;
+                mPendingSetDisc = null;
+                startSetDiscovery(mCurrentSetDisc.mAppId, mCurrentSetDisc.mSetId);
+            } else {
+                mCurrentSetDisc = null;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception : " + e);
+        }
+    }
+
+    /* Callback received from CSIP native layer when a new Coordinated set has been
+     * identified with remote device */
+    protected void onNewSetFound(int setId, BluetoothDevice device, int size,
+            byte[] sirk, UUID pSrvcUuid, boolean lockSupport) {
+        Log.d(TAG, "onNewGroupFound: Address : " + device + ", groupId: " + setId
+                + ", size: " + size + ", uuid: " + pSrvcUuid.toString());
+
+        // Form Coordinated Set Object and store in ArrayList
+        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+        devices.add(device);
+        DeviceGroup cSet = new DeviceGroup(setId, size, devices,
+                new ParcelUuid(pSrvcUuid), lockSupport);
+        mCoordinatedSets.add(cSet);
+
+        // Store sirk in hashmap of setId, sirk
+        setSirkMap.put(setId, sirk);
+
+        // Give Callback to all registered application
+        try {
+            for (GroupAppMap.GroupClientApp app: mAppMap.mApps) {
+                if (app.isRegistered && !app.isLocal) {
+                     if (app.appCb != null)//temp check
+                    app.appCb.onNewGroupFound(setId, device, new ParcelUuid(pSrvcUuid));
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception : " + e);
+        }
+    }
+
+    /* Callback received from CSIP native layer when undiscovered set member is connected */
+    protected void onNewSetMemberFound (int setId, BluetoothDevice device) {
+        Log.d(TAG, "onNewGroupDeviceFound: groupId = " + setId + ", Device = " + device);
+
+        if (mAdapterService == null) {
+            Log.e(TAG, "AdapterService instance is NULL. Return.");
+            return;
+        }
+
+        if (mAdapterService.isIgnoreDevice(device)) {
+            device = mAdapterService.getIdentityAddress(device);
+        }
+        // check if this device is already part of an already existing coordinated set
+        /* Scenario: When a device was not discovered during initial set discovery
+         *           procedure and later user had explicitely paired with this device
+         *           from pair new device UI option. Not required to send onSetMemberFound
+         *           callback to application*/
+        if (setSirkMap.containsKey(setId)) {
+            for (DeviceGroup cSet: mCoordinatedSets) {
+                if (cSet.getDeviceGroupId() == setId &&
+                        (!cSet.getDeviceGroupMembers().contains(device))) {
+                    cSet.getDeviceGroupMembers().add(device);
+                    break;
+                }
+            }
+            return;
+        }
+    }
+
+    /* callback received when lock status is changed for requested coordinated set*/
+    protected void onLockStatusChanged (int appId, int setId, int value, int status,
+            List<BluetoothDevice> devices) {
+        Log.d(TAG, "onExclusiveAccessChanged: appId = " + appId + ", groupId = " + setId +
+                ", value = " + value + ", status = " + status + ", devices = " + devices);
+        GroupAppMap.GroupClientApp app = mAppMap.getById(appId);
+
+        if (app == null) {
+            Log.e(TAG, "Application not found for appId: " + appId);
+            return;
+        }
+
+        try {
+            if (app.isRegistered && app.isLocal) {
+                app.mCallback.onExclusiveAccessChanged(setId, value, status, devices);
+            } else if (app.isRegistered && !app.isLocal) {
+                app.appCb.onExclusiveAccessChanged(setId, value, status, devices);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception : " + e);
+        }
+    }
+
+    /* Callback received when earlier denied lock is now available */
+    protected void onLockAvailable (int appId, int setId, BluetoothDevice device) {
+        Log.d(TAG, "onExclusiveAccessAvailable: Remote(" + device + "), Group Id: "
+                + setId + ", App Id: " + appId);
+
+        GroupAppMap.GroupClientApp app = mAppMap.getById(appId);
+        if (app == null) {
+            Log.e(TAG, "Application not found for appId: " + appId);
+            return;
+        }
+
+        try {
+            if (app.isRegistered && app.isLocal) {
+                app.mCallback.onExclusiveAccessAvailable(setId, device);
+            } else if (app.isRegistered && !app.isLocal) {
+                app.appCb.onExclusiveAccessAvailable(setId, device);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception : " + e);
+        }
+
+    }
+
+    /* Callback received when set size has been changed */
+    /* TODO: Scanarios are unknown. Actions are to be decided */
+    protected void onSetSizeChanged (int setId, int size, BluetoothDevice device) {
+        Log.d(TAG, "onGroupSizeChanged: Group Id: " + setId + ", New Size: " + size +
+          ", Notifying device: " + device);
+
+        // TODO: Logic to be incorporated once use case is understood
+    }
+
+    /* Callback received when set SIRK has been changed */
+    /* TODO: Scanarios are unknown. Actions are to be decided */
+    protected void onSetSirkChanged(int setId, byte[] sirk, BluetoothDevice device) {
+        Log.d(TAG, "onGroupIdChanged Group Id: " + setId + ", Notifying device: " + device);
+
+        // TODO: Logic to be incorporated once use case is understood
+    }
+
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/mcp/McpNativeInterface.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/mcp/McpNativeInterface.java
new file mode 100644
index 0000000..b850ce9
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/mcp/McpNativeInterface.java
@@ -0,0 +1,274 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.mcp;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Mcp Native Interface to/from JNI.
+ */
+public class McpNativeInterface {
+    private static final String TAG = "McpNativeInterface";
+    private static final boolean DBG = true;
+    private BluetoothAdapter mAdapter;
+    @GuardedBy("INSTANCE_LOCK")
+    private static McpNativeInterface sInstance;
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static {
+        classInitNative();
+    }
+
+    private McpNativeInterface() {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mAdapter == null) {
+            Log.wtfStack(TAG, "No Bluetooth Adapter Available");
+        }
+    }
+
+    /**
+     * Get singleton instance.
+     */
+    public static McpNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new McpNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Initializes the native interface.
+     *
+     * priorities to configure.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void init() {
+        initNative();
+    }
+
+    /**
+     * Cleanup the native interface.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void cleanup() {
+        cleanupNative();
+    }
+
+
+    /**
+     * update MCP media supported feature
+     * @param feature
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean mediaControlPointOpcodeSupported(int feature) {
+        return mediaControlPointOpcodeSupportedNative(feature);
+    }
+
+    /**
+     * update MCP media supported feature current value
+     * @param value
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean mediaControlPoint(int value) {
+        return mediaControlPointNative(value);
+    }
+
+  /**
+     * Sets the Mcp media state
+     * @param state
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean mediaState(int state) {
+        return mediaStateNative(state);
+    }
+
+  /**
+     * update MCP media player name
+     * @param player name
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean mediaPlayerName(String playeName) {
+        return mediaPlayerNameNative(playeName);
+    }
+  /**
+     * update track change notification
+     * @param track id
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean trackChanged(int status) {
+        return trackChangedNative(status);
+    }
+  /**
+     * update MCP track position
+     * @param position
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean trackPosition(int position) {
+        return trackPositionNative(position);
+    }
+
+  /**
+     * update MCP track duration
+     * @param duration
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean trackDuration(int duration) {
+        return trackDurationNative(duration);
+    }
+  /**
+     * update MCP track title
+     * @param title
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean trackTitle(String title) {
+        return trackTitleNative(title);
+    }
+  /**
+     * update playing order support of media
+     * @param order
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean playingOrderSupported(int order) {
+        return playingOrderSupportedNative(order);
+    }
+  /**
+     * update playing order value of media
+     * @param value
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean playingOrder(int value) {
+        return playingOrderNative(value);
+    }
+  /**
+     * update active device
+     * @param device
+     * @param setId
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean setActiveDevice(BluetoothDevice device, int setId, int profile) {
+        return setActiveDeviceNative(profile, setId, getByteAddress(device));
+    }
+    /**
+     * Sets Mcp media content control id
+     * @param ccid
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean contentControlId(int ccid) {
+
+        return contentControlIdNative(ccid);
+    }
+    /**
+     * Disconnect Mcp disconnect device
+     * @param device
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean disconnectMcp(BluetoothDevice device) {
+        return disconnectMcpNative(getByteAddress(device));
+    }
+
+    /**
+     * Disconnect Mcp disconnect device
+     * @param device
+     */
+  @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean bondStateChange(BluetoothDevice device, int state) {
+        return bondStateChangeNative(state, getByteAddress(device));
+    }
+
+    private BluetoothDevice getDevice(byte[] address) {
+        return mAdapter.getRemoteDevice(address);
+    }
+
+    private byte[] getByteAddress(BluetoothDevice device) {
+        if (device == null) {
+            return Utils.getBytesFromAddress("00:00:00:00:00:00");
+        }
+        return Utils.getBytesFromAddress(device.getAddress());
+    }
+
+    // Callbacks from the native stack back into the Java framework.
+    // All callbacks are routed via the Service which will disambiguate which
+    private void OnConnectionStateChanged(int state, byte[] address) {
+        if (DBG) {
+            Log.d(TAG, "OnConnectionStateChanged: " + state);
+        }
+        BluetoothDevice device = getDevice(address);
+
+        McpService service = McpService.getMcpService();
+        if (service != null)
+            service.onConnectionStateChanged(device, state);
+    }
+
+    private void MediaControlPointChangedRequest(int state, byte[] address) {
+        BluetoothDevice device = getDevice(address);
+        if (DBG) {
+            Log.d(TAG, "MediaControlPointChangedReq: " + state);
+        }
+        McpService service = McpService.getMcpService();
+        if (service != null)
+            service.onMediaControlPointChangeReq(device, state);
+    }
+
+    private void TrackPositionChangedRequest(int position) {
+        if (DBG) {
+           Log.d(TAG, "TrackPositionChangedRequest: " + position);
+        }
+    McpService service = McpService.getMcpService();
+    if (service != null)
+        service.onTrackPositionChangeReq(position);
+    }
+
+    private void PlayingOrderChangedRequest(int order) {
+        if (DBG) {
+           Log.d(TAG, "PlayingOrderChangedRequest: " + order);
+        }
+    McpService service = McpService.getMcpService();
+    if (service != null)
+        service.onPlayingOrderChangeReq(order);
+    }
+
+    // Native methods that call into the JNI interface
+    private static native void classInitNative();
+    private native void initNative();
+    private native void cleanupNative();
+    private native boolean mediaControlPointOpcodeSupportedNative(int feature);
+    private native boolean mediaControlPointNative(int value);
+    private native boolean mediaStateNative(int state);
+    private native boolean mediaPlayerNameNative(String playerName);
+    private native boolean trackChangedNative(int status);
+    private native boolean trackPositionNative(int position);
+    private native boolean trackDurationNative(int duration);
+    private native boolean trackTitleNative(String title);
+    private native boolean playingOrderSupportedNative(int order);
+    private native boolean playingOrderNative(int value);
+    private native boolean setActiveDeviceNative(int profile, int setId, byte[] address);
+    private native boolean contentControlIdNative(int ccid);
+    private native boolean disconnectMcpNative(byte[] address);
+    private native boolean bondStateChangeNative(int state, byte[] address);
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/mcp/McpService.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/mcp/McpService.java
new file mode 100644
index 0000000..3163382
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/mcp/McpService.java
@@ -0,0 +1,842 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.bluetooth.mcp;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import com.android.bluetooth.btservice.ProfileService;
+import android.bluetooth.BluetoothUuid;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.util.Log;
+import android.os.Message;
+import android.os.Binder;
+import android.os.IBinder;
+
+import com.android.bluetooth.BluetoothMetricsProto;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.apm.ActiveDeviceManagerService;
+import com.android.bluetooth.apm.ApmConst;
+import com.android.bluetooth.acm.AcmService;
+import com.android.bluetooth.btservice.MetricsLogger;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.internal.annotations.VisibleForTesting;
+import android.media.session.PlaybackState;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import com.android.bluetooth.apm.MediaControlManager;
+import android.view.KeyEvent;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import android.media.session.MediaSessionManager;
+import com.android.internal.util.ArrayUtils;
+/**
+ * Provides Bluetooth MCP profile as a service in the Bluetooth application.
+ * @hide
+ */
+public class McpService extends ProfileService {
+
+    private static final String TAG = "McpService";
+    private static final boolean DBG = true;
+    public static final int MUSIC_PLAYER_CONTROL = 28;
+    private static McpService sMcpService;
+    private BroadcastReceiver mBondStateChangedReceiver;
+
+    private BluetoothDevice mActiveDevice;
+    private AdapterService mAdapterService;
+    private McpNativeInterface mNativeInterface;
+    private static McpService sInstance = null;
+    private Context mContext;
+    private McsMessageHandler mHandler;
+    private int mMaxConnectedAudioDevices = 1;
+    private String mActiveMediaPlayerName = new String("");
+
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+                "com.android.bluetooth.mcp.action.CONNECTION_STATE_CHANGED";
+    //native event
+    static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
+    static final int EVENT_TYPE_MEDIA_CONTROL_POINT_CHANGED = 2;
+    static final int EVENT_TYPE_TRACK_POSITION_CHANGED = 3;
+    static final int EVENT_TYPE_PLAYING_ORDER_CHANGED = 4;
+    //MCP to JNI update
+    static final int MEDIA_STATE_UPDATE = 5;
+    static final int MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_UPDATE = 6;
+    static final int MEDIA_CONTROL_POINT_UPDATE = 7;
+    static final int MEDIA_PLAYER_NAME_UPDATE = 8;
+    static final int TRACK_CHANGED_UPDATE = 9;
+    static final int TRACK_TITLE_UPDATE = 10;
+    static final int TRACK_POSITION_UPDATE = 11;
+    static final int TRACK_DURATION_UPDATE = 12;
+    static final int PLAYING_ORDER_SUPPORT_UPDATE = 13;
+    static final int PLAYING_ORDER_UPDATE = 14;
+    static final int CONTENT_CONTROL_ID_UPDATE = 15;
+    static final int ACTIVE_DEVICE_CHANGE = 16;
+    static final int BOND_STATE_CHANGE = 17;
+    static final int MEDIA_CONTROL_MANAGER_INIT = 18;
+
+    static final int PLAYSTATUS_ERROR = -1;
+    static final int PLAYSTATUS_STOPPED = 0;
+    static final int PLAYSTATUS_PLAYING = 1;
+    static final int PLAYSTATUS_PAUSED = 2;
+    static final int PLAYSTATUS_SEEK = 3;
+
+
+    //super set of supported player supported feature
+    static final int MCP_MEDIA_CONTROL_SUP_PLAY          =     1<<0;
+    static final int MCP_MEDIA_CONTROL_SUP_PAUSE         =     1<<1;
+    static final int MCP_MEDIA_CONTROL_SUP_FAST_REWIND   =     1<<2;
+    static final int MCP_MEDIA_CONTROL_SUP_FAST_FORWARD  =     1<<3;
+    static final int MCP_MEDIA_CONTROL_SUP_STOP          =     1<<4;
+    static final int MCP_MEDIA_CONTROL_SUP_PREV_TRACK    =     1<<11;
+    static final int MCP_MEDIA_CONTROL_SUP_NEXT_TRACK    =     1<<12;
+
+    //media control point opcodes
+    static final int MCP_MEDIA_CONTROL_OPCODE_PLAY          =     0x01;
+    static final int MCP_MEDIA_CONTROL_OPCODE_PAUSE         =     0x02;
+    static final int MCP_MEDIA_CONTROL_OPCODE_FAST_REWIND   =     0x03;
+    static final int MCP_MEDIA_CONTROL_OPCODE_FAST_FORWARD  =     0x04;
+    static final int MCP_MEDIA_CONTROL_OPCODE_STOP          =     0x05;
+    static final int MCP_MEDIA_CONTROL_OPCODE_PREV_TRACK    =     0x30;
+    static final int MCP_MEDIA_CONTROL_OPCODE_NEXT_TRACK    =     0x31;
+
+    //as there is not supported api to fetch player details
+    static final int DEFAULT_MEDIA_PLAYER_SUPPORTED_FEATURE = 0x181F;
+    static final int DEFAULT_PLAYER_SUPPORTED_FEATURE = 0x0001; // Single once default
+    static final int DEFAULT_PLAYING_ORDER = 0x01; // Single Once default
+    private int mState = -1;
+    private int mCurrOpCode = -1;
+    private int mSupportedControlPoint = -1;
+    private int mControlPoint = -1;
+    private int mSupportedPlayingOrder = -1;
+    private int mPlayingOrder = -1;
+    private int mCcid = -1;
+    private int mTrackPosition = 0xFFFF;
+    private int mTrackDuration = 0xFFFF;
+    private String mPlayerName = null;
+    private String mTrackTitle = null;
+    private MediaControlManager mMediaControlManager;
+    private MediaSessionManager mMediaSessionManager;
+    /*private class MusicPlayerDetail {
+        private int state;
+        private int featureSupported;
+        private int mSetFeature;
+        private int playingOrderFeatureSupported;
+        private int currentPlayingOrder;
+        private int ccid;
+        private int mTrackPosition;
+        private int currentTrackDuration;
+        private String playerName;
+
+        public MusicPlayerDetail() {
+
+        }
+    };*/
+    //HashMap<MusicPlayerDetail, String> mMusicPlayerMap = new HashMap();
+    @Override
+    protected IProfileServiceBinder initBinder() {
+        return new McpBinder(this);
+    }
+
+    @Override
+    protected void create() {
+        Log.i(TAG, "create()");
+    }
+
+    @Override
+    protected void cleanup() {
+        Log.i(TAG, "cleanup()");
+    }
+
+    @Override
+    protected boolean start() {
+        Log.i(TAG, "start()");
+        if (sMcpService != null) {
+            Log.w(TAG, "McpService is already running");
+            return true;
+        }
+        if (DBG) {
+            Log.d(TAG, " Create McpService Instance");
+        }
+
+        mContext = this;
+        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+                "AdapterService cannot be null when McpService starts");
+        mNativeInterface = Objects.requireNonNull(McpNativeInterface.getInstance(),
+                "McpNativeInterface cannot be null when McpService starts");
+        // Step 2: Get maximum number of connected audio devices
+        mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
+        Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices);
+        //handle to synchronized tx and rx message
+        if (mHandler != null) {
+            mHandler = null;
+        }
+        HandlerThread thread = new HandlerThread("BluetoothMCSHandler");
+        thread.start();
+        Looper looper = thread.getLooper();
+        mHandler = new McsMessageHandler(looper);
+        mNativeInterface.init();
+        Log.d(TAG, "mcp native init done");
+        IntentFilter filter = new IntentFilter();
+
+        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mBondStateChangedReceiver = new BondStateChangedReceiver();
+        mContext.registerReceiver(mBondStateChangedReceiver, filter);
+        setMcpService(this);
+        mMediaSessionManager = (MediaSessionManager) this.getSystemService(
+            this.MEDIA_SESSION_SERVICE);
+        //MediaControlManager.make(this);
+        Message msg = mHandler.obtainMessage();
+        msg.what = MEDIA_CONTROL_MANAGER_INIT;
+        msg.obj = this;
+        mHandler.sendMessageDelayed(msg, 100);
+        return true;
+    }
+
+    @Override
+    protected boolean stop() {
+        Log.i(TAG, "stop()");
+        if (sMcpService == null) {
+           Log.w(TAG, "stop() called before start()");
+           return true;
+        }
+        // Step 8: Mark service as stopped
+        setMcpService(null);
+        // Cleanup native interface
+        mNativeInterface.cleanup();
+        mNativeInterface = null;
+        mContext.unregisterReceiver(mBondStateChangedReceiver);
+        // Clear AdapterService
+        mAdapterService = null;
+        mMaxConnectedAudioDevices = 1;
+        return true;
+    }
+
+    private static void setMcpService(McpService instance) {
+        if (DBG) {
+            Log.d(TAG, "setMcpService(): set to: " + instance);
+        }
+        sMcpService = instance;
+    }
+   /**
+   * Get the McpService instance
+   * @return McpService instance
+   */
+
+    public synchronized static  McpService getMcpService() {
+        if (sMcpService == null) {
+            Log.w(TAG, "getMcpService(): service is null");
+            return null;
+        }
+        return sMcpService;
+    }
+
+    public synchronized static void clearMcpInstance () {
+        Log.v(TAG, "clearing MCP instatnce");
+        sInstance = null;
+        Log.v(TAG, "After clearing MCP instatnce ");
+    }
+
+     public synchronized boolean MediaControlPointOpcodeUpdate(int feature) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_UPDATE;
+        msg.arg1 = feature;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    public synchronized boolean MediaControlPointUpdate(int value) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = MEDIA_CONTROL_POINT_UPDATE;
+        msg.arg1 = value;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized boolean MediaStateUpdate(int state) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = MEDIA_STATE_UPDATE;
+        msg.arg1 = state;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized boolean MediaPlayerNameUpdate(String name) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = MEDIA_PLAYER_NAME_UPDATE;
+        msg.obj = name;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized boolean PlayingOrderSupportedUpdate(int support) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = PLAYING_ORDER_SUPPORT_UPDATE;
+        msg.arg1 = support;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized boolean PlayingOrderUpdate(int support) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = PLAYING_ORDER_UPDATE;
+        msg.arg1 = support;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized boolean TrackChangedUpdate(int status) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = TRACK_CHANGED_UPDATE;
+        msg.arg1 = status;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized boolean TrackTitleUpdate(String title) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = TRACK_TITLE_UPDATE;
+        msg.obj = title;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized boolean TrackDurationUpdate(int duration) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = TRACK_DURATION_UPDATE;
+        msg.arg1 = duration;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized boolean TrackPositionUpdate(int position) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = TRACK_POSITION_UPDATE;
+        msg.arg1 = position;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized boolean ContentControlID(int ccid) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = CONTENT_CONTROL_ID_UPDATE;
+        msg.arg1 = ccid;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private synchronized int convertPlayStateToPlayStatus(PlaybackState state) {
+        int playStatus = PLAYSTATUS_ERROR;
+        switch (state.getState()) {
+            case PlaybackState.STATE_PLAYING:
+                playStatus = PLAYSTATUS_PLAYING;
+                break;
+
+            case PlaybackState.STATE_CONNECTING:
+            case PlaybackState.STATE_NONE:
+                playStatus = PLAYSTATUS_STOPPED;
+                break;
+
+            case PlaybackState.STATE_PAUSED:
+            case PlaybackState.STATE_BUFFERING:
+            case PlaybackState.STATE_STOPPED:
+                playStatus = PLAYSTATUS_PAUSED;
+                break;
+
+            case PlaybackState.STATE_FAST_FORWARDING:
+            case PlaybackState.STATE_SKIPPING_TO_NEXT:
+            case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM:
+            case PlaybackState.STATE_REWINDING:
+            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+                playStatus = PLAYSTATUS_SEEK;
+                break;
+
+            case PlaybackState.STATE_ERROR:
+                playStatus = PLAYSTATUS_ERROR;
+                break;
+
+        }
+        return playStatus;
+    }
+
+
+    /**
+     * Get the active device.
+     *
+     * @return the active device or null if no device is active
+     */
+    public synchronized BluetoothDevice getActiveDevice() {
+        return mActiveDevice;
+    }
+
+    public synchronized int getControlContentID() {
+        int ccid = 1;
+        return ccid;
+    }
+
+    public synchronized void onConnectionStateChanged(BluetoothDevice device, int status) {
+        Log.v(TAG, "onConnectionStateChanged: address=" + device.toString());
+        if (status == 0)
+            return;
+        Message msg = mHandler.obtainMessage();
+        msg.what = EVENT_TYPE_CONNECTION_STATE_CHANGED;
+        msg.obj = device;
+        msg.arg2 = status;
+        mHandler.sendMessage(msg);
+        return;
+    }
+
+    public synchronized boolean onMediaControlPointChangeReq(BluetoothDevice device, int state) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = EVENT_TYPE_MEDIA_CONTROL_POINT_CHANGED;
+        msg.obj = device;
+        msg.arg1 = state;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    public synchronized boolean onTrackPositionChangeReq(int position) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = EVENT_TYPE_TRACK_POSITION_CHANGED;
+        msg.arg1 = position;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    public synchronized boolean onPlayingOrderChangeReq(int order) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = EVENT_TYPE_PLAYING_ORDER_CHANGED;
+        msg.arg1 = order;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    public synchronized boolean SetActiveDevices(BluetoothDevice device, int profile) {
+        Message msg = mHandler.obtainMessage();
+        msg.what = ACTIVE_DEVICE_CHANGE;
+        msg.obj = device;
+        msg.arg1 = 0; //<TBD> it will use to send mark two earbud address in one gp
+        msg.arg2 = profile;
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    public synchronized boolean OnMediaPlayerUpdate(int feature, int state,
+               int playingOrderSupport, int playingOrder, String playerName) {
+        Log.w(TAG, "OnMediaPlayerUpdate for player " + playerName);
+        ContentControlID(getControlContentID());
+        /*
+        if (mMusicPlayerMap.containsKey(playerName)) {
+            Log.v(TAG, "Player is already there");
+        } else {
+            newPlayer =  new MusicPlayerDetail();
+            mMusicPlayerMap.add(newPlayer, playerName);
+        }*/
+
+        MediaPlayerNameUpdate(playerName);
+        MediaStateUpdate(state);
+        //added default value as there is no api for gettiting supported feature from player
+        MediaControlPointOpcodeUpdate(DEFAULT_MEDIA_PLAYER_SUPPORTED_FEATURE);
+        PlayingOrderSupportedUpdate(DEFAULT_PLAYER_SUPPORTED_FEATURE);
+        PlayingOrderUpdate(DEFAULT_PLAYING_ORDER);
+        return true;
+    }
+
+    public synchronized boolean OnMediaStateUpdate(int state) {
+        Log.w(TAG, "OnMediaStateUpdate state " + state);
+        MediaStateUpdate(state);
+        return true;
+    }
+
+    public synchronized boolean OnTrackUpdate(int status, int duration, String title) {
+        Log.w(TAG, "OnTrackUpdate title " + title + " duration " + duration);
+        TrackChangedUpdate(status);
+        if (status != 0) {
+            TrackTitleUpdate(title);
+            TrackDurationUpdate(duration);
+        }
+        return true;
+    }
+
+    public synchronized boolean OnTrackPositionUpdate(int position) {
+        Log.w(TAG, "OnTrackPositionUpdate position " + position);
+        TrackPositionUpdate(position);
+        return true;
+    }
+
+    public synchronized boolean OnPlayingOrderUpdate(int order) {
+        Log.w(TAG, "OnPlayingOrderUpdate order " + order);
+        PlayingOrderUpdate(order);
+        return true;
+    }
+    //As apm is not implemented callback for apm above mention function
+    //implemented workaround
+    public synchronized void updateMetaData(MediaMetadata data) {
+        Log.w(TAG, "updateMetaData data " + data);
+        if (data == null) {
+            return;
+        }
+        //length //TBD to convert into int
+        int duration = (int)data.getLong(MediaMetadata.METADATA_KEY_DURATION);
+        String title = data.getString(MediaMetadata.METADATA_KEY_TITLE);
+        if (title != null && !(title.equals(mTrackTitle))) {
+            TrackChangedUpdate(1);
+            TrackTitleUpdate(title);
+        }
+        if (duration != mTrackDuration)
+            TrackDurationUpdate(duration);
+    }
+
+    public synchronized void updatePlaybackState(PlaybackState playbackState) {
+        Log.w(TAG, "updatePlaybackState state " + playbackState);
+        int state = (int)convertPlayStateToPlayStatus(playbackState);
+
+        if (state != PLAYSTATUS_ERROR && mState != state) {
+            if (state == PLAYSTATUS_STOPPED)
+                state = PLAYSTATUS_PAUSED;
+            MediaStateUpdate(state);
+            if (mCurrOpCode != -1) {
+                MediaControlPointUpdate(mCurrOpCode);
+                mCurrOpCode = -1;
+            }
+        }
+        int position = (int)playbackState.getPosition();
+
+        if (position != mTrackPosition)
+            TrackPositionUpdate(position);
+        float speed = playbackState.getPlaybackSpeed(); //for playback speed
+    }
+
+    public synchronized void updatePlayerName(String packageName, boolean removed) {
+        Log.w(TAG, "updatePlayerName pkg " + packageName + " removed " + removed);
+        String name = null;
+        boolean changed = true;
+        int tCcid = 0;
+        int tPlayersupport = 0;
+        int tMediasupport = 0;
+        if ((removed && packageName == null ) ||
+            removed && packageName.equals(mPlayerName)) {
+            //no active media player
+            MediaStateUpdate(PLAYSTATUS_STOPPED);
+            name = new String("");
+        } else if (packageName != null && !packageName.equals(mPlayerName)) {
+            name = packageName;
+            tCcid = getControlContentID();
+            tPlayersupport = DEFAULT_PLAYER_SUPPORTED_FEATURE;
+            tMediasupport = DEFAULT_MEDIA_PLAYER_SUPPORTED_FEATURE;
+        } else {
+            Log.d(TAG, "player name is same no need to update " + packageName);
+            changed = false;
+        }
+        if (changed) {
+            Log.d(TAG, "sending player change update");
+            MediaControlPointOpcodeUpdate(tMediasupport);
+            PlayingOrderSupportedUpdate(tPlayersupport);
+            MediaPlayerNameUpdate(name);
+            ContentControlID(tCcid);
+        }
+    }
+
+    private int McpPassthroughToKeyCode(int operation) {
+         mCurrOpCode = operation;
+         switch (operation) {
+            case MCP_MEDIA_CONTROL_OPCODE_PLAY:
+                return KeyEvent.KEYCODE_MEDIA_PLAY;
+            case MCP_MEDIA_CONTROL_OPCODE_PAUSE:
+                return KeyEvent.KEYCODE_MEDIA_PAUSE;
+            case MCP_MEDIA_CONTROL_OPCODE_FAST_REWIND:
+                return KeyEvent.KEYCODE_MEDIA_REWIND;
+            case MCP_MEDIA_CONTROL_OPCODE_FAST_FORWARD:
+                return KeyEvent.KEYCODE_MEDIA_FAST_FORWARD;
+            case MCP_MEDIA_CONTROL_OPCODE_STOP:
+                return KeyEvent.KEYCODE_MEDIA_STOP;
+            case MCP_MEDIA_CONTROL_OPCODE_PREV_TRACK:
+                return KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+            case MCP_MEDIA_CONTROL_OPCODE_NEXT_TRACK:
+                return KeyEvent.KEYCODE_MEDIA_NEXT;
+
+            // Fallthrough for all unknown key mappings
+            default:
+                mCurrOpCode = -1;
+                Log.d(TAG, "unknown passthrough");
+                return KeyEvent.KEYCODE_UNKNOWN;
+         }
+     }
+
+    private class BondStateChangedReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
+                return;
+            }
+            int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                                           BluetoothDevice.ERROR);
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+            Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
+            if (sInstance != null)
+                bondStateChanged(device, state);
+        }
+    }
+
+    /**
+     * Process a change in the bonding state for a device.
+     *
+     * @param device the device whose bonding state has changed
+     * @param bondState the new bond state for the device. Possible values are:
+     * {@link BluetoothDevice#BOND_NONE},
+     * {@link BluetoothDevice#BOND_BONDING},
+     * {@link BluetoothDevice#BOND_BONDED}.
+     */
+    @VisibleForTesting
+    void bondStateChanged(BluetoothDevice device, int bondState) {
+        if (DBG) {
+            Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState);
+        }
+        // Remove state machine if the bonding for a device is removed
+        if (bondState != BluetoothDevice.BOND_NONE) {
+            return;
+        }
+        //update to lower layer
+        Message msg = mHandler.obtainMessage();
+        msg.what = BOND_STATE_CHANGE;
+        msg.obj = device;
+        msg.arg1 = bondState;
+        mHandler.sendMessage(msg);
+        return;
+    }
+    private boolean isMcpOnlyDevice(BluetoothDevice device) {
+        ParcelUuid ASCS_UUID =
+           ParcelUuid.fromString("0000184E-0000-1000-8000-00805F9B34FB");
+        AdapterService adapterService = AdapterService.getAdapterService();
+        boolean ascsSupported =
+                    ArrayUtils.contains(adapterService.getRemoteUuids(device), ASCS_UUID);
+        AcmService mAcmService = AcmService.getAcmService();
+        if (mAcmService != null) {
+            if (ascsSupported &&
+                mAcmService.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
+                return false;
+            }
+        }
+        Log.d(TAG,"McpOnly device");
+        return true;
+    }
+     /** Handles MCS messages. */
+    private final class McsMessageHandler extends Handler {
+        private McsMessageHandler(Looper looper) {
+            super(looper);
+        }
+        @Override
+         public synchronized void handleMessage(Message msg) {
+           if (DBG) Log.v(TAG, "McsMessageHandler: received message=" + msg.what);
+
+            switch (msg.what) {
+
+                case ACTIVE_DEVICE_CHANGE:
+                    if (DBG) Log.v(TAG, "ACTIVE_DEVICE_CHANGE msg: " + (BluetoothDevice)msg.obj + " msg2 : " + msg.arg1);
+                    mActiveDevice = (BluetoothDevice)msg.obj;
+                    mNativeInterface.setActiveDevice((BluetoothDevice)msg.obj, msg.arg1, msg.arg2);
+                break;
+
+                case BOND_STATE_CHANGE:
+                    if (DBG) Log.v(TAG, "BOND_STATE_CHANGE msg: " + (BluetoothDevice)msg.obj + " msg2 : " + msg.arg1);
+                    mNativeInterface.bondStateChange((BluetoothDevice)msg.obj, msg.arg2);
+                break;
+
+                case PLAYING_ORDER_SUPPORT_UPDATE:
+                    if (DBG) Log.v(TAG, "PLAYING_ORDER_SUPPORT_UPDATE msg: " + msg.arg1);
+                    mSupportedPlayingOrder = msg.arg1;
+                    mNativeInterface.playingOrder(msg.arg1);
+                break;
+
+                case PLAYING_ORDER_UPDATE:
+                    if (DBG) Log.v(TAG, "PLAYING_ORDER_UPDATE msg: " + msg.arg1);
+                    mPlayingOrder = msg.arg1;
+                    mNativeInterface.playingOrder(msg.arg1);
+                break;
+
+                case MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_UPDATE:
+                    if (DBG) Log.v(TAG, "MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_UPDATE msg: " + msg.arg1);
+                    mSupportedControlPoint = msg.arg1;
+                    mNativeInterface.mediaControlPointOpcodeSupported(msg.arg1);
+                break;
+
+                case MEDIA_CONTROL_POINT_UPDATE:
+                    if (DBG) Log.v(TAG, "MEDIA_CONTROL_POINT_UPDATE msg: " + msg.arg1);
+                    mControlPoint = msg.arg1;
+                    mNativeInterface.mediaControlPoint(msg.arg1);
+                break;
+
+                case MEDIA_PLAYER_NAME_UPDATE:
+                    if (DBG) Log.v(TAG, "MEDIA_PLAYER_NAME_UPDATE msg: " + (String)msg.obj);
+                    String name = (String)msg.obj;
+                    mPlayerName = name;
+                    if (name == null)
+                        name = new String("");
+                    mNativeInterface.mediaPlayerName(name);
+                break;
+
+                case MEDIA_STATE_UPDATE:
+                    if (DBG) Log.v(TAG, "MEDIA_STATE_UPDATE msg: " + msg.arg1);
+                    mState = msg.arg1;
+                    mNativeInterface.mediaState(msg.arg1);
+                break;
+
+                case TRACK_CHANGED_UPDATE:
+                    if (DBG) Log.v(TAG, "TRACK_CHANGED_UPDATE msg: " + msg.arg1);
+                    mNativeInterface.trackChanged(msg.arg1);
+                break;
+
+                case TRACK_DURATION_UPDATE:
+                    if (DBG) Log.v(TAG, "TRACK_DURATION_UPDATE msg: " + msg.arg1);
+                    mTrackDuration = msg.arg1;
+                    mNativeInterface.trackDuration(msg.arg1);
+                break;
+
+                case TRACK_POSITION_UPDATE:
+                    if (DBG) Log.v(TAG, "TRACK_POSITION_UPDATE msg: " + msg.arg1);
+                    mTrackPosition = msg.arg1;
+                    mNativeInterface.trackPosition(msg.arg1);
+                break;
+
+                case TRACK_TITLE_UPDATE:
+                    if (DBG) Log.v(TAG, "TRACK_TITLE_UPDATE msg: " + (String)msg.obj);
+                    String title =  (String)msg.obj;
+                    mTrackTitle = title;
+                    mNativeInterface.trackTitle(title);
+                break;
+
+
+                case CONTENT_CONTROL_ID_UPDATE:
+                    if (DBG) Log.v(TAG, "CONTENT_CONTROL_ID_UPDATE msg: " + msg.arg1);
+                    mCcid = msg.arg1;
+                    mNativeInterface.contentControlId(mCcid);
+                break;
+
+                case EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                    if (DBG) Log.v(TAG, "EVENT_TYPE_CONNECTION_STATE_CHANGED msg: " + msg.arg1);
+                    //update to APM
+                break;
+
+                case EVENT_TYPE_MEDIA_CONTROL_POINT_CHANGED:
+                    if (DBG) Log.v(TAG, "EVENT_TYPE_MEDIA_CONTROL_POINT_CHANGED msg: " + msg.arg1);
+                    BluetoothDevice mMcpDevice = (BluetoothDevice)msg.obj;
+                    int code = McpPassthroughToKeyCode(msg.arg1);
+
+                    if (code != KeyEvent.KEYCODE_UNKNOWN) {
+                        Log.w(TAG, "Valid passthrough, dispatch to media player");
+                    }
+                    if (code == KeyEvent.KEYCODE_MEDIA_PLAY &&
+                        !Objects.equals(mMcpDevice, mActiveDevice) && !isMcpOnlyDevice(mMcpDevice)) {
+                        ActiveDeviceManagerService mActiveDeviceManager = ActiveDeviceManagerService.get();
+                        if (mActiveDeviceManager != null) {
+                            mActiveDeviceManager.setActiveDevice(mMcpDevice, ApmConst.AudioFeatures.MEDIA_AUDIO, false, true);
+                        }
+                    }
+
+                    // WAR- For FF/Rewind UC
+                    if (code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD ||
+                            code == KeyEvent.KEYCODE_MEDIA_REWIND) {
+                        if (mState != PLAYSTATUS_SEEK) {
+                            mState = PLAYSTATUS_SEEK;
+                            MediaStateUpdate(mState);
+                            MediaControlPointUpdate(mCurrOpCode);
+                            mCurrOpCode = -1;
+                            Log.w(TAG, "Update Playstate as seeking for FF/Rewind opcode");
+                        }
+                    } else {
+                        if (mState == PLAYSTATUS_SEEK) { // To-Do
+                        }
+                    }
+
+                    KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, code);
+                    mMediaSessionManager.dispatchMediaKeyEvent(event, false);
+                    event = new KeyEvent(KeyEvent.ACTION_UP, code);
+                    mMediaSessionManager.dispatchMediaKeyEvent(event, false);
+                break;
+
+                case EVENT_TYPE_PLAYING_ORDER_CHANGED:
+                    if (DBG) Log.v(TAG, "EVENT_TYPE_PLAYING_ORDER_CHANGED msg: " + msg.arg1);
+                    //update to APM
+                break;
+
+                case EVENT_TYPE_TRACK_POSITION_CHANGED:
+                    if (DBG) Log.v(TAG, "EVENT_TYPE_TRACK_POSITION_CHANGED msg: " + msg.arg1);
+                    //update to APM
+                break;
+
+                case MEDIA_CONTROL_MANAGER_INIT:
+                    if (DBG) Log.v(TAG, "MEDIA_CONTROL_MANAGER_INIT");
+                    Context context = (Context)msg.obj;
+                    MediaControlManager.make(context);
+                break;
+
+                default:
+                    Log.e(TAG, "unknown message! msg.what=" + msg.what);
+                break;
+            }
+            Log.v(TAG, "Exit handleMessage");
+        }
+    }
+
+    /**
+     * Binder object: must be a static class or memory leak may occur.
+     */
+
+    static class McpBinder extends Binder implements IProfileServiceBinder {
+        private McpService mService;
+
+        private McpService getService() {
+            if (!Utils.checkCallerIsSystemOrActiveUser(TAG)) {
+                return null;
+            }
+
+            if (mService != null && mService.isAvailable()) {
+                return mService;
+            }
+            return null;
+        }
+
+        McpBinder(McpService svc) {
+            mService = svc;
+        }
+
+        @Override
+         public synchronized void cleanup() {
+            mService = null;
+        }
+    }
+}
+
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PCService.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PCService.java
new file mode 100644
index 0000000..f084771
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PCService.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.pc;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.bluetooth.BluetoothMetricsProto;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.MetricsLogger;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+public class PCService extends ProfileService{
+    private static final String TAG = "PCService";
+    private static final boolean DBG = true;
+    private static final int MAX_PACS_STATE_MACHINES = 50;
+
+    private HandlerThread mStateMachinesThread;
+    private final HashMap<BluetoothDevice, PacsClientStateMachine> mStateMachines =
+                new HashMap<>();
+    private BroadcastReceiver mBondStateChangedReceiver;
+
+    private AdapterService mAdapterService;
+    private PacsClientNativeInterface mNativeInterface;
+    private static PCService sInstance = null;
+
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+                "com.android.bluetooth.pacs.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Get the PCService instance. Returns null if the service hasn't been initialized.
+     */
+    public static PCService get() {
+        return sInstance;
+    }
+
+    @Override
+    protected IProfileServiceBinder initBinder() {
+        return null;
+    }
+
+    @Override
+    protected void create() {
+        if (DBG) {
+            Log.d(TAG, "create()");
+        }
+    }
+
+    protected boolean start() {
+
+        if (DBG) {
+            Log.d(TAG, "start()");
+        }
+        if (sInstance != null) {
+            Log.w(TAG, "PCService is already running");
+            return true;
+        }
+
+        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+                "AdapterService cannot be null when PCService starts");
+        mNativeInterface = Objects.requireNonNull(PacsClientNativeInterface.getInstance(),
+                "PacsClientNativeInterface cannot be null when PCService starts");
+
+        // Start handler thread for state machines
+        mStateMachines.clear();
+        mStateMachinesThread = new HandlerThread("PCService.StateMachines");
+        mStateMachinesThread.start();
+        mNativeInterface.init();
+        sInstance = this;
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mBondStateChangedReceiver = new BondStateChangedReceiver();
+        registerReceiver(mBondStateChangedReceiver, filter);
+
+        return true;
+    }
+
+    @Override
+    protected boolean stop() {
+        if (DBG) {
+            Log.d(TAG, "stop()");
+        }
+        if (sInstance == null) {
+            Log.w(TAG, "stop() called before start()");
+            return true;
+        }
+
+        unregisterReceiver(mBondStateChangedReceiver);
+
+        // Mark service as stopped
+        sInstance = null;
+
+        // Destroy state machines and stop handler thread
+        synchronized (mStateMachines) {
+            for (PacsClientStateMachine sm : mStateMachines.values()) {
+                sm.doQuit();
+                sm.cleanup();
+            }
+            mStateMachines.clear();
+        }
+
+        if (mStateMachinesThread != null) {
+            mStateMachinesThread.quitSafely();
+            mStateMachinesThread = null;
+        }
+
+        // Cleanup native interface
+        mNativeInterface.cleanup();
+        mNativeInterface = null;
+
+        // Clear AdapterService
+        mAdapterService = null;
+        return true;
+    }
+
+    @Override
+    protected void cleanup() {
+        if (DBG) {
+            Log.d(TAG, "cleanup()");
+        }
+    }
+
+    /**
+   * Get the PCService instance
+   * @return PCService instance
+   */
+    public static synchronized PCService getPCService() {
+        if (sInstance == null) {
+            Log.w(TAG, "getPCService(): service is NULL");
+            return null;
+        }
+
+        return sInstance;
+    }
+
+    /**
+     * Connects the pacs profile to the passed in device
+     *
+     * @param device is the device with which we will connect the pacs  profile
+     * @return true if pacs profile successfully connected, false otherwise
+     */
+
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) {
+            Log.d(TAG, "connect(): " + device);
+        }
+        if (device == null) {
+            return false;
+        }
+
+        synchronized (mStateMachines) {
+            PacsClientStateMachine smConnect = getOrCreateStateMachine(device);
+            if (smConnect == null) {
+                Log.e(TAG, "Cannot connect to " + device + " : no state machine");
+                return false;
+            }
+            smConnect.sendMessage(PacsClientStateMachine.CONNECT);
+        }
+
+        return true;
+    }
+
+    /**
+     * Disconnects pacs profile for the passed in device
+     *
+     * @param device is the device with which we want to disconnected the pacs profile
+     * @return true if pacs profile successfully disconnected, false otherwise
+     */
+
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) {
+            Log.d(TAG, "disconnect(): " + device);
+        }
+        if (device == null) {
+            return false;
+        }
+        synchronized (mStateMachines) {
+            PacsClientStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting");
+                return false;
+            }
+            int connectionState = stateMachine.getConnectionState();
+            if (connectionState != BluetoothProfile.STATE_CONNECTED
+                    && connectionState != BluetoothProfile.STATE_CONNECTING) {
+                Log.w(TAG, "disconnect: device " + device
+                        + " not connected/connecting, connectionState=" + connectionState);
+                return false;
+            }
+            stateMachine.sendMessage(PacsClientStateMachine.DISCONNECT);
+        }
+        return true;
+    }
+
+    /**
+     * start pacs disocvery for the passed in device
+     *
+     * @param device is the device with which we want to dicscoer the pacs
+     * @return true if pacs discovery is successfull, false otherwise
+     */
+
+    public boolean startPacsDiscovery(BluetoothDevice device) {
+           synchronized (mStateMachines) {
+                Log.i(TAG, "startPacsDiscovery: device=" + device + ", " + Utils.getUidPidString());
+                final PacsClientStateMachine stateMachine = mStateMachines.get(device);
+                if (stateMachine == null) {
+                    Log.w(TAG, "startPacsDiscovery: device " + device + " was never connected/connecting");
+                    return false;
+                }
+                if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+                    Log.w(TAG, "startPacsDiscovery: profile not connected");
+                    return false;
+                }
+                stateMachine.sendMessage(PacsClientStateMachine.START_DISCOVERY);
+           }
+           return true;
+    }
+
+    /**
+     * get sink pacs for the passed in device
+     *
+     * @param device is the device with which we want to get sink pacs
+     * @return sink pacs
+     */
+
+    public BluetoothCodecConfig[] getSinkPacs(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            final PacsClientStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                Log.e(TAG, "Failed to get Sink Pacs");
+                return null;
+            }
+            return stateMachine.getSinkPacs();
+        }
+    }
+
+    /**
+     * get src pacs for the passed in device
+     *
+     * @param device is the device with which we want to get src pacs
+     * @return src pacs
+     */
+
+    public BluetoothCodecConfig[] getSrcPacs(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            final PacsClientStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                Log.e(TAG, "Failed to get Src Pacs");
+                return null;
+            }
+            return stateMachine.getSinkPacs();
+        }
+    }
+
+    /**
+     * get sink locations for the passed in device
+     *
+     * @param device is the device with which we want to get sink location
+     * @return sink locations
+     */
+
+    public int getSinklocations(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            final PacsClientStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                Log.e(TAG, "Failed to get sink locations");
+                return -1;
+            }
+            return stateMachine.getSinklocations();
+        }
+    }
+
+    /**
+     * get src locations for the passed in device
+     *
+     * @param device is the device with which we want to get src location
+     * @return src locations
+     */
+
+    public int getSrclocations(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            final PacsClientStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                Log.e(TAG, "Failed to get src locations");
+                return -1;
+            }
+            return stateMachine.getSrclocations();
+        }
+    }
+
+    /**
+     * get available contexts for the passed in device
+     *
+     * @param device is the device with which we want to get available contexts
+     * @return avaialable contexts
+     */
+
+    public int getAvailableContexts(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            final PacsClientStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                Log.e(TAG, "Failed to get available contexts");
+                return -1;
+            }
+            return stateMachine.getAvailableContexts();
+        }
+    }
+
+    /**
+     * get supported contexts for the passed in device
+     *
+     * @param device is the device with which we want to get supported contexts
+     * @return supported contexts
+     */
+
+    public int getSupportedContexts(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            final PacsClientStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                Log.e(TAG, "Failed to get supported contexts");
+                return -1;
+            }
+            return stateMachine.getSupportedContexts();
+        }
+    }
+
+    /**
+     * Get the current connection state of the profile
+     *
+     * @param device is the remote bluetooth device
+     * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected,
+     * {@link BluetoothProfile#STATE_CONNECTING} if this profile is being connected,
+     * {@link BluetoothProfile#STATE_CONNECTED} if this profile is connected, or
+     * {@link BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected
+     */
+    public int getConnectionState(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            PacsClientStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+            return sm.getConnectionState();
+        }
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean okToConnect(BluetoothDevice device) {
+
+        int bondState = mAdapterService.getBondState(device);
+        if (bondState != BluetoothDevice.BOND_BONDED) {
+            Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
+            return false;
+         }
+        return true;
+    }
+
+    void messageFromNative(PacsClientStackEvent stackEvent) {
+        Objects.requireNonNull(stackEvent.device,
+                "Device should never be null, event: " + stackEvent);
+
+        synchronized (mStateMachines) {
+            BluetoothDevice device = stackEvent.device;
+            PacsClientStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                if (stackEvent.type == PacsClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
+                    switch (stackEvent.valueInt1) {
+                        case PacsClientStackEvent.CONNECTION_STATE_CONNECTED:
+                        case PacsClientStackEvent.CONNECTION_STATE_CONNECTING:
+                            sm = getOrCreateStateMachine(device);
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+            if (sm == null) {
+                Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent);
+                return;
+            }
+            sm.sendMessage(PacsClientStateMachine.STACK_EVENT, stackEvent);
+        }
+    }
+
+    void onConnectionStateChangedFromStateMachine(BluetoothDevice device,
+            int newState, int prevState) {
+        Log.d(TAG, "onConnectionStateChangedFromStateMachine for device: " + device
+                    + " newState: " + newState);
+
+        synchronized (mStateMachines) {
+            if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+                int bondState = mAdapterService.getBondState(device);
+                if (bondState == BluetoothDevice.BOND_NONE) {
+                    removeStateMachine(device);
+                 }
+            } else if (newState == BluetoothProfile.STATE_CONNECTED) {
+               Log.d(TAG, "PacsClient get connected with renderer device: " + device);
+            }
+        }
+    }
+
+    private class BondStateChangedReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
+                return;
+            }
+            int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                                           BluetoothDevice.ERROR);
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+            Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
+            bondStateChanged(device, state);
+        }
+    }
+
+    /**
+     * Process a change in the bonding state for a device.
+     *
+     * @param device the device whose bonding state has changed
+     * @param bondState the new bond state for the device. Possible values are:
+     * {@link BluetoothDevice#BOND_NONE},
+     * {@link BluetoothDevice#BOND_BONDING},
+     * {@link BluetoothDevice#BOND_BONDED}.
+     */
+    @VisibleForTesting
+    void bondStateChanged(BluetoothDevice device, int bondState) {
+        if (DBG) {
+            Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState);
+        }
+        // Remove state machine if the bonding for a device is removed
+        if (bondState != BluetoothDevice.BOND_NONE) {
+            return;
+        }
+
+        synchronized (mStateMachines) {
+             PacsClientStateMachine sm = mStateMachines.get(device);
+             if (sm == null) {
+                 return;
+             }
+             if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
+                 return;
+             }
+             removeStateMachine(device);
+        }
+    }
+
+    private void removeStateMachine(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            PacsClientStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                Log.w(TAG, "removeStateMachine: device " + device
+                        + " does not have a state machine");
+                return;
+            }
+            Log.i(TAG, "removeStateMachine: removing state machine for device: " + device);
+            sm.doQuit();
+            sm.cleanup();
+            mStateMachines.remove(device);
+        }
+    }
+
+    private PacsClientStateMachine getOrCreateStateMachine(BluetoothDevice device) {
+        if (device == null) {
+            Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
+            return null;
+        }
+        synchronized (mStateMachines) {
+            PacsClientStateMachine sm = mStateMachines.get(device);
+            if (sm != null) {
+                return sm;
+            }
+            if (mStateMachines.size() >= MAX_PACS_STATE_MACHINES) {
+                Log.e(TAG, "Maximum number of PACS state machines reached: "
+                        + MAX_PACS_STATE_MACHINES);
+                return null;
+            }
+            if (DBG) {
+                Log.d(TAG, "Creating a new state machine for " + device);
+            }
+            sm = PacsClientStateMachine.make(device, this,
+                    mNativeInterface, mStateMachinesThread.getLooper());
+            mStateMachines.put(device, sm);
+            return sm;
+        }
+    }
+
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PacsClientNativeInterface.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PacsClientNativeInterface.java
new file mode 100644
index 0000000..b7a4c51
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PacsClientNativeInterface.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * Defines the native interface that is used by state machine/service to
+ * send or receive messages from the native stack. This file is registered
+ * for the native methods in the corresponding JNI C++ file.
+ */
+package com.android.bluetooth.pc;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothCodecConfig;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * PacsClient Native Interface to/from JNI.
+ */
+public class PacsClientNativeInterface {
+    private static final String TAG = "PacsClientNativeInterface";
+    private static final boolean DBG = true;
+    private BluetoothAdapter mAdapter;
+    private int pacs_client_id = -1;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static PacsClientNativeInterface sInstance;
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static {
+        classInitNative();
+    }
+
+    private PacsClientNativeInterface() {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mAdapter == null) {
+            Log.wtf(TAG, "No Bluetooth Adapter Available");
+        }
+    }
+
+    /**
+     * Get singleton instance.
+     */
+    public static PacsClientNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new PacsClientNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Initializes the native interface.
+     *
+     * priorities to configure.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void init() {
+        initNative();
+    }
+
+    /**
+     * Cleanup the native interface.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void cleanup() {
+        cleanupNative(pacs_client_id);
+        pacs_client_id = -1;
+    }
+
+    /**
+     * Initiates PacsClient connection to a remote device.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean connectPacsClient(BluetoothDevice device) {
+        return connectPacsClientNative(pacs_client_id, getByteAddress(device));
+    }
+
+    /**
+     * Disconnects PacsClient from a remote device.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean disconnectPacsClient(BluetoothDevice device) {
+        return disconnectPacsClientNative(pacs_client_id, getByteAddress(device));
+    }
+
+    /**
+     * Trigger service discovery for pacs
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean startDiscoveryNative(BluetoothDevice device) {
+        return startDiscoveryNative(pacs_client_id, getByteAddress(device));
+    }
+
+    /**
+     *  get available audio contexts.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean GetAvailableAudioContexts(BluetoothDevice device) {
+        return GetAvailableAudioContextsNative(pacs_client_id, getByteAddress(device));
+    }
+
+    private BluetoothDevice getDevice(byte[] address) {
+        if (mAdapter != null) {
+            return mAdapter.getRemoteDevice(address);
+        } else {
+            return null;
+        }
+    }
+
+    private byte[] getByteAddress(BluetoothDevice device) {
+        if (device == null) {
+            return Utils.getBytesFromAddress("00:00:00:00:00:00");
+        }
+        return Utils.getBytesFromAddress(device.getAddress());
+    }
+
+    private void sendMessageToService(PacsClientStackEvent event) {
+        PCService service = PCService.getPCService();
+        if (service != null) {
+            service.messageFromNative(event);
+        } else {
+            Log.e(TAG, "Event ignored, service not available: " + event);
+        }
+    }
+
+    // Callbacks from the native stack back into the Java framework.
+    // All callbacks are routed via the Service which will disambiguate which
+    // state machine the message should be routed to.
+
+    private void OnInitialized(int state, int client_id) {
+        pacs_client_id = client_id;
+    }
+
+    private void onConnectionStateChanged(byte[] address, int state) {
+        PacsClientStackEvent event =
+                new PacsClientStackEvent(PacsClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = state;
+
+        if (DBG) {
+            Log.d(TAG, "onConnectionStateChanged: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void OnAudioContextAvailable(byte[] address, int available_contexts) {
+        PacsClientStackEvent event =
+                new PacsClientStackEvent(PacsClientStackEvent.EVENT_TYPE_AUDIO_CONTEXT_AVAIL);
+        event.device = getDevice(address);
+        event.valueInt1 = available_contexts;
+
+        if (DBG) {
+            Log.d(TAG, "OnAudioContextAvailable: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void onServiceDiscovery(BluetoothCodecConfig[] sink_pacs_array,
+                                    BluetoothCodecConfig[] src_pacs_array,
+                                    int sink_locations, int src_locations,
+                                    int available_contexts, int supported_contexts,
+                                    int status, byte[] address) {
+        if (status != 0) {
+            Log.e(TAG, "onServiceDiscovery: Failed" + status);
+            return;
+        }
+        PacsClientStackEvent event = new PacsClientStackEvent(
+                PacsClientStackEvent.EVENT_TYPE_SERVICE_DISCOVERY);
+        event.device = getDevice(address);
+        event.sinkCodecConfig = sink_pacs_array;
+        event.srcCodecConfig = src_pacs_array;
+        event.valueInt1 = sink_locations;
+        event.valueInt2 = src_locations;
+        event.valueInt3 = available_contexts;
+        event.valueInt4 = supported_contexts;
+        if (DBG) {
+            Log.d(TAG, "onServiceDiscovery: " + event);
+        }
+        for (BluetoothCodecConfig codecConfig :
+                    sink_pacs_array) {
+                Log.d(TAG, "sink_pacs_array: " + codecConfig);
+        }
+        for (BluetoothCodecConfig codecConfig :
+                    src_pacs_array) {
+                Log.d(TAG, "src_pacs_array: " + codecConfig);
+        }
+        if (DBG) {
+            Log.d(TAG, "sink locs: " + sink_locations + "src locs:" + src_locations);
+            Log.d(TAG, "avail ctxts: " + available_contexts + "supp ctxts: " + supported_contexts);
+        }
+
+        sendMessageToService(event);
+    }
+
+    // Native methods that call into the JNI interface
+    private static native void classInitNative();
+    private native void initNative();
+    private native void cleanupNative(int client_id);
+    private native boolean connectPacsClientNative(int client_id, byte[] address);
+    private native boolean disconnectPacsClientNative(int client_id, byte[] address);
+    private native boolean startDiscoveryNative(int client_id, byte[] address);
+    private native boolean GetAvailableAudioContextsNative(int client_id, byte[] address);
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PacsClientStackEvent.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PacsClientStackEvent.java
new file mode 100644
index 0000000..ff1be61
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PacsClientStackEvent.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.pc;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothCodecConfig;
+
+
+/**
+ * Stack event sent via a callback from JNI to Java, or generated
+ * internally by the Pacs Cleint State Machine.
+ */
+public class PacsClientStackEvent {
+    // Event types for STACK_EVENT message (coming from native)
+    private static final int EVENT_TYPE_NONE = 0;
+    public static final int EVENT_TYPE_INITIALIZED = 1;
+    public static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 2;
+    public static final int EVENT_TYPE_SERVICE_DISCOVERY = 3;
+    public static final int EVENT_TYPE_AUDIO_CONTEXT_AVAIL = 4;
+
+    // Do not modify without updating the HAL bt_pacs_client.h files.
+    // Match up with enum class ConnectionState of bt_pacs_client.h.
+    static final int CONNECTION_STATE_DISCONNECTED = 0;
+    static final int CONNECTION_STATE_CONNECTING = 1;
+    static final int CONNECTION_STATE_CONNECTED = 2;
+    static final int CONNECTION_STATE_DISCONNECTING = 3;
+
+    public int type;
+    public BluetoothDevice device;
+    public BluetoothCodecConfig[] sinkCodecConfig;
+    public BluetoothCodecConfig[] srcCodecConfig;
+    public int valueInt1;
+    public int valueInt2;
+    public int valueInt3;
+    public int valueInt4;
+
+    PacsClientStackEvent(int type) {
+        this.type = type;
+    }
+
+    @Override
+    public String toString() {
+        // event dump
+        StringBuilder result = new StringBuilder();
+        result.append("PacsClientStackEvent {type:" + eventTypeToString(type));
+        result.append(", device:" + device);
+        result.append(", value1:" + valueInt1);
+        result.append(", value2:" + valueInt2);
+        result.append(", value3:" + valueInt3);
+        result.append(", value4:" + valueInt4);
+        if (sinkCodecConfig != null) {
+            result.append(", sinkCodecConfig:" + sinkCodecConfig);
+        }
+        if (srcCodecConfig != null) {
+            result.append(", srcCodecConfig:" + srcCodecConfig);
+        }
+        result.append("}");
+        return result.toString();
+    }
+
+    private static String eventTypeToString(int type) {
+        switch (type) {
+            case EVENT_TYPE_NONE:
+                return "EVENT_TYPE_NONE";
+            case EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                return "EVENT_TYPE_CONNECTION_STATE_CHANGED";
+            case EVENT_TYPE_INITIALIZED:
+                return "EVENT_TYPE_INITIALIZED";
+            case EVENT_TYPE_AUDIO_CONTEXT_AVAIL:
+                return "EVENT_TYPE_AUDIO_CONTEXT_AVAIL";
+            case EVENT_TYPE_SERVICE_DISCOVERY:
+                return "EVENT_TYPE_SERVICE_DISCOVERY";
+            default:
+                return "EVENT_TYPE_UNKNOWN:" + type;
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PacsClientStateMachine.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PacsClientStateMachine.java
new file mode 100644
index 0000000..23ff16e
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/pacsclient/PacsClientStateMachine.java
@@ -0,0 +1,703 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+/**
+ * Bluetooth PacsClient StateMachine. There is one instance per remote device.
+ *  - "Disconnected" and "Connected" are steady states.
+ *  - "Connecting" and "Disconnecting" are transient states until the
+ *     connection / disconnection is completed.
+ *
+ *
+ *                        (Disconnected)
+ *                           |       ^
+ *                   CONNECT |       | DISCONNECTED
+ *                           V       |
+ *                 (Connecting)<--->(Disconnecting)
+ *                           |       ^
+ *                 CONNECTED |       | DISCONNECT
+ *                           V       |
+ *                          (Connected)
+ * NOTES:
+ *  - If state machine is in "Connecting" state and the remote device sends
+ *    DISCONNECT request, the state machine transitions to "Disconnecting" state.
+ *  - Similarly, if the state machine is in "Disconnecting" state and the remote device
+ *    sends CONNECT request, the state machine transitions to "Connecting" state.
+ *
+ *                    DISCONNECT
+ *    (Connecting) ---------------> (Disconnecting)
+ *                 <---------------
+ *                      CONNECT
+ *
+ */
+
+package com.android.bluetooth.pc;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import com.android.bluetooth.Utils;
+import android.bluetooth.BluetoothCodecConfig;
+import android.content.Intent;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.content.Context;
+
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Scanner;
+
+final class PacsClientStateMachine extends StateMachine {
+    private static final boolean DBG = false;
+    private static final String TAG = "PacsClientStateMachine";
+
+    static final int CONNECT = 1;
+    static final int DISCONNECT = 2;
+    static final int START_DISCOVERY = 3;
+    static final int GET_AVAILABLE_CONTEXTS = 4;
+    @VisibleForTesting
+    static final int STACK_EVENT = 101;
+    private static final int CONNECT_TIMEOUT = 201;
+
+    // NOTE: the value is not "final" - it is modified in the unit tests
+    @VisibleForTesting
+    static int sConnectTimeoutMs = 30000;        // 30s
+
+    private Disconnected mDisconnected;
+    private Connecting mConnecting;
+    private Disconnecting mDisconnecting;
+    private Connected mConnected;
+    private int mLastConnectionState = -1;
+
+    private PCService mService;
+    private PacsClientNativeInterface mNativeInterface;
+    private BluetoothCodecConfig[] mSinkPacsConfig;
+    private BluetoothCodecConfig[] mSrcPacsConfig;
+    private int mSinkLocations;
+    private int mSrcLocations;
+    private int mAvailableContexts;
+    private int mSupportedContexts;
+    private Context mContext;
+
+    private final BluetoothDevice mDevice;
+
+    PacsClientStateMachine(BluetoothDevice device, PCService svc,
+            PacsClientNativeInterface nativeInterface, Looper looper) {
+        super(TAG, looper);
+        mDevice = device;
+        mService = svc;
+        mNativeInterface = nativeInterface;
+
+        mDisconnected = new Disconnected();
+        mConnecting = new Connecting();
+        mDisconnecting = new Disconnecting();
+        mConnected = new Connected();
+
+        addState(mDisconnected);
+        addState(mConnecting);
+        addState(mDisconnecting);
+        addState(mConnected);
+
+        setInitialState(mDisconnected);
+    }
+
+    static PacsClientStateMachine make(BluetoothDevice device, PCService svc,
+            PacsClientNativeInterface nativeInterface, Looper looper) {
+        Log.i(TAG, "make for device " + device);
+        PacsClientStateMachine PacsClientSm = new PacsClientStateMachine(device, svc,
+                nativeInterface, looper);
+        PacsClientSm.start();
+        return PacsClientSm;
+    }
+
+    public void doQuit() {
+        log("doQuit for device " + mDevice);
+        quitNow();
+    }
+
+    public void cleanup() {
+        log("cleanup for device " + mDevice);
+    }
+
+    @VisibleForTesting
+    class Disconnected extends State {
+        @Override
+        public void enter() {
+            Log.i(TAG, "Enter Disconnected(" + mDevice + "): " + messageWhatToString(
+                    getCurrentMessage().what));
+
+            removeDeferredMessages(DISCONNECT);
+
+            if (mLastConnectionState != -1) {
+                // Don't broadcast during startup
+                broadcastConnectionState(BluetoothProfile.STATE_DISCONNECTED,
+                        mLastConnectionState);
+            }
+            cleanupDevice();
+        }
+
+        @Override
+        public void exit() {
+            Log.i(TAG, "Exit Disconnected(" + mDevice + "): " + messageWhatToString(
+                    getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Disconnected process message(" + mDevice + "): " + messageWhatToString(
+                    message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    log("Connecting to " + mDevice);
+                    if (!mNativeInterface.connectPacsClient(mDevice)) {
+                        Log.e(TAG, "Disconnected: error connecting to " + mDevice);
+                        break;
+                    }
+                    if (mService.okToConnect(mDevice)) {
+                        transitionTo(mConnecting);
+                    } else {
+                        // Reject the request and stay in Disconnected state
+                        Log.w(TAG, "Outgoing PacsClient Connecting request rejected: " + mDevice);
+                    }
+                    break;
+                case DISCONNECT:
+                    Log.w(TAG, "Disconnected: DISCONNECT ignored: " + mDevice);
+                    break;
+                case STACK_EVENT:
+                    PacsClientStackEvent event = (PacsClientStackEvent) message.obj;
+                    if (DBG) {
+                        Log.d(TAG, "Disconnected: stack event: " + event);
+                    }
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case PacsClientStackEvent.EVENT_TYPE_INITIALIZED:
+                            if(event.valueInt1 != 0) {
+                                Log.e(TAG, "Disconnected: error initializing PACS");
+                                return NOT_HANDLED;
+                            }
+                            Log.d(TAG, "PACS Initialized succesfully (DISCONNECTED)");
+                            break;
+                        case PacsClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1);
+                            break;
+                        default:
+                            Log.e(TAG, "Disconnected: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Disconnected state
+        private void processConnectionEvent(int state) {
+            switch (state) {
+                case PacsClientStackEvent.CONNECTION_STATE_DISCONNECTED:
+                    Log.w(TAG, "Ignore PacsClient DISCONNECTED event: " + mDevice);
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_CONNECTING:
+                    if (mService.okToConnect(mDevice)) {
+                        Log.i(TAG, "Incoming PacsClient Connecting request accepted: " + mDevice
+                            + "state: " + state);
+                        transitionTo(mConnecting);
+                    } else {
+                        // Reject the connection and stay in Disconnected state itself
+                        Log.w(TAG, "Incoming PacsClient Connecting request rejected: " + mDevice
+                            + "state: " + state);
+                        mNativeInterface.disconnectPacsClient(mDevice);
+                    }
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_CONNECTED:
+                    Log.w(TAG, "PacsClient Connected from Disconnected state: " + mDevice
+                            + "state: " + state);
+                    if (mService.okToConnect(mDevice)) {
+                        Log.i(TAG, "Incoming PacsClient Connected request accepted: " + mDevice
+                            + "state: " + state);
+                        transitionTo(mConnected);
+                    } else {
+                        // Reject the connection and stay in Disconnected state itself
+                        Log.w(TAG, "Incoming PacsClient Connected request rejected: " + mDevice
+                            + "state: " + state);
+                        mNativeInterface.disconnectPacsClient(mDevice);
+                    }
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    Log.w(TAG, "Ignore PacsClient DISCONNECTING event: " + mDevice
+                        + "state: " + state);
+                    break;
+                default:
+                    Log.e(TAG, "Incorrect state: " + state + " device: " + mDevice
+                        + "state: " + state);
+                    break;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    class Connecting extends State {
+        @Override
+        public void enter() {
+            Log.i(TAG, "Enter Connecting(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs);
+            broadcastConnectionState(BluetoothProfile.STATE_CONNECTING, mLastConnectionState);
+        }
+
+        @Override
+        public void exit() {
+            Log.i(TAG, "Exit Connecting(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_CONNECTING;
+            removeMessages(CONNECT_TIMEOUT);
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Connecting process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    deferMessage(message);
+                    break;
+                case CONNECT_TIMEOUT:
+                    Log.w(TAG, "Connecting connection timeout: " + mDevice);
+                    mNativeInterface.disconnectPacsClient(mDevice);
+                    PacsClientStackEvent disconnectEvent =
+                            new PacsClientStackEvent(
+                                    PacsClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+                    disconnectEvent.device = mDevice;
+                    disconnectEvent.valueInt1 = PacsClientStackEvent.CONNECTION_STATE_DISCONNECTED;
+                    sendMessage(STACK_EVENT, disconnectEvent);
+                    break;
+                case DISCONNECT:
+                    log("Connecting: connection canceled to " + mDevice);
+                    mNativeInterface.disconnectPacsClient(mDevice);
+                    transitionTo(mDisconnected);
+                    break;
+                case START_DISCOVERY:
+                case GET_AVAILABLE_CONTEXTS:
+                    deferMessage(message);
+                    break;
+                case STACK_EVENT:
+                    PacsClientStackEvent event = (PacsClientStackEvent) message.obj;
+                    log("Connecting: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case PacsClientStackEvent.EVENT_TYPE_INITIALIZED:
+                            if(event.valueInt1 != 0) {
+                                Log.e(TAG, "Disconnected: error initializing PACS");
+                                return NOT_HANDLED;
+                            }
+                            Log.d(TAG, "PACS Initialized succesfully (CONNECTING)");
+                            break;
+                        case PacsClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1);
+                            break;
+                        case PacsClientStackEvent.EVENT_TYPE_SERVICE_DISCOVERY:
+                        case PacsClientStackEvent.EVENT_TYPE_AUDIO_CONTEXT_AVAIL:
+                            deferMessage(message);
+                            break;
+                        default:
+                            Log.e(TAG, "Disconnected: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Connecting state
+        private void processConnectionEvent(int state) {
+            switch (state) {
+                case PacsClientStackEvent.CONNECTION_STATE_DISCONNECTED:
+                    Log.w(TAG, "Connecting device disconnected: " + mDevice);
+                    transitionTo(mDisconnected);
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_CONNECTED:
+                    transitionTo(mConnected);
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_CONNECTING:
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    Log.w(TAG, "Connecting interrupted: device is disconnecting: " + mDevice);
+                    transitionTo(mDisconnecting);
+                    break;
+                default:
+                    Log.e(TAG, "Incorrect state: " + state);
+                    break;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    class Disconnecting extends State {
+        @Override
+        public void enter() {
+            Log.i(TAG, "Enter Disconnecting(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs);
+            broadcastConnectionState(BluetoothProfile.STATE_DISCONNECTING, mLastConnectionState);
+        }
+
+        @Override
+        public void exit() {
+            Log.i(TAG, "Exit Disconnecting(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+            removeMessages(CONNECT_TIMEOUT);
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Disconnecting process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    deferMessage(message);
+                    break;
+                case CONNECT_TIMEOUT: {
+                    Log.w(TAG, "Disconnecting connection timeout: " + mDevice);
+                    mNativeInterface.disconnectPacsClient(mDevice);
+                    PacsClientStackEvent disconnectEvent =
+                            new PacsClientStackEvent(
+                                    PacsClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+                    disconnectEvent.device = mDevice;
+                    disconnectEvent.valueInt1 = PacsClientStackEvent.CONNECTION_STATE_DISCONNECTED;
+                    sendMessage(STACK_EVENT, disconnectEvent);
+                    break;
+                }
+                case START_DISCOVERY:
+                case GET_AVAILABLE_CONTEXTS:
+                case DISCONNECT:
+                    deferMessage(message);
+                    break;
+                case STACK_EVENT:
+                    PacsClientStackEvent event = (PacsClientStackEvent) message.obj;
+                    log("Disconnecting: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case PacsClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1);
+                            break;
+                        case PacsClientStackEvent.EVENT_TYPE_SERVICE_DISCOVERY:
+                        case PacsClientStackEvent.EVENT_TYPE_AUDIO_CONTEXT_AVAIL:
+                            deferMessage(message);
+                            break;
+                        default:
+                            Log.e(TAG, "Disconnected: ignoring stack event: " + event);
+                            break;
+                    }
+
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Disconnecting state
+        private void processConnectionEvent(int state) {
+            switch (state) {
+                case PacsClientStackEvent.CONNECTION_STATE_DISCONNECTED:
+                    Log.i(TAG, "Disconnected: " + mDevice);
+                    transitionTo(mDisconnected);
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_CONNECTED:
+                    if (mService.okToConnect(mDevice)) {
+                        Log.w(TAG, "Disconnecting interrupted: device is connected: " + mDevice);
+                        transitionTo(mConnected);
+                    } else {
+                        // Reject the connection and stay in Disconnecting state
+                        Log.w(TAG, "Incoming PacsClient Connected request rejected: " + mDevice);
+                        mNativeInterface.disconnectPacsClient(mDevice);
+                    }
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_CONNECTING:
+                    if (mService.okToConnect(mDevice)) {
+                        Log.i(TAG, "Disconnecting interrupted: try to reconnect: " + mDevice);
+                        transitionTo(mConnecting);
+                    } else {
+                        // Reject the connection and stay in Disconnecting state
+                        Log.w(TAG, "Incoming PacsClient Connecting request rejected: " + mDevice);
+                        mNativeInterface.disconnectPacsClient(mDevice);
+                    }
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    break;
+                default:
+                    Log.e(TAG, "Incorrect state: " + state);
+                    break;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    class Connected extends State {
+        @Override
+        public void enter() {
+            Log.i(TAG, "Enter Connected(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            removeDeferredMessages(CONNECT);
+            mNativeInterface.startDiscoveryNative(mDevice);
+            broadcastConnectionState(BluetoothProfile.STATE_CONNECTED, mLastConnectionState);
+        }
+
+        @Override
+        public void exit() {
+            Log.i(TAG, "Exit Connected(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_CONNECTED;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Connected process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    Log.w(TAG, "Connected: CONNECT ignored: " + mDevice);
+                    break;
+                case DISCONNECT:
+                    log("Disconnecting from " + mDevice);
+                    if (!mNativeInterface.disconnectPacsClient(mDevice)) {
+                        // If error in the native stack, transition directly to Disconnected state.
+                        Log.e(TAG, "Connected: error disconnecting from " + mDevice);
+                        transitionTo(mDisconnected);
+                        break;
+                    }
+                    transitionTo(mDisconnecting);
+                    break;
+                case START_DISCOVERY:
+                    log("sending start discovery to " + mDevice);
+                    if (!mNativeInterface.startDiscoveryNative(mDevice)) {
+                        Log.e(TAG, "connected: error sending startdiscovery to " + mDevice);
+                    }
+                    break;
+                case GET_AVAILABLE_CONTEXTS:
+                    log("get available audio conxtes from " + mDevice);
+                    mNativeInterface.GetAvailableAudioContexts(mDevice);
+                    break;
+                case STACK_EVENT:
+                    PacsClientStackEvent event = (PacsClientStackEvent) message.obj;
+                    log("Connected: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case PacsClientStackEvent.EVENT_TYPE_INITIALIZED:
+                            deferMessage(message);
+                            break;
+                        case PacsClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1);
+                            break;
+                        case PacsClientStackEvent.EVENT_TYPE_SERVICE_DISCOVERY:
+                            processPacsRecordEvent(event.sinkCodecConfig, event.srcCodecConfig,
+                                                   event.valueInt1, event.valueInt2,
+                                                   event.valueInt3, event.valueInt4);
+                            break;
+                        case PacsClientStackEvent.EVENT_TYPE_AUDIO_CONTEXT_AVAIL:
+                            mAvailableContexts = event.valueInt1;
+                            break;
+                        default:
+                            Log.e(TAG, "Disconnected: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Connected state
+        private void processConnectionEvent(int state) {
+            switch (state) {
+                case PacsClientStackEvent.CONNECTION_STATE_DISCONNECTED:
+                    Log.i(TAG, "Disconnected from " + mDevice);
+                    transitionTo(mDisconnected);
+                    break;
+                case PacsClientStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    Log.i(TAG, "Disconnecting from " + mDevice);
+                    transitionTo(mDisconnecting);
+                    break;
+                default:
+                    Log.e(TAG, "Connection State Device: " + mDevice + " bad state: " + state);
+                    break;
+            }
+        }
+
+        private void processPacsRecordEvent(BluetoothCodecConfig[] sinkCodecConfig,
+                                            BluetoothCodecConfig[] srcCodecConfig,
+                                            int sink_locations, int src_locations,
+                                            int available_contexts, int supported_contexts) {
+             mSinkPacsConfig = sinkCodecConfig;
+             mSrcPacsConfig = srcCodecConfig;
+             mSinkLocations = sink_locations;
+             mSrcLocations =  src_locations;
+             mAvailableContexts = available_contexts;
+             mSupportedContexts = supported_contexts;
+        }
+    }
+
+    int getConnectionState() {
+        String currentState = getCurrentState().getName();
+        switch (currentState) {
+            case "Disconnected":
+                return BluetoothProfile.STATE_DISCONNECTED;
+            case "Connecting":
+                return BluetoothProfile.STATE_CONNECTING;
+            case "Connected":
+                return BluetoothProfile.STATE_CONNECTED;
+            case "Disconnecting":
+                return BluetoothProfile.STATE_DISCONNECTING;
+            default:
+                Log.e(TAG, "Bad currentState: " + currentState);
+                return BluetoothProfile.STATE_DISCONNECTED;
+        }
+    }
+
+    BluetoothDevice getDevice() {
+        return mDevice;
+    }
+
+    synchronized boolean isConnected() {
+        return getCurrentState() == mConnected;
+    }
+
+
+    private void cleanupDevice() {
+        log("cleanup device " + mDevice);
+        mSinkLocations = -1;
+        mSrcLocations = -1;
+        mAvailableContexts = -1;
+        mSupportedContexts = -1;
+    }
+
+    BluetoothCodecConfig[] getSinkPacs() {
+        synchronized (this) {
+            return mSinkPacsConfig;
+        }
+    }
+
+    BluetoothCodecConfig[] getSrcPacs() {
+        synchronized (this) {
+            return mSrcPacsConfig;
+        }
+    }
+
+    int getSinklocations() {
+        synchronized (this) {
+            return mSinkLocations;
+        }
+    }
+
+    int getSrclocations() {
+        synchronized (this) {
+            return mSrcLocations;
+        }
+    }
+
+    int getAvailableContexts() {
+        synchronized (this) {
+            return mAvailableContexts;
+        }
+    }
+
+    int getSupportedContexts() {
+        synchronized (this) {
+            return mSupportedContexts;
+        }
+    }
+
+    // This method does not check for error condition (newState == prevState)
+    private void broadcastConnectionState(int newState, int prevState) {
+        log("Connection state " + mDevice + ": " + profileStateToString(prevState)
+                    + "->" + profileStateToString(newState));
+        mService.onConnectionStateChangedFromStateMachine(mDevice, newState, prevState);
+        Intent intent = new Intent(PCService.ACTION_CONNECTION_STATE_CHANGED);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mService.sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    private static String messageWhatToString(int what) {
+        switch (what) {
+            case CONNECT:
+                return "CONNECT";
+            case DISCONNECT:
+                return "DISCONNECT";
+            case STACK_EVENT:
+                return "STACK_EVENT";
+            case CONNECT_TIMEOUT:
+                return "CONNECT_TIMEOUT";
+            default:
+                break;
+        }
+        return Integer.toString(what);
+    }
+
+    private static String profileStateToString(int state) {
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return "DISCONNECTED";
+            case BluetoothProfile.STATE_CONNECTING:
+                return "CONNECTING";
+            case BluetoothProfile.STATE_CONNECTED:
+                return "CONNECTED";
+            case BluetoothProfile.STATE_DISCONNECTING:
+                return "DISCONNECTING";
+            default:
+                break;
+        }
+        return Integer.toString(state);
+    }
+
+    @Override
+    protected void log(String msg) {
+        if (DBG) {
+            super.log(msg);
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpController.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpController.java
new file mode 100644
index 0000000..ab29aa0
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpController.java
@@ -0,0 +1,733 @@
+/*
+ *Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.vcp;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.BluetoothVcp;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.bluetooth.apm.ApmConst;
+import com.android.bluetooth.apm.DeviceProfileMap;
+import com.android.bluetooth.apm.VolumeManager;
+import com.android.bluetooth.acm.AcmService;
+import com.android.bluetooth.BluetoothMetricsProto;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.MetricsLogger;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.bluetooth.groupclient.GroupService;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+public class VcpController {
+    private static final String TAG = "VcpController";
+    private static final boolean DBG = true;
+    private static final int MAX_VCP_STATE_MACHINES = 50;
+    private static final int VCP_MIN_VOL = 0;
+    private static final int VCP_MAX_VOL = 255;
+    private static final String ACTION_CONNECT_DEVICE =
+                "com.android.bluetooth.vcp.test.action.CONNECT_DEVICE";
+    private static final String ACTION_DISCONNECT_DEVICE =
+                "com.android.bluetooth.vcp.test.action.DISCONNECT_DEVICE";
+
+    private HandlerThread mStateMachinesThread;
+    private final HashMap<BluetoothDevice, VcpControllerStateMachine> mStateMachines =
+                new HashMap<>();
+    private HashMap<BluetoothDevice, Integer> mConnectionMode = new HashMap();
+    private BroadcastReceiver mBondStateChangedReceiver;
+
+    private AdapterService mAdapterService;
+    private VcpControllerNativeInterface mNativeInterface;
+    private DeviceProfileMap mDpm;
+    private AcmService mAcmService;
+    private static VcpController sInstance = null;
+    private Context mContext;
+    private boolean mPtsTest = false;
+    private final BroadcastReceiver mVcpControllerTestReceiver = new VcpControllerTestReceiver();
+
+    private VcpController(Context context) {
+        if (DBG) {
+            Log.d(TAG, "Create VcpController Instance");
+        }
+
+        mContext = context;
+        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+                "AdapterService cannot be null when VcpController starts");
+        mNativeInterface = Objects.requireNonNull(VcpControllerNativeInterface.getInstance(),
+                "VcpControllerNativeInterface cannot be null when VcpController starts");
+
+        // Start handler thread for state machines
+        mStateMachines.clear();
+        mStateMachinesThread = new HandlerThread("VcpController.StateMachines");
+        mStateMachinesThread.start();
+        mNativeInterface.init();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mBondStateChangedReceiver = new BondStateChangedReceiver();
+        mContext.registerReceiver(mBondStateChangedReceiver, filter);
+
+        if (mAdapterService.isAdvBCAAudioFeatEnabled()) {
+            Log.d(TAG, "Adv BCA Audio supported, enable VCP for broadcast");
+            SystemProperties.set("persist.vendor.service.bt.vcpForBroadcast", "true");
+        } else {
+            SystemProperties.set("persist.vendor.service.bt.vcpForBroadcast", "false");
+        }
+        mPtsTest = SystemProperties.getBoolean("persist.vendor.service.bt.vcp_controller.pts", false);
+        if (mPtsTest) {
+            Log.d(TAG, "Register for VcpControllerTestReceiver");
+            IntentFilter filter2 = new IntentFilter();
+            filter2.addAction(ACTION_CONNECT_DEVICE);
+            filter2.addAction(ACTION_DISCONNECT_DEVICE);
+            context.registerReceiver(mVcpControllerTestReceiver, filter2);
+        }
+    }
+
+    /**
+     * Make VcpController instance and Initialize
+     *
+     * @param context: application context
+     * @return VcpController instance
+     */
+    public static VcpController make(Context context) {
+        Log.v(TAG, "make");
+
+        if(sInstance == null) {
+            sInstance = new VcpController(context);
+        }
+        Log.v(TAG, "Exit make");
+        return sInstance;
+    }
+
+    /**
+     * Get the VcpController instance, which provides the public APIs
+     * to volume control operation via VCP connection
+     *
+     * @return VcpController instance
+     */
+    public static synchronized VcpController getVcpController() {
+        if (sInstance == null) {
+            Log.w(TAG, "getVcpController(): service is NULL");
+            return null;
+        }
+
+        return sInstance;
+    }
+
+    public static void clearVcpInstance () {
+        Log.v(TAG, "clearing VCP instatnce");
+        sInstance = null;
+        Log.v(TAG, "After clearing VCP instatnce ");
+    }
+
+    public synchronized void doQuit() {
+        if (DBG) {
+            Log.d(TAG, "doQuit()");
+        }
+        if (sInstance == null) {
+            Log.w(TAG, "doQuit() called before make()");
+            return;
+        }
+
+        // Cleanup native interface
+        mNativeInterface.cleanup();
+        mNativeInterface = null;
+        mContext.unregisterReceiver(mBondStateChangedReceiver);
+        if (mPtsTest) {
+            mContext.unregisterReceiver(mVcpControllerTestReceiver);
+        }
+
+        // Mark service as stopped
+        sInstance = null;
+
+        // Destroy state machines and stop handler thread
+        synchronized (mStateMachines) {
+            for (VcpControllerStateMachine sm : mStateMachines.values()) {
+                sm.doQuit();
+                sm.cleanup();
+            }
+            mStateMachines.clear();
+        }
+
+        if (mStateMachinesThread != null) {
+            mStateMachinesThread.quitSafely();
+            mStateMachinesThread = null;
+        }
+
+        // Clear AdapterService
+        mAdapterService = null;
+    }
+
+    /**
+     * Connect with the remote device for unicast or broadcast mode.
+     *
+     * @param device: the remote device to connect
+     * @param mode: connection mode: can be any of
+     * {@link #BluetoothVcp.MODE_UNICAST} or {@link #BluetoothVcp.MODE_BROADCAST}
+     *
+     * @return true if connect is accepted, false if connect request is rejected.
+     */
+    public boolean connect(BluetoothDevice device, int mode) {
+        if (DBG) {
+            Log.d(TAG, "connect(): " + device + ", mode: " + mode);
+        }
+        if (device == null) {
+            return false;
+        }
+
+        synchronized (mStateMachines) {
+            VcpControllerStateMachine smConnect = getOrCreateStateMachine(device);
+            if (smConnect == null) {
+                Log.e(TAG, "Cannot connect to " + device + " : no state machine");
+            }
+
+            int preConnMode;
+            if (mConnectionMode.containsKey(device)) {
+                preConnMode = mConnectionMode.get(device);
+                if ((preConnMode & mode) == 0) {
+                    int connMode = preConnMode | mode;
+                    mConnectionMode.put(device, connMode);
+                    broadcastConnectionModeChanged(device, connMode);
+                }
+            } else {
+                preConnMode = BluetoothVcp.MODE_NONE;
+                mConnectionMode.put(device, mode);
+                broadcastConnectionModeChanged(device, mode);
+            }
+
+            if (smConnect.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+                smConnect.sendMessage(VcpControllerStateMachine.CONNECT, device);
+            } else {
+                if (preConnMode == BluetoothVcp.MODE_BROADCAST &&
+                        mode == BluetoothVcp.MODE_UNICAST) {
+                    Log.d(TAG, "VCP connection from BROADCAST-ONLY to UNICAST_BROADCAST: " + device);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Disconnect with the remote device for unicast or broadcast mode.
+     *
+     * @param device: the remote device to connect
+     * @param mode: connection mode: can be any of
+     * {@link #BluetoothVcp.MODE_UNICAST} or {@link #BluetoothVcp.MODE_BROADCAST}
+     *
+     * @return true if disconnect is accepted, false if disconnect is rejected.
+     */
+    public boolean disconnect(BluetoothDevice device, int mode) {
+        if (DBG) {
+            Log.d(TAG, "disconnect(): " + device + ", mode: " + mode);
+        }
+        if (device == null) {
+            return false;
+        }
+
+        synchronized (mStateMachines) {
+            int preConnMode = getConnectionMode(device);
+            int connMode = BluetoothVcp.MODE_NONE;
+
+            if ((preConnMode & mode) != 0) {
+                connMode = preConnMode & ~mode;
+                broadcastConnectionModeChanged(device, connMode);
+            } else {
+                Log.d(TAG, "disconnect ignore as Vcp is not connected for mode: " + mode);
+                return false;
+            }
+
+            if (connMode == BluetoothVcp.MODE_NONE) {
+                mConnectionMode.remove(device);
+                VcpControllerStateMachine stateMachine = mStateMachines.get(device);
+                if (stateMachine == null) {
+                    Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting");
+                    return false;
+                }
+                int connectionState = stateMachine.getConnectionState();
+                if (connectionState != BluetoothProfile.STATE_CONNECTED
+                        && connectionState != BluetoothProfile.STATE_CONNECTING) {
+                    Log.w(TAG, "disconnect: device " + device
+                            + " not connected/connecting, connectionState=" + connectionState);
+                    return false;
+                }
+                stateMachine.sendMessage(VcpControllerStateMachine.DISCONNECT, device);
+            } else {
+                mConnectionMode.put(device, connMode);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Set absolute volume to remote device via VCP connection
+     *
+     * @param device: remote device instance
+     * @param volume: requested volume settings for remote device
+     * @return true if set abs volume requst is accepted, false if set
+     * abs volume request is rejected
+     */
+    public boolean setAbsoluteVolume(BluetoothDevice device, int volume, int audioType) {
+        synchronized (mStateMachines) {
+            Log.i(TAG, "setAbsVolume: device=" + device + ", " + Utils.getUidPidString());
+            final VcpControllerStateMachine stateMachine = mStateMachines.get(device);
+
+            if (stateMachine == null) {
+                Log.w(TAG, "setAbsVolume: device " + device + " was never connected/connecting");
+                return false;
+            }
+
+            if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+                Log.w(TAG, "setAbsVolume: profile not connected");
+                return false;
+            }
+
+            stateMachine.sendMessage(VcpControllerStateMachine.SET_VOLUME, volume, audioType, device);
+        }
+        return true;
+    }
+
+    /**
+     * Mute or unmute remote device via VCP connection
+     *
+     * @param device: remote device instance
+     * @param enableMute: true if mute, false if unmute
+     * @return true if mute requst is accepted, false if mute
+     * request is rejected
+     */
+    public boolean setMute(BluetoothDevice device, boolean enableMute) {
+        synchronized (mStateMachines) {
+            Log.i(TAG, "setMute: device=" + device + ", " + "enableMute: " + enableMute);
+            final VcpControllerStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                Log.w(TAG, "setMute: device " + device + " was never connected/connecting");
+                return false;
+            }
+            if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+                Log.w(TAG, "setMute: profile not connected");
+                return false;
+            }
+            if (enableMute) {
+                stateMachine.sendMessage(VcpControllerStateMachine.MUTE, device);
+            } else {
+                stateMachine.sendMessage(VcpControllerStateMachine.UNMUTE, device);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Get current absolute volume of the remote device
+     *
+     * @param device: remote device instance
+     * @return current absolute volume of the remote device
+     */
+    public int getAbsoluteVolume(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            final VcpControllerStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                return -1;
+            }
+            return stateMachine.getVolume();
+        }
+    }
+
+    /**
+     * Get mute status of remote device
+     *
+     * @param device: remote device instance
+     * @return current mute status of the remote device:
+     * true if mute status, false if unmute status
+     */
+    public boolean isMute(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            final VcpControllerStateMachine stateMachine = mStateMachines.get(device);
+            if (stateMachine == null) {
+                return false;
+            }
+            return stateMachine.isMute();
+        }
+    }
+
+    /**
+     * Get the current connection state of the VCP
+     *
+     * @param device is the remote bluetooth device
+     * @return {@link BluetoothProfile#STATE_DISCONNECTED} if VCP is disconnected,
+     * {@link BluetoothProfile#STATE_CONNECTING} if VCP is being connected,
+     * {@link BluetoothProfile#STATE_CONNECTED} if VCP is connected, or
+     * {@link BluetoothProfile#STATE_DISCONNECTING} if VCP is being disconnected
+     */
+    public int getConnectionState(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            VcpControllerStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+            return sm.getConnectionState();
+        }
+    }
+
+    /**
+     * Get current VCP Connection mode
+     *
+     * @param device: remote device instance
+     * @return current connection mode of VCP:
+     * {@link #BluetoothVcp.MODE_NONE} if none VCP connection
+     * {@link #BluetoothVcp.MODE_UNICAST} if VCP is connected for unicast
+     * {@link #BluetoothVcp.MODE_BROADCAST} if VCP is connected for broadcast
+     * {@link #BluetoothVcp.MODE_UNICAST_BROADCAST} if VCP is connected
+     * for both unicast and broadcast
+     */
+    public int getConnectionMode(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            if (mConnectionMode.containsKey(device)) {
+                return mConnectionMode.get(device);
+            }
+            return BluetoothVcp.MODE_NONE;
+        }
+    }
+
+    /**
+     * Check if VCP is connected for broadcast mode
+     *
+     * @param device: remote device instance
+     * @return true if VCP is connected for broadcast or uncast-broadcast
+     * return false if VCP is connected for unicast-only
+     */
+    public boolean isBroadcastDevice(BluetoothDevice device) {
+        if (device == null)
+            return false;
+
+        synchronized (mStateMachines) {
+            if (mConnectionMode.containsKey(device)) {
+                if ((mConnectionMode.get(device) & BluetoothVcp.MODE_BROADCAST) != 0) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean okToConnect(BluetoothDevice device) {
+        // Check if this is an incoming connection in Quiet mode.
+        if (mAdapterService.isQuietModeEnabled()) {
+            Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
+            return false;
+        }
+
+        int bondState = mAdapterService.getBondState(device);
+        if (bondState != BluetoothDevice.BOND_BONDED) {
+            Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
+            return false;
+         }
+        return true;
+    }
+
+    void messageFromNative(VcpStackEvent stackEvent) {
+        Objects.requireNonNull(stackEvent.device,
+                "Device should never be null, event: " + stackEvent);
+
+        synchronized (mStateMachines) {
+            BluetoothDevice device = stackEvent.device;
+            VcpControllerStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                if (stackEvent.type == VcpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
+                    switch (stackEvent.valueInt1) {
+                        case VcpStackEvent.CONNECTION_STATE_CONNECTED:
+                        case VcpStackEvent.CONNECTION_STATE_CONNECTING:
+                            sm = getOrCreateStateMachine(device);
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+            if (sm == null) {
+                Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent);
+                return;
+            }
+            sm.sendMessage(VcpControllerStateMachine.STACK_EVENT, stackEvent);
+        }
+    }
+
+    int getCsipSetId(BluetoothDevice device, ParcelUuid uuid) {
+        GroupService csipService = GroupService.getGroupService();
+        if (csipService != null) {
+            return csipService.getRemoteDeviceGroupId(device, uuid);
+        } else {
+            return -1;
+        }
+    }
+
+    void onConnectionStateChangedFromStateMachine(BluetoothDevice device,
+            int newState, int prevState) {
+        Log.d(TAG, "onConnectionStateChangedFromStateMachine for device: " + device
+                    + " newState: " + newState);
+
+        if (device == null) {
+            Log.d(TAG, "device is null ");
+            return;
+        }
+
+        mDpm = DeviceProfileMap.getDeviceProfileMapInstance();
+        mAcmService = AcmService.getAcmService();
+        BluetoothDevice grpDevice;
+        if (mAcmService != null) {
+            grpDevice = mAcmService.getGroup(device);
+        } else {
+            Log.w(TAG, "AcmService is null");
+            grpDevice = device;
+        }
+
+        synchronized (mStateMachines) {
+            if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+                int bondState = mAdapterService.getBondState(device);
+                if (bondState == BluetoothDevice.BOND_NONE) {
+                    removeStateMachine(device);
+                }
+
+                if (mAcmService != null &&
+                    (mAcmService.isVcpPeerDeviceConnected(device, getCsipSetId(device, null)))) {
+                    Log.d(TAG, "VCP Peer device connected, this is not last member, skip update to APM ");
+                } else {
+                    ///* Update VCP profile disconnected to APM/ACM
+                    Log.d(TAG, "All group members are disconnected, update to APM");
+                    if (mDpm != null) {
+                        mDpm.profileConnectionUpdate(grpDevice,
+                            ApmConst.AudioFeatures.MEDIA_VOLUME_CONTROL, ApmConst.AudioProfiles.VCP, false);
+
+                        mDpm.profileConnectionUpdate(grpDevice,
+                            ApmConst.AudioFeatures.CALL_VOLUME_CONTROL, ApmConst.AudioProfiles.VCP, false);
+                    }
+                }
+                setAbsVolumeSupport(device, false, -1);
+                updateConnState(device, newState);
+                //*/
+                Log.d(TAG, "VCP get disconnected with renderer device: " + device);
+            } else if (newState == BluetoothProfile.STATE_CONNECTED) {
+                Log.d(TAG, "VCP get connected with renderer device: " + device);
+
+                if (mAcmService != null &&
+                    (mAcmService.isVcpPeerDeviceConnected(device, getCsipSetId(device, null)))) {
+                    Log.d(TAG, "VCP Peer device connected, this is not first connected member, skip update to APM ");
+                } else {
+                    ///* Update VCP profile connected to APM/ACM
+                    Log.d(TAG, "The first connected memeber, update to APM");
+                    if (mDpm != null) {
+                        mDpm.profileConnectionUpdate(grpDevice,
+                            ApmConst.AudioFeatures.MEDIA_VOLUME_CONTROL, ApmConst.AudioProfiles.VCP, true);
+
+                        mDpm.profileConnectionUpdate(grpDevice,
+                            ApmConst.AudioFeatures.CALL_VOLUME_CONTROL, ApmConst.AudioProfiles.VCP, true);
+                    }
+                }
+                // Set Abs Volume Support with true until get initial volume of remote
+                //*/
+            }
+        }
+    }
+
+    void setAbsVolumeSupport(BluetoothDevice device, boolean isAbsVolSupported, int initial_volume) {
+        mAcmService = AcmService.getAcmService();
+        if (mAcmService != null) {
+            Log.d(TAG, "Update Abs Volume Support to upper layer ");
+            mAcmService.setAbsVolSupport(device, isAbsVolSupported, initial_volume);
+        }
+    }
+
+    void notifyVolumeChanged(BluetoothDevice device, int volume, int audioType) {
+        Log.d(TAG, "notify volume changed for renderer device: " + device + " audioType: " + audioType);
+        // Notify ACM volume changed for device
+        mAcmService = AcmService.getAcmService();
+        if (mAcmService != null) {
+            mAcmService.onVolumeStateChanged(device, volume, audioType);
+        }
+        Intent intent = new Intent(BluetoothVcp.ACTION_VOLUME_CHANGED);
+        intent.putExtra(BluetoothVcp.EXTRA_VOLUME, volume);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mContext.sendBroadcast(intent, BLUETOOTH_CONNECT,
+             Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    void notifyMuteChanged(BluetoothDevice device, boolean mute) {
+        Log.d(TAG, "notify mute changed for renderer device: " + device + " mute: " + mute);
+        // Notify ACM mute changed
+        mAcmService = AcmService.getAcmService();
+        if (mAcmService != null) {
+            mAcmService.onMuteStatusChanged (device, mute);
+        }
+        Intent intent = new Intent(BluetoothVcp.ACTION_MUTE_CHANGED);
+        intent.putExtra(BluetoothVcp.EXTRA_MUTE, mute);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mContext.sendBroadcast(intent, BLUETOOTH_CONNECT,
+             Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    void broadcastConnectionModeChanged(BluetoothDevice device, int mode) {
+        Log.d(TAG, "broadccast connection mode changed for device: " + device + ", mode: " + mode);
+        Intent intent = new Intent(BluetoothVcp.ACTION_CONNECTION_MODE_CHANGED);
+        intent.putExtra(BluetoothVcp.EXTRA_MODE, mode);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mContext.sendBroadcast(intent, BLUETOOTH_CONNECT,
+             Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    public void updateConnState(BluetoothDevice device, int newState) {
+        Log.d(TAG, "updateConnState: device: " + device + ", state: " + newState);
+        VolumeManager mVolumeManager = VolumeManager.get();
+        mVolumeManager.onConnStateChange(device, newState, ApmConst.AudioProfiles.VCP);
+    }
+
+    private class BondStateChangedReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
+                return;
+            }
+            int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                                           BluetoothDevice.ERROR);
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+            Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
+            bondStateChanged(device, state);
+        }
+    }
+
+    /**
+     * Process a change in the bonding state for a device.
+     *
+     * @param device the device whose bonding state has changed
+     * @param bondState the new bond state for the device. Possible values are:
+     * {@link BluetoothDevice#BOND_NONE},
+     * {@link BluetoothDevice#BOND_BONDING},
+     * {@link BluetoothDevice#BOND_BONDED}.
+     */
+    @VisibleForTesting
+    void bondStateChanged(BluetoothDevice device, int bondState) {
+        if (DBG) {
+            Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState);
+        }
+        // Remove state machine if the bonding for a device is removed
+        if (bondState != BluetoothDevice.BOND_NONE) {
+            return;
+        }
+
+        synchronized (mStateMachines) {
+             VcpControllerStateMachine sm = mStateMachines.get(device);
+             if (sm == null) {
+                 return;
+             }
+             if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
+                 return;
+             }
+             mConnectionMode.remove(device);
+             removeStateMachine(device);
+        }
+    }
+
+    private void removeStateMachine(BluetoothDevice device) {
+        synchronized (mStateMachines) {
+            VcpControllerStateMachine sm = mStateMachines.get(device);
+            if (sm == null) {
+                Log.w(TAG, "removeStateMachine: device " + device
+                        + " does not have a state machine");
+                return;
+            }
+            Log.i(TAG, "removeStateMachine: removing state machine for device: " + device);
+            sm.doQuit();
+            sm.cleanup();
+            mStateMachines.remove(device);
+        }
+    }
+
+    private VcpControllerStateMachine getOrCreateStateMachine(BluetoothDevice device) {
+        if (device == null) {
+            Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
+            return null;
+        }
+        synchronized (mStateMachines) {
+            VcpControllerStateMachine sm = mStateMachines.get(device);
+            if (sm != null) {
+                return sm;
+            }
+            if (mStateMachines.size() >= MAX_VCP_STATE_MACHINES) {
+                Log.e(TAG, "Maximum number of VCP state machines reached: "
+                        + MAX_VCP_STATE_MACHINES);
+                return null;
+            }
+            if (DBG) {
+                Log.d(TAG, "Creating a new state machine for " + device);
+            }
+            sm = VcpControllerStateMachine.make(device, this, mContext,
+                    mNativeInterface, mStateMachinesThread.getLooper());
+            mStateMachines.put(device, sm);
+            return sm;
+        }
+    }
+
+    private class VcpControllerTestReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(ACTION_CONNECT_DEVICE)) {
+                Log.d(TAG, "Receive ACTION_CONNECT_DEVICE");
+                int mode = intent.getIntExtra(BluetoothVcp.EXTRA_MODE, 0);
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                connect(device, mode);
+            } else if (action.equals(ACTION_DISCONNECT_DEVICE)) {
+                Log.d(TAG, "Receive ACTION_DISCONNECT_DEVICE");
+                int mode = intent.getIntExtra(BluetoothVcp.EXTRA_MODE, 0);
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                disconnect(device, mode);
+            }
+        }
+    }
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpControllerNativeInterface.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpControllerNativeInterface.java
new file mode 100644
index 0000000..89cc69b
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpControllerNativeInterface.java
@@ -0,0 +1,200 @@
+/*
+ *Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * Defines the native interface that is used by state machine/service to
+ * send or receive messages from the native stack. This file is registered
+ * for the native methods in the corresponding JNI C++ file.
+ */
+package com.android.bluetooth.vcp;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Vcp Controller Native Interface to/from JNI.
+ */
+public class VcpControllerNativeInterface {
+    private static final String TAG = "VcpControllerNativeInterface";
+    private static final boolean DBG = true;
+    private BluetoothAdapter mAdapter;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static VcpControllerNativeInterface sInstance;
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static {
+        classInitNative();
+    }
+
+    private VcpControllerNativeInterface() {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mAdapter == null) {
+            Log.wtfStack(TAG, "No Bluetooth Adapter Available");
+        }
+    }
+
+    /**
+     * Get singleton instance.
+     */
+    public static VcpControllerNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new VcpControllerNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Initializes the native interface.
+     *
+     * priorities to configure.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void init() {
+        initNative();
+    }
+
+    /**
+     * Cleanup the native interface.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void cleanup() {
+        cleanupNative();
+    }
+
+    /**
+     * Initiates Vcp connection to a remote device.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean connectVcp(BluetoothDevice device, boolean isDirect) {
+        return connectVcpNative(getByteAddress(device), isDirect);
+    }
+
+    /**
+     * Disconnects Vcp from a remote device.
+     *
+     * @param device the remote device
+     * @return true on success, otherwise false.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean disconnectVcp(BluetoothDevice device) {
+        return disconnectVcpNative(getByteAddress(device));
+    }
+
+    /**
+     * Sets the Vcp Abs volume
+     * @param volume
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean setAbsVolume(int volume, BluetoothDevice device) {
+        return setAbsVolumeNative(volume, getByteAddress(device));
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean mute(BluetoothDevice device) {
+        return muteNative(getByteAddress(device));
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean unmute(BluetoothDevice device) {
+        return unmuteNative(getByteAddress(device));
+    }
+
+    private BluetoothDevice getDevice(byte[] address) {
+        return mAdapter.getRemoteDevice(address);
+    }
+
+    private byte[] getByteAddress(BluetoothDevice device) {
+        if (device == null) {
+            return Utils.getBytesFromAddress("00:00:00:00:00:00");
+        }
+        return Utils.getBytesFromAddress(device.getAddress());
+    }
+
+    private void sendMessageToService(VcpStackEvent event) {
+        VcpController service = VcpController.getVcpController();
+        if (service != null) {
+            service.messageFromNative(event);
+        } else {
+            Log.e(TAG, "Event ignored, service not available: " + event);
+        }
+    }
+
+    // Callbacks from the native stack back into the Java framework.
+    // All callbacks are routed via the Service which will disambiguate which
+    // state machine the message should be routed to.
+    private void onConnectionStateChanged(int state, byte[] address) {
+        VcpStackEvent event =
+                new VcpStackEvent(VcpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = state;
+
+        if (DBG) {
+            Log.d(TAG, "onConnectionStateChanged: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void OnVolumeStateChange(int volume, int mute, byte[] address) {
+        VcpStackEvent event = new VcpStackEvent(
+                VcpStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = volume;
+        event.valueInt2 = mute;
+
+        if (DBG) {
+            Log.d(TAG, "OnVolumeStateChange: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    private void OnVolumeFlagsChange(int flags, byte[] address) {
+        VcpStackEvent event = new VcpStackEvent(
+                VcpStackEvent.EVENT_TYPE_VOLUME_FLAGS_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = flags;
+
+        if (DBG) {
+            Log.d(TAG, "OnVolumeFlagsChange: " + event);
+        }
+        sendMessageToService(event);
+    }
+
+    // Native methods that call into the JNI interface
+    private static native void classInitNative();
+    private native void initNative();
+    private native void cleanupNative();
+    private native boolean connectVcpNative(byte[] address, boolean isDirect);
+    private native boolean disconnectVcpNative(byte[] address);
+    private native boolean setAbsVolumeNative(int volume, byte[] address);
+    private native boolean muteNative(byte[] address);
+    private native boolean unmuteNative(byte[] address);
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpControllerStateMachine.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpControllerStateMachine.java
new file mode 100644
index 0000000..fc35753
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpControllerStateMachine.java
@@ -0,0 +1,1046 @@
+/*
+ *Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+/**
+ * Bluetooth VCP StateMachine. There is one instance per remote device.
+ *  - "Disconnected" and "Connected" are steady states.
+ *  - "Connecting" and "Disconnecting" are transient states until the
+ *     connection / disconnection is completed.
+ *
+ *
+ *                        (Disconnected)
+ *                           |       ^
+ *                   CONNECT |       | DISCONNECTED
+ *                           V       |
+ *                 (Connecting)<--->(Disconnecting)
+ *                           |       ^
+ *                 CONNECTED |       | DISCONNECT
+ *                           V       |
+ *                          (Connected)
+ * NOTES:
+ *  - If state machine is in "Connecting" state and the remote device sends
+ *    DISCONNECT request, the state machine transitions to "Disconnecting" state.
+ *  - Similarly, if the state machine is in "Disconnecting" state and the remote device
+ *    sends CONNECT request, the state machine transitions to "Connecting" state.
+ *
+ *                    DISCONNECT
+ *    (Connecting) ---------------> (Disconnecting)
+ *                 <---------------
+ *                      CONNECT
+ *
+ */
+
+package com.android.bluetooth.vcp;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import com.android.bluetooth.Utils;
+import android.bluetooth.BluetoothVcp;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Scanner;
+
+final class VcpControllerStateMachine extends StateMachine {
+    private static final boolean DBG = true;
+    private static final String TAG = "VcpControllerStateMachine";
+
+    static final int CONNECT = 1;
+    static final int DISCONNECT = 2;
+    static final int SET_VOLUME = 3;
+    static final int MUTE = 4;
+    static final int UNMUTE = 5;
+
+    @VisibleForTesting
+    static final int STACK_EVENT = 101;
+    private static final int CONNECT_TIMEOUT = 201;
+    private static final int SET_ABS_VOL_TIMEOUT = 202;
+    private static final int CHANGE_MUTE_TIMEOUT = 203;
+
+    private static final int UNMUTE_STATE = 0;
+    private static final int MUTE_STATE = 1;
+    private static final int VOLUME_SETTING_NOT_PERSISTED = 0x00;
+    private static final int VOLUME_SETTING_PERSISTED = 0x01;
+
+    private static final int MAX_ERROR_RETRY_TIMES = 3;
+    private static final int VCP_MAX_VOL = 255;
+    // The default VCP volume 0x77 (119)
+    private static final int VCP_DEFAULT_VOL = 119;
+    private static final int CMD_TIMEOUT_DELAY = 2000;
+
+
+    // NOTE: the value is not "final" - it is modified in the unit tests
+    @VisibleForTesting
+    static int sConnectTimeoutMs = 30000;        // 30s
+
+    private Disconnected mDisconnected;
+    private Connecting mConnecting;
+    private Disconnecting mDisconnecting;
+    private Connected mConnected;
+    private int mLastConnectionState = -1;
+
+    private VcpController mVcpController;
+    private VcpControllerNativeInterface mNativeInterface;
+    private Context mContext;
+
+    /* Current remote volume */
+    private int mRemoteVolume;
+    /* Requested volume in progress of Native Layer setAbsVolume */
+    private int mRequestedVolume;
+    /* Cached new volume if has a requested volume in progress */
+    private int mCachedVolume;
+    private int mAbsVolRetryTimes;
+    private boolean mAbsVolSetInProgress;
+
+    /* Current remote mute state */
+    private int mMuteState;
+    /* Requested mute state in progress of Native Layer mute/unMute */
+    private int mRequestedMuteState;
+    /* Cached new mute state if has a requested mute state in progress */
+    private int mCachedMuteState;
+    private int mChangeMuteRetryTimes;
+    private boolean mMuteChangeInProgress;
+
+    private int mVolumeFlags;
+    private final BluetoothDevice mDevice;
+    private int mVolumeControlAudioType;
+    private int mCachedVolumeControlAudioType;
+
+    VcpControllerStateMachine(BluetoothDevice device, VcpController svc, Context context,
+            VcpControllerNativeInterface nativeInterface, Looper looper) {
+        super(TAG, looper);
+        mDevice = device;
+        mVcpController = svc;
+        mContext = context;
+        mNativeInterface = nativeInterface;
+
+        mDisconnected = new Disconnected();
+        mConnecting = new Connecting();
+        mDisconnecting = new Disconnecting();
+        mConnected = new Connected();
+
+        addState(mDisconnected);
+        addState(mConnecting);
+        addState(mDisconnecting);
+        addState(mConnected);
+
+        setInitialState(mDisconnected);
+    }
+
+    static VcpControllerStateMachine make(BluetoothDevice device, VcpController svc,
+            Context context, VcpControllerNativeInterface nativeInterface, Looper looper) {
+        Log.i(TAG, "make for device " + device);
+        VcpControllerStateMachine VcpControllerSm =
+                new VcpControllerStateMachine(device, svc, context, nativeInterface, looper);
+        VcpControllerSm.start();
+        return VcpControllerSm;
+    }
+
+    public void doQuit() {
+        log("doQuit for device " + mDevice);
+        quitNow();
+    }
+
+    public void cleanup() {
+        log("cleanup for device " + mDevice);
+    }
+
+    @VisibleForTesting
+    class Disconnected extends State {
+        @Override
+        public void enter() {
+            Log.i(TAG, "Enter Disconnected(" + mDevice + "): " + messageWhatToString(
+                    getCurrentMessage().what));
+
+            removeDeferredMessages(DISCONNECT);
+            if (mLastConnectionState != -1) {
+                // Don't broadcast during startup
+                broadcastConnectionState(BluetoothProfile.STATE_DISCONNECTED,
+                        mLastConnectionState);
+            }
+
+            cleanupDevice();
+        }
+
+        @Override
+        public void exit() {
+            log("Exit Disconnected(" + mDevice + "): " + messageWhatToString(
+                    getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Disconnected process message(" + mDevice + "): " + messageWhatToString(
+                    message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    log("Connecting to " + device);
+
+                    if (!mDevice.equals(device)) {
+                        Log.e(TAG, "CONNECT failed, device=" + device + ", currentDev=" + mDevice);
+                        break;
+                    }
+
+                    if (!mNativeInterface.connectVcp(mDevice, true)) {
+                        Log.e(TAG, "Disconnected: error connecting to " + mDevice);
+                        break;
+                    }
+
+                    transitionTo(mConnecting);
+                    break;
+                case DISCONNECT:
+                    Log.w(TAG, "Disconnected: DISCONNECT ignored: " + mDevice);
+                    break;
+                case STACK_EVENT:
+                    VcpStackEvent event = (VcpStackEvent) message.obj;
+                    if (DBG) {
+                        Log.d(TAG, "Disconnected: stack event: " + event);
+                    }
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtfStack(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                        break;
+                    }
+                    switch (event.type) {
+                        case VcpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1);
+                            break;
+                        default:
+                            Log.e(TAG, "Disconnected: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                default:
+                    Log.e(TAG, "Unexpected msg " + messageWhatToString(message.what)
+                            + ": " + message);
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Disconnected state
+        private void processConnectionEvent(int state) {
+            switch (state) {
+                case VcpStackEvent.CONNECTION_STATE_DISCONNECTED:
+                    Log.w(TAG, "Ignore VCP DISCONNECTED event: " + mDevice);
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_CONNECTING:
+                    Log.i(TAG, "Incoming VCP Connecting request accepted: " + mDevice);
+                    if (mVcpController.okToConnect(mDevice)) {
+                        transitionTo(mConnecting);
+                    } else {
+                        // Reject the connection and stay in Disconnected state itself
+                        Log.w(TAG, "Incoming VCP Connecting request rejected: " + mDevice);
+                        mNativeInterface.disconnectVcp(mDevice);
+                    }
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_CONNECTED:
+                    Log.w(TAG, "VCP Connected from Disconnected state: " + mDevice);
+                    if (mVcpController.okToConnect(mDevice)) {
+                        Log.i(TAG, "Incoming VCP Connected request accepted: " + mDevice);
+                        transitionTo(mConnected);
+                    } else {
+                        // Reject the connection and stay in Disconnected state itself
+                        Log.w(TAG, "Incoming VCP Connected request rejected: " + mDevice);
+                        mNativeInterface.disconnectVcp(mDevice);
+                    }
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    Log.w(TAG, "Ignore VCP DISCONNECTING event: " + mDevice);
+                    break;
+                default:
+                    Log.e(TAG, "Incorrect state: " + state + " device: " + mDevice);
+                    break;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    class Connecting extends State {
+        @Override
+        public void enter() {
+            Log.i(TAG, "Enter Connecting(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs);
+            broadcastConnectionState(BluetoothProfile.STATE_CONNECTING, mLastConnectionState);
+        }
+
+        @Override
+        public void exit() {
+            log("Exit Connecting(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_CONNECTING;
+            removeMessages(CONNECT_TIMEOUT);
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Connecting process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    deferMessage(message);
+                    break;
+                case CONNECT_TIMEOUT:
+                    Log.w(TAG, "Connecting connection timeout: " + mDevice);
+                    mNativeInterface.disconnectVcp(mDevice);
+                    // We timed out trying to connect, transition to Disconnected state
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    if (!mDevice.equals(device)) {
+                        Log.e(TAG, "Unknown device timeout " + device);
+                        break;
+                    }
+                    Log.w(TAG, "CONNECT_TIMEOUT");
+                    transitionTo(mDisconnected);
+                    break;
+                case DISCONNECT:
+                    log("Connecting: connection canceled to " + mDevice);
+                    mNativeInterface.disconnectVcp(mDevice);
+                    transitionTo(mDisconnected);
+                    break;
+                case SET_VOLUME:
+                case MUTE:
+                case UNMUTE:
+                case SET_ABS_VOL_TIMEOUT:
+                case CHANGE_MUTE_TIMEOUT:
+                    deferMessage(message);
+                    break;
+                case STACK_EVENT:
+                    VcpStackEvent event = (VcpStackEvent) message.obj;
+                    log("Connecting: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtfStack(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case VcpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1);
+                            break;
+                        default:
+                            Log.e(TAG, "Connecting: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Connecting state
+        private void processConnectionEvent(int state) {
+            switch (state) {
+                case VcpStackEvent.CONNECTION_STATE_DISCONNECTED:
+                    Log.w(TAG, "Connecting device disconnected: " + mDevice);
+                    transitionTo(mDisconnected);
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_CONNECTED:
+                    transitionTo(mConnected);
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_CONNECTING:
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    Log.w(TAG, "Connecting interrupted: device is disconnecting: " + mDevice);
+                    transitionTo(mDisconnecting);
+                    break;
+                default:
+                    Log.e(TAG, "Incorrect state: " + state);
+                    break;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    class Disconnecting extends State {
+        @Override
+        public void enter() {
+            Log.i(TAG, "Enter Disconnecting(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs);
+            broadcastConnectionState(BluetoothProfile.STATE_DISCONNECTING, mLastConnectionState);
+        }
+
+        @Override
+        public void exit() {
+            log("Exit Disconnecting(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_DISCONNECTING;
+            removeMessages(CONNECT_TIMEOUT);
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Disconnecting process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT:
+                    deferMessage(message);
+                    break;
+                case CONNECT_TIMEOUT: {
+                    Log.w(TAG, "Disconnecting connection timeout: " + mDevice);
+                    mNativeInterface.disconnectVcp(mDevice);
+                    // We timed out trying to connect, transition to Disconnected state
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    if (!mDevice.equals(device)) {
+                        Log.e(TAG, "Unknown device timeout " + device);
+                        break;
+                    }
+                    transitionTo(mDisconnected);
+                    Log.w(TAG, "CONNECT_TIMEOUT");
+                    break;
+                }
+                case DISCONNECT:
+                    deferMessage(message);
+                    break;
+                case SET_VOLUME:
+                case MUTE:
+                case UNMUTE:
+                case SET_ABS_VOL_TIMEOUT:
+                case CHANGE_MUTE_TIMEOUT:
+                    deferMessage(message);
+                    break;
+                case STACK_EVENT:
+                    VcpStackEvent event = (VcpStackEvent) message.obj;
+                    log("Disconnecting: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtfStack(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case VcpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1);
+                            break;
+                        default:
+                            Log.e(TAG, "Disconnecting: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Disconnecting state
+        private void processConnectionEvent(int state) {
+            switch (state) {
+                case VcpStackEvent.CONNECTION_STATE_DISCONNECTED:
+                    Log.i(TAG, "Disconnected: " + mDevice);
+                    transitionTo(mDisconnected);
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_CONNECTED:
+                    if (mVcpController.okToConnect(mDevice)) {
+                        Log.w(TAG, "Disconnecting interrupted: device is connected: " + mDevice);
+                        transitionTo(mConnected);
+                    } else {
+                        // Reject the connection and stay in Disconnecting state
+                        Log.w(TAG, "Incoming VCP Connected request rejected: " + mDevice);
+                        mNativeInterface.disconnectVcp(mDevice);
+                    }
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_CONNECTING:
+                    if (mVcpController.okToConnect(mDevice)) {
+                        Log.i(TAG, "Disconnecting interrupted: try to reconnect: " + mDevice);
+                        transitionTo(mConnecting);
+                    } else {
+                        // Reject the connection and stay in Disconnecting state
+                        Log.w(TAG, "Incoming VCP Connecting request rejected: " + mDevice);
+                        mNativeInterface.disconnectVcp(mDevice);
+                    }
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    break;
+                default:
+                    Log.e(TAG, "Incorrect state: " + state);
+                    break;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    class Connected extends State {
+        @Override
+        public void enter() {
+            Log.i(TAG, "Enter Connected(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            removeDeferredMessages(CONNECT);
+            broadcastConnectionState(BluetoothProfile.STATE_CONNECTED, mLastConnectionState);
+        }
+
+        @Override
+        public void exit() {
+            log("Exit Connected(" + mDevice + "): "
+                    + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = BluetoothProfile.STATE_CONNECTED;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            log("Connected process message(" + mDevice + "): "
+                    + messageWhatToString(message.what));
+
+            switch (message.what) {
+                case CONNECT: {
+                    Log.w(TAG, "Connected: CONNECT ignored: " + mDevice);
+                    break;
+                }
+                case DISCONNECT: {
+                    log("Disconnecting from " + mDevice);
+                    if (!mNativeInterface.disconnectVcp(mDevice)) {
+                        // If error in the native stack, transition directly to Disconnected state.
+                        Log.e(TAG, "Connected: error disconnecting from " + mDevice);
+                        transitionTo(mDisconnected);
+                        break;
+                    }
+                    transitionTo(mDisconnecting);
+                    break;
+                }
+                case SET_VOLUME: {
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    if (!mDevice.equals(device)) {
+                        Log.w(TAG, "SET_VOLUME failed " + device
+                                + " is not currentDevice");
+                        break;
+                    }
+                    log("Set volume for " + device);
+
+                    processSetAbsVolume(message.arg1, message.arg2);
+                    break;
+                }
+                case MUTE: {
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    if (!mDevice.equals(device)) {
+                        Log.w(TAG, "Mute failed " + device
+                                + " is not currentDevice");
+                        break;
+                    }
+                    log("Mute for " + device);
+
+                    processSetMute();
+                    break;
+                }
+                case UNMUTE: {
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    if (!mDevice.equals(device)) {
+                        Log.w(TAG, "Unmute failed " + device
+                                + " is not currentDevice");
+                        break;
+                    }
+                    log("Unmute for " + device);
+
+                    processSetUnmute();
+                    break;
+                }
+                case SET_ABS_VOL_TIMEOUT: {
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    if (!mDevice.equals(device)) {
+                        Log.w(TAG, "Set abs vol timeout failed " + device
+                                + " is not currentDevice");
+                        break;
+                    }
+
+                    mAbsVolSetInProgress = false;
+                    if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) {
+                        Log.w(TAG, "Set abs vol retry exceed max times");
+                        mRequestedVolume = -1;
+                        mAbsVolRetryTimes = 0;
+                        break;
+                    } else {
+                        mAbsVolRetryTimes += 1;
+                        if (mNativeInterface.setAbsVolume(mRequestedVolume, mDevice)) {
+                            sendMessageDelayed(SET_ABS_VOL_TIMEOUT, mDevice,
+                                                            CMD_TIMEOUT_DELAY);
+                            mAbsVolSetInProgress = true;
+                        } else {
+                            mRequestedVolume = -1;
+                            mAbsVolRetryTimes = 0;
+                            Log.e(TAG, "Set absolute volume failed for device: " + mDevice);
+                        }
+                    }
+                    break;
+                }
+                case CHANGE_MUTE_TIMEOUT: {
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    if (!mDevice.equals(device)) {
+                        Log.w(TAG, "Mute timeout failed " + device
+                                + " is not currentDevice");
+                        break;
+                    }
+
+                    mMuteChangeInProgress = false;
+                    if (mChangeMuteRetryTimes >= MAX_ERROR_RETRY_TIMES) {
+                        Log.w(TAG, "Mute retry exceed max times");
+                        mChangeMuteRetryTimes = 0;
+                        mRequestedMuteState = -1;
+                        break;
+                    } else {
+                        mChangeMuteRetryTimes += 1;
+                        boolean ret;
+                        if (mRequestedMuteState == MUTE_STATE) {
+                            ret = mNativeInterface.mute(mDevice);
+                        } else {
+                            ret = mNativeInterface.unmute(mDevice);
+                        }
+
+                        if (ret) {
+                            sendMessageDelayed(CHANGE_MUTE_TIMEOUT, mDevice,
+                                                            CMD_TIMEOUT_DELAY);
+                            mMuteChangeInProgress = true;
+                        } else {
+                            mChangeMuteRetryTimes = 0;
+                            mRequestedMuteState = -1;
+                           Log.e(TAG, "Change Mute failed for device: " + mDevice);
+                        }
+                    }
+                    break;
+                }
+                case STACK_EVENT:
+                    VcpStackEvent event = (VcpStackEvent) message.obj;
+                    log("Connected: stack event: " + event);
+                    if (!mDevice.equals(event.device)) {
+                        Log.wtfStack(TAG, "Device(" + mDevice + "): event mismatch: " + event);
+                    }
+                    switch (event.type) {
+                        case VcpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            processConnectionEvent(event.valueInt1);
+                            break;
+                        case VcpStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED:
+                                processVolumeStateEvent(event.valueInt1, event.valueInt2);
+                            break;
+                        case VcpStackEvent.EVENT_TYPE_VOLUME_FLAGS_CHANGED:
+                            processVolumeFlagsChanged(event.valueInt1);
+                            break;
+                        default:
+                            Log.e(TAG, "Connected: ignoring stack event: " + event);
+                            break;
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        // in Connected state
+        private void processConnectionEvent(int state) {
+            switch (state) {
+                case VcpStackEvent.CONNECTION_STATE_DISCONNECTED:
+                    Log.i(TAG, "Disconnected from " + mDevice);
+                    transitionTo(mDisconnected);
+                    break;
+                case VcpStackEvent.CONNECTION_STATE_DISCONNECTING:
+                    Log.i(TAG, "Disconnecting from " + mDevice);
+                    transitionTo(mDisconnecting);
+                    break;
+                default:
+                    Log.e(TAG, "Connection State Device: " + mDevice + " bad state: " + state);
+                    break;
+            }
+        }
+    }
+
+    private void processSetAbsVolume(int volume, int audioType) {
+        log("process set absolute volume");
+
+        if (mAbsVolSetInProgress) {
+            mCachedVolume = volume;
+            mCachedVolumeControlAudioType = audioType;
+            Log.w(TAG, "There is already a volume command in progress, cache volume:  " +
+                    mCachedVolume + " cached audio type: " + audioType);
+            return;
+        }
+
+        if (mRemoteVolume == -1) {
+            Log.w(TAG, "remote not tell initial volume");
+            return;
+        }
+
+        if (mRemoteVolume == volume) {
+            Log.w(TAG, "Ignore set abs volume as current volume equals to requested volume");
+            return;
+        }
+
+        Log.d(TAG, "set abs volume for audio type: " + audioType);
+        if (mNativeInterface.setAbsVolume(volume, mDevice)) {
+            sendMessageDelayed(SET_ABS_VOL_TIMEOUT, mDevice,
+                                                CMD_TIMEOUT_DELAY);
+            mAbsVolSetInProgress = true;
+            mRequestedVolume = volume;
+            mVolumeControlAudioType = audioType;
+            mCachedVolume = -1;
+        } else {
+            Log.e(TAG, "Set absolute volume failed for device: " + mDevice);
+        }
+    }
+
+    private void processSetMute() {
+        log("process mute");
+
+        if (mMuteChangeInProgress) {
+            mCachedMuteState = MUTE_STATE;
+            Log.w(TAG, "There is already a mute change in progress, cache mute");
+            return;
+        }
+
+        if (mRemoteVolume == -1) {
+            Log.w(TAG, "remote not tell initial volume");
+            return;
+        }
+
+        if (mMuteState == MUTE_STATE) {
+            Log.w(TAG, "Ignore mute request as current state is mute");
+            return;
+        }
+
+        if (mNativeInterface.mute(mDevice)) {
+            sendMessageDelayed(CHANGE_MUTE_TIMEOUT, mDevice,
+                                                CMD_TIMEOUT_DELAY);
+            mMuteChangeInProgress = true;
+            mRequestedMuteState = MUTE_STATE;
+            mCachedMuteState = -1;
+        } else {
+            Log.e(TAG, "Mute failed for device: " + mDevice);
+        }
+    }
+
+    private void processSetUnmute() {
+        log("process unmute");
+
+        if (mMuteChangeInProgress) {
+            mCachedMuteState = UNMUTE_STATE;
+            Log.w(TAG, "There is already a mute change in progress, cache unmute");
+            return;
+        }
+
+        if (mRemoteVolume == -1) {
+            Log.w(TAG, "remote not tell initial volume");
+            return;
+        }
+
+        if (mMuteState == UNMUTE_STATE) {
+            Log.w(TAG, "Ignore unmute request as current state is unmute");
+            return;
+        }
+
+        if (mNativeInterface.unmute(mDevice)) {
+            sendMessageDelayed(CHANGE_MUTE_TIMEOUT, mDevice,
+                                                CMD_TIMEOUT_DELAY);
+            mMuteChangeInProgress = true;
+            mRequestedMuteState = UNMUTE_STATE;
+            mCachedMuteState = -1;
+        } else {
+            Log.e(TAG, "Unmute failed for device: " + mDevice);
+        }
+    }
+
+    private void processVolumeStateEvent(int vcpVol, int mute) {
+        log("process volume state event");
+
+        if (mRemoteVolume == -1 || mMuteState != mute ||
+                mMuteChangeInProgress == true) {
+            processMuteChanged(mute);
+        }
+
+        if (mRemoteVolume == -1 || mRemoteVolume != vcpVol ||
+                mAbsVolSetInProgress == true) {
+            processVolumeChanged(vcpVol);
+        }
+    }
+
+    private void processVolumeChanged(int vcpVol) {
+        log("process volume setting changed");
+
+        if (mAbsVolSetInProgress == true) {
+            mAbsVolSetInProgress = false;
+            removeMessages(SET_ABS_VOL_TIMEOUT);
+            if (mRequestedVolume == vcpVol) {
+                mRequestedVolume = -1;
+                mAbsVolRetryTimes = 0;
+
+                if ((mCachedVolume != -1) && (mCachedVolume != vcpVol)) {
+                    mVcpController.notifyVolumeChanged(mDevice, vcpVol, mVolumeControlAudioType);
+                    mVolumeControlAudioType = -1;
+                    Log.w(TAG, "Set cached volume to remote");
+                    if (mNativeInterface.setAbsVolume(mCachedVolume, mDevice)) {
+                        sendMessageDelayed(SET_ABS_VOL_TIMEOUT, mDevice,
+                                CMD_TIMEOUT_DELAY);
+                        mAbsVolSetInProgress = true;
+                        mRequestedVolume = mCachedVolume;
+                        mVolumeControlAudioType = mCachedVolumeControlAudioType;
+                        mCachedVolumeControlAudioType = -1;
+                        mCachedVolume = -1;
+                        return;
+                    } else {
+                        Log.e(TAG, "Set cached volume failed for device: " + mDevice);
+                        mCachedVolume = -1;
+                    }
+                }
+            } else {
+                Log.w(TAG, "Remote changed volume not equal to requested volume");
+                if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) {
+                    Log.w(TAG, "Set abs vol retry exceed max times");
+                    mRequestedVolume = -1;
+                    mAbsVolRetryTimes = 0;
+                } else {
+                    mAbsVolRetryTimes += 1;
+                    if (mNativeInterface.setAbsVolume(mRequestedVolume, mDevice)) {
+                        sendMessageDelayed(SET_ABS_VOL_TIMEOUT, mDevice,
+                                CMD_TIMEOUT_DELAY);
+                        mAbsVolSetInProgress = true;
+                        return;
+                    } else {
+                        Log.e(TAG, "Set absolute volume failed for device: " + mDevice);
+                        mRequestedVolume = -1;
+                        mAbsVolRetryTimes = 0;
+                    }
+                }
+            }
+        }
+
+        if (mRemoteVolume == -1) {
+            // Set initial volume if volume flags is not persisted
+            if ((mVolumeFlags == VOLUME_SETTING_NOT_PERSISTED)) {
+                int initialVolume = VCP_DEFAULT_VOL;
+                if (vcpVol != initialVolume) {
+                    mRemoteVolume = vcpVol;
+                    Log.w(TAG, "Set initial volume to remote if volume persisted flag is false");
+                    if (mNativeInterface.setAbsVolume(initialVolume, mDevice)) {
+                        sendMessageDelayed(SET_ABS_VOL_TIMEOUT, mDevice,
+                                                            CMD_TIMEOUT_DELAY);
+                        mAbsVolSetInProgress = true;
+                        mRequestedVolume = initialVolume;
+                        mVcpController.setAbsVolumeSupport(mDevice, true, initialVolume);
+                        mVcpController.updateConnState(mDevice, BluetoothProfile.STATE_CONNECTED);
+                        return;
+                    } else {
+                        Log.e(TAG, "Set absolute volume failed for device: " + mDevice);
+                    }
+                }
+            }
+            Log.w(TAG, "Set abs volume support and update initial volume to ACM");
+            mRemoteVolume = vcpVol;
+            mVcpController.setAbsVolumeSupport(mDevice, true, vcpVol);
+            mVcpController.updateConnState(mDevice, BluetoothProfile.STATE_CONNECTED);
+            return;
+        }
+
+        if (mRemoteVolume != vcpVol) {
+            mRemoteVolume = vcpVol;
+            mVcpController.notifyVolumeChanged(mDevice, vcpVol, mVolumeControlAudioType);
+            mVolumeControlAudioType = -1;
+            long pecentVolChanged = ((long)vcpVol * 100) / 0xff;
+            Log.w(TAG, "percent volume changed: " + pecentVolChanged + "%");
+        }
+    }
+
+    private void processMuteChanged(int mute) {
+        log("process mute changed");
+
+        if (mMuteChangeInProgress == true) {
+            mMuteChangeInProgress = false;
+            mChangeMuteRetryTimes = 0;
+            removeMessages(CHANGE_MUTE_TIMEOUT);
+
+            if ((mCachedMuteState != -1) && (mCachedMuteState != mute)) {
+                Log.w(TAG, "Set cached mute state to remote");
+                boolean ret;
+                if (mCachedMuteState == MUTE_STATE) {
+                    ret = mNativeInterface.mute(mDevice);
+                } else {
+                    ret = mNativeInterface.unmute(mDevice);
+                }
+
+                if (ret) {
+                    sendMessageDelayed(CHANGE_MUTE_TIMEOUT, mDevice,
+                            CMD_TIMEOUT_DELAY);
+                    mMuteChangeInProgress = true;
+                    mRequestedMuteState = mCachedMuteState;
+                    mCachedMuteState = -1;
+                    return;
+                }
+                mCachedMuteState = -1;
+            }
+        }
+
+        if (mMuteState != mute) {
+            mMuteState = mute;
+            boolean  isMute = (mMuteState == MUTE_STATE) ? true : false;
+            mVcpController.notifyMuteChanged(mDevice, isMute);
+            Log.w(TAG, "Mute state changed to " + mMuteState);
+        }
+    }
+
+    private void processVolumeFlagsChanged(int flags) {
+        log("process volume flags changed");
+        mVolumeFlags = flags;
+    }
+
+    int getConnectionState() {
+        String currentState = getCurrentState().getName();
+        switch (currentState) {
+            case "Disconnected":
+                return BluetoothProfile.STATE_DISCONNECTED;
+            case "Connecting":
+                return BluetoothProfile.STATE_CONNECTING;
+            case "Connected":
+                return BluetoothProfile.STATE_CONNECTED;
+            case "Disconnecting":
+                return BluetoothProfile.STATE_DISCONNECTING;
+            default:
+                Log.e(TAG, "Bad currentState: " + currentState);
+                return BluetoothProfile.STATE_DISCONNECTED;
+        }
+    }
+
+    int getVolume() {
+        return mRemoteVolume;
+    }
+
+    boolean isMute() {
+        if (mMuteState == MUTE_STATE)
+            return true;
+        else
+            return false;
+    }
+
+    BluetoothDevice getDevice() {
+        return mDevice;
+    }
+
+    synchronized boolean isConnected() {
+        return getCurrentState() == mConnected;
+    }
+
+    // This method does not check for error condition (newState == prevState)
+    private void broadcastConnectionState(int newState, int prevState) {
+        log("Connection state " + mDevice + ": " + profileStateToString(prevState)
+                    + "->" + profileStateToString(newState));
+        mVcpController.onConnectionStateChangedFromStateMachine(mDevice,
+                newState, prevState);
+
+        Intent intent = new Intent(BluetoothVcp.ACTION_CONNECTION_STATE_CHANGED);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mContext.sendBroadcast(intent, BLUETOOTH_CONNECT,
+             Utils.getTempAllowlistBroadcastOptions());
+    }
+
+    private void cleanupDevice() {
+        log("cleanup device " + mDevice);
+        mRemoteVolume = -1;
+        mRequestedVolume = -1;
+        mCachedVolume = -1;
+        mAbsVolRetryTimes = 0;
+        mAbsVolSetInProgress = false;
+        mMuteState = -1;
+        mRequestedMuteState = -1;
+        mCachedMuteState = -1;
+        mChangeMuteRetryTimes = 0;
+        mMuteChangeInProgress = false;
+        mVolumeFlags = -1;
+        mVolumeControlAudioType = -1;
+        mCachedVolumeControlAudioType = -1;
+    }
+
+    private static String messageWhatToString(int what) {
+        switch (what) {
+            case CONNECT:
+                return "CONNECT";
+            case DISCONNECT:
+                return "DISCONNECT";
+            case STACK_EVENT:
+                return "STACK_EVENT";
+            case CONNECT_TIMEOUT:
+                return "CONNECT_TIMEOUT";
+            case SET_VOLUME:
+                return "SET_VOLUME";
+            case MUTE:
+                return "MUTE";
+            case UNMUTE:
+                return "UNMUTE";
+            case SET_ABS_VOL_TIMEOUT:
+                return "SET_ABS_VOL_TIMEOUT";
+            case CHANGE_MUTE_TIMEOUT:
+                return "CHANGE_MUTE_TIMEOUT";
+            default:
+                return "UNKNOWN(" + what + ")";
+        }
+    }
+
+    private static String profileStateToString(int state) {
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return "DISCONNECTED";
+            case BluetoothProfile.STATE_CONNECTING:
+                return "CONNECTING";
+            case BluetoothProfile.STATE_CONNECTED:
+                return "CONNECTED";
+            case BluetoothProfile.STATE_DISCONNECTING:
+                return "DISCONNECTING";
+            default:
+                break;
+        }
+        return Integer.toString(state);
+    }
+
+    public void dump(StringBuilder sb) {
+        ProfileService.println(sb, "mDevice: " + mDevice);
+        ProfileService.println(sb, "  StateMachine: " + this);
+        // Dump the state machine logs
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+        super.dump(new FileDescriptor(), printWriter, new String[]{});
+        printWriter.flush();
+        stringWriter.flush();
+        ProfileService.println(sb, "  StateMachineLog:");
+        Scanner scanner = new Scanner(stringWriter.toString());
+        while (scanner.hasNextLine()) {
+            String line = scanner.nextLine();
+            ProfileService.println(sb, "    " + line);
+        }
+        scanner.close();
+    }
+
+    @Override
+    protected void log(String msg) {
+        if (DBG) {
+            super.log(msg);
+        }
+    }
+}
+
diff --git a/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpStackEvent.java b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpStackEvent.java
new file mode 100644
index 0000000..e77f95d
--- /dev/null
+++ b/le_audio/packages/apps/Bluetooth/src/com/android/bluetooth/vcp/VcpStackEvent.java
@@ -0,0 +1,79 @@
+/*
+ *Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.vcp;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * Stack event sent via a callback from JNI to Java, or generated
+ * internally by the VCP State Machine.
+ */
+public class VcpStackEvent {
+    // Event types for STACK_EVENT message (coming from native)
+    private static final int EVENT_TYPE_NONE = 0;
+    public static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
+    public static final int EVENT_TYPE_VOLUME_STATE_CHANGED = 2;
+    public static final int EVENT_TYPE_VOLUME_FLAGS_CHANGED = 3;
+
+    // Do not modify without updating the HAL bt_vcp.h files.
+    // Match up with enum class ConnectionState of bt_vcp_controller.h.
+    static final int CONNECTION_STATE_DISCONNECTED = 0;
+    static final int CONNECTION_STATE_CONNECTING = 1;
+    static final int CONNECTION_STATE_CONNECTED = 2;
+    static final int CONNECTION_STATE_DISCONNECTING = 3;
+
+    public int type;
+    public BluetoothDevice device;
+    public int valueInt1;
+    public int valueInt2;
+
+    VcpStackEvent(int type) {
+        this.type = type;
+    }
+
+    @Override
+    public String toString() {
+        // event dump
+        StringBuilder result = new StringBuilder();
+        result.append("VcpStackEvent {type:" + eventTypeToString(type));
+        result.append(", device:" + device);
+        result.append(", value1:" + valueInt1);
+        result.append(", value2:" + valueInt2);
+        result.append("}");
+        return result.toString();
+    }
+
+    private static String eventTypeToString(int type) {
+        switch (type) {
+            case EVENT_TYPE_NONE:
+                return "EVENT_TYPE_NONE";
+            case EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                return "EVENT_TYPE_CONNECTION_STATE_CHANGED";
+            case EVENT_TYPE_VOLUME_STATE_CHANGED:
+                return "EVENT_TYPE_VOLUME_STATE_CHANGED";
+            case EVENT_TYPE_VOLUME_FLAGS_CHANGED:
+                return "EVENT_TYPE_VOLUME_FLAGS_CHANGED";
+            default:
+                return "EVENT_TYPE_UNKNOWN:" + type;
+        }
+    }
+}
+
diff --git a/le_audio/packages/apps/Settings/Android.bp b/le_audio/packages/apps/Settings/Android.bp
new file mode 100644
index 0000000..ec3867d
--- /dev/null
+++ b/le_audio/packages/apps/Settings/Android.bp
@@ -0,0 +1,22 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+filegroup {
+    name: "settings-bluetooth-adva-srcs",
+    srcs: ["src/**/*.java"],
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BADevicePreferenceController.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BADevicePreferenceController.java
new file mode 100644
index 0000000..7f79465
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BADevicePreferenceController.java
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+package com.android.settings.bluetooth;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settings.bluetooth.BluetoothDeviceUpdater;
+import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater;
+import com.android.settings.connecteddevice.dock.DockUpdater;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import android.util.Log;
+import androidx.annotation.Keep;
+
+@Keep
+public class BADevicePreferenceController extends BasePreferenceController
+        implements LifecycleObserver, OnStart, OnStop, BleBroadcastSourceInfoPreferenceCallback {
+
+    private static final String TAG = "BADevicePreferenceController";
+    //Up to 3 Elements can be viewed here
+    private static final int MAX_DEVICE_NUM = 3;
+
+    private PreferenceGroup mPreferenceGroup;
+    private BluetoothBroadcastSourceInfoEntries mBleSourceInfoUpdater;
+    private int mPreferenceSize;
+    private CachedBluetoothDevice mCachedDevice;
+
+    public BADevicePreferenceController(Context context, Lifecycle lifecycle, String preferenceKey) {
+        super(context, preferenceKey);
+
+        lifecycle.addObserver(this);
+        BroadcastScanAssistanceUtils.debug(TAG, "constructor: KEY" + preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
+                )
+                ? AVAILABLE
+                : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return new String("added_sources");
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        BroadcastScanAssistanceUtils.debug(TAG, "displayPreference");
+        super.displayPreference(screen);
+        mPreferenceGroup = screen.findPreference(getPreferenceKey());
+        mPreferenceGroup.setVisible(false);
+
+        if (isAvailable()) {
+            BroadcastScanAssistanceUtils.debug(TAG, "registering wth BleSrcInfo updaters");
+            final Context context = screen.getContext();
+            if (mBleSourceInfoUpdater != null) {
+                mBleSourceInfoUpdater.setPrefContext(context);
+            }
+        }
+    }
+
+    @Override
+    public void onStart() {
+        if (mBleSourceInfoUpdater != null) {
+            mBleSourceInfoUpdater.registerCallback();
+        }
+    }
+
+    @Override
+    public void onStop() {
+        if (mBleSourceInfoUpdater != null) {
+            mBleSourceInfoUpdater.unregisterCallback();
+        }
+    }
+
+    public void init(DashboardFragment fragment, CachedBluetoothDevice device) {
+        BroadcastScanAssistanceUtils.debug(TAG, "Init");
+        mCachedDevice = device;
+        mBleSourceInfoUpdater = new BluetoothBroadcastSourceInfoEntries(fragment.getContext(),
+                fragment, BADevicePreferenceController.this,
+                device);
+        mPreferenceSize = 0;
+    }
+
+    @Override
+    public void onBroadcastSourceInfoAdded(Preference preference) {
+        BroadcastScanAssistanceUtils.debug(TAG, "onBroadcastSourceInfoAdded");
+
+        if (mPreferenceSize < MAX_DEVICE_NUM) {
+            boolean ret = mPreferenceGroup.addPreference(preference);
+            BroadcastScanAssistanceUtils.debug(TAG, "addPreference returns" + ret);
+            mPreferenceSize++;
+        }
+        updatePreferenceVisiblity();
+    }
+
+    @Override
+    public void onBroadcastSourceInfoRemoved(Preference preference) {
+         BroadcastScanAssistanceUtils.debug(TAG, "onBroadcastSourceInfoRemoved");
+        mPreferenceSize--;
+        boolean ret = mPreferenceGroup.removePreference(preference);
+        BroadcastScanAssistanceUtils.debug(TAG, "removePreference returns " + ret);
+        updatePreferenceVisiblity();
+    }
+
+    @VisibleForTesting
+    void setPreferenceGroup(PreferenceGroup preferenceGroup) {
+        mPreferenceGroup = preferenceGroup;
+    }
+
+    @VisibleForTesting
+    void updatePreferenceVisiblity() {
+        BroadcastScanAssistanceUtils.debug(TAG, "updatePreferenceVisiblity:"  + mPreferenceSize);
+        mPreferenceGroup.setVisible(mPreferenceSize > 0);
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoDetailsController.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoDetailsController.java
new file mode 100644
index 0000000..e470b29f
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoDetailsController.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.settings.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+import android.bluetooth.BleBroadcastSourceChannel;
+import android.bluetooth.BleBroadcastSourceChannel;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+import java.lang.String;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.preference.EditTextPreference;
+import androidx.preference.MultiSelectListPreference;
+import com.android.settingslib.widget.ActionButtonsPreference;
+
+import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.VendorCachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.MapProfile;
+import com.android.settingslib.bluetooth.PanProfile;
+import com.android.settingslib.bluetooth.PbapServerProfile;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import androidx.appcompat.app.AlertDialog;
+import android.text.Html;
+import android.text.TextUtils;
+import android.content.DialogInterface;
+import android.widget.Toast;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Arrays;
+import java.util.Iterator;
+import com.android.settings.R;
+import java.util.Map;
+
+/**
+ * This class Broadcast Source Info details of the given Scan delegator
+ */
+public class BleBroadcastSourceInfoDetailsController extends BluetoothDetailsController
+        implements Preference.OnPreferenceClickListener,
+         Preference.OnPreferenceChangeListener, CachedBluetoothDevice.Callback {
+    private static final String TAG = "BleBroadcastSourceInfoDetailsController";
+    private final String EMPTY_BD_ADDRESS = "00:00:00:00:00:00";
+
+    //Display controls
+    private static final String KEY_SOURCE_INFO_GROUP = "broadcast_source_details_category";
+    private static final String KEY_SOURCE_ID = "broadcast_si_sourceId";
+    private static final String KEY_SOURCE_DEVICE = "broadcast_si_source_address";
+    private static final String KEY_SOURCE_ENC_STATUS = "broadcast_si_encryption_state";
+    private static final String KEY_SOURCE_METADATA = "broadcast_si_metadata";
+    private static final String KEY_SOURCE_METADATA_STATE = "broadcast_si_metadata_state";
+    private static final String KEY_SOURCE_AUDIO_STATE = "broadcast_si_audio_state";
+
+    //Input Controls
+    private static final String KEY_SOURCE_METADATA_SWITCH = "broadcast_si_enable_metadata_sync";
+    private static final String KEY_SOURCE_AUDIOSYNC_SWITCH = "broadcast_si_enable_audio_sync";
+    private static final String KEY_UPDATE_BCAST_CODE = "update_broadcast_code";
+    private static final String KEY_UPDATE_SOURCE_INFO = "bcast_si_update_button";
+    private static final String KEY_REMOVE_SOURCE_INFO = "bcast_si_remove_button";
+
+    private CachedBluetoothDevice mCachedDevice;
+    private VendorCachedBluetoothDevice mVendorCachedDevice;
+    private PreferenceCategory mSourceInfoContainer;
+
+    private Preference mSourceIdPref;
+    private Preference mSourceDevicePref;
+    private Preference mSourceEncStatusPref;
+    private Preference mSourceMetadataPref;
+    private Preference mSourceMetadataSyncStatusPref;
+    private MultiSelectListPreference mSourceAudioSyncStatusPref;
+    private SwitchPreference mSourceMetadataSyncSwitchPref;
+    private SwitchPreference mSourceAudioSyncSwitchPref;
+    private EditTextPreference mSourceUpdateBcastCodePref;
+    private ActionButtonsPreference mSourceUpdateSourceInfoPref;
+    private ActionButtonsPreference mSourceRemoveSourceInfoPref;
+    private boolean mIsValueChanged = false;
+    private BleBroadcastSourceInfo mBleBroadcastSourceInfo;
+    private BleBroadcastAudioScanAssistManager mScanAssistanceMgr;
+    private boolean isBroadcastPINUpdated = false;
+    private String mBroadcastCode;
+    private int mSourceInfoIndex;
+    private String EMPTY_ENTRY = "EMPTY ENTRY";
+    private int mMetadataSyncState;
+    private int mAudioSyncState;
+    private boolean mIsButtonRefreshOnly = false;
+    private boolean mGroupOp = false;
+    private AlertDialog mScanAssistGroupOpDialog = null;
+    private List<BleBroadcastSourceChannel> mBisIndicies;
+    private boolean mPAsyncCtrlNeeded = false;
+
+    public BleBroadcastSourceInfoDetailsController(Context context,
+            PreferenceFragmentCompat fragment,
+            BleBroadcastSourceInfo bleSourceInfo, CachedBluetoothDevice device,
+            int sourceInfoIndex, Lifecycle lifecycle) {
+        super(context, fragment, device, lifecycle);
+        Context mContext = context;
+        mBleBroadcastSourceInfo = bleSourceInfo;
+        mCachedDevice = device;
+        LocalBluetoothManager mgr = Utils.getLocalBtManager(context);
+        LocalBluetoothProfileManager profileManager = mgr.getProfileManager();
+        mVendorCachedDevice = VendorCachedBluetoothDevice.getVendorCachedBluetoothDevice(device, profileManager);
+        mScanAssistanceMgr = mVendorCachedDevice.getScanAssistManager();
+        lifecycle.addObserver(this);
+        mSourceInfoIndex = sourceInfoIndex;
+        clearInputs();
+        mPAsyncCtrlNeeded = false;
+    }
+
+    private void clearInputs()
+    {    //Keep the  default state of Metadata as ON always
+         mMetadataSyncState =
+               BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC;
+         mAudioSyncState =
+               BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_INVALID;
+         mBroadcastCode = null;
+         isBroadcastPINUpdated = false;
+    }
+
+    private void triggerRemoveBroadcastSource() {
+        if (mScanAssistanceMgr != null) {
+            mScanAssistanceMgr.removeBroadcastSource(
+            mBleBroadcastSourceInfo.getSourceId(), mGroupOp);
+        }
+    }
+
+    private void onRemoveBroadcastSourceInfoPressed() {
+        BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":onRemoveBroadcastSourceInfoPressed:" +
+                                        mBleBroadcastSourceInfo);
+
+        ///*_CSIP
+        if (mCachedDevice.isGroupDevice()) {
+            String name = mCachedDevice.getName();
+            if (TextUtils.isEmpty(name)) {
+                name = mContext.getString(R.string.bluetooth_device);
+            }
+            String message = mContext.getString(R.string.group_remove_source_message, name);
+            String title = mContext.getString(R.string.group_remove_source_title);
+
+            DialogInterface.OnClickListener groupOpListener = new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    if (mScanAssistGroupOpDialog != null) {
+                        mScanAssistGroupOpDialog.dismiss();
+                    }
+                    mGroupOp = true;
+                    triggerRemoveBroadcastSource();
+                }
+            };
+            DialogInterface.OnClickListener nonGroupOpListener = new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    if (mScanAssistGroupOpDialog != null) {
+                        mScanAssistGroupOpDialog.dismiss();
+                    }
+
+                    mGroupOp = false;
+                    triggerRemoveBroadcastSource();
+                }
+            };
+            mGroupOp = false;
+            mScanAssistGroupOpDialog = BroadcastScanAssistanceUtils.showAssistanceGroupOptionsDialog(mContext,
+                mScanAssistGroupOpDialog, groupOpListener, nonGroupOpListener, title, Html.fromHtml(message));
+        } else {
+        //_CSIP*/
+            mGroupOp = false;
+            triggerRemoveBroadcastSource();
+        ///*_CSIP
+        }
+        //_CSIP*/
+    }
+
+    private int getSyncState(int metadataSyncState, int audioSyncState) {
+
+        if (audioSyncState == BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED &&
+            metadataSyncState == BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC) {
+            return BleBroadcastAudioScanAssistManager.SYNC_METADATA_AUDIO;
+        }
+
+        if (audioSyncState == BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED &&
+            metadataSyncState != BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC) {
+            return BleBroadcastAudioScanAssistManager.SYNC_AUDIO;
+        }
+        if (audioSyncState != BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED &&
+            metadataSyncState == BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC) {
+            return BleBroadcastAudioScanAssistManager.SYNC_METADATA;
+        }
+
+        return -1;
+    }
+
+     private void triggerUpdateBroadcastSource() {
+         if (mScanAssistanceMgr != null) {
+              if (mIsValueChanged == true) {
+                  int syncState = getSyncState(mMetadataSyncState, mAudioSyncState);
+                if (syncState == -1) {
+                    Log.e(TAG, "triggerUpdateBroadcastSource: Invalid sync Input, Ignore");
+                    return;
+                }
+                  mScanAssistanceMgr.updateBroadcastSource(
+                      mBleBroadcastSourceInfo.getSourceId(),
+                      getSyncState(mMetadataSyncState, mAudioSyncState),
+                      mBisIndicies, mGroupOp);
+                  mIsValueChanged = false;
+              }
+              if (isBroadcastPINUpdated) {
+                  mScanAssistanceMgr.setBroadcastCode(
+                      mBleBroadcastSourceInfo.getSourceId(),mBroadcastCode, mGroupOp);
+                  isBroadcastPINUpdated = false;
+              }
+              clearInputs();
+          }
+    }
+
+    private void onUpdateBroadcastSourceInfoPressed() {
+        BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  +
+               "onUpdateBroadcastSourceInfoPressed:" + mBleBroadcastSourceInfo);
+
+        ///*_CSIP
+        if (mCachedDevice.isGroupDevice()) {
+            String name = mCachedDevice.getName();
+            if (TextUtils.isEmpty(name)) {
+                name = mContext.getString(R.string.bluetooth_device);
+            }
+            String message = mContext.getString(R.string.group_update_source_message, name);
+            String title = mContext.getString(R.string.group_update_source_title);
+
+            DialogInterface.OnClickListener groupOpListener = new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    mGroupOp = true;
+                    triggerUpdateBroadcastSource();
+                }
+            };
+            DialogInterface.OnClickListener nonGroupOpListener = new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    mGroupOp = false;
+                    triggerUpdateBroadcastSource();
+                }
+            };
+            mGroupOp = false;
+            mScanAssistGroupOpDialog = BroadcastScanAssistanceUtils.showAssistanceGroupOptionsDialog(mContext,
+                mScanAssistGroupOpDialog, groupOpListener, nonGroupOpListener, title, Html.fromHtml(message));
+        } else {
+        //_CSIP*/
+            mGroupOp = false;
+            triggerUpdateBroadcastSource();
+        ///*_CSIP
+        }
+        //_CSIP*/
+    }
+
+    @Override
+    protected void init(PreferenceScreen screen) {
+        mSourceInfoContainer =
+                 (PreferenceCategory)screen.findPreference(getPreferenceKey());
+        mSourceIdPref = (Preference)mSourceInfoContainer.findPreference(
+                         KEY_SOURCE_ID);
+        mSourceDevicePref = (Preference)mSourceInfoContainer.findPreference(
+                         KEY_SOURCE_DEVICE);
+        mSourceEncStatusPref = (Preference)mSourceInfoContainer.findPreference(
+                         KEY_SOURCE_ENC_STATUS);
+        mSourceMetadataPref = (Preference)mSourceInfoContainer.findPreference(
+                         KEY_SOURCE_METADATA);
+        mSourceMetadataSyncStatusPref = (Preference)mSourceInfoContainer.findPreference(
+                         KEY_SOURCE_METADATA_STATE);
+        if (mPAsyncCtrlNeeded) {
+            mSourceMetadataSyncSwitchPref = (SwitchPreference)mSourceInfoContainer.findPreference(
+                         KEY_SOURCE_METADATA_SWITCH);
+
+            if (mSourceMetadataSyncSwitchPref != null) {
+                mSourceMetadataSyncSwitchPref.setOnPreferenceClickListener(this);
+            }
+        }
+        mSourceAudioSyncStatusPref = (MultiSelectListPreference)mSourceInfoContainer.findPreference(
+                         KEY_SOURCE_AUDIO_STATE);
+        if (mSourceAudioSyncStatusPref != null) {
+            mSourceAudioSyncStatusPref.setOnPreferenceChangeListener(this);
+        }
+
+        mSourceAudioSyncSwitchPref = (SwitchPreference)mSourceInfoContainer.findPreference(
+                         KEY_SOURCE_AUDIOSYNC_SWITCH);
+        if (mSourceAudioSyncSwitchPref != null) {
+            mSourceAudioSyncSwitchPref.setOnPreferenceClickListener(this);
+        }
+        mSourceUpdateBcastCodePref =
+                     (EditTextPreference)mSourceInfoContainer.findPreference(
+                         KEY_UPDATE_BCAST_CODE);
+        if (mSourceUpdateBcastCodePref != null) {
+            mSourceUpdateBcastCodePref.setOnPreferenceClickListener(this);
+            mSourceUpdateBcastCodePref.setOnPreferenceChangeListener(this);
+        }
+        mSourceUpdateSourceInfoPref =
+                     ((ActionButtonsPreference)mSourceInfoContainer.findPreference(
+                         KEY_UPDATE_SOURCE_INFO))
+                         .setButton1Text(R.string.update_sourceinfo_btn_txt)
+                         .setButton1Enabled(false)
+                         .setButton1OnClickListener((view)->onUpdateBroadcastSourceInfoPressed())
+                         .setButton2Text(R.string.remove_sourceinfo_btn_txt)
+                         .setButton2Icon(R.drawable.ic_settings_close)
+                         .setButton2Enabled(false)
+                         .setButton2OnClickListener((view)->onRemoveBroadcastSourceInfoPressed());
+        refresh();
+    }
+
+    @Override
+    public void onDeviceAttributesChanged() {
+        //update the Local variable If the receiverState is
+        //updated with some values
+        final Map<Integer, BleBroadcastSourceInfo>  srcInfos =
+                    mVendorCachedDevice.getAllBleBroadcastreceiverStates();
+        if (srcInfos == null) {
+            return;
+        }
+        for (Map.Entry<Integer, BleBroadcastSourceInfo> entry: srcInfos.entrySet()) {
+            Integer index = entry.getKey();
+            BleBroadcastSourceInfo sourceInfo = entry.getValue();
+            String toastString = null;
+            if (index == mSourceInfoIndex) {
+                BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":matching source Info");
+                if (sourceInfo.isEmptyEntry()) {
+                    BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":source info seem to be removed");
+                    toastString = "Source Info Removal";
+                    mBleBroadcastSourceInfo = sourceInfo;
+                }
+                else if (sourceInfo.equals(mBleBroadcastSourceInfo) != true) {
+                    //toast Message
+                    mBleBroadcastSourceInfo = sourceInfo;
+                    BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":Update in Broadcast Source Information");
+                    toastString = "Source Info Update";
+                } else {
+                    BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":No Update to Source Information values");
+                }
+            } else {
+                BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":Ignore this case");
+            }
+            if (toastString != null) {
+                Toast toast = Toast.makeText(mContext, toastString, Toast.LENGTH_SHORT);
+                toast.show();
+            }
+        }
+        refresh();
+    }
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        String key = preference.getKey();
+        BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":onPreferenceChange" + newValue);
+        if (key.equals(KEY_UPDATE_BCAST_CODE)) {
+            EditTextPreference pref = (EditTextPreference)preference;
+            String code = (String)newValue;
+            //Use different flag for Broadcast pin
+            isBroadcastPINUpdated = true;
+            mBroadcastCode = (String)newValue;
+        } else if (key.equals(KEY_SOURCE_AUDIO_STATE)) {
+            BroadcastScanAssistanceUtils.debug(TAG, ">>Checked:" +newValue);
+            CharSequence[] getEntriesSeqence =
+                ((MultiSelectListPreference)preference).getEntries();
+            Set<String> valueSet = ((MultiSelectListPreference)preference).getValues();
+
+            String[] selectedStrings = new String[((Set<String>) newValue).size()];
+
+             //noinspection unchecked
+             int j =0;
+             for (String value : (Set<String>) newValue) {
+                 selectedStrings[j] = value;
+                 for (int i=0; i<mBisIndicies.size(); i++) {
+                     if (value.equals(mBisIndicies.get(i).getDescription())) {
+                        BroadcastScanAssistanceUtils.debug(TAG, "Selected: value["+ i + "]- " + value);
+                        if (mBisIndicies.get(i).getStatus() == true) {
+                            mBisIndicies.get(i).setStatus(false);
+                        } else {
+                            mBisIndicies.get(i).setStatus(true);
+                        }
+                    }
+                 }
+                 BroadcastScanAssistanceUtils.debug(TAG, "value["+ j++ + "]- " + value);
+             }
+             mIsValueChanged = true;
+        }
+        mIsButtonRefreshOnly = true;
+        refresh();
+        return true;
+    }
+
+   /**
+     * When the pref for a ble broadcast source info details is clicked on, necessary action will be
+     * taken and updateBroadcastSourceInfo would be called as needed
+     */
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        String key = preference.getKey();
+        BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":onPreferenceClick");
+        mIsValueChanged = true;
+        if (mPAsyncCtrlNeeded) {
+            if (key.equals(KEY_SOURCE_METADATA_SWITCH)) {
+                SwitchPreference pref = (SwitchPreference)preference;
+                BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":Meta data sync state: " + pref.isChecked());
+                if (pref.isChecked()) mMetadataSyncState = BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC;
+                else mMetadataSyncState = BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IDLE;
+
+                //Update the audio sync state as well
+                if (mSourceAudioSyncSwitchPref != null) {
+                    if (mSourceAudioSyncSwitchPref.isChecked()) {
+                        mAudioSyncState = BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED;
+                    }
+                    else {
+                        mAudioSyncState = BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED;
+                    }
+                }
+            }
+        }
+        if (key.equals(KEY_SOURCE_AUDIOSYNC_SWITCH)) {
+            SwitchPreference pref = (SwitchPreference)preference;
+            BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":Audio sync state:  " + pref.isChecked());
+
+            if (pref.isChecked()) mAudioSyncState = BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED;
+            else mAudioSyncState = BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED;
+
+            if (mPAsyncCtrlNeeded) {
+                //Update the metadata sync state as well
+                if (mSourceMetadataSyncSwitchPref != null) {
+                    if (mSourceMetadataSyncSwitchPref.isChecked()) {
+                        mMetadataSyncState = BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED;
+                    }
+                    else {
+                        mMetadataSyncState = BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED;
+                    }
+                }
+            }
+        } else if (key.equals(KEY_UPDATE_BCAST_CODE)) {
+            EditTextPreference pref = (EditTextPreference)preference;
+            BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":>>Pin code updated:  " + pref.getText());
+            //Use different flag for Broadcast pin
+            mIsValueChanged = false;
+            isBroadcastPINUpdated = true;
+            mBroadcastCode = pref.getText();
+        } else {
+            BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":unhandled preference");
+            mIsValueChanged = false;
+        }
+        BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":onPreferenceClick" + mBleBroadcastSourceInfo);
+        mIsButtonRefreshOnly = true;
+        refresh();
+        return true;
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mCachedDevice.unregisterCallback(this);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mCachedDevice.registerCallback(this);
+    }
+
+    String getEncryptionStatusString(int encryptionStatus) {
+
+        switch(encryptionStatus) {
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_INVALID:
+                return "ENCRYPTION STATE UNKNOWN";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_UNENCRYPTED:
+                return "UNENCRYPTED STREAMING";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED:
+                return "PIN UPDATE NEEDED";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_DECRYPTING:
+                return "DECRYPTING SUCCESSFULLY";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_BADCODE:
+                return "INCORRECT BROADCAST PIN";
+        }
+        return "ENCRYPTION STATE UNKNOWN";
+    }
+
+     String getMetadataSyncStatusString(int metadataSyncStatus) {
+
+        switch(metadataSyncStatus) {
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IDLE:
+                return "IDLE";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_INVALID:
+                return "UNKNOWN";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC:
+                return "IN SYNC";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_NO_PAST:
+                return "NO PAST";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_SYNCINFO_REQ:
+                return "SYNCINFO NEEDED";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_SYNC_FAIL:
+                return "SYNC FAIL";
+        }
+        return "UNKNOWN";
+    }
+
+     String getAudioSyncStatusString(int audioSyncStatus) {
+
+        switch(audioSyncStatus) {
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_INVALID:
+                return "UNKNOWN";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED:
+                return "NOT IN SYNC";
+            case BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED:
+                return "IN SYNC";
+        }
+        return "UNKNOWN";
+    }
+
+    private boolean isPinUpdatedNeeded() {
+        boolean ret = false;
+
+        if (BroadcastScanAssistanceUtils.isLocalDevice(mBleBroadcastSourceInfo.getSourceDevice())) {
+            BroadcastScanAssistanceUtils.debug(TAG, "Local Device, Dont allow User to update PWD");
+            return false;
+        }
+        if (mBleBroadcastSourceInfo.getEncryptionStatus()
+           == BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_PIN_NEEDED) {
+            ret = true;
+        }
+
+        BroadcastScanAssistanceUtils.debug(TAG, "isPinUpdatedNeeded return" + ret);
+        return ret;
+    }
+
+    /**
+     * Refreshes the state of the switches for all profiles, possibly adding or removing switches as
+     * needed.
+     */
+    @Override
+    protected void refresh() {
+        BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":refresh: " + mBleBroadcastSourceInfo + " mSourceIndex" + mSourceInfoIndex);
+        mSourceIdPref.setSummary(
+                 String.valueOf(mBleBroadcastSourceInfo.getSourceId()));
+
+        BluetoothDevice dev = mBleBroadcastSourceInfo.getSourceDevice();
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        String s = null;
+        if (dev != null && adapter != null) {
+            if (adapter.getAddress().equals(dev.getAddress()))
+            {
+               s = adapter.getName() + "(Self)";
+            } else {
+               s = dev.getAlias();
+            }
+            if (s == null) {
+                s = String.valueOf(dev.getAddress());
+            }
+        }
+        if (s == null || s.equals(EMPTY_BD_ADDRESS)) {
+            BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":NULL source device");
+            s = "EMPTY_ENTRY";
+        }
+        mSourceDevicePref.setSummary(s);
+        mSourceEncStatusPref.setSummary(
+                getEncryptionStatusString(
+                          mBleBroadcastSourceInfo.getEncryptionStatus())
+                          );
+
+        if (mBleBroadcastSourceInfo.isEmptyEntry()) {
+            BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":Source Information seem to be Empty");
+            if (mPAsyncCtrlNeeded) {
+                mSourceMetadataSyncSwitchPref.setEnabled(false);
+            }
+            mSourceAudioSyncSwitchPref.setEnabled(false);
+            mSourceUpdateBcastCodePref.setEnabled(false);
+            //Disable 'remove and update source Info' if It is empty entry
+            mSourceUpdateSourceInfoPref.setButton1Enabled(false);
+            mSourceUpdateSourceInfoPref.setButton2Enabled(false);
+            mSourceAudioSyncStatusPref.setEnabled(false);
+            mIsValueChanged = false;
+        } else {
+            //enable the Input controls
+            if (mPAsyncCtrlNeeded) {
+                mSourceMetadataSyncSwitchPref.setEnabled(true);
+            }
+            mSourceAudioSyncSwitchPref.setEnabled(true);
+            mSourceUpdateBcastCodePref.setEnabled(isPinUpdatedNeeded());
+
+            if (mIsButtonRefreshOnly != true) {
+                mSourceMetadataSyncStatusPref.setSummary(
+                     getMetadataSyncStatusString(mBleBroadcastSourceInfo.getMetadataSyncState())
+                       );
+                   mSourceAudioSyncStatusPref.setSummary(
+                     getAudioSyncStatusString(mBleBroadcastSourceInfo.getAudioSyncState())
+                       );
+                   mBisIndicies = mBleBroadcastSourceInfo.getBroadcastChannelsSyncStatus();
+                   if (mBisIndicies != null) {
+                       String[] bisNames = new String[mBisIndicies.size()];
+                       boolean[] bisStatuses = new boolean[mBisIndicies.size()];
+                       Set<String> hashSet = new HashSet<String>();
+                       for (int i=0; i<mBisIndicies.size(); i++) {
+                              bisNames[i] = mBisIndicies.get(i).getDescription();
+                           bisStatuses[i] = mBisIndicies.get(i).getStatus();
+                       }
+                       hashSet.addAll(Arrays.asList(bisNames));
+                       mSourceAudioSyncStatusPref.setEntries(bisNames);
+                       mSourceAudioSyncStatusPref.setEntryValues(bisNames);
+                       mSourceAudioSyncStatusPref.setValues(hashSet);
+                   }
+
+                //Reflect the controls based on the status
+                if (mPAsyncCtrlNeeded) {
+                    mSourceMetadataSyncSwitchPref.setChecked(mBleBroadcastSourceInfo.getMetadataSyncState() ==
+                        BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC);
+                }
+                mSourceAudioSyncSwitchPref.setChecked(mBleBroadcastSourceInfo.getAudioSyncState() ==
+                    BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED);
+
+                int getFirstSyncedBisIndex = -1;
+                if (mBisIndicies != null) {
+                    for (int i=0; i<mBisIndicies.size(); i++) {
+                        if (mBisIndicies.get(i).getStatus() == true) {
+                            getFirstSyncedBisIndex = i;
+                            break;
+                        }
+                    }
+                }
+                byte[] metadata = null;
+                if (getFirstSyncedBisIndex != -1) {
+                    metadata = mBisIndicies.get(getFirstSyncedBisIndex).getMetadata();
+                }
+                if (metadata != null) {
+                    String metaDataStr = new String(metadata);
+                    BroadcastScanAssistanceUtils.debug(TAG, mSourceInfoIndex  + ":Metadata:" + metaDataStr);
+                    mSourceMetadataPref.setSummary(metaDataStr);
+                } else {
+                    mSourceMetadataPref.setSummary("NONE");
+                }
+                if (mBleBroadcastSourceInfo != null &&
+                    (mBleBroadcastSourceInfo.getMetadataSyncState() != BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC &&
+                    mBleBroadcastSourceInfo.getAudioSyncState() != BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED)) {
+                    //Remove source Info button
+                    mSourceUpdateSourceInfoPref.setButton2Enabled(true);
+                } else {
+                    mSourceUpdateSourceInfoPref.setButton2Enabled(false);
+                }
+            }
+            //User can update OR remove only if the Source info is not an Empty Entry
+            if (mIsValueChanged || isBroadcastPINUpdated) {
+                //User can Update only if any of the entries are modified by user action
+                mSourceUpdateSourceInfoPref.setButton1Enabled(true);
+            } else {
+                mSourceUpdateSourceInfoPref.setButton1Enabled(false);
+            }
+            mIsButtonRefreshOnly = false;
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_SOURCE_INFO_GROUP;
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoDetailsFragment.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoDetailsFragment.java
new file mode 100644
index 0000000..ec0d3a5
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoDetailsFragment.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.settings.bluetooth;
+
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.content.Context;
+import android.os.Bundle;
+import android.provider.DeviceConfig;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+
+import com.android.settings.R;
+import com.android.settings.core.SettingsUIDeviceConfig;
+import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.slices.BlockingSlicePrefController;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BleBroadcastSourceInfoDetailsFragment extends RestrictedDashboardFragment {
+    public static final String KEY_DEVICE_ADDRESS = "device_address";
+    public static final String KEY_SOURCE_INFO = "broadcast_source_info";
+    public static final String KEY_SOURCE_INFO_INDEX = "broadcast_source_index";
+    private static final String TAG = "SourceInfoDetailsFrg";
+    private Context mContext;
+
+    String mDeviceAddress;
+    CachedBluetoothDevice mCachedDevice;
+    LocalBluetoothManager mManager;
+    BleBroadcastSourceInfo mBleBroadcastSourceInfo;
+    Integer mSourceInfoIndex = -1;
+
+    public BleBroadcastSourceInfoDetailsFragment() {
+        super(DISALLOW_CONFIG_BLUETOOTH);
+    }
+
+    CachedBluetoothDevice getCachedDevice(String deviceAddress) {
+        BluetoothDevice remoteDevice =
+                 mManager.getBluetoothAdapter().getRemoteDevice(deviceAddress);
+        return mManager.getCachedDeviceManager().findDevice(remoteDevice);
+     }
+
+    public static BleBroadcastSourceInfoDetailsFragment newInstance(String deviceAddress) {
+        Bundle args = new Bundle(1);
+        args.putString(KEY_DEVICE_ADDRESS, deviceAddress);
+        BleBroadcastSourceInfoDetailsFragment fragment = new BleBroadcastSourceInfoDetailsFragment();
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        mDeviceAddress = getArguments().getString(KEY_DEVICE_ADDRESS);
+        mManager = Utils.getLocalBtManager(context);
+        mBleBroadcastSourceInfo = getArguments().getParcelable(KEY_SOURCE_INFO);
+        mCachedDevice = getCachedDevice(mDeviceAddress);
+        mSourceInfoIndex = getArguments().getInt(KEY_SOURCE_INFO_INDEX);
+        super.onAttach(context);
+        if (mCachedDevice == null) {
+            // Close this page if device is null with invalid device mac address
+            Log.w(TAG, "onAttach() CachedDevice is null!");
+            finish();
+            return;
+        }
+        if (mBleBroadcastSourceInfo == null) {
+            Log.w(TAG, "onAttach()  mBleBroadcastSourceInfo null!");
+            finish();
+            return;
+        }
+        if (mSourceInfoIndex == null) {
+            Log.w(TAG, "onAttach()  mSourceInfoIndex null!");
+            finish();
+            return;
+        }
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.BLUETOOTH_DEVICE_DETAILS;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.bcast_source_info_details_fragment;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+        ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
+
+        if (mCachedDevice != null && mBleBroadcastSourceInfo != null) {
+            Lifecycle lifecycle = getSettingsLifecycle();
+            controllers.add(new BleBroadcastSourceInfoDetailsController(context, this, mBleBroadcastSourceInfo,
+                    mCachedDevice, mSourceInfoIndex, lifecycle));
+        }
+        return controllers;
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoPreference.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoPreference.java
new file mode 100644
index 0000000..50c4636
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoPreference.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.UserManager;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.ImageView;
+import android.util.Log;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.GearPreference;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.Integer;
+import java.lang.String;
+/**
+ * BleBroadcastSourceInfoPreference is the preference type used to display each
+ * Broadcast Source information stored in the Remote Scan delegator.
+ */
+public final class BleBroadcastSourceInfoPreference extends GearPreference implements
+        CachedBluetoothDevice.Callback {
+    private static final String TAG = "BleBroadcastSourceInfoPreference";
+
+    private static String EMPTY_BD_ADDR = "00:00:00:00:00:00";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SortType.TYPE_DEFAULT,
+            SortType.TYPE_FIFO})
+    public @interface SortType {
+        int TYPE_DEFAULT = 1;
+        int TYPE_FIFO = 2;
+    }
+
+    private final CachedBluetoothDevice mCachedDevice;
+    private BleBroadcastSourceInfo mBleSourceInfo;
+    private final Integer mIndex;
+    private final long mCurrentTime;
+    private final int mType;
+
+    ///private String contentDescription = null;
+    //@VisibleForTesting
+    //boolean mNeedNotifyHierarchyChanged = false;
+    /* Talk-back descriptions for various BT icons */
+    Resources mResources;
+
+    public BleBroadcastSourceInfoPreference(Context context, CachedBluetoothDevice device,
+            BleBroadcastSourceInfo sourceInfo,
+            Integer index, @SortType int type) {
+        super(context, null);
+        mResources = getContext().getResources();
+        mIndex = index;
+
+        mCachedDevice = device;
+        mBleSourceInfo = sourceInfo;
+        mCachedDevice.registerCallback(this);
+        mCurrentTime = System.currentTimeMillis();
+        mType = type;
+
+        onDeviceAttributesChanged();
+    }
+
+
+    @Override
+    protected boolean shouldHideSecondTarget() {
+        return (mBleSourceInfo == null);
+    }
+
+    @Override
+    protected int getSecondTargetResId() {
+        return R.layout.preference_widget_gear;
+    }
+
+    CachedBluetoothDevice getCachedDevice() {
+        return mCachedDevice;
+    }
+
+    public BleBroadcastSourceInfo getBleBroadcastSourceInfo() {
+        return mBleSourceInfo;
+    }
+
+    public void setBleBroadcastSourceInfo(BleBroadcastSourceInfo srcInfo) {
+        mBleSourceInfo = srcInfo;
+        //refresh
+        onDeviceAttributesChanged();
+    }
+
+    Integer getSourceInfoIndex() {
+        return mIndex;
+    }
+
+    @Override
+    protected void onPrepareForRemoval() {
+        super.onPrepareForRemoval();
+        mCachedDevice.unregisterCallback(this);
+    }
+
+    String formSyncSummaryString(BleBroadcastSourceInfo srcInfo) {
+        String metadataStatus = "Metadata Synced";
+        String audioSyncStatus = "Audio Synced";
+
+        if (srcInfo.getMetadataSyncState() == BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_IN_SYNC) {
+            metadataStatus = "Metadata Synced";
+        } else {
+            metadataStatus = "Metadata not synced";
+        }
+
+        if (srcInfo.getAudioSyncState() == BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_SYNCHRONIZED) {
+            audioSyncStatus = "Audio Synced";
+        } else {
+            audioSyncStatus = "Audio not synced";
+        }
+        return metadataStatus + ", " + audioSyncStatus;
+    }
+
+    public void onDeviceAttributesChanged() {
+        BluetoothDevice dev = mBleSourceInfo.getSourceDevice();
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        String s = null;
+        if (dev != null && adapter != null) {
+            if (adapter.getAddress().equals(dev.getAddress()))
+            {
+               s = adapter.getName() + "(Self)";
+            } else {
+               s = dev.getAlias();
+            }
+            if (s == null) {
+                s = String.valueOf(dev.getAddress());
+            }
+        }
+        if (s == null || s.equals(EMPTY_BD_ADDR)) {
+            BroadcastScanAssistanceUtils.debug(TAG, "seem to be an entry source Info");
+            s = "EMPTY ENTRY";
+        }
+        setTitle(s);
+        setIcon(R.drawable.ic_media_stream);
+        if (!mBleSourceInfo.isEmptyEntry()) {
+            //Show the status only If it is not an Empty Entry
+            setSummary(formSyncSummaryString(mBleSourceInfo));
+        } else {
+            setSummary("");
+        }
+        setVisible(true);
+
+        // This could affect ordering, so notify that
+        notifyHierarchyChanged();
+    }
+
+
+
+    @Override
+    public boolean equals(Object o) {
+        if ((o == null) || !(o instanceof BleBroadcastSourceInfoPreference)) {
+            BroadcastScanAssistanceUtils.debug(TAG, "Not an Instance of BleBroadcastSourceInfoPreference:");
+            return false;
+        }
+        BleBroadcastSourceInfo otherSrc = ((BleBroadcastSourceInfoPreference) o).mBleSourceInfo;
+        BroadcastScanAssistanceUtils.debug(TAG, "Comparing: " + mBleSourceInfo);
+        BroadcastScanAssistanceUtils.debug(TAG, "TO: " + otherSrc);
+        boolean ret = (mBleSourceInfo.getSourceId() == otherSrc.getSourceId());
+        BroadcastScanAssistanceUtils.debug(TAG, "equals returns: " + ret);
+
+        return ret;
+    }
+
+    @Override
+    public int hashCode() {
+        return mBleSourceInfo.hashCode();
+    }
+
+    @Override
+    public int compareTo(Preference another) {
+        if (!(another instanceof BleBroadcastSourceInfoPreference)) {
+            // Rely on default sort
+            return super.compareTo(another);
+        }
+
+        switch (mType) {
+            case SortType.TYPE_DEFAULT:
+                BroadcastScanAssistanceUtils.debug(TAG, ">>compareTo");
+                return mIndex > ((BleBroadcastSourceInfoPreference) another).getSourceInfoIndex() ? 1 : -1;
+            case SortType.TYPE_FIFO:
+                return mCurrentTime > ((BleBroadcastSourceInfoPreference) another).mCurrentTime ? 1 : -1;
+            default:
+                return super.compareTo(another);
+        }
+    }
+
+    void onClicked() {
+        Context context = getContext();
+
+        final MetricsFeatureProvider metricsFeatureProvider =
+                FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoPreferenceCallback.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoPreferenceCallback.java
new file mode 100644
index 0000000..8f9a4fb
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoPreferenceCallback.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.settings.bluetooth;
+
+import androidx.preference.Preference;
+
+/**
+ * Callback to add or remove {@link Preference} in Ble broadcast source info
+ * entries.
+ */
+public interface BleBroadcastSourceInfoPreferenceCallback {
+    /**
+     * Called when a Ble broadcast sourc Information is added
+     * @param preference present the device
+     */
+    void onBroadcastSourceInfoAdded(Preference preference);
+
+    /**
+     * Called when a Ble broadast source Information is removed
+     * @param preference present the device
+     */
+    void onBroadcastSourceInfoRemoved(Preference preference);
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoUpdater.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoUpdater.java
new file mode 100644
index 0000000..6baa20a
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BleBroadcastSourceInfoUpdater.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.settings.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.content.Context;
+import java.util.Iterator;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.widget.GearPreference;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.VendorCachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.lang.Integer;
+
+/**
+ * Update the Ble broadcast source Info preference entries. It retrieves the Bluetooth broadcast source
+ * information using CachedBluetoothDevice object from setting library
+ * {@link BluetoothCallback}. It notifies the upper level whether to add/remove the preference
+ * through {@link BleBroadcastSourceInfoPreferenceCallback}
+ *
+ * In {@link BleBroadcastSourceInfoUpdater}, it uses {@link BluetoothDeviceFilter.Filter} to detect
+ * whether the {@link CachedBluetoothDevice} is relevant.
+ */
+public abstract class BleBroadcastSourceInfoUpdater implements CachedBluetoothDevice.Callback,
+                                                             BluetoothCallback {
+    private static final String TAG = "BleBroadcastSourceInfoUpdater";
+    private static final boolean DBG = false;
+
+    protected final BleBroadcastSourceInfoPreferenceCallback mBleSourceInfoPreferenceCallback;
+    protected final Map<Integer, Preference> mPreferenceMap;
+    protected Context mPrefContext;
+    protected DashboardFragment mFragment;
+    protected final CachedBluetoothDevice mCachedDevice;
+    protected final VendorCachedBluetoothDevice mVendorCachedDevice;
+    private LocalBluetoothManager mLocalManager;
+
+    final GearPreference.OnGearClickListener mSourceInfoEntryListener = pref -> {
+        launchSourceInfoDetails(pref);
+    };
+
+    public BleBroadcastSourceInfoUpdater(Context context, DashboardFragment fragment,
+            BleBroadcastSourceInfoPreferenceCallback aBleSourceInfoPreferenceCallback,
+            CachedBluetoothDevice device) {
+        this(fragment, aBleSourceInfoPreferenceCallback ,device);
+    }
+
+    BleBroadcastSourceInfoUpdater(DashboardFragment fragment,
+            BleBroadcastSourceInfoPreferenceCallback aBleSourceInfoPreferenceCallback,
+            CachedBluetoothDevice device) {
+        mCachedDevice = device;
+        LocalBluetoothManager mgr = Utils.getLocalBtManager(mPrefContext);
+        LocalBluetoothProfileManager profileManager = mgr.getProfileManager();
+        mVendorCachedDevice = VendorCachedBluetoothDevice.getVendorCachedBluetoothDevice(device, profileManager);
+        mFragment = fragment;
+        mBleSourceInfoPreferenceCallback = aBleSourceInfoPreferenceCallback;
+        mPreferenceMap = new HashMap<Integer, Preference>();
+        mLocalManager = Utils.getLocalBtManager(mPrefContext);
+        mLocalManager.getEventManager().registerCallback(this);
+    }
+
+    /**
+     * Register the bluetooth event callback and update the list
+     */
+    public void registerCallback() {
+        mCachedDevice.registerCallback(this);
+        forceUpdate();
+    }
+
+    /**
+     * Unregister the bluetooth event callback
+     */
+    public void unregisterCallback() {
+        mCachedDevice.unregisterCallback(this);
+    }
+
+    @Override
+    public void onBluetoothStateChanged(int bluetoothState) {
+        BroadcastScanAssistanceUtils.debug(TAG, "onBluetoothStateChanged");
+        if (bluetoothState == BluetoothAdapter.STATE_OFF) {
+            removeAllBleBroadcastSourceInfosFromPreference();
+        }
+        //forceUpdate();
+    }
+
+    /**
+     * Force to update the list of bluetooth devices
+     */
+    public void forceUpdate() {
+        if (mCachedDevice != null &&
+              mVendorCachedDevice.getNumberOfBleBroadcastReceiverStates() > 0) {
+            final Map<Integer, BleBroadcastSourceInfo> srcInfos =
+                    mVendorCachedDevice.getAllBleBroadcastreceiverStates();
+            if (srcInfos == null) {
+                Log.e(TAG, "srcInfos is null");
+                return;
+            }
+            for (Map.Entry<Integer, BleBroadcastSourceInfo> entry: srcInfos.entrySet()) {
+                update(entry.getKey(), entry.getValue());
+            }
+        } else {
+          BroadcastScanAssistanceUtils.debug(TAG, "remove all the preferences as there are no rcvr states");
+          removeAllBleBroadcastSourceInfosFromPreference();
+        }
+    }
+
+    public void removeAllBleBroadcastSourceInfosFromPreference() {
+        Iterator<Map.Entry<Integer, Preference>> entries = mPreferenceMap.entrySet().iterator();
+        while (entries.hasNext())  {
+        //for (Map.Entry<Integer, Preference> entry: mPreferenceMap.entrySet()) {
+            Map.Entry<Integer, Preference> entry = entries.next();
+            //removePreference(entry.getKey(), entry.getValue());
+            mBleSourceInfoPreferenceCallback.onBroadcastSourceInfoRemoved(entry.getValue());
+        }
+        mPreferenceMap.clear();
+    }
+
+     @Override
+     public void onDeviceAttributesChanged() {
+         BroadcastScanAssistanceUtils.debug(TAG, "onDeviceAttributesChanged");
+         forceUpdate();
+     }
+
+    /**
+     * Set the context to generate the {@link Preference}, so it could get the correct theme.
+     */
+    public void setPrefContext(Context context) {
+        mPrefContext = context;
+    }
+
+    /**
+     * Update whether to show {@link CachedBluetoothDevice} in the list.
+     */
+    protected void update(Integer index, BleBroadcastSourceInfo sourceInfo) {
+        addPreference(index, sourceInfo);
+    }
+
+    /**
+     * Add the {@link Preference} that represents the {@code cachedDevice}
+     */
+    protected void addPreference(Integer index, BleBroadcastSourceInfo sourceInfo) {
+        final BluetoothDevice device = sourceInfo.getSourceDevice();
+        final byte sourceId = sourceInfo.getSourceId();
+        if (mPreferenceMap.containsKey(index) == false) {
+            BroadcastScanAssistanceUtils.debug(TAG, "source info addition");
+            BleBroadcastSourceInfoPreference sourceInfoPreference =
+                    new BleBroadcastSourceInfoPreference(mPrefContext,
+                            mCachedDevice,
+                            sourceInfo,
+                            index,
+                            BleBroadcastSourceInfoPreference.SortType.TYPE_DEFAULT);
+            sourceInfoPreference.setOnGearClickListener(mSourceInfoEntryListener);
+            if (this instanceof Preference.OnPreferenceClickListener) {
+                sourceInfoPreference.setOnPreferenceClickListener(
+                        (Preference.OnPreferenceClickListener)this);
+            }
+            BroadcastScanAssistanceUtils.debug(TAG, "source info newly added: " + index);
+            mPreferenceMap.put(index, sourceInfoPreference);
+            mBleSourceInfoPreferenceCallback.onBroadcastSourceInfoAdded(sourceInfoPreference);
+        } else {
+            BleBroadcastSourceInfoPreference pref = (BleBroadcastSourceInfoPreference)mPreferenceMap.get(index);
+            BleBroadcastSourceInfo currentSi = pref.getBleBroadcastSourceInfo();
+            if (currentSi != null && currentSi.equals(sourceInfo)) {
+                BroadcastScanAssistanceUtils.debug(TAG, "No change in SI" + index);
+            } else {
+                BroadcastScanAssistanceUtils.debug(TAG, "source info Updated: " + index);
+                pref.setBleBroadcastSourceInfo (sourceInfo);
+
+                /*mBleSourceInfoPreferenceCallback.onBroadcastSourceInfoRemoved(mPreferenceMap.get(index));
+                mPreferenceMap.remove(index);
+
+                BleBroadcastSourceInfoPreference sourceInfoPreference =
+                    new BleBroadcastSourceInfoPreference(mPrefContext,
+                            mCachedDevice,
+                            sourceInfo,
+                            index,
+                            BleBroadcastSourceInfoPreference.SortType.TYPE_DEFAULT);
+                sourceInfoPreference.setOnGearClickListener(mSourceInfoEntryListener);
+                if (this instanceof Preference.OnPreferenceClickListener) {
+                    sourceInfoPreference.setOnPreferenceClickListener(
+                        (Preference.OnPreferenceClickListener)this);
+                }
+                BroadcastScanAssistanceUtils.debug(TAG, "source info added again: " + index);
+                mPreferenceMap.put(index, sourceInfoPreference);
+                mBleSourceInfoPreferenceCallback.onBroadcastSourceInfoAdded(sourceInfoPreference);*/
+            }
+        }
+    }
+
+    /**
+     * Remove the {@link Preference} that represents the {@code cachedDevice}
+     */
+    protected void removePreference(int index, Preference pref) {
+        if (mPreferenceMap.containsKey(index)) {
+            mBleSourceInfoPreferenceCallback.onBroadcastSourceInfoRemoved(mPreferenceMap.get(index));
+            mPreferenceMap.remove(index);
+        }
+    }
+
+    /**
+     * Get {@link CachedBluetoothDevice} from {@link Preference} and it is used to init
+     * {@link SubSettingLauncher} to launch {@link BluetoothDeviceDetailsFragment}
+     */
+    protected void launchSourceInfoDetails(Preference preference) {
+        final BleBroadcastSourceInfo srcInfo =
+                ((BleBroadcastSourceInfoPreference) preference).getBleBroadcastSourceInfo();
+        if (srcInfo == null) {
+            return;
+        }
+        final int index = ((BleBroadcastSourceInfoPreference) preference).getSourceInfoIndex();
+        final Bundle args = new Bundle();
+        args.putString(BleBroadcastSourceInfoDetailsFragment.KEY_DEVICE_ADDRESS,
+                mCachedDevice.getAddress());
+        args.putParcelable(BleBroadcastSourceInfoDetailsFragment.KEY_SOURCE_INFO,
+                srcInfo);
+        args.putInt(BleBroadcastSourceInfoDetailsFragment.KEY_SOURCE_INFO_INDEX,
+                      index);
+
+        new SubSettingLauncher(mFragment.getContext())
+                .setDestination(BleBroadcastSourceInfoDetailsFragment.class.getName())
+                .setArguments(args)
+                .setTitleRes(R.string.source_info_details_title)
+                .setSourceMetricsCategory(mFragment.getMetricsCategory())
+                .launch();
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastEnableController.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastEnableController.java
new file mode 100644
index 0000000..29f565c
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastEnableController.java
@@ -0,0 +1,247 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package com.android.settings.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothBroadcast;
+import android.content.Context;
+import android.util.Log;
+import android.os.SystemProperties;
+import android.os.Handler;
+import android.os.Message;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.BroadcastProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settings.connecteddevice.BluetoothDashboardFragment;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import androidx.annotation.Keep;
+
+@Keep
+public class BluetoothBroadcastEnableController extends TogglePreferenceController
+    implements LifecycleObserver, OnResume, OnPause, OnDestroy, BluetoothCallback {
+
+    public static final String TAG = "BluetoothBroadcastEnableController";
+    public static final int BROADCAST_AUDIO_MASK = 0x04;
+    public static final String BLUETOOTH_LE_AUDIO_MASK_PROP = "persist.vendor.service.bt.adv_audio_mask";
+    public static final String KEY_BROADCAST_ENABLE = "bluetooth_screen_broadcast_enable";
+    private RestrictedSwitchPreference mPreference = null;
+    private BluetoothAdapter mBluetoothAdapter;
+    private boolean mState = false;
+    private boolean reset_pending = false;
+    private Context mContext;
+    private BroadcastProfile mBapBroadcastProfile = null;
+    private boolean isBluetoothLeBroadcastAudioSupported = false;
+    private boolean mCallbacksRegistered = false;
+    private LocalBluetoothManager mManager = null;
+    public BluetoothBroadcastEnableController(Context context, String key) {
+        super(context, key);
+        Log.d(TAG, "Constructor() with key");
+        Init(context);
+    }
+
+    private void Init(Context context) {
+        mContext = context;
+        int leAudioMask = SystemProperties.getInt(BLUETOOTH_LE_AUDIO_MASK_PROP, 0);
+        isBluetoothLeBroadcastAudioSupported = ((leAudioMask & BROADCAST_AUDIO_MASK) == BROADCAST_AUDIO_MASK);
+        if(isBluetoothLeBroadcastAudioSupported){
+           mManager = Utils.getLocalBtManager(context);
+           mBapBroadcastProfile = (BroadcastProfile) mManager.getProfileManager().getBroadcastProfile();
+           if (!mCallbacksRegistered) {
+               Log.d(TAG, "Registering EventManager callbacks");
+               mCallbacksRegistered = true;
+               mManager.getEventManager().registerCallback(this);
+           }
+        }
+        Log.d(TAG, "Init done");
+    }
+
+    private void updateState(boolean newState) {
+        Log.d(TAG, "updateState req " + Boolean.toString(newState));
+        if (newState != mState) {
+             if (mPreference != null) mPreference.setEnabled(false);
+             Log.d(TAG, "updateState to " + Boolean.toString(newState));
+             mBapBroadcastProfile.setBroadcastMode(newState);
+        }
+    }
+
+    private void onStateChanged(boolean newState) {
+        Log.d(TAG, "onStateChanged " + Boolean.toString(newState));
+        mState = newState;
+        if (mPreference != null) mPreference.setChecked(mState);
+        if (mState == false && reset_pending == true) {
+            reset_pending = false;
+            updateState(true);
+        }
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            Log.d(TAG, "BT state, msg = " + Integer.toString(msg.what));
+            switch (msg.what) {
+            case BluetoothAdapter.STATE_ON:
+                if (mPreference != null) mPreference.setEnabled(true);
+                mBapBroadcastProfile = (BroadcastProfile) mManager.getProfileManager().getBroadcastProfile();
+                break;
+            case BluetoothAdapter.STATE_TURNING_ON:
+            case BluetoothAdapter.STATE_OFF:
+            case BluetoothAdapter.STATE_TURNING_OFF:
+                reset_pending = false;
+                onStateChanged(false);
+                mBapBroadcastProfile = null;
+                if (mPreference != null) mPreference.setEnabled(false);
+                break;
+            }
+        }
+    };
+
+    @Override
+    public void onBluetoothStateChanged(int newBtState) {
+        Log.d(TAG, "onBluetoothStateChanged" + Integer.toString(newBtState));
+
+        switch (newBtState) {
+            case BluetoothAdapter.STATE_ON:
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(newBtState), 200);
+                break;
+            case BluetoothAdapter.STATE_TURNING_ON:
+            case BluetoothAdapter.STATE_OFF:
+            case BluetoothAdapter.STATE_TURNING_OFF:
+                mHandler.sendMessage(mHandler.obtainMessage(newBtState));
+                break;
+        }
+    }
+
+    @Override
+    public void onBroadcastStateChanged(int newBapState) {
+        Log.d(TAG, "onBroadcastStateChanged" + Integer.toString(newBapState));
+
+        switch (newBapState) {
+            case BluetoothBroadcast.STATE_ENABLED:
+                if (mPreference != null) mPreference.setEnabled(true);
+                onStateChanged(true);
+                break;
+            case BluetoothBroadcast.STATE_DISABLED:
+                if (mPreference != null) mPreference.setEnabled(true);
+                onStateChanged(false);
+                break;
+        }
+    }
+
+    @Override
+    public void onBroadcastKeyGenerated() {
+         Log.d(TAG, "onBroadcastKeyGenerated");
+         // Encryption key got updated. Reset BAP?
+         if (mState == true) {
+             reset_pending = true;
+             updateState(false);
+         }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        Log.d(TAG, "getPreferenceKey");
+        return KEY_BROADCAST_ENABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        Log.d(TAG, "displayPreference");
+        mPreference = screen.findPreference(getPreferenceKey());
+        if(isBluetoothLeBroadcastAudioSupported) {
+          mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+          onBluetoothStateChanged(mBluetoothAdapter.getState());
+          if ((mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) &&
+              (mBapBroadcastProfile.isProfileReady())) {
+            int bapState = mBapBroadcastProfile.getBroadcastStatus();
+            Log.d(TAG, "get status done");
+            if ((bapState == BluetoothBroadcast.STATE_ENABLED) ||
+                 (bapState == BluetoothBroadcast.STATE_STREAMING))
+                onStateChanged(true);
+            else
+                onStateChanged(false);
+          }
+        } else {
+          mPreference.setVisible(false);
+        }
+    }
+
+    @Override
+    public boolean isChecked() {
+        return mState;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        updateState(isChecked);
+        return true;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        Log.d(TAG, "getAvailabilityStatus");
+        if(isBluetoothLeBroadcastAudioSupported) {
+            return AVAILABLE;
+        } else {
+            return UNSUPPORTED_ON_DEVICE;
+        }
+    }
+
+    @Override
+    public boolean hasAsyncUpdate() {
+        Log.d(TAG, "hasAsyncUpdate");
+        return true;
+    }
+
+    @Override
+    public boolean isPublicSlice() {
+        Log.d(TAG, "isPublicSlice");
+        return true;
+    }
+
+    @Override
+    public void onResume() {
+        Log.d(TAG, "onResume");
+    }
+
+    @Override
+    public void onPause() {
+        Log.d(TAG, "onPause");
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.d(TAG, "onDestory");
+        mCallbacksRegistered = false;
+        if (mManager != null)
+            mManager.getEventManager().unregisterCallback(this);
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastPinController.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastPinController.java
new file mode 100644
index 0000000..500d941
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastPinController.java
@@ -0,0 +1,237 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+
+package com.android.settings.bluetooth;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothBroadcast;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import android.view.View;
+import android.view.LayoutInflater;
+import android.text.TextUtils;
+import android.widget.TextView;
+import android.bluetooth.BluetoothAdapter;
+import android.os.Handler;
+import android.os.SystemProperties;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.BroadcastProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.RestrictedPreference;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.widget.LayoutPreference;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import androidx.annotation.Keep;
+
+/**
+ * Controller that shows Pin for BLE Broadcast Audio
+ */
+@Keep
+public class BluetoothBroadcastPinController extends BasePreferenceController
+    implements OnDestroy, BluetoothCallback {
+    public static final String TAG = "BluetoothBroadcastPinController";
+    public static final int BROADCAST_AUDIO_MASK = 0x04;
+    public static final String BLUETOOTH_LE_AUDIO_MASK_PROP = "persist.vendor.service.bt.adv_audio_mask";
+    public static final String KEY_BROADCAST_AUDIO_PIN = "bluetooth_screen_broadcast_pin_configure";
+
+    private BluetoothAdapter mBluetoothAdapter;
+    private Fragment mFragment = null;
+    private MetricsFeatureProvider mMetricsFeatureProvider;
+    @VisibleForTesting
+    RestrictedPreference mPreference;
+    private Context mContext;
+
+    private boolean isBluetoothLeBroadcastAudioSupported = false;
+    private boolean mCallbacksRegistered = false;
+    private LocalBluetoothManager mManager = null;
+    private Handler mHandler;
+    private Runnable mRunnable = new Runnable() {
+        @Override
+        public void run() {
+            onBroadcastKeyGenerated();
+        }
+    };
+
+    public BluetoothBroadcastPinController(Context context) {
+        super(context, KEY_BROADCAST_AUDIO_PIN);
+        int leAudioMask = SystemProperties.getInt(BLUETOOTH_LE_AUDIO_MASK_PROP, 0);
+        isBluetoothLeBroadcastAudioSupported = ((leAudioMask & BROADCAST_AUDIO_MASK) == BROADCAST_AUDIO_MASK);
+        Log.d(TAG, "Constructor()");
+        mContext = context;
+        mHandler = new Handler();
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if(isBluetoothLeBroadcastAudioSupported) {
+          mManager = Utils.getLocalBtManager(context);
+          if (!mCallbacksRegistered) {
+              Log.d(TAG, "Registering EventManager callbacks");
+              mCallbacksRegistered = true;
+              mManager.getEventManager().registerCallback(this);
+          }
+        }
+    }
+
+    public BluetoothBroadcastPinController(Context context, PreferenceFragmentCompat fragment, String prefKey) {
+        super(context, KEY_BROADCAST_AUDIO_PIN);
+        Log.d(TAG, "PinController()" + prefKey);
+        mFragment = fragment;
+        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+    }
+
+    @VisibleForTesting
+    public void setFragment(Fragment fragment) {
+        Log.d(TAG, "setFragment");
+        mFragment = fragment;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        Log.d(TAG, "getAvailabilityStatus");
+        if(isBluetoothLeBroadcastAudioSupported) {
+            return AVAILABLE;
+        } else {
+            return UNSUPPORTED_ON_DEVICE;
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BROADCAST_AUDIO_PIN;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        Log.d(TAG, "displayPreference");
+        mPreference = screen.findPreference(getPreferenceKey());
+        if(isBluetoothLeBroadcastAudioSupported) {
+          onBroadcastKeyGenerated();
+        } else {
+          mPreference.setVisible(false);
+        }
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        Log.d(TAG, "PinController: handlePreferenceTreeClick");
+        if (KEY_BROADCAST_AUDIO_PIN.equals(preference.getKey())) {
+            Log.d(TAG, "PinController: handlePreferenceTreeClick true");
+            new BluetoothBroadcastPinFragment()
+		.show(mFragment.getFragmentManager(), "PinFragment");
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public void onBluetoothStateChanged(int newBtState) {
+        Log.d(TAG, "onBluetoothStateChanged" + Integer.toString(newBtState));
+        int delay = 0;
+        switch (newBtState) {
+            case BluetoothAdapter.STATE_ON:
+              delay = 200;
+            case BluetoothAdapter.STATE_OFF:
+              mHandler.postDelayed(mRunnable, delay);
+            break;
+        }
+    }
+
+    private String convertBytesToString(byte[] pin) {
+        if (pin.length != 16) {
+           Log.e (TAG, "Not 16 bytes ++++++++++++");
+           return "";
+        }
+        byte[] temp = new byte[16];
+        int i = 0, j = 0;
+        // Reverse the pin and discard the padding
+        for (i = 0; i < 16; i++) {
+            if (pin[15-i] == 0) break;
+            temp[j++] = pin[15-i];
+        }
+        String str;
+        if (j == 0)
+           str = new String(""); // unencrypted
+        else
+           str = new String(Arrays.copyOfRange(temp,0,j), StandardCharsets.UTF_8);
+        Log.d(TAG, "Pin: " + str);
+        return str;
+    }
+
+    @Override
+    public void onBroadcastKeyGenerated() {
+        Log.d(TAG, "onBroadcastKeyGenerated");
+        String summary = "Broadcast code: ";
+        String keyStr = "Unavailable";
+
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        LocalBluetoothProfileManager profileManager = mManager.getProfileManager();
+        BroadcastProfile bapProfile = (BroadcastProfile) profileManager.getBroadcastProfile();
+        if ((mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) &&
+            (bapProfile.isProfileReady())) {
+          byte[] key = bapProfile.getEncryptionKey();
+          // Key can only be 16 byte long
+          if (key.length == 16) {
+              for(int i = 0; i<key.length; i++) {
+                  Log.d(TAG, "pin(" + Integer.toString(i) + "): " + String.format("%02X", key[i]));
+              }
+              keyStr = convertBytesToString(key);
+          }
+          if (keyStr.equals("")) summary = "No Broadcast code";
+          mPreference.setSummary(summary + keyStr);
+          mPreference.setVisible(true);
+          if (keyStr.equals("Unavailable")) {
+            mPreference.setEnabled(false);
+          } else {
+            mPreference.setEnabled(true);
+          }
+        } else {
+          mPreference.setSummary(summary + keyStr);
+          mPreference.setEnabled(false);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.d(TAG, "onDestory");
+        mCallbacksRegistered = false;
+        if (mManager != null)
+            mManager.getEventManager().unregisterCallback(this);
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastPinFragment.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastPinFragment.java
new file mode 100644
index 0000000..eb8068b
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastPinFragment.java
@@ -0,0 +1,256 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+
+package com.android.settings.bluetooth;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.DialogFragment;
+
+import android.app.settings.SettingsEnums;
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.bluetooth.BluetoothBroadcastEnableController;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.BroadcastProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Dialog fragment for renaming a Bluetooth device.
+ */
+public class BluetoothBroadcastPinFragment extends InstrumentedDialogFragment
+        implements RadioGroup.OnCheckedChangeListener {
+
+    public static BluetoothBroadcastPinFragment newInstance() {
+        Log.d(TAG, "newInstance");
+        BluetoothBroadcastPinFragment frag = new BluetoothBroadcastPinFragment();
+        return frag;
+    }
+
+    public static final String TAG = "BluetoothBroadcastPinFragment";
+
+    private Context mContext;
+    @VisibleForTesting
+    AlertDialog mAlertDialog = null;
+    private Dialog mDialog = null;
+    private Button mOkButton = null;
+    private TextView mCurrentPinView;
+
+    private String mCurrentPin = "4308";
+    private int mUserSelectedPinConfiguration = -1;
+
+    private List<Integer> mRadioButtonIds = new ArrayList<>();
+    private List<String> mRadioButtonStrings = new ArrayList<>();
+
+    private int getDialogTitle() {
+       return R.string.bluetooth_broadcast_pin_configure_dialog;
+    }
+
+    private void updatePinConfiguration() {
+        Log.d(TAG, "updatePinConfiguration with " + Integer.toString(mUserSelectedPinConfiguration));
+        if (mUserSelectedPinConfiguration == -1) {
+          Log.e(TAG, "no pin selected");
+          return;
+        }
+        // Call lower layer to generate new pin
+        LocalBluetoothManager mManager = Utils.getLocalBtManager(mContext);
+        LocalBluetoothProfileManager profileManager = mManager.getProfileManager();
+        BroadcastProfile bapProfile = (BroadcastProfile) profileManager.getBroadcastProfile();
+        if (mUserSelectedPinConfiguration != 0)
+           bapProfile.setEncryption(true, mUserSelectedPinConfiguration, false);
+        else
+           bapProfile.setEncryption(false, mUserSelectedPinConfiguration, false);
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        Log.d(TAG, "onAttach");
+        super.onAttach(context);
+        mContext = context;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.d(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        Log.d(TAG, "onActivityCreated");
+        super.onActivityCreated(savedInstanceState);
+        //Dialog mDialog = onCreateDialog(new Bundle());
+        //this.show(this.getActivity().getSupportFragmentManager(), "PinFragment");
+    }
+
+    /*
+    public void show() {
+        Log.e(TAG, "show");
+        this.show(this.getActivity().getSupportFragmentManager(), "PinFragment");
+    }
+    */
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        //String deviceName = getDeviceName();
+        Log.d(TAG, "onCreateDialog - enter");
+        if (savedInstanceState != null) {
+            Log.e(TAG, "savedInstanceState != null");
+        }
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+                .setTitle(getDialogTitle())
+                .setView(createDialogView())
+                .setPositiveButton(R.string.okay, (dialog, which) -> {
+                    //setDeviceName(mDeviceNameView.getText().toString().trim());
+                    updatePinConfiguration();
+                })
+                .setNegativeButton(android.R.string.cancel, null);
+        mAlertDialog = builder.create();
+        Log.d(TAG, "onCreateDialog - exit");
+        return mAlertDialog;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.BLUETOOTH_FRAGMENT;
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        Log.d(TAG, "onSaveInstanceState");
+    }
+
+    private int getRadioButtonGroupId() {
+        return R.id.bluetooth_broadcast_pin_config_radio_group;
+    }
+
+    private void setCurrentPin(String pin) {
+        mCurrentPin = pin;
+    }
+
+    private String getCurrentPin() {
+        return mCurrentPin;
+    }
+
+    @Override
+    public void onCheckedChanged(RadioGroup group, int checkedId) {
+        Log.d(TAG, "Index changed to " + checkedId);
+        // radioButton = (RadioButton) view.findViewById(checkedId);
+        int index = mRadioButtonIds.indexOf(checkedId);
+        Log.d(TAG, "index");
+        String[] stringArrayValues = getContext().getResources().getStringArray(
+                R.array.bluetooth_broadcast_pin_config_values);
+        mUserSelectedPinConfiguration = Integer.parseInt(stringArrayValues[index]);
+        Log.d(TAG, "Selected Pin Configuration " + Integer.toString(mUserSelectedPinConfiguration));
+    }
+
+    private View createDialogView() {
+        Log.d(TAG, "onCreateDialogView - enter");
+        final LayoutInflater layoutInflater = (LayoutInflater)getActivity()
+            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View view = layoutInflater.inflate(R.xml.bluetooth_broadcast_pin_config, null);
+
+        final RadioGroup radioGroup = (RadioGroup) view.findViewById(getRadioButtonGroupId());
+        if (radioGroup == null) {
+            Log.e (TAG, "Not able to find RadioGroup");
+            return null;
+        }
+        radioGroup.clearCheck();
+        radioGroup.setOnCheckedChangeListener(this);
+
+        // Fill up the Radio Group
+        mRadioButtonIds.add(R.id.bluetooth_broadcast_pin_unencrypted);
+        mRadioButtonIds.add(R.id.bluetooth_broadcast_pin_4);
+        mRadioButtonIds.add(R.id.bluetooth_broadcast_pin_16);
+        String[] stringArray = getContext().getResources().getStringArray(
+                R.array.bluetooth_broadcast_pin_config_titles);
+        for (int i = 0; i < stringArray.length; i++) {
+            mRadioButtonStrings.add(stringArray[i]);
+        }
+        RadioButton radioButton;
+        for (int i = 0; i < mRadioButtonStrings.size(); i++) {
+            radioButton = (RadioButton) view.findViewById(mRadioButtonIds.get(i));
+            if (radioButton == null) {
+                Log.e(TAG, "Unable to show dialog by no radio button:" + mRadioButtonIds.get(i));
+                return null;
+            }
+            radioButton.setText(mRadioButtonStrings.get(i));
+            radioButton.setEnabled(true);
+        }
+
+        mCurrentPinView = (TextView) view.findViewById(R.id.bluetooth_broadcast_current_pin);
+        //mCurrentPinView.setText("Current Pin is " + getCurrentPin());
+        Log.d(TAG, "onCreateDialogView - exit");
+        return view;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.d(TAG, "onDestroy");
+        mAlertDialog = null;
+        mOkButton = null;
+        mCurrentPinView = null;
+        mRadioButtonIds = new ArrayList<>();
+        mRadioButtonStrings = new ArrayList<>();
+        mUserSelectedPinConfiguration = -1;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.d(TAG, "onResume");
+        if (mOkButton == null) {
+            if (mAlertDialog != null) {
+                mOkButton = mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
+                mOkButton.setEnabled(true);
+            } else {
+                Log.d(TAG, "onResume: mAlertDialog is null");
+            }
+        }
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastSourceInfoEntries.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastSourceInfoEntries.java
new file mode 100644
index 0000000..110d9b7
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothBroadcastSourceInfoEntries.java
@@ -0,0 +1,52 @@
+/*
+ *Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * 
+ * Copyright (C) 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.
+ */
+package com.android.settings.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.settings.bluetooth.BleBroadcastSourceInfoPreferenceCallback;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+/**
+ * Maintain and update saved bluetooth devices(bonded but not connected)
+ */
+public class BluetoothBroadcastSourceInfoEntries extends BleBroadcastSourceInfoUpdater
+        implements Preference.OnPreferenceClickListener {
+    private static final String TAG = "BluetoothBroadcastSourceInfoEntries";
+
+
+    public BluetoothBroadcastSourceInfoEntries(Context context, DashboardFragment fragment,
+            BleBroadcastSourceInfoPreferenceCallback bleBroadcastSourceInfoPreferenceCallback,
+            CachedBluetoothDevice device) {
+        super(context, fragment, bleBroadcastSourceInfoPreferenceCallback, device);
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        final BleBroadcastSourceInfo srcInfo = ((BleBroadcastSourceInfoPreference) preference)
+                .getBleBroadcastSourceInfo();
+        BroadcastScanAssistanceUtils.debug(TAG, "onPreferenceClick: " + srcInfo);
+        return true;
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDetailsAddSourceButtonController.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDetailsAddSourceButtonController.java
new file mode 100644
index 0000000..536bc1b
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDetailsAddSourceButtonController.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.settings.bluetooth;
+
+import android.content.Context;
+
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.widget.ActionButtonsPreference;
+import com.android.settingslib.bluetooth.BCProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import android.util.Log;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import com.android.settings.core.SubSettingLauncher;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.bluetooth.BluetoothProfile;
+/**
+ * This class adds two buttons: one to connect/disconnect from a device (depending on the current
+ * connected state), and one to "Search for LE audio Broadcast sources" around.
+ */
+public class BluetoothDetailsAddSourceButtonController extends BluetoothDetailsController
+                                    implements CachedBluetoothDevice.Callback {
+    private static final String KEY_ACTION_BUTTONS = "sync_helper_buttons";
+    private static final String TAG = "BluetoothDetailsAddSourceButtonController";
+    private boolean mIsConnected = false;
+
+    private ActionButtonsPreference mActionButtons;
+    protected LocalBluetoothProfileManager mProfileManager;
+    private LocalBluetoothManager mLocalBluetoothManager;
+    private  BCProfile mBCProfile = null;
+
+
+    public BluetoothDetailsAddSourceButtonController(Context context, PreferenceFragmentCompat fragment,
+            CachedBluetoothDevice device, Lifecycle lifecycle) {
+        super(context, fragment, device, lifecycle);
+        device.registerCallback(this);
+
+    }
+
+    private void onAddLESourcePressed() {
+         final Bundle args = new Bundle();
+         args.putString(BluetoothSADetail.KEY_DEVICE_ADDRESS,
+                 mCachedDevice.getDevice().getAddress());
+         args.putShort(BluetoothSADetail.KEY_GROUP_OP,
+                 (short)0);
+
+        new SubSettingLauncher(mContext)
+                .setDestination(BluetoothSADetail.class.getName())
+                .setArguments(args)
+                .setTitleRes(R.string.bluetooth_search_broadcasters)
+                .setSourceMetricsCategory(SettingsEnums.BLUETOOTH_DEVICE_PICKER)
+                 .launch();
+    }
+
+    @Override
+    public void onDeviceAttributesChanged() {
+        refresh();
+    }
+
+    @Override
+    protected void init(PreferenceScreen screen) {
+        BroadcastScanAssistanceUtils.debug(TAG, "init");
+        mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+        mProfileManager = mLocalBluetoothManager.getProfileManager();
+        mBCProfile = (BCProfile)mProfileManager.getBCProfile();
+
+        mActionButtons = ((ActionButtonsPreference) screen.findPreference(
+                getPreferenceKey()))
+                .setButton1Text(R.string.add_source_button_text)
+                .setButton1Icon(R.drawable.ic_add_24dp)
+                .setButton1OnClickListener((view) -> onAddLESourcePressed())
+                .setButton1Enabled(false)
+                ;
+    }
+
+    @Override
+    protected void refresh() {
+        BroadcastScanAssistanceUtils.debug(TAG, "refresh");
+        if (mBCProfile != null) {
+            mIsConnected = mBCProfile.getConnectionStatus(mCachedDevice.getDevice()) == BluetoothProfile.STATE_CONNECTED;
+        }
+        if (mIsConnected) {
+            mActionButtons
+                   .setButton1Enabled(true);
+        } else {
+             BroadcastScanAssistanceUtils.debug(TAG, "Bass is not connected for thsi device>>");
+             mActionButtons
+                    .setButton1Enabled(false);
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_ACTION_BUTTONS;
+    }
+
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothSADetail.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothSADetail.java
new file mode 100644
index 0000000..174332c
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothSADetail.java
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.settings.bluetooth;
+
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+import android.text.Html;
+import android.widget.EditText;
+import android.widget.RadioButton;
+import android.view.View;
+import android.widget.RadioGroup;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.IBluetoothManager;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.List;
+import android.bluetooth.le.ScanRecord;
+import android.app.Activity;
+
+import androidx.appcompat.app.AlertDialog;
+import android.content.DialogInterface;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.VendorCachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.bluetooth.BCProfile;
+
+import android.bluetooth.BleBroadcastAudioScanAssistManager;
+import android.bluetooth.BleBroadcastAudioScanAssistCallback;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BleBroadcastSourceChannel;
+
+import com.android.settingslib.search.Indexable;
+import com.android.settingslib.widget.FooterPreference;
+import androidx.preference.Preference;
+import android.widget.ListView;
+import android.text.BidiFormatter;
+import android.widget.ArrayAdapter;
+import android.widget.AdapterView;
+import android.widget.CheckedTextView;
+
+/**
+ * BluetoothSADetail is a page to scan bluetooth devices and pair them.
+ */
+public class BluetoothSADetail extends DeviceListPreferenceFragment implements
+        Indexable {
+    private static final String TAG = "BluetoothSADetail";
+    private static final boolean DBG = true;
+
+    public static final String KEY_DEVICE_ADDRESS = "device_address";
+    public static final String KEY_GROUP_OP = "group_op";
+    static final String KEY_AVAIL_LE_AUDIO_SOURCES = "available_audio_sources";
+    static final String KEY_FOOTER_PREF = "footer_preference";
+    static final String SCAN_DEL_NAME = "Scan Delegator";
+
+    BluetoothProgressCategory mAvailableDevicesCategory;
+    FooterPreference mFooterPreference;
+
+    private AlertDialog mScanAssistDetailsDialog;
+    private boolean mInitialScanStarted;
+    private CachedBluetoothDevice mCachedDevice;
+    Preference mScanDelegatorName;
+    String mSyncState;
+    BleBroadcastAudioScanAssistManager mScanAssistManager;
+    protected LocalBluetoothProfileManager mProfileManager;
+    String mBroadcastCode;
+    Context mContext;
+    CachedBluetoothDevice clickedDevice = null;
+    String mBroadcastPinCode = null;
+    boolean mScanning = true;
+    boolean mGroupOperation = false;
+    AlertDialog mCommonMsgDialog = null;
+
+    private String getBluetoothName(BluetoothDevice dev) {
+        String aliasName = null;
+        if (dev == null) {
+            aliasName = SCAN_DEL_NAME;
+        } else {
+            aliasName = dev.getAlias();
+            aliasName = TextUtils.isEmpty(aliasName) ? dev.getAddress() : aliasName;
+            if (aliasName == null) {
+                aliasName = SCAN_DEL_NAME;
+            }
+        }
+        BroadcastScanAssistanceUtils.debug(TAG, "getBluetoothName returns" + aliasName);
+        return aliasName;
+    }
+
+    private int getSourceSelectionErrMessage(int status) {
+        int errorMessage = R.string.bluetooth_source_selection_error_message;
+        switch (status) {
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_COLOCATED_SRC_UNAVAILABLE:
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_SOURCE_UNAVAILABLE:
+                errorMessage = R.string.bluetooth_source_selection_error_src_unavail_message;
+                break;
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_SELECTED:
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID:
+                errorMessage = R.string.bluetooth_source_selection_error_message;
+                break;
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_DUPLICATE_ADDITION :
+                errorMessage = R.string.bluetooth_source_dup_addition_error_message;
+                break;
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_NO_EMPTY_SLOT :
+                errorMessage = R.string.bluetooth_source_no_empty_slot_error_message;
+                break;
+        }
+        return errorMessage;
+    }
+
+    private int getSourceAdditionErrMessage(int status) {
+        int errorMessage = R.string.bluetooth_source_addition_error_message;
+        switch (status) {
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_DUPLICATE_ADDITION :
+                errorMessage = R.string.bluetooth_source_dup_addition_error_message;
+                break;
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_NO_EMPTY_SLOT :
+                errorMessage = R.string.bluetooth_source_no_empty_slot_error_message;
+                break;
+        }
+        return errorMessage;
+    }
+
+    private int getSourceRemovalErrMessage(int status) {
+        int errorMessage = R.string.bluetooth_source_removal_error_message;
+        switch (status) {
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_FATAL :
+                errorMessage = R.string.bluetooth_source_removal_error_message;
+                break;
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_GROUP_OP :
+                errorMessage = R.string.bluetooth_source_remove_invalid_group_op;
+                break;
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID :
+                errorMessage = R.string.bluetooth_source_remove_invalid_src_id;
+                break;
+        }
+        return errorMessage;
+    }
+
+    private int getSourceUpdateErrMessage(int status) {
+        int errorMessage = R.string.bluetooth_source_update_error_message;
+        switch (status) {
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_FATAL :
+                errorMessage = R.string.bluetooth_source_update_error_message;
+                break;
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_GROUP_OP :
+                errorMessage = R.string.bluetooth_source_update_invalid_group_op;
+                break;
+            case BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID :
+                errorMessage = R.string.bluetooth_source_update_invalid_src_id;
+                break;
+        }
+        return errorMessage;
+    }
+
+    BleBroadcastAudioScanAssistCallback mScanAssistCallback = new BleBroadcastAudioScanAssistCallback() {
+        DialogInterface.OnClickListener commonMessageListener = new DialogInterface.OnClickListener() {
+             public void onClick(DialogInterface dialog, int which) {
+                 BroadcastScanAssistanceUtils.debug(TAG, ">>OK clicked");
+                 if (mCommonMsgDialog != null) {
+                     mCommonMsgDialog.dismiss();
+                 }
+                 finish();
+             }
+        };
+        public void onBleBroadcastSourceFound(ScanResult res) {
+            BroadcastScanAssistanceUtils.debug(TAG, "onBleBroadcastSourceFound" + res.getDevice());
+
+            CachedBluetoothDevice cachedDevice = mLocalManager.getCachedDeviceManager().findDevice(res.getDevice());
+
+            if (cachedDevice != null) {
+                BroadcastScanAssistanceUtils.debug(TAG, "seems like CachedDevice entry already present for this device");
+            } else     {
+                //Create a Device entry for this,
+                //If this is randon Address, there would new CachedDevice Entry for this random address Instance
+                //However this wont have the name
+                cachedDevice  = mLocalManager.getCachedDeviceManager().addDevice(res.getDevice());
+                //udate the Name for this device from ADV: HACK
+                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+                if (res.getDevice().getAddress().equals(adapter.getAddress())) {
+                    BroadcastScanAssistanceUtils.debug(TAG, "Self DEVICE:");
+                } else {
+                    ScanRecord rec = res.getScanRecord();
+                    if (rec != null && rec.getDeviceName() != null) {
+                        String  s = rec.getDeviceName();
+                        BroadcastScanAssistanceUtils.debug(TAG,"setting name as " + s);
+                        cachedDevice.setName(s);
+                    }
+                }
+            }
+
+            BluetoothDevicePreference pref = mDevicePreferenceMap.get(cachedDevice);
+            if (pref != null) {
+                //If the Prefernce alread Created, just update the
+                //Scan Result
+                //pref.SetScanResult(res);
+                 BroadcastScanAssistanceUtils.debug(TAG, "Preference is already present" + res.getDevice());
+                return;
+            }
+            // Prevent updates while the list shows one of the state messages
+            if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) return;
+            //if (mFilter.matches(cachedDevice.getDevice())) {
+               createDevicePreference(cachedDevice);
+            //}
+            //
+            VendorCachedBluetoothDevice vDev = VendorCachedBluetoothDevice.getVendorCachedBluetoothDevice(cachedDevice, mProfileManager);
+            vDev.setScanResult(res);
+        };
+
+        public void onBleBroadcastSourceSelected(BluetoothDevice rcvr, int status,
+                                List<BleBroadcastSourceChannel> broadcastSourceIndicies) {
+            BroadcastScanAssistanceUtils.debug(TAG, "onBleBroadcastSourceSelected" + status + "sel indicies:" +  broadcastSourceIndicies);
+            if (status == BleBroadcastAudioScanAssistCallback.BASS_STATUS_SUCCESS) {
+                launchSyncAndBroadcastIndexOptions(broadcastSourceIndicies);
+            } else {
+                String aliasName = getBluetoothName(rcvr);
+                mCommonMsgDialog = BroadcastScanAssistanceUtils.showScanAssistError(mContext, rcvr.getName(),
+                    getSourceSelectionErrMessage(status), commonMessageListener);
+
+            }
+        };
+
+        public void onBleBroadcastAudioSourceAdded(BluetoothDevice rcvr,
+                                                byte srcId,
+                                                int status) {
+
+             BroadcastScanAssistanceUtils.debug(TAG, "onBleBroadcastAudioSourceAdded: rcvr:" + rcvr +
+                "status:" + status + "srcId" + srcId);
+            if (status == BleBroadcastAudioScanAssistCallback.BASS_STATUS_SUCCESS) {
+                //Show Dialog
+                if (mGroupOperation) {
+                    String aliasName = getBluetoothName(rcvr);
+                     mCommonMsgDialog = BroadcastScanAssistanceUtils.showScanAssistError(mContext, aliasName,
+                        R.string.bluetooth_source_added_message, commonMessageListener);
+                }
+                if(mBroadcastPinCode != null) {
+                    if (status == BleBroadcastAudioScanAssistCallback.BASS_STATUS_SUCCESS
+                        && mScanAssistManager != null) {
+                        mScanAssistManager.setBroadcastCode(srcId,mBroadcastPinCode, mGroupOperation);
+                    }
+                    mBroadcastPinCode = null;
+                }
+                finish();
+            } else {
+                String aliasName = getBluetoothName(rcvr);
+                mCommonMsgDialog = BroadcastScanAssistanceUtils.showScanAssistError(mContext, aliasName,
+                    getSourceAdditionErrMessage(status), commonMessageListener);
+            }
+        };
+
+        public void onBleBroadcastAudioSourceUpdated(BluetoothDevice rcvr,
+                                             byte srcId,
+                                             int status) {
+              BroadcastScanAssistanceUtils.debug(TAG, "onBleBroadcastAudioSourceUpdated: rcvr:" + rcvr +
+                "status:" + status + "srcId" + srcId);
+             if (status != BleBroadcastAudioScanAssistCallback.BASS_STATUS_SUCCESS) {
+                 String aliasName = getBluetoothName(rcvr);
+                 mCommonMsgDialog = BroadcastScanAssistanceUtils.showScanAssistError(mContext, aliasName,
+                    getSourceUpdateErrMessage(status), commonMessageListener);
+             }
+        };
+
+        public void onBleBroadcastPinUpdated(BluetoothDevice rcvr,
+                                                byte srcId,
+                                                int status) {
+
+              BroadcastScanAssistanceUtils.debug(TAG, "onBleBroadcastPinUpdated: rcvr:" + rcvr +
+                "status:" + status + "srcId" + srcId);
+             if (status != BleBroadcastAudioScanAssistCallback.BASS_STATUS_SUCCESS) {
+                  String aliasName = getBluetoothName(rcvr);
+                 mCommonMsgDialog = BroadcastScanAssistanceUtils.showScanAssistError(mContext, aliasName,
+                    R.string.bluetooth_source_setpin_error_message, commonMessageListener);
+             }
+        };
+        public void onBleBroadcastAudioSourceRemoved(BluetoothDevice rcvr,
+                                             byte srcId,
+                                             int status) {
+              BroadcastScanAssistanceUtils.debug(TAG, "onBleBroadcastAudioSourceRemoved: rcvr:" + rcvr +
+                "status:" + status + "srcId" + srcId);
+             if (status != BleBroadcastAudioScanAssistCallback.BASS_STATUS_SUCCESS) {
+                  String aliasName = getBluetoothName(rcvr);
+                 mCommonMsgDialog = BroadcastScanAssistanceUtils.showScanAssistError(mContext, aliasName,
+                    getSourceRemovalErrMessage(status), commonMessageListener);
+             }
+        };
+    };
+
+
+    public BluetoothSADetail() {
+        super(DISALLOW_CONFIG_BLUETOOTH);
+    }
+
+    void createDevicePreference(CachedBluetoothDevice cachedDevice) {
+        if (mDeviceListGroup == null) {
+            Log.w(TAG, "Trying to create a device preference before the list group/category "
+                    + "exists!");
+            return;
+        }
+
+        String key = cachedDevice.getDevice().getAddress();
+        BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key);
+
+        if (preference == null) {
+            preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice,
+                    true/*mShowDevicesWithoutNames*/, BluetoothDevicePreference.SortType.TYPE_FIFO);
+            preference.setKey(key);
+            //Set hideSecondTarget is true if it's bonded device.
+            //preference.hideSecondTarget(true);
+            mDeviceListGroup.addPreference(preference);
+        }
+
+        initDevicePreference(preference);
+        Log.w(TAG, "adding" + cachedDevice + "to the Pref map");
+        mDevicePreferenceMap.put(cachedDevice, preference);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        mInitialScanStarted = false;
+    }
+
+    @Override
+    public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
+        //Do nothing
+    }
+
+    @Override
+    public void onStart() {
+        BroadcastScanAssistanceUtils.debug(TAG, "OnStart Called");
+        super.onStart();
+        if (mLocalManager == null){
+            Log.e(TAG, "Bluetooth is not supported on this device");
+            return;
+        }
+        updateContent(mBluetoothAdapter.getState());
+        mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isDiscovering());
+        if (mScanAssistManager == null) {
+            if (mProfileManager == null) {
+                mProfileManager = mLocalManager.getProfileManager();
+            }
+            BCProfile bcProfile = (BCProfile)mProfileManager.getBCProfile();
+            mScanAssistManager = bcProfile.getBSAManager(
+                                  mCachedDevice.getDevice(), mScanAssistCallback);
+            if (mScanAssistManager == null) {
+                Log.e(TAG, "On Start: not able to instantiate scanAssistManager");
+                //return;
+            }
+        }
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        BroadcastScanAssistanceUtils.debug(TAG, "OnAttach Called");
+        super.onAttach(context);
+        mContext = context;
+        String deviceAddress = getArguments().getString(KEY_DEVICE_ADDRESS);
+        mGroupOperation = getArguments().getShort(KEY_GROUP_OP) == (short)1;
+        BluetoothDevice remoteDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+                deviceAddress);
+        if (mLocalManager == null) {
+            Log.e(TAG, "Local mgr is NULL");
+            mLocalManager = Utils.getLocalBtManager(getActivity());
+            if (mLocalManager == null) {
+               Log.e(TAG, "Bluetooth is not supported on this device");
+               return;
+            }
+        }
+        mCachedDevice = mLocalManager.getCachedDeviceManager().findDevice(remoteDevice);
+        if (mCachedDevice == null) {
+            //goBack();
+            return;
+        } else {
+            mProfileManager = mLocalManager.getProfileManager();
+            BCProfile bcProfile = (BCProfile)mProfileManager.getBCProfile();
+            mScanAssistManager = bcProfile.getBSAManager(
+                                  mCachedDevice.getDevice(), mScanAssistCallback);
+            if (mScanAssistManager == null) {
+                Log.e(TAG, "not able to instantiate scanAssistManager");
+                //return;
+            }
+        }
+    }
+
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mLocalManager == null){
+            Log.e(TAG, "Bluetooth is not supported on this device");
+            return;
+        }
+        // Make the device only visible to connected devices.
+        disableScanning();
+        //clear the preference map onStop
+        mDevicePreferenceMap.clear();
+        mScanAssistManager = null;
+    }
+
+    @Override
+    void initPreferencesFromPreferenceScreen() {
+        mScanDelegatorName = findPreference("bt_bcast_rcvr_device");
+        mScanDelegatorName.setSelectable(false);
+        if (mCachedDevice == null) {
+            mScanDelegatorName.setSummary(SCAN_DEL_NAME);
+        } else {
+            mScanDelegatorName.setSummary(getBluetoothName(mCachedDevice.getDevice()));
+        }
+        mAvailableDevicesCategory = (BluetoothProgressCategory) findPreference(KEY_AVAIL_LE_AUDIO_SOURCES);
+        mFooterPreference = (FooterPreference) findPreference(KEY_FOOTER_PREF);
+        mFooterPreference.setSelectable(false);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.BLUETOOTH_PAIRING;
+    }
+
+    @Override
+    void enableScanning() {
+        // Clear all device states before first scan
+        if (!mInitialScanStarted) {
+            if (mAvailableDevicesCategory != null) {
+                removeAllDevices();
+            }
+            mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
+            mInitialScanStarted = true;
+        }
+        //Call to Scan for LE Audio Sources
+        if (mScanAssistManager != null) {
+            BroadcastScanAssistanceUtils.debug(TAG, "call searchforLeAudioBroadcasters");
+            mScanAssistManager.searchforLeAudioBroadcasters();
+        }
+    }
+
+    @Override
+    void disableScanning() {
+        if (mScanAssistManager != null && mScanning == true) {
+            BroadcastScanAssistanceUtils.debug(TAG, "call stopSearchforLeAudioBroadcasters");
+            mScanAssistManager.stopSearchforLeAudioBroadcasters();
+            mScanning = false;
+        }
+    }
+
+    private int getSyncStateFromSelection (String s) {
+        int ret = -1;
+        if (s == null) {
+            BroadcastScanAssistanceUtils.debug(TAG, "getSyncStateFromSelection:Invalid Input");
+        } else {
+            if (mSyncState.equals("Sync Metadata")) {
+                ret = BleBroadcastAudioScanAssistManager.SYNC_METADATA;
+            } else {
+                ret = BleBroadcastAudioScanAssistManager.SYNC_METADATA_AUDIO;
+            }
+        }
+        return ret;
+    }
+
+    void launchSyncAndBroadcastIndexOptions(List<BleBroadcastSourceChannel> broadcastSourceIndicies) {
+         Context context = getContext();
+
+         final View dialogView;
+         String title, message;
+         Activity activity = getActivity();
+         if (isAdded() && activity != null) {
+             dialogView = getLayoutInflater().inflate(R.layout.select_source_prompt, null);
+             String name = null;
+             if (clickedDevice != null) {
+                 name = clickedDevice.getName();
+             }
+             if (TextUtils.isEmpty(name)) {
+                 name = context.getString(R.string.bluetooth_device);
+             }
+             if (mGroupOperation) {
+                 message = context.getString(R.string.bluetooth_grp_source_selection_options_detail, name);
+             } else {
+                 message = context.getString(R.string.bluetooth_source_selection_options_detail, name);
+             }
+             title = context.getString(R.string.bluetooth_source_selection_options_detail_title);
+
+             /*
+             //BIS Selection choice
+             ListView bisSelectionList;
+             bisSelectionList = dialogView.findViewById(R.id.lv);
+             bisSelectionList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+             ArrayAdapter<BleBroadcastSourceChannel> arrayAdapter =
+             new ArrayAdapter<BleBroadcastSourceChannel>(context, android.R.layout.simple_list_item_multiple_choice , broadcastSourceIndicies);
+
+             bisSelectionList.setAdapter(arrayAdapter);
+
+             bisSelectionList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                @Override
+                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                    BroadcastScanAssistanceUtils.debug(TAG, "onItemClick: " +position);
+                    CheckedTextView v = (CheckedTextView) view;
+                    boolean currentCheck = v.isChecked();
+                    BleBroadcastSourceChannel bisIndex = (BleBroadcastSourceChannel) bisSelectionList.getItemAtPosition(position);
+                    bisIndex.setStatus(currentCheck);
+                }
+            });
+            */
+             DialogInterface.OnClickListener cancelAddSourceListener = new DialogInterface.OnClickListener() {
+             public void onClick(DialogInterface dialog, int which) {
+                 BroadcastScanAssistanceUtils.debug(TAG, ">>Cancel clicked");
+                finish();
+             }
+             };
+
+             DialogInterface.OnClickListener addSourceListener = new DialogInterface.OnClickListener() {
+             public void onClick(DialogInterface dialog, int which) {
+                 /*
+                 Radio Buttons
+                 final RadioGroup group = dialogView.findViewById(R.id.syncStateOptions);
+                 int selectedId = group.getCheckedRadioButtonId();
+                 RadioButton radioSelectedButton = (RadioButton) dialogView.findViewById(selectedId);
+                 mSyncState = radioSelectedButton.getText().toString();
+                 BroadcastScanAssistanceUtils.debug(TAG, "mSyncState: " +  mSyncState);
+                 */
+                if (clickedDevice == null) {
+                    Log.w(TAG, "Ignore as there is no clicked device");
+                }
+                if (clickedDevice.getAddress().equals(mBluetoothAdapter.getAddress())) {
+                    BroadcastScanAssistanceUtils.debug(TAG, ">>Local Adapter");
+                    mBroadcastPinCode = null;
+                } else {
+                    EditText broadcastPIN = dialogView.findViewById(R.id.broadcastPINcode);
+                    mBroadcastPinCode = broadcastPIN.getText().toString();
+                    BroadcastScanAssistanceUtils.debug(TAG, "broadcastPinCode: " + mBroadcastPinCode);
+                    if (TextUtils.isEmpty(mBroadcastPinCode)) {
+                        BroadcastScanAssistanceUtils.debug(TAG, "Empty broacast PinCode");
+                        mBroadcastPinCode = null;
+                    }
+                }
+                if (mScanAssistManager != null && clickedDevice != null) {
+                     mScanAssistManager.addBroadcastSource(clickedDevice.getDevice(),
+                                                           /*getSyncStateFromSelection(mSyncState)*/
+                                                           BleBroadcastAudioScanAssistManager.SYNC_METADATA_AUDIO,
+                                                           broadcastSourceIndicies, mGroupOperation);
+                }
+             }
+             };
+             EditText broadcastPIN = dialogView.findViewById(R.id.broadcastPINcode);
+             if (clickedDevice != null && clickedDevice.getAddress().equals(mBluetoothAdapter.getAddress())) {
+                 BroadcastScanAssistanceUtils.debug(TAG, "Local Adapter");
+                 mBroadcastPinCode = null;
+                 broadcastPIN.setVisibility(View.INVISIBLE);
+                 if (mGroupOperation) {
+                     message = context.getString(R.string.bluetooth_col_grp_source_selection_options_detail, name);
+                 } else {
+                     message = context.getString(R.string.bluetooth_col_source_selection_options_detail, name);
+                 }
+             }
+             mScanAssistDetailsDialog = BroadcastScanAssistanceUtils.showScanAssistDetailsDialog(context,
+                 mScanAssistDetailsDialog, addSourceListener, cancelAddSourceListener, title,
+                 Html.fromHtml(message), dialogView);
+         }
+    }
+
+    @Override
+    void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
+        disableScanning();
+        clickedDevice = btPreference.getBluetoothDevice();
+		VendorCachedBluetoothDevice vDevice = VendorCachedBluetoothDevice.getVendorCachedBluetoothDevice(clickedDevice, mProfileManager);
+        if (mScanAssistManager != null) {
+            BroadcastScanAssistanceUtils.debug(TAG, "calling selectAudioSource");
+            mScanAssistManager.selectBroadcastSource(vDevice.getScanResult(), mGroupOperation);
+        }
+    }
+    void updateContent(int bluetoothState) {
+        switch (bluetoothState) {
+            case BluetoothAdapter.STATE_ON:
+                mDevicePreferenceMap.clear();
+                //mBluetoothAdapter.enable();
+
+                addDeviceCategory(mAvailableDevicesCategory,
+                        R.string.bluetooth_preference_found_media_devices,
+                        BluetoothDeviceFilter.ALL_FILTER, false);
+                updateFooterPreference(mFooterPreference);
+                //mAlwaysDiscoverable.start();
+                enableScanning();
+                break;
+
+            case BluetoothAdapter.STATE_OFF:
+                finish();
+                break;
+        }
+    }
+
+    @Override
+    void updateFooterPreference(Preference myDevicePreference) {
+        final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
+        myDevicePreference.setTitle(getString(
+                R.string.bluetooth_footer_mac_message,
+                bidiFormatter.unicodeWrap(mCachedDevice.getAddress())));
+    }
+
+    @Override
+    public void onBluetoothStateChanged(int bluetoothState) {
+        super.onBluetoothStateChanged(bluetoothState);
+        updateContent(bluetoothState);
+        if (bluetoothState == BluetoothAdapter.STATE_ON) {
+            showBluetoothTurnedOnToast();
+        }
+    }
+
+
+
+    @Override
+    public int getHelpResource() {
+        return R.string.help_url_bluetooth;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.bluetooth_search_bcast_sources;
+    }
+
+    @Override
+    public String getDeviceListKey() {
+        return KEY_AVAIL_LE_AUDIO_SOURCES;
+    }
+
+    void showBluetoothTurnedOnToast() {
+        Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
+                Toast.LENGTH_SHORT).show();
+    }
+}
diff --git a/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BroadcastScanAssistanceUtils.java b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BroadcastScanAssistanceUtils.java
new file mode 100644
index 0000000..9883e70
--- /dev/null
+++ b/le_audio/packages/apps/Settings/src/com/android/settings/bluetooth/BroadcastScanAssistanceUtils.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.Toast;
+import android.text.InputType;
+import android.view.View;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
+import android.bluetooth.BluetoothAdapter;
+
+/**
+ * BroadcastScanAssistanceUtils is a helper class that contains constants for various
+ * Android resource IDs, debug logging flags, and static methods
+ * for creating BASS dialogs.
+ */
+public final class BroadcastScanAssistanceUtils {
+
+    private static final String TAG = "BroadcastScanAsssitanceBroadcastScanAssistanceUtils";
+    static final boolean BASS_DBG = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private BroadcastScanAssistanceUtils() {
+    }
+
+    static void debug(String TAG, String msg) {
+        if (BASS_DBG) {
+           Log.d(TAG, msg);
+        }
+    }
+
+    static boolean isLocalDevice(BluetoothDevice dev) {
+        boolean ret = false;
+        if (dev != null) {
+              BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+              ret = btAdapter.getAddress().equals(dev.getAddress());
+        }
+        Log.d(TAG, "isLocalBroadcastSource returns" +ret);
+        return ret;
+    }
+
+    static AlertDialog showScanAssistError(Context context, String name, int messageResId,
+                  DialogInterface.OnClickListener okListener) {
+        return showScanAssistError(context, name, messageResId, Utils.getLocalBtManager(context), okListener);
+    }
+
+    private static AlertDialog showScanAssistError(Context context, String name, int messageResId,
+            LocalBluetoothManager manager, DialogInterface.OnClickListener okListener) {
+        String message = context.getString(messageResId, name);
+        Context activity = manager.getForegroundActivity();
+        AlertDialog dialog = null;
+        if (manager.isForegroundActivity()) {
+            try {
+               dialog = new AlertDialog.Builder(activity)
+                        .setTitle(R.string.bluetooth_error_title)
+                        .setMessage(message)
+                        .setPositiveButton(android.R.string.ok, okListener)
+                        .show();
+            } catch (Exception e) {
+                Log.e(TAG, "Cannot show error dialog.", e);
+                return null;
+            }
+            return dialog;
+        } else {
+            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+            return dialog;
+        }
+    }
+    // Create (or recycle existing) and show disconnect dialog.
+    static AlertDialog showScanAssistDetailsDialog(Context context,
+            AlertDialog dialog,
+            DialogInterface.OnClickListener addSourceListener,
+            DialogInterface.OnClickListener cancelAddSourceListener,
+            CharSequence title, CharSequence message,
+            View customView
+            ) {
+        if (dialog == null) {
+            dialog = new AlertDialog.Builder(context)
+                    .setPositiveButton(android.R.string.ok, addSourceListener)
+                    .setNegativeButton(android.R.string.cancel, cancelAddSourceListener)
+                    .setView(customView)
+                    .create();
+        } else {
+            if (dialog.isShowing()) {
+                dialog.dismiss();
+            }
+            // use disconnectListener for the correct profile(s)
+            //CharSequence okText = context.getText(android.R.string.ok);
+            //dialog.setButton(DialogInterface.BUTTON_POSITIVE,
+            //        okText, addSourceListener);
+        }
+        dialog.setTitle(title);
+        dialog.setMessage(message);
+        dialog.show();
+        return dialog;
+    }
+
+    static AlertDialog showAssistanceGroupOptionsDialog(Context context,
+            AlertDialog dialog,
+            DialogInterface.OnClickListener groupOpListener,
+            DialogInterface.OnClickListener singleDevListener,
+            CharSequence title, CharSequence message) {
+        if (dialog == null) {
+            Log.d(TAG, "showAssistanceGroupOptionsDialog creation");
+            dialog = new AlertDialog.Builder(context)
+                    .setPositiveButton(R.string.yes, groupOpListener)
+                    .setNegativeButton(R.string.no, singleDevListener)
+                    .create();
+        } else {
+            if (dialog.isShowing()) {
+                dialog.dismiss();
+            }
+            // use disconnectListener for the correct profile(s)
+            //CharSequence okText = context.getText(android.R.string.yes);
+            //dialog.setButton(DialogInterface.BUTTON_POSITIVE,
+            //        okText, groupOpListener);
+        }
+        dialog.setTitle(title);
+        dialog.setMessage(message);
+        dialog.show();
+        return dialog;
+    }
+}
diff --git a/le_audio/system/bt/binder/Android.bp b/le_audio/system/bt/binder/Android.bp
new file mode 100644
index 0000000..f246cbf
--- /dev/null
+++ b/le_audio/system/bt/binder/Android.bp
@@ -0,0 +1,28 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+// AIDL interface between libbluetooth-binder and framework.jar
+filegroup {
+    name: "libbluetooth-binder-aidl-adva",
+    srcs: [
+        "android/bluetooth/IBluetoothSyncHelper.aidl",
+        "android/bluetooth/IBleBroadcastAudioScanAssistCallback.aidl",
+        "android/bluetooth/IBluetoothBroadcast.aidl",
+    ],
+}
+
diff --git a/le_audio/system/bt/binder/android/bluetooth/BleBroadcastSourceChannel.aidl b/le_audio/system/bt/binder/android/bluetooth/BleBroadcastSourceChannel.aidl
new file mode 100644
index 0000000..4b94f94
--- /dev/null
+++ b/le_audio/system/bt/binder/android/bluetooth/BleBroadcastSourceChannel.aidl
@@ -0,0 +1,23 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package android.bluetooth;
+
+parcelable BleBroadcastSourceChannel;
+
diff --git a/le_audio/system/bt/binder/android/bluetooth/BleBroadcastSourceInfo.aidl b/le_audio/system/bt/binder/android/bluetooth/BleBroadcastSourceInfo.aidl
new file mode 100644
index 0000000..8eb14da
--- /dev/null
+++ b/le_audio/system/bt/binder/android/bluetooth/BleBroadcastSourceInfo.aidl
@@ -0,0 +1,22 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package android.bluetooth;
+
+parcelable BleBroadcastSourceInfo;
+
diff --git a/le_audio/system/bt/binder/android/bluetooth/IBleBroadcastAudioScanAssistCallback.aidl b/le_audio/system/bt/binder/android/bluetooth/IBleBroadcastAudioScanAssistCallback.aidl
new file mode 100644
index 0000000..223f813
--- /dev/null
+++ b/le_audio/system/bt/binder/android/bluetooth/IBleBroadcastAudioScanAssistCallback.aidl
@@ -0,0 +1,48 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.BleBroadcastSourceChannel;
+import android.bluetooth.le.ScanResult;
+
+/** @hide */
+interface IBleBroadcastAudioScanAssistCallback {
+    void onBleBroadcastSourceFound(in ScanResult scanres);
+    void onBleBroadcastAudioSourceSelected(in BluetoothDevice device,
+            in int status,
+            in List<BleBroadcastSourceChannel>
+            broadcastSourceChannels);
+
+    void onBleBroadcastAudioSourceAdded(in BluetoothDevice rcvr,
+                                        in byte srcId,
+                                        in int status);
+    void onBleBroadcastAudioSourceUpdated(in BluetoothDevice rcvr,
+                                          in byte srcId,
+                                          in int status);
+
+    void onBleBroadcastPinUpdated(in BluetoothDevice rcvr,
+                                  in byte srcId,
+                                  in int status);
+    void onBleBroadcastAudioSourceRemoved(in BluetoothDevice rcvr,
+                                          in byte srcId,
+                                          in int status);
+}
diff --git a/le_audio/system/bt/binder/android/bluetooth/IBluetoothBroadcast.aidl b/le_audio/system/bt/binder/android/bluetooth/IBluetoothBroadcast.aidl
new file mode 100644
index 0000000..e3054e9
--- /dev/null
+++ b/le_audio/system/bt/binder/android/bluetooth/IBluetoothBroadcast.aidl
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * APIs for Bluetooth Broadcast service
+ *
+ * @hide
+ */
+interface IBluetoothBroadcast {
+    // Public API
+    boolean SetBroadcast(in boolean enable, in String packageName);
+    boolean SetEncryption(in boolean enable, in int enc_len,
+                          in boolean use_existing, in String packageName);
+    byte[] GetEncryptionKey(in String packageName);
+    int GetBroadcastStatus(in String packageName);
+}
diff --git a/le_audio/system/bt/binder/android/bluetooth/IBluetoothSyncHelper.aidl b/le_audio/system/bt/binder/android/bluetooth/IBluetoothSyncHelper.aidl
new file mode 100644
index 0000000..495c1ff
--- /dev/null
+++ b/le_audio/system/bt/binder/android/bluetooth/IBluetoothSyncHelper.aidl
@@ -0,0 +1,75 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BleBroadcastSourceInfo;
+import android.bluetooth.IBleBroadcastAudioScanAssistCallback;
+import android.bluetooth.le.ScanResult;
+
+/**
+ * APIs for Bluetooth Bluetooth Scan offloader service
+ *
+ * @hide
+ */
+interface IBluetoothSyncHelper {
+    // Public API
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
+    boolean startScanOffload (in BluetoothDevice device,
+                              in boolean groupOp);
+    boolean stopScanOffload (in BluetoothDevice device,
+                             in boolean groupOp);
+
+    void registerAppCallback(in BluetoothDevice device,
+                             in IBleBroadcastAudioScanAssistCallback cb);
+    void unregisterAppCallback(in BluetoothDevice device,
+                               in IBleBroadcastAudioScanAssistCallback cb);
+
+    boolean searchforLeAudioBroadcasters (in BluetoothDevice device);
+    boolean stopSearchforLeAudioBroadcasters(in BluetoothDevice device);
+
+    boolean addBroadcastSource(in BluetoothDevice device,
+                               in BleBroadcastSourceInfo srcInfo,
+                               in boolean groupOp
+                                );
+    boolean selectBroadcastSource(in BluetoothDevice device,
+                                  in ScanResult scanRes,
+                                  in boolean groupOp
+                                  );
+    boolean updateBroadcastSource(in BluetoothDevice device,
+                                  in BleBroadcastSourceInfo srcInfo,
+                                  in boolean groupOp
+                                  );
+    boolean setBroadcastCode (in BluetoothDevice device,
+                              in BleBroadcastSourceInfo srcInfo,
+                              in boolean groupOp
+                              );
+    boolean removeBroadcastSource (in BluetoothDevice device,
+                                   in byte SourceId,
+                                   in boolean groupOp
+                                   );
+    List<BleBroadcastSourceInfo> getAllBroadcastSourceInformation(
+                                             in BluetoothDevice device);
+}
diff --git a/le_audio/system/bt/bta/Android.bp b/le_audio/system/bt/bta/Android.bp
new file mode 100644
index 0000000..53cf3bd
--- /dev/null
+++ b/le_audio/system/bt/bta/Android.bp
@@ -0,0 +1,88 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+cc_defaults {
+    name: "fluoride_bta_defaults_qti_adva",
+    defaults: ["fluoride_defaults"],
+    local_include_dirs: [
+        "include",
+    ],
+    include_dirs: [
+        "vendor/qcom/opensource/commonsys/system/bt",
+        "vendor/qcom/opensource/commonsys/system/bt/bta/include",
+        "vendor/qcom/opensource/commonsys/system/bt/bta/ag",
+        "vendor/qcom/opensource/commonsys/system/bt/btcore/include",
+        "vendor/qcom/opensource/commonsys/system/bt/hci/include",
+        "vendor/qcom/opensource/commonsys/system/bt/internal_include",
+        "vendor/qcom/opensource/commonsys/system/bt/stack/include",
+        "vendor/qcom/opensource/commonsys/system/bt/stack/btm",
+        "vendor/qcom/opensource/commonsys/system/bt/udrv/include",
+        "vendor/qcom/opensource/commonsys/system/bt/vnd/include",
+        "vendor/qcom/opensource/commonsys/system/bt/utils/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_ext/system_bt_ext",
+        "vendor/qcom/opensource/commonsys/bluetooth_ext/vhal/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_ext/system_bt_ext/bta/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_ext/system_bt_ext/btif/include",
+        "vendor/qcom/opensource/commonsys-intf/bluetooth/include",
+        "vendor/qcom/opensource/commonsys/system/bt/device/include",
+         "vendor/qcom/opensource/commonsys/system/bt/btif/include",
+         "vendor/qcom/opensource/commonsys/system/bt/bta/include",
+         "vendor/qcom/opensource/commonsys/system/bt/bta/sys",
+         "vendor/qcom/opensource/commonsys/bluetooth_lea/system/bt",
+         "vendor/qcom/opensource/commonsys/bluetooth_lea/system/bt/bta/include",
+         "vendor/qcom/opensource/commonsys/bluetooth_lea/vhal/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_lea/system/bt/bta/bap",
+        "vendor/qcom/opensource/commonsys/bluetooth_lea/system/bt/btif/include",
+        "system/bt/common/"
+    ],
+    shared_libs: [
+        "libcutils",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    cflags: [
+        "-DBUILDCFG",
+        "-DADV_AUDIO_FEATURE=1",
+    ],
+}
+
+// BTA static library for target
+// ========================================================
+cc_library_static {
+    name: "libbt-bta_qti_adva",
+    defaults: ["fluoride_bta_defaults_qti_adva"],
+    enabled: false,
+    srcs: [
+        "csip/bta_csip_act.cc",
+        "csip/bta_csip_api.cc",
+        "csip/bta_csip_main.cc",
+        "csip/bta_csip_utils.cc",
+        "bap/ascs_client.cc",
+        "bap/pacs_client.cc",
+        "bap/gattc_ops_queue.cc",
+        "bap/gatts_ops_queue.cc",
+        "bap/uclient_main.cc",
+        "bap/uclient_strm_mgr.cc",
+        "bap/uclient_strm_tracker.cc",
+        "bap/connected_iso.cc",
+        "bap/uclient_alarm.cc",
+        "vcp/bta_vcp_controller.cc",
+        "dm/bta_dm_adv_audio.cc",
+        "mcp/bta_mcp_main.cc",
+        "cc/bta_cc_main.cc",
+    ],
+}
diff --git a/le_audio/system/bt/bta/bap/ascs_client.cc b/le_audio/system/bt/bta/bap/ascs_client.cc
new file mode 100644
index 0000000..1be5f7c
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/ascs_client.cc
@@ -0,0 +1,1731 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+#include "bta_gatt_api.h"
+#include "bta_ascs_client_api.h"
+#include "gattc_ops_queue.h"
+#include <map>
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include "stack/btm/btm_int.h"
+#include "device/include/controller.h"
+
+#include <vector>
+#include "btif/include/btif_bap_config.h"
+#include "osi/include/log.h"
+#include "btif_util.h"
+
+namespace bluetooth {
+namespace bap {
+namespace ascs {
+
+using base::Closure;
+using bluetooth::bap::GattOpsQueue;
+
+Uuid ASCS_UUID               = Uuid::FromString("184E");
+Uuid ASCS_SINK_ASE_UUID      = Uuid::FromString("2BC4");
+Uuid ASCS_SRC_ASE_UUID       = Uuid::FromString("2BC5");
+Uuid ASCS_ASE_CP_UUID        = Uuid::FromString("2BC6");
+
+class AscsClientImpl;
+AscsClientImpl* instance;
+
+typedef uint8_t codec_type_t[5];
+
+enum class ProfleOP {
+  CONNECT,
+  DISCONNECT
+};
+
+struct ProfileOperation {
+  uint16_t client_id;
+  ProfleOP type;
+};
+
+enum class DevState {
+  IDLE = 0,
+  CONNECTING,
+  CONNECTED,
+  DISCONNECTING
+};
+
+void ascs_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
+void encryption_callback(const RawAddress*, tGATT_TRANSPORT, void*,
+                         tBTM_STATUS);
+
+std::map<uint8_t, std::string> resp_codes = {
+  {0x01,   "Un Supported Opcode"},
+  {0x02,   "Invalid Length"},
+  {0x03,   "Invalid ASE ID"},
+  {0x04,   "Invalid ASE SM Transition"},
+  {0x05,   "Invalid ASE Direction"},
+  {0x06,   "Un Supported Audio Capabilities"},
+  {0x07,   "Un Supported Config Param"},
+  {0x08,   "Rejected Config Param"},
+  {0x09,   "Invalid Config Param"},
+  {0x0A,   "Un Supported Metadata"},
+  {0x0B,   "Rejected Metadata"},
+  {0x0C,   "Invalid Metadata"},
+  {0x0D,   "InSufficient Resources"},
+  {0x0E,   "Unspecified Error"},
+};
+
+std::map<uint8_t, std::string> reason_codes = {
+  {0x01,   "Codec ID"},
+  {0x02,   "Codec Specific Config"},
+  {0x03,   "SDU Interval"},
+  {0x04,   "Framing"},
+  {0x05,   "PHY"},
+  {0x06,   "Maximum SDU Size"},
+  {0x07,   "RTN"},
+  {0x08,   "MTL"},
+  {0x09,   "PD"},
+  {0x0A,   "Invalid ASE CIS Mapping"},
+};
+
+std::vector<AseParams> sink_ase_value_list, src_ase_value_list;
+AseParams ase;
+
+struct AscsDevice {
+  RawAddress address;
+  /* This is true only during first connection to profile, until we store the
+   * device */
+  bool first_connection;
+  bool service_changed_rcvd;
+
+  uint16_t conn_id;
+  std::vector<Ase> sink_ase_list;
+  std::vector<Ase> src_ase_list;
+  uint16_t ase_cp_handle;
+  uint16_t ase_cp_ccc_handle;
+  uint16_t srv_changed_ccc_handle;
+  bool discovery_completed;
+  uint8_t num_ases_read;
+  bool notifications_enabled;
+  DevState state;
+  bool is_congested;
+  std::vector<ProfileOperation> profile_queue;
+  std::vector<uint16_t> connected_client_list; //list client requested for connection
+  AscsDevice(const RawAddress& address) : address(address) {}
+};
+
+class AscsDevices {
+ public:
+  void Add(AscsDevice device) {
+    if (FindByAddress(device.address) != nullptr) return;
+
+    devices.push_back(device);
+  }
+
+  void Remove(const RawAddress& address) {
+    for (auto it = devices.begin(); it != devices.end();) {
+      if (it->address != address) {
+        ++it;
+        continue;
+      }
+
+      it = devices.erase(it);
+      return;
+    }
+  }
+
+  AscsDevice* FindByAddress(const RawAddress& address) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&address](const AscsDevice& device) {
+                               return device.address == address;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  AscsDevice* FindByConnId(uint16_t conn_id) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&conn_id](const AscsDevice& device) {
+                               return device.conn_id == conn_id;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  size_t size() { return (devices.size()); }
+
+  std::vector<AscsDevice> devices;
+};
+
+class AscsClientImpl : public AscsClient {
+ public:
+  ~AscsClientImpl() override = default;
+
+  AscsClientImpl() : gatt_client_id(BTA_GATTS_INVALID_IF) {};
+
+  bool Register(AscsClientCallbacks *callback) {
+    LOG(WARNING) << __func__  << callback;
+    // looks for client is already registered
+    bool is_client_registered = false;
+    for (auto it : callbacks) {
+      AscsClientCallbacks *pac_callback = it.second;
+      if(callback == pac_callback) {
+        is_client_registered = true;
+        break;
+      }
+    }
+
+    LOG(WARNING) << __func__ ;
+
+    if(is_client_registered) {
+      LOG(WARNING) << __func__  << " already registered";
+      return false;
+    }
+
+    if(gatt_client_id == BTA_GATTS_INVALID_IF) {
+      BTA_GATTC_AppRegister(
+        ascs_gattc_callback,
+        base::Bind(
+          [](AscsClientCallbacks *callback, uint8_t client_id, uint8_t status) {
+            if (status != GATT_SUCCESS) {
+              LOG(ERROR) << "Can't start ASCS profile - no gatt "
+                            "clients left!";
+              return;
+            }
+
+            if (instance) {
+              LOG(WARNING) << " ASCS gatt_client_id "
+                           << instance->gatt_client_id;
+              instance->gatt_client_id = client_id;
+              instance->callbacks.insert(std::make_pair(
+                          ++instance->ascs_client_id, callback));
+              callback->OnAscsInitialized(0, instance->ascs_client_id);
+            }
+          },
+          callback), true);
+    } else {
+      instance->callbacks.insert(std::make_pair(
+                    ++instance->ascs_client_id, callback));
+      callback->OnAscsInitialized(0, instance->ascs_client_id);
+    }
+    return true;
+  }
+
+  bool Deregister (uint16_t client_id) {
+    bool status = false;
+    auto it = callbacks.find(client_id);
+    if (it != callbacks.end()) {
+      callbacks.erase(it);
+      if(callbacks.empty()) {
+       // deregister with GATT
+       LOG(WARNING) << __func__ << " Gatt de-register from ascs";
+       BTA_GATTC_AppDeregister(gatt_client_id);
+       gatt_client_id = BTA_GATTS_INVALID_IF;
+      }
+      status = true;
+    }
+    return status;
+  }
+
+  uint8_t GetClientCount () {
+    return callbacks.size();
+  }
+
+  void Connect(uint16_t client_id, const RawAddress& address,
+                         bool is_direct) override {
+    LOG(WARNING) << __func__ << " " << address;
+    AscsDevice *dev = ascsDevices.FindByAddress(address);
+    ProfileOperation op;
+    op.client_id = client_id;
+    op.type = ProfleOP::CONNECT;
+
+    if(dev == nullptr) {
+      AscsDevice pac_dev(address);
+      ascsDevices.Add(pac_dev);
+      dev = ascsDevices.FindByAddress(address);
+    }
+    if (dev == nullptr) {
+      LOG(ERROR) << __func__ << "dev is null";
+      return;
+    }
+
+    switch(dev->state) {
+      case DevState::IDLE: {
+        BTA_GATTC_Open(gatt_client_id, address, is_direct,
+                                           GATT_TRANSPORT_LE, false);
+        dev->state = DevState::CONNECTING;
+        dev->profile_queue.push_back(op);
+      } break;
+      case DevState::CONNECTING: {
+        dev->profile_queue.push_back(op);
+      } break;
+      case DevState::CONNECTED: {
+        auto iter = std::find_if(dev->connected_client_list.begin(),
+                                 dev->connected_client_list.end(),
+                             [&client_id](uint16_t id) {
+                               return id == client_id;
+                             });
+
+        if(iter == dev->connected_client_list.end())
+          dev->connected_client_list.push_back(client_id);
+
+        auto it = callbacks.find(client_id);
+        if (it != callbacks.end()) {
+          AscsClientCallbacks *callback = it->second;
+          callback->OnConnectionState(address, GattState::CONNECTED);
+        }
+      } break;
+      case DevState::DISCONNECTING: {
+        dev->profile_queue.push_back(op);
+      } break;
+    }
+  }
+
+  void Disconnect(uint16_t client_id, const RawAddress& address) override {
+    LOG(WARNING) << __func__ << " " << address;
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+
+    ProfileOperation op;
+    op.client_id = client_id;
+    op.type = ProfleOP::DISCONNECT;
+
+    switch(dev->state) {
+      case DevState::CONNECTING: {
+        auto iter = std::find_if(dev->profile_queue.begin(),
+                                 dev->profile_queue.end(),
+                             [&client_id]( ProfileOperation entry) {
+                               return ((entry.type == ProfleOP::CONNECT) &&
+                                      (entry.client_id == client_id));
+                             });
+        // If it is the last client requested for disconnect
+        if(iter != dev->profile_queue.end() &&
+           dev->profile_queue.size() == 1) {
+          if (dev->conn_id) {
+            // Removes all registrations for connection.
+            BTA_GATTC_CancelOpen(dev->conn_id, address, false);
+            GattOpsQueue::Clean(dev->conn_id);
+            BTA_GATTC_Close(dev->conn_id);
+            dev->profile_queue.push_back(op);
+            dev->state = DevState::DISCONNECTING;
+          } else {
+            // clear the connection queue and
+            // move the state to DISCONNECTING to better track
+            dev->profile_queue.clear();
+            dev->state = DevState::DISCONNECTING;
+            dev->profile_queue.push_back(op);
+          }
+        } else {
+           // remove the connection entry from the list
+           // as the same client has requested for disconnection
+           dev->profile_queue.erase(iter);
+        }
+      } break;
+      case DevState::CONNECTED: {
+        auto iter = std::find_if(dev->connected_client_list.begin(),
+                                 dev->connected_client_list.end(),
+                             [&client_id]( uint16_t stored_client_id) {
+                               return stored_client_id == client_id;
+                             });
+        // if it is the last client requested for disconnection
+        if(iter != dev->connected_client_list.end() &&
+           dev->connected_client_list.size() == 1) {
+          if (dev->conn_id) {
+            // Removes all registrations for connection.
+            BTA_GATTC_CancelOpen(dev->conn_id, address, false);
+            GattOpsQueue::Clean(dev->conn_id);
+            BTA_GATTC_Close(dev->conn_id);
+            dev->profile_queue.push_back(op);
+            dev->state = DevState::DISCONNECTING;
+          }
+        } else {
+          // remove the client from connected_client_list
+          dev->connected_client_list.erase(iter);
+          // remove the  pending gatt ops( not the ongoing one )
+          // initiated from client which requested disconnect
+          // TODO and send callback as disconnected
+        }
+      } break;
+      case DevState::DISCONNECTING: {
+        dev->profile_queue.push_back(op);
+      } break;
+      default:
+        break;
+    }
+  }
+
+  void StartDiscovery(uint16_t client_id, const RawAddress& address) override {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+    LOG(WARNING) << __func__ << " " << address;
+
+    switch(dev->state) {
+      case DevState::CONNECTED: {
+        auto iter = std::find_if(dev->connected_client_list.begin(),
+                                 dev->connected_client_list.end(),
+                             [&client_id]( uint16_t stored_client_id) {
+                  LOG(WARNING) << __func__ << client_id << stored_client_id;
+                               return stored_client_id == client_id;
+                             });
+        // check if the client present in the connected client list
+        if(iter == dev->connected_client_list.end()) {
+           break;
+        }
+        // check if the discovery is already finished
+        // send back the same results to the other client
+        if(dev->discovery_completed && dev->notifications_enabled) {
+          sink_ase_value_list.clear();
+          src_ase_value_list.clear();
+          auto iter = callbacks.find(client_id);
+          if (iter != callbacks.end()) {
+            for (auto it : dev->sink_ase_list) {
+              memcpy(&ase, (void *) &it.ase_params, sizeof(ase));
+              sink_ase_value_list.push_back(ase);
+            }
+            for (auto it : dev->src_ase_list) {
+              memcpy(&ase, (void *) &it.ase_params, sizeof(ase));
+              src_ase_value_list.push_back(ase);
+            }
+
+            AscsClientCallbacks *callback = iter->second;
+            // send out the callback as service discovery completed
+            callback->OnSearchComplete(0, dev->address,
+                                          sink_ase_value_list,
+                                          src_ase_value_list);
+          }
+          break;
+        }
+        // reset it
+        dev->num_ases_read = 0x00;
+        dev->discovery_completed = false;
+        dev->notifications_enabled = false;
+        // queue the request to GATT queue module
+        GattOpsQueue::ServiceSearch(client_id, dev->conn_id, &ASCS_UUID);
+      } break;
+      default:
+        break;
+    }
+  }
+
+  void CodecConfig(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseCodecConfigOp> codec_configs) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    std::vector<uint8_t> vect_val;
+    uint8_t opcode = static_cast<uint8_t> (AseOpId::CODEC_CONFIG);
+    uint8_t num_ases = codec_configs.size();
+    if (!dev || (dev->state != DevState::CONNECTED)) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Num ASEs :" << loghex(num_ases);
+
+    vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
+    vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
+
+    auto it = codec_configs.begin();
+    while (it != codec_configs.end()) {
+      vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
+      vect_val.insert(vect_val.end(), &it->tgt_latency, &it->tgt_latency + 1);
+      vect_val.insert(vect_val.end(), &it->tgt_phy, &it->tgt_phy + 1);
+      vect_val.insert(vect_val.end(), it->codec_id,
+                ((uint8_t *)it->codec_id) + sizeof(codec_type_t));
+
+      vect_val.insert(vect_val.end(), &it->codec_params_len,
+                                      &it->codec_params_len + 1);
+      vect_val.insert(vect_val.end(), it->codec_params.begin(),
+                                      it->codec_params.end());
+
+      LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
+      LOG(INFO) << ": Target Latency = " << loghex(it->tgt_latency);
+      LOG(INFO) << ": target Phy = " << loghex(it->tgt_phy);
+      LOG(INFO) << ": Codec ID = " << loghex(it->codec_id[0]);
+      it++;
+    }
+
+    GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
+                                      dev->ase_cp_handle, vect_val,
+                                      GATT_WRITE, nullptr, nullptr);
+  }
+
+  void QosConfig(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseQosConfigOp> qos_configs) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    std::vector<uint8_t> vect_val;
+    uint8_t opcode = static_cast<uint8_t> (AseOpId::QOS_CONFIG);
+    uint8_t num_ases = qos_configs.size();
+    if (!dev || (dev->state != DevState::CONNECTED)) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Num ASEs :" << loghex(num_ases);
+
+    vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
+    vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
+
+    auto it = qos_configs.begin();
+    while (it != qos_configs.end()) {
+      vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
+      vect_val.insert(vect_val.end(), &it->cig_id, &it->cig_id + 1);
+      vect_val.insert(vect_val.end(), &it->cis_id, &it->cis_id + 1);
+
+      vect_val.insert(vect_val.end(), it->sdu_interval,
+                     (uint8_t *)it->sdu_interval + sizeof(sdu_interval_t));
+
+      // test change it->framing = 0xFF;
+      vect_val.insert(vect_val.end(), &it->framing, &it->framing + 1);
+      vect_val.insert(vect_val.end(), &it->phy, &it->phy + 1);
+
+      vect_val.insert(vect_val.end(), (uint8_t *) &it->max_sdu_size,
+                   (uint8_t *)&it->max_sdu_size + sizeof(uint16_t));
+
+      vect_val.insert(vect_val.end(), &it->retrans_number,
+                                      &it->retrans_number + 1);
+
+      vect_val.insert(vect_val.end(), (uint8_t *) &it->trans_latency,
+                   (uint8_t *)&it->trans_latency + sizeof(uint16_t));
+
+      vect_val.insert(vect_val.end(), it->present_delay,
+                (uint8_t *)it->present_delay + sizeof(presentation_delay_t));
+
+      LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
+      LOG(INFO) << ": Cig Id = " << loghex(it->cig_id);
+      LOG(INFO) << ": Cis Id = " << loghex(it->cis_id);
+      LOG(INFO) << ": SDU interval ="
+                << " " << loghex(it->sdu_interval[0])
+                << " " << loghex(it->sdu_interval[1])
+                << " " << loghex(it->sdu_interval[2]);
+      LOG(INFO) << ": Framing = " << loghex(it->framing);
+      LOG(INFO) << ": Phy = " << loghex(it->phy);
+      LOG(INFO) << ": Max SDU size = " << loghex(it->max_sdu_size);
+      LOG(INFO) << ": RTN = " << loghex(it->retrans_number);
+      LOG(INFO) << ": MTL = " << loghex(it->trans_latency);
+      LOG(INFO) << ": PD ="
+                << " " << loghex(it->present_delay[0])
+                << " " << loghex(it->present_delay[1])
+                << " " << loghex(it->present_delay[2]);
+      it++;
+    }
+
+    GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
+                                      dev->ase_cp_handle, vect_val,
+                                      GATT_WRITE, nullptr, nullptr);
+  }
+
+  void Enable(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseEnableOp> enable_ops) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    std::vector<uint8_t> vect_val;
+    uint8_t opcode = static_cast<uint8_t> (AseOpId::ENABLE);
+    uint8_t num_ases = enable_ops.size();
+    if (!dev || (dev->state != DevState::CONNECTED)) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Num ASEs :" << loghex(num_ases);
+
+    vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
+    vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
+
+    auto it = enable_ops.begin();
+    while (it != enable_ops.end()) {
+      LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
+      vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
+      // test change it->meta_data_len = 0xFF;
+      vect_val.insert(vect_val.end(), &it->meta_data_len,
+                                      &it->meta_data_len + 1);
+      vect_val.insert(vect_val.end(), it->meta_data.begin(),
+                                      it->meta_data.end());
+      it++;
+    }
+
+    GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
+                                      dev->ase_cp_handle, vect_val,
+                                      GATT_WRITE, nullptr, nullptr);
+  }
+
+  void StartReady(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseStartReadyOp> start_ready_ops) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    std::vector<uint8_t> vect_val;
+    uint8_t opcode = static_cast<uint8_t> (AseOpId::START_READY);
+    uint8_t num_ases = start_ready_ops.size();
+    if (!dev || (dev->state != DevState::CONNECTED)) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Num ASEs :" << loghex(num_ases);
+
+    vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
+    vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
+
+    auto it = start_ready_ops.begin();
+    while (it != start_ready_ops.end()) {
+      LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
+      vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
+      it++;
+    }
+
+    GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
+                                      dev->ase_cp_handle, vect_val,
+                                      GATT_WRITE, nullptr, nullptr);
+  }
+
+  void Disable(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseDisableOp> disable_ops) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    std::vector<uint8_t> vect_val;
+    uint8_t opcode = static_cast<uint8_t> (AseOpId::DISABLE);
+    uint8_t num_ases = disable_ops.size();
+    if (!dev || (dev->state != DevState::CONNECTED)) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Num ASEs :" << loghex(num_ases);
+
+    vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
+    vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
+
+    auto it = disable_ops.begin();
+    while (it != disable_ops.end()) {
+      LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
+      vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
+      it++;
+    }
+
+    GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
+                                      dev->ase_cp_handle, vect_val,
+                                      GATT_WRITE, nullptr, nullptr);
+  }
+
+  void StopReady(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseStopReadyOp> stop_ready_ops) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    std::vector<uint8_t> vect_val;
+    uint8_t opcode = static_cast<uint8_t> (AseOpId::STOP_READY);
+    uint8_t num_ases = stop_ready_ops.size();
+    if (!dev || (dev->state != DevState::CONNECTED)) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Num ASEs :" << loghex(num_ases);
+
+    vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
+    vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
+
+    auto it = stop_ready_ops.begin();
+    while (it != stop_ready_ops.end()) {
+      LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
+      vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
+      it++;
+    }
+
+    GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
+                                      dev->ase_cp_handle, vect_val,
+                                      GATT_WRITE, nullptr, nullptr);
+  }
+
+  void Release(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseReleaseOp> release_ops) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    std::vector<uint8_t> vect_val;
+    uint8_t opcode = static_cast<uint8_t> (AseOpId::RELEASE);
+    uint8_t num_ases = release_ops.size();
+    if (!dev || (dev->state != DevState::CONNECTED)) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Num ASEs :" << loghex(num_ases);
+
+    vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
+    vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
+
+    auto it = release_ops.begin();
+    while (it != release_ops.end()) {
+      LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
+      vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
+      it++;
+    }
+
+    GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
+                                      dev->ase_cp_handle, vect_val,
+                                      GATT_WRITE, nullptr, nullptr);
+  }
+
+  void UpdateStream(uint16_t client_id, const RawAddress& address,
+                    std::vector<AseUpdateMetadataOp> metadata_ops) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    std::vector<uint8_t> vect_val;
+    uint8_t opcode = static_cast<uint8_t> (AseOpId::UPDATE_META_DATA);
+    uint8_t num_ases = metadata_ops.size();
+    if (!dev || (dev->state != DevState::CONNECTED)) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Num ASEs :" << loghex(num_ases);
+
+    vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
+    vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
+
+    auto it = metadata_ops.begin();
+    while (it != metadata_ops.end()) {
+      LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
+      vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
+      vect_val.insert(vect_val.end(), &it->meta_data_len,
+                                      &it->meta_data_len + 1);
+      vect_val.insert(vect_val.end(), it->meta_data.begin(),
+                                      it->meta_data.end());
+      it++;
+    }
+
+    GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
+                                      dev->ase_cp_handle, vect_val,
+                                      GATT_WRITE, nullptr, nullptr);
+  }
+
+  bool GetAseParams(const RawAddress& address, uint8_t ase_id,
+                    AseParams *ase_params) {
+    bool ase_found = false;
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return false;
+    }
+
+    // first look for sink ASEs
+    for (auto it = dev->sink_ase_list.begin();
+              it != dev->sink_ase_list.end(); it++) {
+      if (it->ase_params.ase_id == ase_id) {
+        *ase_params = it->ase_params;
+        ase_found = true;
+        break;
+      }
+    }
+    if(ase_found) return ase_found;
+
+    for (auto it = dev->src_ase_list.begin();
+              it != dev->src_ase_list.end(); it++) {
+      if (it->ase_params.ase_id == ase_id) {
+        *ase_params = it->ase_params;
+        ase_found = true;
+        break;
+      }
+    }
+    return ase_found;
+  }
+
+  bool GetAseHandle(const RawAddress& address, uint8_t ase_id,
+                    uint16_t *ase_handle) {
+    bool ase_found = false;
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return false;
+    }
+
+    // first look for sink ASEs
+    for (auto it = dev->sink_ase_list.begin();
+              it != dev->sink_ase_list.end(); it++) {
+      if (it->ase_params.ase_id == ase_id) {
+        *ase_handle = it->ase_handle;
+        ase_found = true;
+        break;
+      }
+    }
+    if(ase_found) return ase_found;
+
+    for (auto it = dev->src_ase_list.begin();
+              it != dev->src_ase_list.end(); it++) {
+      if (it->ase_params.ase_id == ase_id) {
+        *ase_handle = it->ase_handle;
+        ase_found = true;
+        break;
+      }
+    }
+    return ase_found;
+  }
+
+  void GetAseState(uint16_t client_id, const RawAddress& address,
+                   uint8_t ase_id) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+    LOG(WARNING) << __func__ << " " << address;
+
+    switch(dev->state) {
+      case DevState::CONNECTED: {
+        uint16_t ase_handle;
+        auto iter = std::find_if(dev->connected_client_list.begin(),
+                                 dev->connected_client_list.end(),
+                             [&client_id]( uint16_t stored_client_id) {
+                               return stored_client_id == client_id;
+                             });
+        // check if the client present in the connected client list
+        if(iter == dev->connected_client_list.end()) {
+           break;
+        }
+
+        // check if the discovery is already finished
+        // send back the same results to the other client
+        if(dev->discovery_completed && dev->notifications_enabled) {
+          auto iter = callbacks.find(client_id);
+          AseParams ase_params;
+          if(iter != callbacks.end() &&
+             GetAseParams(address, ase_id, &ase_params)) {
+            AscsClientCallbacks *callback = iter->second;
+            callback->OnAseState(dev->address, ase_params);
+          }
+          break;
+        }
+
+        if(GetAseHandle(address, ase_id, &ase_handle)) {
+          // queue the request to GATT queue module
+          GattOpsQueue::ReadCharacteristic(client_id, dev->conn_id,
+                         ase_handle,
+                         AscsClientImpl::OnReadAseStateStatic, nullptr);
+        }
+      } break;
+      default:
+        LOG(WARNING) << __func__ << "un-handled event";
+        break;
+    }
+  }
+
+  void OnGattConnected(tGATT_STATUS status, uint16_t conn_id,
+                       tGATT_IF client_if, RawAddress address,
+                       tBTA_TRANSPORT transport, uint16_t mtu) {
+
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    if (!dev) {
+      /* When device is quickly disabled and enabled in settings, this case
+       * might happen */
+      LOG(ERROR) << "Closing connection to non ascs device, address="
+                   << address;
+      BTA_GATTC_Close(conn_id);
+      return;
+    }
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Status : " << loghex(status);
+
+    if(dev->state == DevState::CONNECTING) {
+      if (status != GATT_SUCCESS) {
+        LOG(ERROR) << "Failed to connect to ASCS device";
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if(it->type == ProfleOP::CONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              AscsClientCallbacks *callback = iter->second;
+              callback->OnConnectionState(address, GattState::DISCONNECTED);
+            }
+            dev->profile_queue.erase(it);
+          } else {
+            it++;
+          }
+        }
+        dev->state = DevState::IDLE;
+        ascsDevices.Remove(address);
+        return;
+      }
+    } else if(dev->state == DevState::DISCONNECTING) {
+      // TODO will this happens ?
+      // it could have called the cancel open to expect the
+      // open cancelled event
+      if (status != GATT_SUCCESS) {
+        LOG(ERROR) << "Failed to connect to ASCS device";
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if(it->type == ProfleOP::DISCONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              AscsClientCallbacks *callback = iter->second;
+              callback->OnConnectionState(address, GattState::DISCONNECTED);
+            }
+            dev->profile_queue.erase(it);
+          } else {
+            it++;
+          }
+        }
+        dev->state = DevState::IDLE;
+        ascsDevices.Remove(address);
+        return;
+      } else {
+        // gatt connected successfully
+        // if the disconnect entry is found we need to initiate the
+        // gatt disconnect. may be a race condition just after sending
+        // cancel open gatt connected event received
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if(it->type == ProfleOP::DISCONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              // Removes all registrations for connection.
+              BTA_GATTC_CancelOpen(dev->conn_id, address, false);
+              GattOpsQueue::Clean(dev->conn_id);
+              BTA_GATTC_Close(dev->conn_id);
+              break;
+            }
+          } else {
+            it++;
+          }
+        }
+        return;
+      }
+    } else {
+      // return unconditinally
+      return;
+    }
+
+    // success scenario code
+    dev->conn_id = conn_id;
+
+    tACL_CONN* p_acl = btm_bda_to_acl(address, BT_TRANSPORT_LE);
+    if (p_acl != nullptr &&
+        controller_get_interface()->supports_ble_2m_phy() &&
+        HCI_LE_2M_PHY_SUPPORTED(p_acl->peer_le_features)) {
+      LOG(INFO) << address << " set preferred PHY to 2M";
+      BTM_BleSetPhy(address, PHY_LE_2M, PHY_LE_2M, 0);
+    }
+
+    /* verify bond */
+    uint8_t sec_flag = 0;
+    BTM_GetSecurityFlagsByTransport(address, &sec_flag, BT_TRANSPORT_LE);
+
+    if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) {
+      /* if link has been encrypted */
+      OnEncryptionComplete(address, true);
+      return;
+    }
+
+    if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) {
+      /* if bonded and link not encrypted */
+      sec_flag = BTM_BLE_SEC_ENCRYPT;
+      LOG(WARNING) << "trying to encrypt now";
+      BTM_SetEncryption(address, BTA_TRANSPORT_LE, encryption_callback, nullptr,
+                        sec_flag);
+      return;
+    }
+
+    /* otherwise let it go through */
+    OnEncryptionComplete(address, true);
+  }
+
+  void OnGattDisconnected(tGATT_STATUS status, uint16_t conn_id,
+                          tGATT_IF client_if, RawAddress remote_bda,
+                          tBTA_GATT_REASON reason) {
+    AscsDevice* dev = ascsDevices.FindByAddress(remote_bda);
+    if (!dev) {
+      LOG(ERROR) << "Skipping unknown device disconnect, conn_id="
+                 << loghex(conn_id);
+      return;
+    }
+
+    LOG(INFO) << __func__ << ": BD Addr : " << remote_bda
+                          << ", Status : " << loghex(status)
+                          << ", state: " << static_cast<int>(dev->state);
+
+    switch(dev->state) {
+      case DevState::CONNECTING: {
+        // sudden disconnection
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if(it->type == ProfleOP::CONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              AscsClientCallbacks *callback = iter->second;
+              callback->OnConnectionState(remote_bda, GattState::DISCONNECTED);
+            }
+            it = dev->profile_queue.erase(it);
+          } else {
+            it++;
+          }
+        }
+      } break;
+      case DevState::CONNECTED: {
+        // sudden disconnection
+        for (auto it = dev->connected_client_list.begin();
+                             it != dev->connected_client_list.end();) {
+          // get the callback and update the upper layers
+          auto iter = callbacks.find(*it);
+          if (iter != callbacks.end()) {
+            AscsClientCallbacks *callback = iter->second;
+            callback->OnConnectionState(remote_bda, GattState::DISCONNECTED);
+          }
+          it = dev->connected_client_list.erase(it);
+        }
+      } break;
+      case DevState::DISCONNECTING: {
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if(it->type == ProfleOP::DISCONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              AscsClientCallbacks *callback = iter->second;
+              callback->OnConnectionState(remote_bda, GattState::DISCONNECTED);
+            }
+            it = dev->profile_queue.erase(it);
+          } else {
+            it++;
+          }
+        }
+
+        for (auto it = dev->connected_client_list.begin();
+                             it != dev->connected_client_list.end();) {
+          // get the callback and update the upper layers
+          it = dev->connected_client_list.erase(it);
+        }
+        // check if the connection queue is not empty
+        // if not initiate the Gatt connection
+      } break;
+      default:
+        break;
+    }
+
+    if (dev->conn_id) {
+      GattOpsQueue::Clean(dev->conn_id);
+      BTA_GATTC_Close(dev->conn_id);
+      dev->conn_id = 0;
+    }
+
+    dev->state = DevState::IDLE;
+    ascsDevices.Remove(remote_bda);
+  }
+
+  void OnConnectionUpdateComplete(uint16_t conn_id, tBTA_GATTC* p_data) {
+    AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(ERROR) << "Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+  }
+
+  void OnEncryptionComplete(const RawAddress& address, bool success) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(ERROR) << "Skipping unknown device" << address;
+      return;
+    }
+
+    if(dev->state != DevState::CONNECTING) {
+      LOG(ERROR) << "received in wrong state" << address;
+      return;
+    }
+
+    LOG(INFO) << __func__ << ": BD Addr : " << address
+                          << ": Status : " << loghex(success);
+
+    // encryption failed
+    if (!success) {
+      for (auto it = dev->profile_queue.begin();
+                           it != dev->profile_queue.end();) {
+        if(it->type == ProfleOP::CONNECT) {
+          // get the callback and update the upper layers
+          auto iter = callbacks.find(it->client_id);
+          if (iter != callbacks.end()) {
+            AscsClientCallbacks *callback = iter->second;
+            callback->OnConnectionState(address, GattState::DISCONNECTED);
+          }
+          // change the type to disconnect
+          it->type = ProfleOP::DISCONNECT;
+        } else {
+          it++;
+        }
+      }
+      dev->state = DevState::DISCONNECTING;
+      // Removes all registrations for connection.
+      BTA_GATTC_CancelOpen(dev->conn_id, address, false);
+      BTA_GATTC_Close(dev->conn_id);
+    } else {
+      for (auto it = dev->profile_queue.begin();
+                           it != dev->profile_queue.end();) {
+        if(it->type == ProfleOP::CONNECT) {
+          // get the callback and update the upper layers
+          auto iter = callbacks.find(it->client_id);
+          if (iter != callbacks.end()) {
+            dev->connected_client_list.push_back(it->client_id);
+            AscsClientCallbacks *callback = iter->second;
+            callback->OnConnectionState(address, GattState::CONNECTED);
+          }
+          dev->profile_queue.erase(it);
+        } else {
+          it++;
+        }
+      }
+      dev->state = DevState::CONNECTED;
+    }
+  }
+
+  void OnServiceChangeEvent(const RawAddress& address) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(ERROR) << "Skipping unknown device" << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": address=" << address;
+    dev->first_connection = true;
+    dev->service_changed_rcvd = true;
+    GattOpsQueue::Clean(dev->conn_id);
+  }
+
+  void OnServiceDiscDoneEvent(const RawAddress& address) {
+    AscsDevice* dev = ascsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(ERROR) << "Skipping unknown device" << address;
+      return;
+    }
+    if (dev->service_changed_rcvd) {
+      // queue the request to GATT queue module with dummu client id
+      GattOpsQueue::ServiceSearch(0XFF, dev->conn_id, &ASCS_UUID);
+    }
+  }
+
+  void OnServiceSearchComplete(uint16_t conn_id, tGATT_STATUS status) {
+    AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(ERROR) << "Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    LOG(INFO) << __func__ << ": BD Addr : " << dev->address
+                          << ": Status : " << loghex(status);
+
+    uint16_t client_id = GattOpsQueue::ServiceSearchComplete(conn_id,
+                                          status);
+    auto iter = callbacks.find(client_id);
+    if (status != GATT_SUCCESS) {
+      /* close connection and report service discovery complete with error */
+      LOG(ERROR) << "Service discovery failed";
+      if (iter != callbacks.end()) {
+        AscsClientCallbacks *callback = iter->second;
+        std::vector<AseParams> ase_value_list;
+        callback->OnSearchComplete(0xFF, dev->address, ase_value_list,
+                                   ase_value_list);
+      }
+      return;
+    }
+
+    const std::vector<gatt::Service>* services = BTA_GATTC_GetServices(conn_id);
+
+    const gatt::Service* service = nullptr;
+    if (services) {
+      for (const gatt::Service& tmp : *services) {
+        if (tmp.uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER)) {
+          LOG(INFO) << "Found UUID_SERVCLASS_GATT_SERVER, handle="
+                    << loghex(tmp.handle);
+          const gatt::Service* service_changed_service = &tmp;
+          find_server_changed_ccc_handle(conn_id, service_changed_service);
+        } else if (tmp.uuid == ASCS_UUID) {
+          LOG(INFO) << "Found ASCS service, handle=" << loghex(tmp.handle);
+          service = &tmp;
+        }
+      }
+    } else {
+      LOG(ERROR) << "no services found for conn_id: " << conn_id;
+      return;
+    }
+
+    if (!service) {
+      LOG(ERROR) << "No ASCS service found";
+      if (iter != callbacks.end()) {
+        AscsClientCallbacks *callback = iter->second;
+        std::vector<AseParams> ase_value_list;
+        callback->OnSearchComplete(0xFF, dev->address, ase_value_list,
+                                   ase_value_list);
+      }
+      return;
+    }
+
+    for (const gatt::Characteristic& charac : service->characteristics) {
+      if (charac.uuid == ASCS_SINK_ASE_UUID ||
+          charac.uuid == ASCS_SRC_ASE_UUID) {
+        Ase ase_info;
+        ase_info.ase_handle = charac.value_handle;
+        GattOpsQueue::ReadCharacteristic(
+                 client_id, conn_id, charac.value_handle,
+                 AscsClientImpl::OnReadOnlyPropertiesReadStatic, nullptr);
+
+        ase_info.ase_ccc_handle =
+            find_ccc_handle(conn_id, charac.value_handle);
+
+        if(charac.uuid == ASCS_SINK_ASE_UUID) {
+          dev->sink_ase_list.push_back(ase_info);
+        } else if(charac.uuid == ASCS_SRC_ASE_UUID) {
+          dev->src_ase_list.push_back(ase_info);
+        }
+        if(ase_info.ase_ccc_handle) {
+          /* Register and enable the Audio Status Notification */
+          tGATT_STATUS register_status;
+          register_status = BTA_GATTC_RegisterForNotifications(
+              conn_id, dev->address, ase_info.ase_handle);
+          if (register_status != GATT_SUCCESS) {
+            LOG(ERROR) << __func__
+                       << ": BTA_GATTC_RegisterForNotifications failed, status="
+                       << loghex(register_status);
+          }
+          std::vector<uint8_t> value(2);
+          uint8_t* ptr = value.data();
+          UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
+          GattOpsQueue::WriteDescriptor(
+              client_id, conn_id, ase_info.ase_ccc_handle,
+              std::move(value), GATT_WRITE, nullptr, nullptr);
+        }
+      } else if (charac.uuid == ASCS_ASE_CP_UUID) {
+        dev->ase_cp_handle = charac.value_handle;
+
+        dev->ase_cp_ccc_handle =
+            find_ccc_handle(conn_id, charac.value_handle);
+        if(dev->ase_cp_ccc_handle) {
+          /* Register and enable the Audio Status Notification */
+          tGATT_STATUS register_status;
+          register_status = BTA_GATTC_RegisterForNotifications(
+              conn_id, dev->address, dev->ase_cp_handle);
+          if (register_status != GATT_SUCCESS) {
+            LOG(ERROR) << __func__
+                       << ": BTA_GATTC_RegisterForNotifications failed, status="
+                       << loghex(register_status);
+          }
+          std::vector<uint8_t> value(2);
+          uint8_t* ptr = value.data();
+          UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
+          GattOpsQueue::WriteDescriptor(
+              client_id, conn_id, dev->ase_cp_ccc_handle,
+              std::move(value), GATT_WRITE, nullptr, nullptr);
+        }
+      } else {
+         LOG(WARNING) << "Unknown characteristic found:" << charac.uuid;
+      }
+    }
+
+    dev->notifications_enabled = true;
+
+    if (dev->service_changed_rcvd) {
+      dev->service_changed_rcvd = false;
+    }
+  }
+
+  const char* GetAseState(uint8_t event) {
+    switch (event) {
+      CASE_RETURN_STR(ASE_STATE_IDLE)
+      CASE_RETURN_STR(ASE_STATE_CODEC_CONFIGURED)
+      CASE_RETURN_STR(ASE_STATE_QOS_CONFIGURED)
+      CASE_RETURN_STR(ASE_STATE_ENABLING)
+      CASE_RETURN_STR(ASE_STATE_STREAMING)
+      CASE_RETURN_STR(ASE_STATE_DISABLING)
+      CASE_RETURN_STR(ASE_STATE_RELEASING)
+      default:
+       return "Unknown State";
+    }
+  }
+
+  const char* GetAseDirection(uint8_t event) {
+    switch (event) {
+      CASE_RETURN_STR(ASE_DIRECTION_SINK)
+      CASE_RETURN_STR(ASE_DIRECTION_SOURCE)
+      default:
+       return "Unknown Direction";
+    }
+  }
+
+  void ParseAseParams(uint8_t *p, AseParams *ase_params, uint8_t ase_dir) {
+    STREAM_TO_UINT8(ase_params->ase_id, p);
+    STREAM_TO_UINT8(ase_params->ase_state, p);
+    LOG(INFO) << __func__
+              << ": ASE Id = " << loghex(ase_params->ase_id)
+              << ": ASE State = " << GetAseState(ase_params->ase_state)
+              << ": ASE Direction = " << GetAseDirection(ase_dir);
+    switch(ase_params->ase_state) {
+      case ASE_STATE_CODEC_CONFIGURED: {
+        AseCodecConfigParams *codec_config =
+                         &ase_params->codec_config_params;
+        STREAM_TO_UINT8(codec_config->framing, p);
+        STREAM_TO_UINT8(codec_config->pref_phy, p);
+
+        STREAM_TO_UINT8(codec_config->pref_rtn, p);
+        STREAM_TO_UINT16(codec_config->mtl, p);
+        STREAM_TO_ARRAY(&(codec_config->pd_min), p,
+                       static_cast<int> (sizeof(presentation_delay_t)));
+        STREAM_TO_ARRAY(&(codec_config->pd_max), p,
+                       static_cast<int> (sizeof(presentation_delay_t)));
+        STREAM_TO_ARRAY(&(codec_config->pref_pd_min), p,
+                       static_cast<int> (sizeof(presentation_delay_t)));
+        STREAM_TO_ARRAY(&(codec_config->pref_pd_max), p,
+                       static_cast<int> (sizeof(presentation_delay_t)));
+        STREAM_TO_ARRAY(&(codec_config->codec_id),
+                          p, static_cast<int> (sizeof(codec_type_t)));
+        STREAM_TO_UINT8(codec_config->codec_params_len, p);
+        if(codec_config->codec_params_len) {
+          codec_config->codec_params.resize(codec_config->codec_params_len);
+          STREAM_TO_ARRAY(codec_config->codec_params.data(),
+                  p, codec_config->codec_params_len);
+        }
+        LOG(INFO) << ": Framing = " << loghex(codec_config->framing);
+        LOG(INFO) << ": Pref Phy = " << loghex(codec_config->pref_phy);
+        LOG(INFO) << ": Pref RTN = " << loghex(codec_config->pref_rtn);
+        LOG(INFO) << ": MTL = " << loghex(codec_config->mtl);
+        LOG(INFO) << ": PD Min ="
+                  << " " << loghex(codec_config->pd_min[0])
+                  << " " << loghex(codec_config->pd_min[1])
+                  << " " << loghex(codec_config->pd_min[2]);
+        LOG(INFO) << ": PD Max ="
+                  << " " << loghex(codec_config->pd_max[0])
+                  << " " << loghex(codec_config->pd_max[1])
+                  << " " << loghex(codec_config->pd_max[2]);
+        LOG(INFO) << ": Pref PD Min ="
+                  << " " << loghex(codec_config->pref_pd_min[0])
+                  << " " << loghex(codec_config->pref_pd_min[1])
+                  << " " << loghex(codec_config->pref_pd_min[2]);
+        LOG(INFO) << ": Pref PD Max ="
+                  << " " << loghex(codec_config->pref_pd_max[0])
+                  << " " << loghex(codec_config->pref_pd_max[1])
+                  << " " << loghex(codec_config->pref_pd_max[2]);
+
+        LOG(INFO) << ": Codec ID = " << loghex(codec_config->codec_id[0]);
+      } break;
+      case ASE_STATE_QOS_CONFIGURED: {
+        AseQosConfigParams *qos_config = &ase_params->qos_config_params;
+        STREAM_TO_UINT8(qos_config->cig_id, p);
+        STREAM_TO_UINT8(qos_config->cis_id, p);
+        STREAM_TO_ARRAY(&(qos_config->sdu_interval), p,
+                       static_cast<int> (sizeof(sdu_interval_t)));
+        STREAM_TO_UINT8(qos_config->framing, p);
+        STREAM_TO_UINT8(qos_config->phy, p);
+        STREAM_TO_UINT16(qos_config->max_sdu_size, p);
+        STREAM_TO_UINT8(qos_config->rtn, p);
+        STREAM_TO_UINT16(qos_config->mtl, p);
+        STREAM_TO_ARRAY(&(qos_config->pd), p,
+                       static_cast<int> (sizeof(presentation_delay_t)));
+
+        LOG(INFO) << ": Cig Id = " << loghex(qos_config->cig_id);
+        LOG(INFO) << ": Cis Id = " << loghex(qos_config->cis_id);
+        LOG(INFO) << ": SDU interval ="
+                  << " " << loghex(qos_config->sdu_interval[0])
+                  << " " << loghex(qos_config->sdu_interval[1])
+                  << " " << loghex(qos_config->sdu_interval[2]);
+        LOG(INFO) << ": Framing = " << loghex(qos_config->framing);
+        LOG(INFO) << ": Phy = " << loghex(qos_config->phy);
+        LOG(INFO) << ": Max SDU size = " << loghex(qos_config->max_sdu_size);
+        LOG(INFO) << ": RTN = " << loghex(qos_config->rtn);
+        LOG(INFO) << ": MTL = " << loghex(qos_config->mtl);
+        LOG(INFO) << ": PD ="
+                  << " " << loghex(qos_config->pd[0])
+                  << " " << loghex(qos_config->pd[1])
+                  << " " << loghex(qos_config->pd[2]);
+      } break;
+      case ASE_STATE_ENABLING:
+      case ASE_STATE_STREAMING:
+      case ASE_STATE_DISABLING: {
+        AseGenericParams *gen_params = &ase_params->generic_params;
+        STREAM_TO_UINT8(gen_params->cig_id, p);
+        STREAM_TO_UINT8(gen_params->cis_id, p);
+        STREAM_TO_UINT8(gen_params->meta_data_len, p);
+        if(gen_params->meta_data_len) {
+          gen_params->meta_data.resize(gen_params->meta_data_len);
+          STREAM_TO_ARRAY(gen_params->meta_data.data(),
+                  p, gen_params->meta_data_len);
+        }
+        LOG(INFO) << ": Cig Id = " << loghex(gen_params->cig_id);
+        LOG(INFO) << ": Cis Id = " << loghex(gen_params->cis_id);
+      } break;
+    }
+  }
+
+  void ParseAseNotification(uint16_t conn_id,
+                             uint16_t handle, uint16_t len, uint8_t* value ) {
+    uint8_t *p = value;
+    bool ase_found = false;
+    AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(INFO) << __func__
+                << ": Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    for (auto it = dev->sink_ase_list.begin();
+              it != dev->sink_ase_list.end(); it++) {
+      if (it->ase_handle == handle) {
+        LOG(INFO) << __func__ << ": BD Addr : " << dev->address;
+        ParseAseParams(p, &it->ase_params, ASE_DIRECTION_SINK);
+        for (auto iter : callbacks) {
+          AscsClientCallbacks *ascs_callback = iter.second;
+          ascs_callback->OnAseState(dev->address, it->ase_params);
+        }
+        ase_found = true;
+        break;
+      }
+    }
+    if(!ase_found) {
+      for (auto it = dev->src_ase_list.begin();
+                it != dev->src_ase_list.end(); it++) {
+        if (it->ase_handle == handle) {
+          LOG(INFO) << __func__ << ": BD Addr : " << dev->address;
+          ParseAseParams(p, &it->ase_params,ASE_DIRECTION_SOURCE);
+          for (auto iter : callbacks) {
+            AscsClientCallbacks *ascs_callback = iter.second;
+            ascs_callback->OnAseState(dev->address, it->ase_params);
+          }
+          ase_found = true;
+          break;
+        }
+      }
+    }
+  }
+
+  void OnNotificationEvent(uint16_t conn_id, uint16_t handle, uint16_t len,
+                           uint8_t* value) {
+    uint8_t* p = value;
+    AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(INFO) << __func__
+                << ": Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    // check if the notification is for ASEs
+    if( dev->ase_cp_handle == handle) { // control point notification
+      AseCpNotification cp_notification;
+      STREAM_TO_UINT8(cp_notification.ase_opcode, p);
+      STREAM_TO_UINT8(cp_notification.num_ases, p);
+      uint8_t num_ases = cp_notification.num_ases;
+      std::vector<AseOpStatus> ase_cp_notify_list;
+      AseOpStatus status;
+      bool notify = false;
+
+      while(num_ases--) {
+        STREAM_TO_UINT8(status.ase_id, p);
+        STREAM_TO_UINT8(status.resp_code, p);
+        STREAM_TO_UINT8(status.reason, p);
+        if(status.resp_code) {
+          LOG(ERROR) << __func__
+                     << ": ASE Id = " << loghex(status.ase_id)
+                     << ": Resp code = " << resp_codes[status.resp_code];
+          if(status.reason) {
+            LOG(ERROR) << ": Reason = " << reason_codes[status.reason];
+          }
+          notify = true;
+        }
+
+        ase_cp_notify_list.push_back(status);
+      }
+      if(notify) {
+        for (auto iter : callbacks) {
+          AscsClientCallbacks *ascs_callback = iter.second;
+          LOG(ERROR) << __func__ << " ASE Operation failed";
+          ascs_callback->OnAseOpFailed(dev->address,
+                                       (AseOpId) cp_notification.ase_opcode,
+                                       ase_cp_notify_list);
+        }
+      }
+    } else {
+      ParseAseNotification(conn_id, handle, len, value);
+    }
+  }
+
+
+  void OnCongestionEvent(uint16_t conn_id, bool congested) {
+    AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(INFO) << __func__
+                << ": Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    LOG(WARNING) << __func__ << ": conn_id:" << loghex(conn_id)
+                             << ", congested: " << congested;
+    dev->is_congested = congested;
+    GattOpsQueue::CongestionCallback(conn_id, congested);
+  }
+
+  void OnReadAseState(uint16_t client_id,
+                            uint16_t conn_id, tGATT_STATUS status,
+                            uint16_t handle, uint16_t len, uint8_t* value,
+                            void* data) {
+
+    AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(ERROR) << __func__ << "unknown conn_id=" << loghex(conn_id);
+      return;
+    }
+    LOG(WARNING) << __func__;
+
+    // check if the notification is for ASEs
+    ParseAseNotification(conn_id, handle, len, value);
+  }
+
+  void OnReadOnlyPropertiesRead(uint16_t client_id,
+                                uint16_t conn_id, tGATT_STATUS status,
+                                uint16_t handle, uint16_t len,
+                                uint8_t *value, void* data) {
+    AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
+    uint8_t *p = value;
+    if (!dev) {
+      LOG(ERROR) << __func__ << "unknown conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    for (auto it = dev->sink_ase_list.begin();
+              it != dev->sink_ase_list.end(); it++) {
+      if (it->ase_handle == handle) {
+        dev->num_ases_read++;
+        ParseAseParams(p, &it->ase_params, ASE_DIRECTION_SINK);
+        break;
+      }
+    }
+
+    for (auto it = dev->src_ase_list.begin();
+              it != dev->src_ase_list.end(); it++) {
+      if (it->ase_handle == handle) {
+        dev->num_ases_read++;
+        ParseAseParams(p, &it->ase_params, ASE_DIRECTION_SOURCE);
+        break;
+      }
+    }
+
+    LOG(INFO) << __func__ << ": num_ases_read : "
+                          << loghex(dev->num_ases_read);
+
+    if(dev->num_ases_read == (dev->sink_ase_list.size() +
+                              dev->src_ase_list.size())) {
+      sink_ase_value_list.clear();
+      src_ase_value_list.clear();
+      dev->discovery_completed = true;
+      // Now update using service discovery callback
+      auto iter = callbacks.find(client_id);
+      if (iter != callbacks.end()) {
+        for (auto it : dev->sink_ase_list) {
+          memcpy(&ase, (void *) &it.ase_params, sizeof(ase));
+          sink_ase_value_list.push_back(ase);
+        }
+        for (auto it : dev->src_ase_list) {
+          memcpy(&ase, (void *) &it.ase_params, sizeof(ase));
+          src_ase_value_list.push_back(ase);
+        }
+        AscsClientCallbacks *callback = iter->second;
+        // check if all ascs characteristics are read
+        // send out the callback as service discovery completed
+        callback->OnSearchComplete(0, dev->address,
+                                      sink_ase_value_list,
+                                      src_ase_value_list);
+      }
+    }
+  }
+
+  static void OnReadOnlyPropertiesReadStatic(uint16_t client_id,
+                                             uint16_t conn_id,
+                                             tGATT_STATUS status,
+                                             uint16_t handle, uint16_t len,
+                                             uint8_t* value, void* data) {
+    if (instance)
+      instance->OnReadOnlyPropertiesRead(client_id, conn_id, status, handle,
+                                         len, value, data);
+  }
+
+  static void OnReadAseStateStatic(uint16_t client_id,
+                             uint16_t conn_id,
+                             tGATT_STATUS status,
+                             uint16_t handle, uint16_t len,
+                             uint8_t* value, void* data) {
+    if (instance)
+      instance->OnReadAseState(client_id, conn_id, status, handle,
+                               len, value, data);
+  }
+
+ private:
+  uint8_t gatt_client_id = BTA_GATTS_INVALID_IF;
+  uint16_t ascs_client_id = 0;
+  AscsDevices ascsDevices;
+  // client id to callbacks mapping
+  std::map<uint16_t, AscsClientCallbacks *> callbacks;
+
+  void find_server_changed_ccc_handle(uint16_t conn_id,
+                                      const gatt::Service* service) {
+    AscsDevice* ascsDevice = ascsDevices.FindByConnId(conn_id);
+    if (!ascsDevice) {
+      LOG(ERROR) << "Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+    for (const gatt::Characteristic& charac : service->characteristics) {
+      if (charac.uuid == Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD)) {
+        ascsDevice->srv_changed_ccc_handle =
+            find_ccc_handle(conn_id, charac.value_handle);
+        if (!ascsDevice->srv_changed_ccc_handle) {
+          LOG(ERROR) << __func__
+                     << ": cannot find service changed CCC descriptor";
+          continue;
+        }
+        LOG(INFO) << __func__ << " service_changed_ccc="
+                  << loghex(ascsDevice->srv_changed_ccc_handle);
+        break;
+      }
+    }
+  }
+
+  // Find the handle for the client characteristics configuration of a given
+  // characteristics
+  uint16_t find_ccc_handle(uint16_t conn_id, uint16_t char_handle) {
+    const gatt::Characteristic* p_char =
+        BTA_GATTC_GetCharacteristic(conn_id, char_handle);
+
+    if (!p_char) {
+      LOG(WARNING) << __func__ << ": No such characteristic: " << char_handle;
+      return 0;
+    }
+
+    for (const gatt::Descriptor& desc : p_char->descriptors) {
+      if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG))
+        return desc.handle;
+    }
+
+    return 0;
+  }
+};
+
+const char* get_gatt_event_name(uint32_t event) {
+  switch (event) {
+    CASE_RETURN_STR(BTA_GATTC_DEREG_EVT)
+    CASE_RETURN_STR(BTA_GATTC_OPEN_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CLOSE_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SEARCH_CMPL_EVT)
+    CASE_RETURN_STR(BTA_GATTC_NOTIF_EVT)
+    CASE_RETURN_STR(BTA_GATTC_ENC_CMPL_CB_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CONN_UPDATE_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SRVC_CHG_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SRVC_DISC_DONE_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CONGEST_EVT)
+    default:
+      return "Unknown Event";
+  }
+}
+
+void ascs_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
+  if (p_data == nullptr || !instance) return;
+
+  LOG(INFO) << __func__ << ": Event : " << get_gatt_event_name(event);
+
+  switch (event) {
+    case BTA_GATTC_DEREG_EVT:
+      break;
+
+    case BTA_GATTC_OPEN_EVT: {
+      tBTA_GATTC_OPEN& o = p_data->open;
+      instance->OnGattConnected(o.status, o.conn_id, o.client_if, o.remote_bda,
+                                o.transport, o.mtu);
+      break;
+    }
+
+    case BTA_GATTC_CLOSE_EVT: {
+      tBTA_GATTC_CLOSE& c = p_data->close;
+      instance->OnGattDisconnected(c.status, c.conn_id, c.client_if,
+                                   c.remote_bda, c.reason);
+    } break;
+
+    case BTA_GATTC_SEARCH_CMPL_EVT:
+      instance->OnServiceSearchComplete(p_data->search_cmpl.conn_id,
+                                        p_data->search_cmpl.status);
+      break;
+
+    case BTA_GATTC_NOTIF_EVT:
+      if (!p_data->notify.is_notify ||
+           p_data->notify.len > GATT_MAX_ATTR_LEN) {
+        LOG(ERROR) << __func__ << ": rejected BTA_GATTC_NOTIF_EVT. is_notify="
+                   << p_data->notify.is_notify
+                   << ", len=" << p_data->notify.len;
+        break;
+      }
+      instance->OnNotificationEvent(p_data->notify.conn_id,
+                                    p_data->notify.handle, p_data->notify.len,
+                                    p_data->notify.value);
+      break;
+
+    case BTA_GATTC_ENC_CMPL_CB_EVT:
+      instance->OnEncryptionComplete(p_data->enc_cmpl.remote_bda, true);
+      break;
+
+    case BTA_GATTC_CONN_UPDATE_EVT:
+      instance->OnConnectionUpdateComplete(p_data->conn_update.conn_id, p_data);
+      break;
+
+    case BTA_GATTC_SRVC_CHG_EVT:
+      instance->OnServiceChangeEvent(p_data->remote_bda);
+      break;
+
+    case BTA_GATTC_SRVC_DISC_DONE_EVT:
+      instance->OnServiceDiscDoneEvent(p_data->remote_bda);
+      break;
+    case BTA_GATTC_CONGEST_EVT:
+      instance->OnCongestionEvent(p_data->congest.conn_id,
+                                  p_data->congest.congested);
+      break;
+    default:
+      break;
+  }
+}
+
+void encryption_callback(const RawAddress* address, tGATT_TRANSPORT, void*,
+                         tBTM_STATUS status) {
+  if (instance) {
+    instance->OnEncryptionComplete(*address,
+                                   status == BTM_SUCCESS ? true : false);
+  }
+}
+
+void AscsClient::Init(AscsClientCallbacks* callbacks) {
+  if (instance) {
+    instance->Register(callbacks);
+  } else {
+    instance = new AscsClientImpl();
+    instance->Register(callbacks);
+  }
+}
+
+void AscsClient::CleanUp(uint16_t client_id) {
+  if(instance->GetClientCount()) {
+    instance->Deregister(client_id);
+    if(!instance->GetClientCount()) {
+      delete instance;
+      instance = nullptr;
+    }
+  }
+}
+
+AscsClient* AscsClient::Get() {
+  CHECK(instance);
+  return instance;
+}
+
+}  // namespace ascs
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/bap/connected_iso.cc b/le_audio/system/bt/bta/bap/connected_iso.cc
new file mode 100644
index 0000000..60dae7c
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/connected_iso.cc
@@ -0,0 +1,1556 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 "bta_bap_uclient_api.h"
+#include "btm_int.h"
+#include <list>
+#include "state_machine.h"
+#include "stack/include/btm_ble_api_types.h"
+#include "bt_trace.h"
+#include "btif_util.h"
+#include "osi/include/properties.h"
+
+namespace bluetooth {
+namespace bap {
+namespace cis {
+
+typedef struct {
+  uint8_t status;
+  uint16_t cis_handle;
+  uint8_t reason;
+} tBTM_BLE_CIS_DISCONNECTED_EVT_PARAM;
+
+typedef struct {
+  uint8_t status;
+  uint16_t conn_handle;
+} tBTM_BLE_CIS_DATA_PATH_EVT_PARAM;
+
+typedef struct {
+uint8_t status;
+uint8_t cig_id;
+} tBTM_BLE_SET_CIG_REMOVE_PARAM;
+
+struct CIS;
+class CisInterfaceCallbacks;
+using bluetooth::bap::cis::CisInterfaceCallbacks;
+
+struct tIsoSetUpDataPath {
+  uint16_t conn_handle;
+  uint8_t data_path_direction;
+  uint8_t data_path_id;
+};
+
+struct tIsoRemoveDataPath {
+  uint16_t conn_handle;
+  uint8_t data_path_direction;
+};
+
+enum IsoHciEvent {
+  CIG_CONFIGURE_REQ = 0,
+  CIG_CONFIGURED_EVT,
+  CIS_CREATE_REQ,
+  CIS_STATUS_EVT,
+  CIS_ESTABLISHED_EVT,
+  CIS_DISCONNECT_REQ,
+  CIS_DISCONNECTED_EVT,
+  CIG_REMOVE_REQ,
+  CIG_REMOVED_EVT,
+  SETUP_DATA_PATH_REQ,
+  SETUP_DATA_PATH_DONE_EVT,
+  REMOVE_DATA_PATH_REQ,
+  REMOVE_DATA_PATH_DONE_EVT,
+  CIS_CREATE_REQ_DUMMY
+};
+
+struct DataPathNode {
+  IsoHciEvent type;
+  union {
+    tIsoSetUpDataPath setup_datapath;
+    tIsoRemoveDataPath rmv_datapath;
+  };
+};
+
+class CisStateMachine : public bluetooth::common::StateMachine {
+ public:
+  enum {
+    kStateIdle,
+    kStateSettingDataPath,
+    kStateReady,
+    kStateEstablishing,
+    kStateDestroying,
+    kStateEstablished,
+  };
+
+  class StateIdle : public State {
+   public:
+    StateIdle(CisStateMachine& sm)
+        : State(sm, kStateIdle), cis_(sm.GetCis()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Idle"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    CIS &cis_;
+  };
+
+  class StateSettingDataPath : public State {
+   public:
+    StateSettingDataPath(CisStateMachine& sm)
+        : State(sm, kStateSettingDataPath), cis_(sm.GetCis()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "SettingDataPath"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    CIS &cis_;
+  };
+
+  class StateReady : public State {
+   public:
+    StateReady(CisStateMachine& sm)
+        : State(sm, kStateReady), cis_(sm.GetCis()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Ready"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    CIS &cis_;
+  };
+
+  class StateDestroying : public State {
+   public:
+    StateDestroying(CisStateMachine& sm)
+        : State(sm, kStateDestroying), cis_(sm.GetCis()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Destroying"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    CIS &cis_;
+  };
+
+  class StateEstablishing : public State {
+   public:
+    StateEstablishing(CisStateMachine& sm)
+        : State(sm, kStateEstablishing), cis_(sm.GetCis()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Establishing"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    CIS &cis_;
+  };
+
+  class StateEstablished : public State {
+   public:
+    StateEstablished(CisStateMachine& sm)
+        : State(sm, kStateEstablished), cis_(sm.GetCis()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Established"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    CIS &cis_;
+  };
+
+  CisStateMachine(CIS &cis) :
+       cis(cis) {
+    state_idle_ = new StateIdle(*this);
+    state_setting_data_path_ = new StateSettingDataPath(*this);
+    state_ready_ = new StateReady(*this);
+    state_destroying_ = new StateDestroying(*this);
+    state_establishing_ = new StateEstablishing(*this);
+    state_established_ = new StateEstablished(*this);
+
+    AddState(state_idle_);
+    AddState(state_setting_data_path_);
+    AddState(state_ready_);
+    AddState(state_destroying_);
+    AddState(state_establishing_);
+    AddState(state_established_);
+
+    SetInitialState(state_idle_);
+  }
+
+  CIS  &GetCis() { return cis; }
+
+  const char* GetEventName(uint32_t event) {
+    switch (event) {
+      CASE_RETURN_STR(CIG_CONFIGURE_REQ)
+      CASE_RETURN_STR(CIG_CONFIGURED_EVT)
+      CASE_RETURN_STR(CIS_CREATE_REQ)
+      CASE_RETURN_STR(CIS_STATUS_EVT)
+      CASE_RETURN_STR(CIS_ESTABLISHED_EVT)
+      CASE_RETURN_STR(CIS_DISCONNECT_REQ)
+      CASE_RETURN_STR(CIS_DISCONNECTED_EVT)
+      CASE_RETURN_STR(CIG_REMOVE_REQ)
+      CASE_RETURN_STR(CIG_REMOVED_EVT)
+      CASE_RETURN_STR(SETUP_DATA_PATH_REQ)
+      CASE_RETURN_STR(SETUP_DATA_PATH_DONE_EVT)
+      CASE_RETURN_STR(REMOVE_DATA_PATH_REQ)
+      CASE_RETURN_STR(REMOVE_DATA_PATH_DONE_EVT)
+      CASE_RETURN_STR(CIS_CREATE_REQ_DUMMY)
+      default:
+       return "Unknown Event";
+    }
+  }
+
+ private:
+  CIS &cis;
+  StateIdle *state_idle_;
+  StateSettingDataPath *state_setting_data_path_;
+  StateReady *state_ready_;
+  StateDestroying *state_destroying_;
+  StateEstablishing *state_establishing_;
+  StateEstablished *state_established_;
+};
+
+struct CIS {
+  uint8_t cig_id;
+  uint8_t cis_id;
+  uint16_t cis_handle;
+  bool to_air_setup_done;
+  bool from_air_setup_done;
+  uint8_t datapath_status;
+  uint8_t disc_direction;
+  uint8_t direction; // input or output or both
+  CisInterfaceCallbacks *cis_callback;
+  RawAddress peer_bda;
+  CISConfig cis_config;
+  CisStateMachine cis_sm;
+  CisState cis_state;
+  std::list <DataPathNode> datapath_queue;
+
+  CIS(uint8_t cig_id, uint8_t cis_id, uint8_t direction,
+      CisInterfaceCallbacks* callback):
+      cig_id(cig_id), cis_id(cis_id), direction(direction),
+      cis_callback(callback),
+      cis_sm(*this) {
+      to_air_setup_done = false;
+      from_air_setup_done = false;
+  }
+};
+
+struct CreateCisNode {
+  uint8_t cig_id;
+  std::vector<uint8_t> cis_ids;
+  std::vector<uint16_t> cis_handles;
+  RawAddress peer_bda;
+};
+
+struct CIG {
+  CIGConfig cig_config;
+  CigState cig_state;
+  std::map<RawAddress, uint8_t> clients_list; // address and count
+  std::map<uint8_t, CIS *> cis_list; // cis id to CIS
+};
+
+class CisInterfaceImpl;
+CisInterfaceImpl *instance;
+
+static void hci_cig_param_callback(tBTM_BLE_SET_CIG_RET_PARAM *param);
+static void hci_cig_param_test_callback(tBTM_BLE_SET_CIG_PARAM_TEST_RET *param);
+static void hci_cig_remove_param_callback(uint8_t status, uint8_t cig_id);
+static void hci_cis_create_status_callback( uint8_t status);
+static void hci_cis_create_callback(tBTM_BLE_CIS_ESTABLISHED_EVT_PARAM *param);
+static void hci_cis_setup_datapath_callback( uint8_t status,
+                                              uint16_t conn_handle);
+static void hci_cis_disconnect_callback(uint8_t status, uint16_t cis_handle,
+                                         uint8_t reason);
+
+void CisStateMachine::StateIdle::OnEnter() {
+  LOG(INFO) << __func__ << ": CIS State : " << GetState();
+}
+
+void CisStateMachine::StateIdle::OnExit() {
+
+}
+
+bool CisStateMachine::StateIdle::ProcessEvent(uint32_t event, void* p_data) {
+  LOG(INFO) <<__func__  <<": CIS State = " << GetState()
+                        <<": Event = " << cis_.cis_sm.GetEventName(event);
+  LOG(INFO) <<__func__  <<": CIS Id = " << loghex(cis_.cis_id);
+  LOG(INFO) <<__func__  <<": CIS Handle = " << loghex(cis_.cis_handle);
+
+  bool cis_status = true;
+  switch (event) {
+    case SETUP_DATA_PATH_REQ: {
+      tIsoSetUpDataPath *data_path_info = (tIsoSetUpDataPath *) p_data;
+      tBTM_BLE_SET_ISO_DATA_PATH_PARAM p_params;
+      p_params.conn_handle = cis_.cis_handle;
+      p_params.data_path_dir = data_path_info->data_path_direction >> 1;
+      p_params.data_path_id = data_path_info->data_path_id;
+      p_params.codec_id[0] = 0x06;
+      memset(&p_params.codec_id[1], 0x00, sizeof(p_params.codec_id) - 1);
+      memset(&p_params.cont_delay, 0x00, sizeof(p_params.cont_delay));
+      p_params.codec_config_length = 0x00;
+      p_params.codec_config = nullptr;
+      p_params.p_cb = &hci_cis_setup_datapath_callback;
+      if(BTM_BleSetIsoDataPath(&p_params) == HCI_SUCCESS) {
+        cis_.cis_sm.TransitionTo(CisStateMachine::kStateSettingDataPath);
+        DataPathNode node = {
+                             .type = SETUP_DATA_PATH_REQ,
+                             .setup_datapath = {
+                               .conn_handle = cis_.cis_handle,
+                               .data_path_direction  =
+                                    data_path_info->data_path_direction,
+                               .data_path_id = data_path_info->data_path_id
+                             },
+                            };
+        cis_.datapath_queue.push_back(node);
+      }
+    } break;
+    default:
+      cis_status = false;
+      break;
+  }
+  return cis_status;
+}
+
+void CisStateMachine::StateSettingDataPath::OnEnter() {
+  LOG(INFO) << __func__ << ": CIS State : " << GetState();
+}
+
+void CisStateMachine::StateSettingDataPath::OnExit() {
+
+}
+
+bool CisStateMachine::StateSettingDataPath::ProcessEvent(uint32_t event,
+                                                         void* p_data) {
+  LOG(INFO) <<__func__  <<": CIS State = " << GetState()
+                        <<": Event = " << cis_.cis_sm.GetEventName(event);
+  LOG(INFO) <<__func__  <<": CIS Id = " << loghex(cis_.cis_id);
+  LOG(INFO) <<__func__  <<": CIS Handle = " << loghex(cis_.cis_handle);
+
+  bool cis_status = true;
+  switch (event) {
+    case SETUP_DATA_PATH_REQ: {
+      // add them to the queue
+      tIsoSetUpDataPath *data_path_info = (tIsoSetUpDataPath *) p_data;
+
+      DataPathNode node = {
+                           .type = SETUP_DATA_PATH_REQ,
+                           .setup_datapath = {
+                           .conn_handle =  cis_.cis_handle,
+                           .data_path_direction  =
+                                data_path_info->data_path_direction,
+                            .data_path_id = data_path_info->data_path_id
+                           }
+                          };
+
+      cis_.datapath_queue.push_back(node);
+    } break;
+    case SETUP_DATA_PATH_DONE_EVT: {
+      tBTM_BLE_CIS_DATA_PATH_EVT_PARAM *param =
+                  (tBTM_BLE_CIS_DATA_PATH_EVT_PARAM *) p_data;
+      cis_.datapath_status = param->status;
+
+      if(!cis_.datapath_queue.empty()) {
+        if(cis_.datapath_status == ISO_HCI_SUCCESS) {
+          DataPathNode node = cis_.datapath_queue.front();
+          if(node.type == SETUP_DATA_PATH_REQ) {
+            uint8_t direction = node.setup_datapath.data_path_direction;
+            if(direction == DIR_TO_AIR) {
+              cis_.to_air_setup_done = true;
+            } else if( direction == DIR_FROM_AIR) {
+              cis_.from_air_setup_done = true;
+            }
+          }
+        }
+        // remove the entry as it is processed
+        cis_.datapath_queue.pop_front();
+      }
+
+      // check if there are any more entries in queue now
+      // expect the queue entry to be of setup datapath only
+      if(!cis_.datapath_queue.empty()) {
+        DataPathNode node = cis_.datapath_queue.front();
+        if(node.type == SETUP_DATA_PATH_REQ) {
+          tBTM_BLE_SET_ISO_DATA_PATH_PARAM p_params;
+          p_params.conn_handle = node.setup_datapath.conn_handle;
+          p_params.data_path_dir = node.setup_datapath.data_path_direction >> 1;
+          p_params.data_path_id = node.setup_datapath.data_path_id;
+          p_params.codec_id[0] = 0x06;
+          memset(&p_params.codec_id[1], 0x00, sizeof(p_params.codec_id) - 1);
+          memset(&p_params.cont_delay, 0x00, sizeof(p_params.cont_delay));
+          p_params.codec_config_length = 0x00;
+          p_params.codec_config = nullptr;
+          p_params.p_cb = &hci_cis_setup_datapath_callback;
+          if(BTM_BleSetIsoDataPath(&p_params) != HCI_SUCCESS) {
+            LOG(ERROR) << "Setup Datapath Failed";
+            cis_.datapath_queue.pop_front();
+            cis_.cis_sm.TransitionTo(CisStateMachine::kStateReady);
+          }
+        } else {
+          LOG(ERROR) << "Unexpected entry";
+        }
+      } else {
+        cis_.cis_sm.TransitionTo(CisStateMachine::kStateReady);
+      }
+    } break;
+    default:
+      cis_status = false;
+      break;
+  }
+  return cis_status;
+}
+
+
+void CisStateMachine::StateReady::OnEnter() {
+  LOG(INFO) << __func__ << ": CIS State : " << GetState();
+  // update the ready state incase of transitioned from states except
+  // setting up datapath as CIG state event is sufficient for transition
+  // from setting up data path to ready.
+  if(cis_.cis_sm.PreviousStateId() != CisStateMachine::kStateSettingDataPath) {
+    cis_.cis_callback->OnCisState(cis_.cig_id, cis_.cis_id,
+                                  cis_.direction,
+                                  CisState::READY);
+  }
+}
+
+void CisStateMachine::StateReady::OnExit() {
+
+}
+
+bool CisStateMachine::StateReady::ProcessEvent(uint32_t event, void* p_data) {
+  LOG(INFO) <<__func__  <<": CIS State = " << GetState()
+                        <<": Event = " << cis_.cis_sm.GetEventName(event);
+  LOG(INFO) <<__func__  <<": CIS Id = " << loghex(cis_.cis_id);
+  LOG(INFO) <<__func__  <<": CIS Handle = " << loghex(cis_.cis_handle);
+
+  bool cis_status = true;
+  switch (event) {
+    case CIS_CREATE_REQ: {
+      tBTM_BLE_ISO_CREATE_CIS_CMD_PARAM cmd_data;
+      CreateCisNode *pNode = (CreateCisNode *) p_data;
+      cmd_data.cis_count = pNode->cis_ids.size();
+      cmd_data.p_cb = &hci_cis_create_status_callback;
+      cmd_data.p_evt_cb = &hci_cis_create_callback;
+      tACL_CONN* acl = btm_bda_to_acl(pNode->peer_bda, BT_TRANSPORT_LE);
+      if(!acl) {
+        BTIF_TRACE_DEBUG("%s create_cis return ", __func__);
+        return false;
+      }
+      for (auto i: pNode->cis_handles) {
+
+        tBTM_BLE_CHANNEL_MAP map = { .cis_conn_handle = i,
+                                     .acl_conn_handle = acl->hci_handle };
+        cmd_data.link_conn_handles.push_back(map);
+      }
+      if(BTM_BleCreateCis(&cmd_data, &hci_cis_disconnect_callback)
+                          == HCI_SUCCESS)
+        cis_.cis_sm.TransitionTo(CisStateMachine::kStateEstablishing);
+    } break;
+    case CIS_CREATE_REQ_DUMMY: {
+      cis_.cis_sm.TransitionTo(CisStateMachine::kStateEstablishing);
+    } break;
+    default:
+      cis_status = false;
+      break;
+  }
+  return cis_status;
+}
+
+
+void CisStateMachine::StateDestroying::OnEnter() {
+  LOG(INFO) << __func__ << ": CIS State : " << GetState();
+  cis_.cis_callback->OnCisState(cis_.cig_id, cis_.cis_id,
+                                cis_.direction,
+                                CisState::DESTROYING);
+}
+
+void CisStateMachine::StateDestroying::OnExit() {
+
+}
+
+bool CisStateMachine::StateDestroying::ProcessEvent(uint32_t event,
+                                                    void* p_data) {
+  LOG(INFO) <<__func__  <<": CIS State = " << GetState()
+                        <<": Event = " << cis_.cis_sm.GetEventName(event);
+  LOG(INFO) <<__func__  <<": CIS Id = " << loghex(cis_.cis_id);
+  LOG(INFO) <<__func__  <<": CIS Handle = " << loghex(cis_.cis_handle);
+
+  bool cis_status = true;
+  switch (event) {
+    case CIS_DISCONNECTED_EVT: {
+      tBTM_BLE_CIS_DISCONNECTED_EVT_PARAM *param =
+                    (tBTM_BLE_CIS_DISCONNECTED_EVT_PARAM *) p_data;
+      if(param->status != ISO_HCI_SUCCESS) {
+        LOG(ERROR) <<__func__  << " cis disconnection failed";
+        cis_.cis_sm.TransitionTo(cis_.cis_sm.PreviousStateId());
+      } else {
+        cis_.cis_sm.TransitionTo(CisStateMachine::kStateReady);
+      }
+    } break;
+    default:
+      cis_status = false;
+      break;
+  }
+  return cis_status;
+}
+
+
+void CisStateMachine::StateEstablishing::OnEnter() {
+  LOG(INFO) << __func__ << ": CIS State : " << GetState();
+  cis_.cis_callback->OnCisState(cis_.cig_id, cis_.cis_id,
+                                cis_.direction,
+                                CisState::ESTABLISHING);
+}
+
+void CisStateMachine::StateEstablishing::OnExit() {
+
+}
+
+bool CisStateMachine::StateEstablishing::ProcessEvent(uint32_t event,
+                                                      void* p_data) {
+  LOG(INFO) <<__func__  <<": CIS State = " << GetState()
+                        <<": Event = " << cis_.cis_sm.GetEventName(event);
+  LOG(INFO) <<__func__  <<": CIS Id = " << loghex(cis_.cis_id);
+  LOG(INFO) <<__func__  <<": CIS Handle = " << loghex(cis_.cis_handle);
+
+  bool cis_status = true;
+  switch (event) {
+    case CIS_STATUS_EVT: {
+      uint8_t status = *((uint8_t *)(p_data));
+      if(status != ISO_HCI_SUCCESS) {
+        cis_.cis_sm.TransitionTo(CisStateMachine::kStateReady);
+      }
+    } break;
+    case CIS_ESTABLISHED_EVT: {
+      tBTM_BLE_CIS_ESTABLISHED_EVT_PARAM *param =
+                     (tBTM_BLE_CIS_ESTABLISHED_EVT_PARAM *) p_data;
+      if(param->status != ISO_HCI_SUCCESS) {
+        cis_.cis_sm.TransitionTo(CisStateMachine::kStateReady);
+      } else {
+        cis_.cis_sm.TransitionTo(CisStateMachine::kStateEstablished);
+
+      }
+    } break;
+    default:
+      cis_status = false;
+      break;
+  }
+  return cis_status;
+}
+
+void CisStateMachine::StateEstablished::OnEnter() {
+  LOG(INFO) << __func__ << ": CIS State : " << GetState();
+  cis_.disc_direction = cis_.direction;
+  cis_.cis_callback->OnCisState(cis_.cig_id, cis_.cis_id,
+                                cis_.direction,
+                                CisState::ESTABLISHED);
+}
+
+void CisStateMachine::StateEstablished::OnExit() {
+
+}
+
+bool CisStateMachine::StateEstablished::ProcessEvent(uint32_t event,
+                                                     void* p_data) {
+  LOG(INFO) <<__func__  <<": CIS State = " << GetState()
+                        <<": Event = " << cis_.cis_sm.GetEventName(event);
+  LOG(INFO) <<__func__  <<": CIS Id = " << loghex(cis_.cis_id);
+  LOG(INFO) <<__func__  <<": CIS Handle = " << loghex(cis_.cis_handle);
+
+  switch (event) {
+    case CIS_DISCONNECT_REQ:
+      if(BTM_BleIsoCisDisconnect(cis_.cis_handle, 0x13 ,
+                                 &hci_cis_disconnect_callback) ==
+                                 HCI_SUCCESS) {
+        cis_.cis_sm.TransitionTo(CisStateMachine::kStateDestroying);
+      }
+      break;
+    case CIS_DISCONNECTED_EVT: {
+      tBTM_BLE_CIS_DISCONNECTED_EVT_PARAM *param =
+                    (tBTM_BLE_CIS_DISCONNECTED_EVT_PARAM *) p_data;
+      if(param->status != ISO_HCI_SUCCESS) {
+        LOG(ERROR) <<__func__  << " cis disconnection failed";
+        cis_.cis_sm.TransitionTo(cis_.cis_sm.PreviousStateId());
+      } else {
+        cis_.cis_sm.TransitionTo(CisStateMachine::kStateReady);
+      }
+    } break;
+    default:
+      break;
+  }
+  return true;
+}
+
+class CisInterfaceImpl : public CisInterface {
+ public:
+  CisInterfaceImpl(CisInterfaceCallbacks* callback):
+     callbacks(callback)  { }
+
+  ~CisInterfaceImpl() override = default;
+
+  void CleanUp () {
+
+  }
+
+  CigState GetCigState(const uint8_t &cig_id) override {
+    CIG *cig = GetCig(cig_id);
+    if (cig != nullptr) {
+      return cig->cig_state;
+    } else {
+      return CigState::IDLE;
+    }
+  }
+
+  CisState GetCisState(const uint8_t &cig_id, uint8_t cis_id) override {
+    return CisState::READY;
+  }
+
+  uint8_t GetCisCount(const uint8_t &cig_id) override {
+    return 0;
+  }
+
+  IsoHciStatus CreateCig(RawAddress client_peer_bda, bool reconfig,
+                         CIGConfig &cig_config,
+                         std::vector<CISConfig> &cis_configs) override {
+    // check if CIG already exists
+    LOG(INFO) << __func__  << " : CIG Id = " << loghex(cig_config.cig_id);
+    CIG *cig = GetCig(cig_config.cig_id);
+    if (cig != nullptr) {
+      auto it = cig->clients_list.find(client_peer_bda);
+      if (it == cig->clients_list.end()) {
+        cig->clients_list.insert(std::make_pair(client_peer_bda, 0x01));
+      } else {
+        if(!reconfig) {
+          // increment the count
+          it->second++;
+        }
+      }
+      // check if params are same for group requested
+      // and for the group alredy exists
+      if(cig->cig_state == CigState::CREATING) {
+        return ISO_HCI_IN_PROGRESS;
+      } else if(IsCigParamsSame(cig_config, cis_configs)) {
+        if(cig->cig_state == CigState::CREATED) {
+          return ISO_HCI_SUCCESS;
+        }
+      }
+    }
+
+    // check if the CIS vector length is same as cis count passed
+    // in CIG confifuration
+    if(cig_config.cis_count != cis_configs.size()) {
+      return ISO_HCI_FAILED;
+    }
+
+    char value[PROPERTY_VALUE_MAX] = {0};
+    bool create_cig = false;
+    property_get("persist.vendor.btstack.get_cig_test_param", value, "");
+    uint16_t ft_m_s, ft_s_m, iso_int, clk_accuracy, nse, pdu_m_s, pdu_s_m, bn_m_s, bn_s_m;
+    int res = sscanf(value, "%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu", &ft_m_s, &ft_s_m, &iso_int,
+                            &clk_accuracy, &nse, &pdu_m_s, &pdu_s_m, &bn_m_s, &bn_s_m);
+    LOG(WARNING) << __func__<< ": FT_M_S: " << loghex(ft_m_s) << ", FT_S_M: " << loghex(ft_s_m)
+                 << ", ISO_Interval: " << loghex(iso_int) << ", slave_clock: " << loghex(clk_accuracy)
+                 << ", NSE: " << loghex(nse) << ", PDU_M_S:" << loghex(pdu_m_s)
+                 << " PDU_S_M:" << loghex(pdu_s_m) << ", BN_M_S: " << loghex(bn_m_s)
+                 << ", BN_S_M: " << loghex(bn_s_m);
+    if (res == 9) {
+      tBTM_BLE_SET_CIG_PARAM_TEST p_data_test;
+      p_data_test.cig_id = cig_config.cig_id;
+      memcpy(&p_data_test.sdu_int_s_to_m, &cig_config.sdu_interval_s_to_m,
+              sizeof(p_data_test.sdu_int_s_to_m));
+
+      memcpy(&p_data_test.sdu_int_m_to_s, &cig_config.sdu_interval_m_to_s,
+              sizeof(p_data_test.sdu_int_m_to_s));
+
+      p_data_test.ft_m_to_s = ft_m_s;
+      p_data_test.ft_s_to_m = ft_s_m;
+      p_data_test.iso_interval = iso_int;
+      p_data_test.slave_clock_accuracy = clk_accuracy;
+      p_data_test.packing = cig_config.packing;
+      p_data_test.framing = cig_config.framing;
+      p_data_test.cis_count = cig_config.cis_count;
+
+      for (auto it = cis_configs.begin(); it != cis_configs.end();) {
+        tBTM_BLE_CIS_TEST_CONFIG cis_config;
+        cis_config.cis_id = it->cis_id;
+        cis_config.nse = nse;
+        cis_config.max_sdu_m_to_s = it->max_sdu_m_to_s;
+        cis_config.max_sdu_s_to_m = it->max_sdu_s_to_m;
+        cis_config.max_pdu_m_to_s = it->max_sdu_m_to_s;
+        cis_config.max_pdu_s_to_m = it->max_sdu_s_to_m;
+        cis_config.phy_m_to_s = it->phy_m_to_s;
+        cis_config.phy_s_to_m = it->phy_s_to_m;
+        cis_config.bn_m_to_s = bn_m_s;
+        cis_config.bn_s_to_m = 0;
+        if (cis_config.max_sdu_s_to_m > 0) {
+          cis_config.bn_s_to_m = bn_s_m;
+          if (cis_config.max_sdu_m_to_s > 0 && cis_config.nse > 13) {
+            cis_config.nse = 13;
+          }
+        }
+        p_data_test.cis_config.push_back(cis_config);
+        it++;
+      }
+      p_data_test.p_cb = &hci_cig_param_test_callback;
+      create_cig = (BTM_BleSetCigParametersTest(&p_data_test) == HCI_SUCCESS);
+    } else {
+      tBTM_BLE_ISO_SET_CIG_CMD_PARAM p_data;
+      p_data.cig_id = cig_config.cig_id;
+      memcpy(&p_data.sdu_int_s_to_m, &cig_config.sdu_interval_s_to_m,
+              sizeof(p_data.sdu_int_s_to_m));
+
+      memcpy(&p_data.sdu_int_m_to_s, &cig_config.sdu_interval_m_to_s,
+              sizeof(p_data.sdu_int_m_to_s));
+
+      p_data.slave_clock_accuracy = 0x00;
+      p_data.packing = cig_config.packing;
+      p_data.framing = cig_config.framing;
+      p_data.max_transport_latency_m_to_s = cig_config.max_tport_latency_m_to_s;
+      p_data.max_transport_latency_s_to_m = cig_config.max_tport_latency_s_to_m;
+      p_data.cis_count = cig_config.cis_count;
+
+      for (auto it = cis_configs.begin(); it != cis_configs.end();) {
+        tBTM_BLE_CIS_CONFIG cis_config;
+        memcpy(&cis_config, &(*it), sizeof(tBTM_BLE_CIS_CONFIG));
+        p_data.cis_config.push_back(cis_config);
+        it++;
+      }
+      p_data.p_cb = &hci_cig_param_callback;
+      create_cig = (BTM_BleSetCigParam(&p_data) == HCI_SUCCESS);
+    }
+    if(create_cig) {
+      // create new CIG and add it to the list
+      if(cig == nullptr) {
+        CIG *cig = new (CIG);
+        cig_list.insert(std::make_pair(cig_config.cig_id, cig));
+        cig->cig_config = cig_config;
+        cig->cig_state = CigState::CREATING;
+
+        for(uint8_t i = 0; i < cig_config.cis_count; i++)  {
+          uint8_t direction = 0;
+          if(cis_configs[i].max_sdu_m_to_s) direction |= DIR_TO_AIR;
+          if(cis_configs[i].max_sdu_s_to_m) direction |= DIR_FROM_AIR;
+
+          CIS *cis = new CIS(cig_config.cig_id, cis_configs[i].cis_id,
+                         direction, callbacks);
+          cis->cis_config = cis_configs[i];
+          cig->cis_list.insert(std::make_pair(cis_configs[i].cis_id, cis));
+        }
+
+        auto it = cig->clients_list.find(client_peer_bda);
+        if (it == cig->clients_list.end()) {
+          cig->clients_list.insert(std::make_pair(client_peer_bda, 0x01));
+        } else {
+          // increment the count
+          it->second++;
+          LOG(WARNING) << __func__  << "count " << loghex(it->second);
+        }
+      } else {
+        cig->cig_config = cig_config;
+        cig->cig_state = CigState::CREATING;
+
+        uint8_t i = 0;
+        for (auto it = cig->cis_list.begin(); it != cig->cis_list.end();) {
+          CIS *cis = it->second;
+          cis->cis_config = cis_configs[i];
+          cis->cis_sm.TransitionTo(CisStateMachine::kStateIdle);
+          it++; i++;
+        }
+
+        auto it = cig->clients_list.find(client_peer_bda);
+        if (it == cig->clients_list.end()) {
+          cig->clients_list.insert(std::make_pair(client_peer_bda, 0x01));
+        }
+      }
+      return ISO_HCI_IN_PROGRESS;
+    } else {
+      return ISO_HCI_FAILED;
+    }
+  }
+
+  IsoHciStatus RemoveCig(RawAddress client_peer_bda, uint8_t cig_id) override {
+    LOG(INFO) <<__func__  << ": CIG Id = " << loghex(cig_id);
+    // check if the CIG exists
+    CIG *cig = GetCig(cig_id);
+    if (cig == nullptr) {
+      return ISO_HCI_FAILED;
+    }
+
+    if(cig->cig_state == CigState::IDLE ||
+       cig->cig_state == CigState::CREATING) {
+      return ISO_HCI_FAILED;
+    } else if(cig->cig_state == CigState::CREATED) {
+
+      auto it = cig->clients_list.find(client_peer_bda);
+      if (it == cig->clients_list.end()) {
+        return ISO_HCI_FAILED;
+      } else {
+        // decrement the count
+        it->second--;
+        LOG(WARNING) << __func__  << ": Count : " << loghex(it->second);
+      }
+
+      // check if all clients have voted off then go for CIG removal
+      uint8_t vote_on_count = 0;
+      for (auto it = cig->clients_list.begin();
+                                it != cig->clients_list.end();) {
+        vote_on_count += it->second;
+        it++;
+      }
+
+      if(vote_on_count) {
+        LOG(WARNING) << __func__  << " : Vote On Count : "
+                                  << loghex(vote_on_count);
+        return ISO_HCI_SUCCESS;
+      }
+
+      // check if any of the CIS are in established/streaming state
+      // if so return false as it is not allowed
+      if(IsCisActive(cig_id, 0xFF)) return ISO_HCI_FAILED;
+
+      if(BTM_BleRemoveCig(cig_id, &hci_cig_remove_param_callback)
+              == HCI_SUCCESS) {
+        cig->cig_state = CigState::REMOVING;
+        return ISO_HCI_IN_PROGRESS;
+      } else return ISO_HCI_FAILED;
+    }
+    return ISO_HCI_FAILED;
+  }
+
+  IsoHciStatus CreateCis(uint8_t cig_id, std::vector<uint8_t> cis_ids,
+                         RawAddress peer_bda) override  {
+    LOG(INFO) <<__func__  << ": CIG Id = " << loghex(cig_id);
+    LOG(INFO) <<__func__  << ": No. of CISes = " << loghex(cis_ids.size());
+
+    IsoHciStatus ret;
+    uint32_t cur_state;
+    // check if the CIG exists
+    CIG *cig = GetCig(cig_id);
+    if (cig == nullptr) {
+      return ISO_HCI_FAILED;
+    }
+
+    if(cig->cig_state != CigState::CREATED) {
+      return ISO_HCI_FAILED;
+    }
+
+    bool cis_created = false;
+    CreateCisNode param;
+    param.cig_id = cig_id;
+    param.cis_ids = cis_ids;
+    param.peer_bda = peer_bda;
+    std::vector<uint16_t> cis_handles;
+
+    for (auto i: cis_ids) {
+      CIS *cis = GetCis(cig_id, i);
+      if (cis == nullptr) {
+        return ISO_HCI_FAILED;
+      }
+      cis_handles.push_back(cis->cis_handle);
+    }
+    param.cis_handles = cis_handles;
+
+    for (auto i: cis_ids) {
+      LOG(INFO) <<__func__ << ": CIS Id = " << loghex(i);
+      // check if CIS ID mentioned is present as part of CIG
+      CIS *cis = GetCis(cig_id, i);
+      if (cis == nullptr) {
+        ret = ISO_HCI_FAILED;
+        break;
+      }
+
+      cur_state = cis->cis_sm.StateId();
+
+      // check if CIS is already created or in progress
+      if(cur_state == CisStateMachine::kStateEstablishing) {
+        ret = ISO_HCI_IN_PROGRESS;
+        break;
+      } else if(cur_state == CisStateMachine::kStateEstablished) {
+        ret = ISO_HCI_SUCCESS;
+        break;
+      } else if(cur_state == CisStateMachine::kStateDestroying) {
+        ret = ISO_HCI_FAILED;
+        break;
+      }
+      if (cis_created == false) {
+        // queue it if there is pending create CIS
+        if (cis_queue.size()) {
+          // hand it over to the CIS module
+          // check if the new request is already exists
+          // as the head entry in the list
+          CreateCisNode& head = cis_queue.front();
+          if(head.cig_id == cig_id && head.cis_ids == cis_ids &&
+             head.peer_bda == peer_bda) {
+             if(cis->cis_sm.ProcessEvent(
+                          IsoHciEvent::CIS_CREATE_REQ, &param)) {
+               ret = ISO_HCI_IN_PROGRESS;
+             } else {
+               ret = ISO_HCI_FAILED;
+               break;
+             }
+          } else {
+            cis_queue.push_back(param);
+          }
+        } else {
+          cis_queue.push_back(param);
+          if(cis->cis_sm.ProcessEvent(IsoHciEvent::CIS_CREATE_REQ,
+                                          &param)) {
+            ret = ISO_HCI_IN_PROGRESS;
+          } else {
+            ret = ISO_HCI_FAILED;
+            break;
+          }
+        }
+        cis_created = true;
+      } else {
+        if(cis->cis_sm.ProcessEvent(IsoHciEvent::CIS_CREATE_REQ_DUMMY,
+                                        &peer_bda)) {
+          ret = ISO_HCI_IN_PROGRESS;
+        } else {
+          ret = ISO_HCI_FAILED;
+          break;
+        }
+      }
+    }
+    return ret;
+  }
+
+  IsoHciStatus DisconnectCis(uint8_t cig_id, uint8_t cis_id,
+                             uint8_t direction) override {
+    LOG(INFO) <<__func__  << ": CIG Id = " << loghex(cig_id)
+                          << ": CIS Id = " << loghex(cis_id);
+
+    uint32_t cur_state;
+    // check if the CIG exists
+    CIG *cig = GetCig(cig_id);
+    if (cig == nullptr) {
+      return ISO_HCI_FAILED;
+    }
+
+    if(cig->cig_state != CigState::CREATED) {
+      return ISO_HCI_FAILED;
+    }
+
+    // check if CIS ID mentioned is present as part of CIG
+    CIS *cis = GetCis(cig_id, cis_id);
+    if (cis == nullptr) {
+      return ISO_HCI_FAILED;
+    }
+
+    if(cis->disc_direction & direction) {
+       // remove the direction bit form disc direciton
+       cis->disc_direction &= ~direction;
+    }
+
+    if(cis->disc_direction) return ISO_HCI_SUCCESS;
+
+    // if all directions are voted off go for CIS disconneciton
+    cur_state = cis->cis_sm.StateId();
+
+    // check if CIS is not created or in progress
+    if(cur_state == CisStateMachine::kStateReady) {
+      return ISO_HCI_SUCCESS;
+    } else if(cur_state == CisStateMachine::kStateEstablishing) {
+      return ISO_HCI_FAILED;
+    } else if(cur_state == CisStateMachine::kStateDestroying) {
+      return ISO_HCI_IN_PROGRESS;
+    }
+
+    LOG(INFO) <<__func__  << " Request issued to CIS SM";
+    // hand it over to the CIS module
+    if(cis->cis_sm.ProcessEvent(
+              IsoHciEvent::CIS_DISCONNECT_REQ, nullptr)) {
+      return ISO_HCI_IN_PROGRESS;
+    } else return ISO_HCI_FAILED;
+  }
+
+  IsoHciStatus SetupDataPath(uint8_t cig_id, uint8_t cis_id,
+          uint8_t data_path_direction, uint8_t data_path_id)  override {
+    LOG(INFO) <<__func__  << ": CIG Id = " << loghex(cig_id)
+                          << ": CIS Id = " << loghex(cis_id);
+
+    uint32_t cur_state;
+    // check if the CIG exists
+    CIG *cig = GetCig(cig_id);
+    if (cig == nullptr) {
+      return ISO_HCI_FAILED;
+    }
+
+    if(cig->cig_state != CigState::CREATED) {
+      return ISO_HCI_FAILED;
+    }
+
+    // check if CIS ID mentioned is present as part of CIG
+    CIS *cis = GetCis(cig_id, cis_id);
+    if (cis == nullptr) {
+      return ISO_HCI_FAILED;
+    }
+
+    cur_state = cis->cis_sm.StateId();
+
+    // check if CIS is not created or in progress
+    if(cur_state == CisStateMachine::kStateReady ||
+       cur_state == CisStateMachine::kStateEstablishing ||
+       cur_state == CisStateMachine::kStateDestroying) {
+      return ISO_HCI_FAILED;
+    } else if(cur_state == CisStateMachine::kStateEstablished) {
+      // return success as it is already created
+      return ISO_HCI_SUCCESS;
+    }
+
+    // hand it over to the CIS module
+    tIsoSetUpDataPath data_path_info;
+    data_path_info.data_path_direction = data_path_direction;
+    data_path_info.data_path_id = data_path_id;
+
+    if(cis->cis_sm.ProcessEvent(
+        IsoHciEvent::SETUP_DATA_PATH_REQ, &data_path_info)) {
+      return ISO_HCI_IN_PROGRESS;
+    } else return ISO_HCI_FAILED;
+  }
+
+  IsoHciStatus RemoveDataPath(uint8_t cig_id, uint8_t cis_id,
+                      uint8_t data_path_direction) override {
+    LOG(INFO) <<__func__  << ": CIG Id = " << loghex(cig_id)
+                          << ": CIS Id = " << loghex(cis_id);
+
+    uint32_t cur_state;
+    // check if the CIG exists
+    CIG *cig = GetCig(cig_id);
+    if (cig == nullptr) {
+      return ISO_HCI_FAILED;
+    }
+
+    if(cig->cig_state != CigState::CREATED) {
+      return ISO_HCI_FAILED;
+    }
+
+    // check if CIS ID mentioned is present as part of CIG
+    CIS *cis = GetCis(cig_id, cis_id);
+    if (cis == nullptr) {
+      return ISO_HCI_FAILED;
+    }
+
+    cur_state = cis->cis_sm.StateId();
+
+    // check if CIS is not created or in progress
+    if(cur_state == CisStateMachine::kStateReady ||
+       cur_state == CisStateMachine::kStateEstablishing ||
+       cur_state == CisStateMachine::kStateDestroying ||
+       cur_state == CisStateMachine::kStateEstablished) {
+      return ISO_HCI_FAILED;
+    }
+
+    // hand it over to the CIS module
+    if(cis->cis_sm.ProcessEvent(
+           IsoHciEvent::REMOVE_DATA_PATH_REQ, &data_path_direction)) {
+      return ISO_HCI_SUCCESS;
+    } else return ISO_HCI_FAILED;
+  }
+
+  const char* GetEventName(uint32_t event) {
+    switch (event) {
+      CASE_RETURN_STR(CIG_CONFIGURED_EVT)
+      CASE_RETURN_STR(CIS_STATUS_EVT)
+      CASE_RETURN_STR(CIS_ESTABLISHED_EVT)
+      CASE_RETURN_STR(CIS_DISCONNECTED_EVT)
+      CASE_RETURN_STR(CIG_REMOVED_EVT)
+      CASE_RETURN_STR(SETUP_DATA_PATH_DONE_EVT)
+      CASE_RETURN_STR(REMOVE_DATA_PATH_DONE_EVT)
+      default:
+       return "Unknown Event";
+    }
+  }
+
+  IsoHciStatus ProcessEvent (uint32_t event, void* p_data) {
+    LOG(INFO) <<__func__ <<": Event = " << GetEventName(event);
+    switch (event) {
+      case CIG_CONFIGURED_EVT: {
+        tBTM_BLE_SET_CIG_RET_PARAM *param =
+                            (tBTM_BLE_SET_CIG_RET_PARAM *) p_data;
+        LOG(INFO) <<__func__ <<": CIG Id = " << loghex(param->cig_id)
+                  << ": status = " << loghex(param->status);
+
+        auto it = cig_list.find(param->cig_id);
+        if (it == cig_list.end()) {
+          return ISO_HCI_FAILED;
+        }
+
+        if(!param->status) {
+          uint8_t i = 0;
+          CIG *cig = it->second;
+          tIsoSetUpDataPath data_path_info;
+
+          for (auto it = cig->cis_list.begin();
+                    it != cig->cis_list.end(); it++) {
+            CIS *cis = it->second;
+            cis->cis_handle = *(param->conn_handle + i++);
+            cis->cis_sm.Start();
+            if(cis->direction & DIR_TO_AIR) {
+              data_path_info.data_path_direction = DIR_TO_AIR;
+              data_path_info.data_path_id = 0x01;
+              cis->cis_sm.ProcessEvent(IsoHciEvent::SETUP_DATA_PATH_REQ,
+                                        &data_path_info);
+            }
+            if(cis->direction & DIR_FROM_AIR) {
+              data_path_info.data_path_direction = DIR_FROM_AIR;
+              data_path_info.data_path_id = 0x01;
+              cis->cis_sm.ProcessEvent(IsoHciEvent::SETUP_DATA_PATH_REQ,
+                                        &data_path_info);
+            }
+          }
+        } else {
+          // delete CIG and CIS
+          CIG *cig = it->second;
+          cig->cig_state = CigState::IDLE;
+
+          while (!cig->cis_list.empty()) {
+            auto it = cig->cis_list.begin();
+            CIS * cis = it->second;
+            cig->cis_list.erase(it);
+            delete cis;
+          }
+          callbacks->OnCigState(param->cig_id, CigState::IDLE);
+          cig_list.erase(it);
+          delete cig;
+        }
+
+      } break;
+      case CIG_REMOVED_EVT: {
+        tBTM_BLE_SET_CIG_REMOVE_PARAM *param =
+                                (tBTM_BLE_SET_CIG_REMOVE_PARAM *) p_data;
+        auto it = cig_list.find(param->cig_id);
+        if (it == cig_list.end()) {
+          return ISO_HCI_FAILED;
+        } else {
+          // delete CIG and CIS
+          CIG *cig = it->second;
+          while (!cig->cis_list.empty()) {
+            auto it = cig->cis_list.begin();
+            CIS * cis = it->second;
+            cig->cis_list.erase(it);
+            delete cis;
+          }
+          cig->cig_state = CigState::IDLE;
+          cig_list.erase(it);
+          callbacks->OnCigState(param->cig_id, CigState::IDLE);
+          delete cig;
+        }
+      } break;
+      case CIS_STATUS_EVT: {
+        // clear the first entry from cis queue and send the next
+        // CIS creation request queue it if there is pending create CIS
+        CreateCisNode &head = cis_queue.front();
+        for (auto i: head.cis_ids) {
+          CIS *cis = GetCis(head.cig_id, i);
+          if(cis) {
+            cis->cis_sm.ProcessEvent(IsoHciEvent::CIS_STATUS_EVT, p_data);
+          }
+        }
+      } break;
+      case CIS_ESTABLISHED_EVT: {
+        tBTM_BLE_CIS_ESTABLISHED_EVT_PARAM *param =
+                       (tBTM_BLE_CIS_ESTABLISHED_EVT_PARAM *) p_data;
+        LOG(INFO) << __func__  << ": CIS handle = "
+                                  << loghex(param->connection_handle)
+                                  << ": Status = " << loghex(param->status);
+        CIS *cis = GetCis(param->connection_handle);
+        if (cis == nullptr) {
+          return ISO_HCI_FAILED;
+        } else {
+          cis->cis_sm.ProcessEvent(IsoHciEvent::CIS_ESTABLISHED_EVT, p_data);
+        }
+        bool cis_status = false;
+        if (cis_queue.size()) {
+          cis_queue.pop_front();
+        }
+        while(cis_queue.size() && !cis_status) {
+          CreateCisNode &head = cis_queue.front();
+          CIS *cis = GetCis(head.cig_id, head.cis_ids[0]);
+          if(cis == nullptr ||
+             cis->cis_sm.StateId() == CisStateMachine::kStateEstablished) {
+            // remove the entry
+            cis_queue.pop_front();
+          } else if(cis) {
+            IsoHciStatus hci_status =  CreateCis(head.cig_id, head.cis_ids,
+                                                 head.peer_bda);
+            if(hci_status == ISO_HCI_SUCCESS ||
+               hci_status == ISO_HCI_IN_PROGRESS) {
+              cis_status = true;
+            } else {
+              // remove the entry
+              cis_queue.pop_front();
+            }
+          }
+        }
+      } break;
+      case CIS_DISCONNECTED_EVT: {
+        tBTM_BLE_CIS_DISCONNECTED_EVT_PARAM *param =
+                      (tBTM_BLE_CIS_DISCONNECTED_EVT_PARAM *) p_data;
+        CIS *cis = GetCis(param->cis_handle);
+        if (cis == nullptr) {
+          return ISO_HCI_FAILED;
+        } else {
+          cis->cis_sm.ProcessEvent(IsoHciEvent::CIS_DISCONNECTED_EVT, p_data);
+        }
+      } break;
+      case SETUP_DATA_PATH_DONE_EVT: {
+        tBTM_BLE_CIS_DATA_PATH_EVT_PARAM *param =
+                   (tBTM_BLE_CIS_DATA_PATH_EVT_PARAM *) p_data;
+
+        CIS *cis = GetCis(param->conn_handle);
+        CIG *cig = nullptr;
+        if (cis == nullptr) {
+          return ISO_HCI_FAILED;
+        } else {
+          cis->cis_sm.ProcessEvent(IsoHciEvent::SETUP_DATA_PATH_DONE_EVT,
+                                                  p_data);
+        }
+        uint8_t cig_id = cis->cig_id;
+
+        auto it = cig_list.find(cig_id);
+        if (it == cig_list.end()) {
+          break;
+        } else {
+          // delete CIG and CIS
+          cig = it->second;
+        }
+
+        uint8_t num_cis_is_ready = 0;
+        for(auto it = cig->cis_list.begin(); it != cig->cis_list.end(); it++) {
+          CIS *cis = it->second;
+          if(cis->cis_sm.StateId() == CisStateMachine::kStateReady) {
+            num_cis_is_ready++;
+          }
+        }
+
+        // check if all setup data paths are completed
+        if(num_cis_is_ready == cig->cis_list.size()) {
+          cig->cig_state = CigState::CREATED;
+          callbacks->OnCigState(cig_id, CigState::CREATED);
+        }
+      } break;
+      case REMOVE_DATA_PATH_DONE_EVT: {
+        tBTM_BLE_CIS_DATA_PATH_EVT_PARAM *param =
+                   (tBTM_BLE_CIS_DATA_PATH_EVT_PARAM *) p_data;
+        CIS *cis = GetCis(param->conn_handle);
+        if (cis == nullptr) {
+            return ISO_HCI_FAILED;
+        } else {
+          cis->cis_sm.ProcessEvent(IsoHciEvent::REMOVE_DATA_PATH_DONE_EVT,
+                                                  p_data);
+        }
+      } break;
+      default:
+        break;
+    }
+    return ISO_HCI_SUCCESS;
+  }
+
+ private:
+  std::map<uint8_t, CIG *> cig_list; // cig id to CIG structure
+  std::list <CreateCisNode> cis_queue;
+  CisInterfaceCallbacks *callbacks;
+  // 0xFF will be passed for cis id in case search is for any of the
+  // CIS part of that group
+  bool IsCisActive(uint8_t cig_id, uint8_t cis_id)  {
+    bool is_cis_active = false;
+    auto it = cig_list.find(cig_id);
+    if (it == cig_list.end()) {
+      return is_cis_active;
+    } else {
+      CIG *cig = it->second;
+      if(cis_id != 0XFF) {
+        auto it = cig->cis_list.find(cis_id);
+        if (it != cig->cis_list.end()) {
+          CIS *cis = it->second;
+          if(cis->cis_sm.StateId() == CisStateMachine::kStateEstablished) {
+            is_cis_active = true;
+          }
+        }
+      } else {
+        for (auto it : cig->cis_list) {
+          CIS *cis = it.second;
+          if(cis->cis_sm.StateId() == CisStateMachine::kStateEstablished) {
+            is_cis_active = true;
+            break;
+          }
+        }
+      }
+    }
+    return is_cis_active;
+  }
+
+  bool IsCigParamsSame(CIGConfig &cig_config,
+                       std::vector<CISConfig> &cis_configs)  {
+    CIG *cig = GetCig(cig_config.cig_id);
+    bool is_params_same = true;
+    uint8_t i = 0;
+
+    if(cig == nullptr || (cis_configs.size() != cig->cig_config.cis_count)) {
+      LOG(WARNING) << __func__  << ": Count is different ";
+      return false;
+    }
+
+    if(cig->cig_config.cig_id != cig_config.cig_id ||
+       cig->cig_config.cis_count != cig_config.cis_count ||
+       cig->cig_config.packing !=  cig_config.packing ||
+       cig->cig_config.framing != cig_config.framing ||
+       cig->cig_config.max_tport_latency_m_to_s !=
+                          cig_config.max_tport_latency_m_to_s ||
+       cig->cig_config.max_tport_latency_s_to_m !=
+                            cig_config.max_tport_latency_s_to_m ||
+       cig->cig_config.sdu_interval_m_to_s[0] !=
+                         cig_config.sdu_interval_m_to_s[0] ||
+       cig->cig_config.sdu_interval_m_to_s[1] !=
+                         cig_config.sdu_interval_m_to_s[1] ||
+       cig->cig_config.sdu_interval_m_to_s[2] !=
+                         cig_config.sdu_interval_m_to_s[2] ||
+       cig->cig_config.sdu_interval_s_to_m[0] !=
+                         cig_config.sdu_interval_s_to_m[0] ||
+       cig->cig_config.sdu_interval_s_to_m[1] !=
+                         cig_config.sdu_interval_s_to_m[1] ||
+       cig->cig_config.sdu_interval_s_to_m[2] !=
+                         cig_config.sdu_interval_s_to_m[2]) {
+      LOG(WARNING) << __func__  << " cig params are different ";
+      return false;
+    }
+
+    for (auto it = cig->cis_list.begin(); it != cig->cis_list.end();) {
+      CIS *cis = it->second;
+      if(cis->cis_config.cis_id  ==  cis_configs[i].cis_id &&
+         cis->cis_config.max_sdu_m_to_s == cis_configs[i].max_sdu_m_to_s &&
+         cis->cis_config.max_sdu_s_to_m == cis_configs[i].max_sdu_s_to_m &&
+         cis->cis_config.phy_m_to_s == cis_configs[i].phy_m_to_s  &&
+         cis->cis_config.phy_s_to_m == cis_configs[i].phy_s_to_m  &&
+         cis->cis_config.rtn_m_to_s == cis_configs[i].rtn_m_to_s  &&
+         cis->cis_config.rtn_s_to_m == cis_configs[i].rtn_s_to_m) {
+        it++; i++;
+      } else {
+        is_params_same = false;
+        break;
+      }
+    }
+    LOG(WARNING) << __func__  << ": is_params_same : "
+                              << loghex(is_params_same);
+    return is_params_same;
+  }
+
+  bool IsCisExists(uint8_t cig_id, uint8_t cis_id)  {
+    bool is_cis_exists = false;
+    auto it = cig_list.find(cig_id);
+    if (it != cig_list.end()) {
+      CIG *cig = it->second;
+      auto it = cig->cis_list.find(cis_id);
+      if (it != cig->cis_list.end()) {
+        is_cis_exists = true;
+      }
+    }
+    return is_cis_exists;
+  }
+
+  CIS *GetCis(uint8_t cig_id, uint8_t cis_id)  {
+    auto it = cig_list.find(cig_id);
+    if (it != cig_list.end()) {
+      CIG *cig = it->second;
+      auto it = cig->cis_list.find(cis_id);
+      if (it != cig->cis_list.end()) {
+        return it->second;
+      }
+    }
+    return nullptr;
+  }
+
+  CIG *GetCig(uint8_t cig_id)  {
+    auto it = cig_list.find(cig_id);
+    if (it != cig_list.end()) {
+      return it->second;
+    }
+    return nullptr;
+  }
+
+  CIS *GetCis(uint16_t cis_handle)  {
+    bool cis_found = false;
+    CIS *cis = nullptr;
+    for (auto it : cig_list) {
+      CIG *cig = it.second;
+      if(cig->cig_state == CigState::CREATED ||
+         cig->cig_state == CigState::CREATING) {
+        for (auto it : cig->cis_list) {
+          cis = it.second;
+          if(cis->cis_handle == cis_handle) {
+            cis_found = true;
+            break;
+          }
+        }
+      }
+      if(cis_found) return cis;
+    }
+    return nullptr;
+  }
+
+  // TODO to remove if there is no need
+  bool IsCisEstablished(uint8_t cig_id, uint8_t cis_id) {
+    bool is_cis_established = false;
+    auto it = cig_list.find(cig_id);
+    if (it == cig_list.end()) {
+      return false;
+    } else {
+      CIG *cig = it->second;
+      if(cis_id != 0XFF) {
+        auto it = cig->cis_list.find(cis_id);
+        if (it != cig->cis_list.end()) {
+          CIS *cis = it->second;
+          if(cis->cis_sm.StateId() == CisStateMachine::kStateEstablished) {
+            is_cis_established = true;
+          }
+        }
+      } else {
+        for (auto it : cig->cis_list) {
+          CIS *cis = it.second;
+          if(cis->cis_sm.StateId() == CisStateMachine::kStateEstablished) {
+            is_cis_established = true;
+            break;
+          }
+        }
+      }
+    }
+    return is_cis_established;
+  }
+
+  // TODO to remove if there is no need
+  bool IsCisStreaming(uint8_t cig_id, uint8_t cis_id)  {
+    bool is_cis_streaming = false;
+    auto it = cig_list.find(cig_id);
+    if (it == cig_list.end()) {
+      return false;
+    } else {
+      CIG *cig = it->second;
+      if(cis_id != 0XFF) {
+        auto it = cig->cis_list.find(cis_id);
+        if (it != cig->cis_list.end()) {
+          CIS *cis = it->second;
+          if(cis->cis_sm.StateId() == CisStateMachine::kStateEstablished) {
+            is_cis_streaming = true;
+          }
+        }
+      } else {
+        for (auto it : cig->cis_list) {
+          CIS *cis = it.second;
+          if(cis->cis_sm.StateId() == CisStateMachine::kStateEstablished) {
+            is_cis_streaming = true;
+            break;
+          }
+        }
+      }
+    }
+    return is_cis_streaming;
+  }
+};
+
+void CisInterface::Initialize(
+                   CisInterfaceCallbacks* callbacks) {
+  if (instance) {
+    LOG(ERROR) << "Already initialized!";
+  } else {
+    instance = new CisInterfaceImpl(callbacks);
+  }
+}
+
+void CisInterface::CleanUp() {
+
+  CisInterfaceImpl* ptr = instance;
+  instance = nullptr;
+  ptr->CleanUp();
+  delete ptr;
+}
+
+CisInterface* CisInterface::Get() {
+  CHECK(instance);
+  return instance;
+}
+
+static void hci_cig_param_callback(tBTM_BLE_SET_CIG_RET_PARAM *param) {
+  if (instance) {
+    instance->ProcessEvent(IsoHciEvent::CIG_CONFIGURED_EVT, param);
+  }
+}
+
+static void hci_cig_param_test_callback(tBTM_BLE_SET_CIG_PARAM_TEST_RET *param) {
+  if (instance) {
+    instance->ProcessEvent(IsoHciEvent::CIG_CONFIGURED_EVT, param);
+  }
+}
+
+static void hci_cig_remove_param_callback(uint8_t status, uint8_t cig_id) {
+  tBTM_BLE_SET_CIG_REMOVE_PARAM param = { .status = status,
+                                          .cig_id = cig_id };
+  if (instance) {
+    instance->ProcessEvent(IsoHciEvent::CIG_REMOVED_EVT, &param);
+  }
+}
+
+static void hci_cis_create_status_callback ( uint8_t status) {
+  if (instance) {
+    instance->ProcessEvent(IsoHciEvent::CIS_STATUS_EVT, &status);
+  }
+}
+
+static void hci_cis_create_callback (
+              tBTM_BLE_CIS_ESTABLISHED_EVT_PARAM *param) {
+  if (instance) {
+    instance->ProcessEvent(IsoHciEvent::CIS_ESTABLISHED_EVT, param);
+  }
+}
+
+static void hci_cis_setup_datapath_callback ( uint8_t status,
+                            uint16_t conn_handle) {
+  tBTM_BLE_CIS_DATA_PATH_EVT_PARAM param = { .status = status,
+                                             .conn_handle = conn_handle };
+  if (instance) {
+    instance->ProcessEvent(IsoHciEvent::SETUP_DATA_PATH_DONE_EVT, &param);
+  }
+}
+
+static void hci_cis_disconnect_callback ( uint8_t status, uint16_t cis_handle,
+                                          uint8_t reason) {
+  tBTM_BLE_CIS_DISCONNECTED_EVT_PARAM param = { .status = status,
+                                                .cis_handle = cis_handle,
+                                                .reason = reason
+                                             };
+  if (instance) {
+    instance->ProcessEvent(IsoHciEvent::CIS_DISCONNECTED_EVT, &param);
+  }
+}
+
+#if 0
+static void hci_cis_remove_datapath_callback ( uint8_t status,
+                                              uint16_t conn_handle) {
+  tBTM_BLE_CIS_DATA_PATH_EVT_PARAM param = { .status = status,
+                                             .conn_handle = conn_handle };
+  if (instance) {
+    instance->ProcessEvent(IsoHciEvent::REMOVE_DATA_PATH_DONE_EVT, &param);
+  }
+}
+#endif
+
+}  // namespace ucast
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/bap/gattc_ops_queue.cc b/le_audio/system/bt/bta/bap/gattc_ops_queue.cc
new file mode 100644
index 0000000..7c7b187
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/gattc_ops_queue.cc
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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 "gattc_ops_queue.h"
+
+#include <list>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace bluetooth {
+namespace bap {
+
+using gatt_operation = GattOpsQueue::gatt_operation;
+using bluetooth::Uuid;
+
+constexpr uint8_t GATT_READ_CHAR = 1;
+constexpr uint8_t GATT_READ_DESC = 2;
+constexpr uint8_t GATT_WRITE_CHAR = 3;
+constexpr uint8_t GATT_WRITE_DESC = 4;
+constexpr uint8_t GATT_SERV_SEARCH = 5;
+
+struct gatt_read_op_data {
+  BAP_GATT_READ_OP_CB cb;
+  void* cb_data;
+};
+
+std::unordered_map<uint16_t, std::list<gatt_operation>>
+    GattOpsQueue::gatt_op_queue;
+std::unordered_set<uint16_t> GattOpsQueue::gatt_op_queue_executing;
+
+std::unordered_map<uint16_t, bool> GattOpsQueue::congestion_queue;
+
+void GattOpsQueue::mark_as_not_executing(uint16_t conn_id) {
+  gatt_op_queue_executing.erase(conn_id);
+}
+
+void GattOpsQueue::gatt_read_op_finished(uint16_t conn_id, tGATT_STATUS status,
+                                         uint16_t handle, uint16_t len,
+                                         uint8_t* value, void* data) {
+  gatt_read_op_data* tmp = (gatt_read_op_data*)data;
+  BAP_GATT_READ_OP_CB tmp_cb = tmp->cb;
+  void* tmp_cb_data = tmp->cb_data;
+
+  APPL_TRACE_DEBUG("%s: conn_id=0x%x handle=%d status=%d len=%d", __func__,
+    conn_id, handle, status, len);
+
+  osi_free(data);
+
+  auto map_ptr = gatt_op_queue.find(conn_id);
+  if (map_ptr == gatt_op_queue.end() || map_ptr->second.empty()) {
+    APPL_TRACE_DEBUG("%s: no more operations queued for conn_id %d", __func__,
+                     conn_id);
+    return;
+  }
+
+  std::list<gatt_operation>& gatt_ops = map_ptr->second;
+  gatt_operation op = gatt_ops.front();
+  gatt_ops.pop_front();
+
+  mark_as_not_executing(conn_id);
+  gatt_execute_next_op(conn_id);
+
+  if (tmp_cb) {
+    tmp_cb(op.client_id, conn_id, status, handle, len, value, tmp_cb_data);
+    return;
+  }
+}
+
+struct gatt_write_op_data {
+  BAP_GATT_WRITE_OP_CB cb;
+  void* cb_data;
+};
+
+void GattOpsQueue::gatt_write_op_finished(uint16_t conn_id, tGATT_STATUS status,
+                                          uint16_t handle, void* data) {
+  gatt_write_op_data* tmp = (gatt_write_op_data*)data;
+  BAP_GATT_WRITE_OP_CB tmp_cb = tmp->cb;
+  void* tmp_cb_data = tmp->cb_data;
+
+  APPL_TRACE_DEBUG("%s: conn_id=0x%x handle=%d status=%d", __func__, conn_id,
+    handle, status);
+
+  osi_free(data);
+
+  auto map_ptr = gatt_op_queue.find(conn_id);
+  if (map_ptr == gatt_op_queue.end() || map_ptr->second.empty()) {
+    APPL_TRACE_DEBUG("%s: no more operations queued for conn_id %d", __func__,
+                     conn_id);
+    return;
+  }
+
+  std::list<gatt_operation>& gatt_ops = map_ptr->second;
+  gatt_operation op = gatt_ops.front();
+  gatt_ops.pop_front();
+
+  mark_as_not_executing(conn_id);
+  gatt_execute_next_op(conn_id);
+
+  if (tmp_cb) {
+    tmp_cb(op.client_id, conn_id, status, handle, tmp_cb_data);
+    return;
+  }
+}
+
+void GattOpsQueue::gatt_execute_next_op(uint16_t conn_id) {
+  APPL_TRACE_DEBUG("%s: conn_id=0x%x", __func__, conn_id);
+  if (gatt_op_queue.empty()) {
+    APPL_TRACE_DEBUG("%s: op queue is empty", __func__);
+    return;
+  }
+
+  auto ptr = congestion_queue.find(conn_id);
+
+  if (ptr != congestion_queue.end()) {
+    bool is_congested = ptr->second;
+    APPL_TRACE_DEBUG("%s: congestion queue exist, conn_id: %d, is_congested: %d",
+                                               __func__, conn_id, is_congested);
+    if(is_congested) {
+      APPL_TRACE_DEBUG("%s: lower layer is congested", __func__);
+      return;
+    }
+  }
+
+  auto map_ptr = gatt_op_queue.find(conn_id);
+
+  if (map_ptr == gatt_op_queue.end()) {
+    APPL_TRACE_DEBUG("%s: Queue is null", __func__);
+    return;
+  }
+
+  if (map_ptr->second.empty()) {
+    APPL_TRACE_DEBUG("%s: queue is empty for conn_id: %d", __func__,
+                     conn_id);
+    return;
+  }
+
+  if (gatt_op_queue_executing.count(conn_id)) {
+    APPL_TRACE_DEBUG("%s: can't enqueue next op, already executing", __func__);
+    return;
+  }
+
+  gatt_op_queue_executing.insert(conn_id);
+
+  std::list<gatt_operation>& gatt_ops = map_ptr->second;
+
+  gatt_operation& op = gatt_ops.front();
+
+  APPL_TRACE_DEBUG("%s: op.type=%d, handle=%d", __func__, op.type,
+    op.handle);
+  if (op.type == GATT_READ_CHAR) {
+    gatt_read_op_data* data =
+        (gatt_read_op_data*)osi_malloc(sizeof(gatt_read_op_data));
+    data->cb = op.read_cb;
+    data->cb_data = op.read_cb_data;
+    BTA_GATTC_ReadCharacteristic(conn_id, op.handle, GATT_AUTH_REQ_NONE,
+                                 gatt_read_op_finished, data);
+
+  } else if (op.type == GATT_READ_DESC) {
+    gatt_read_op_data* data =
+        (gatt_read_op_data*)osi_malloc(sizeof(gatt_read_op_data));
+    data->cb = op.read_cb;
+    data->cb_data = op.read_cb_data;
+    BTA_GATTC_ReadCharDescr(conn_id, op.handle, GATT_AUTH_REQ_NONE,
+                            gatt_read_op_finished, data);
+
+  } else if (op.type == GATT_WRITE_CHAR) {
+    gatt_write_op_data* data =
+        (gatt_write_op_data*)osi_malloc(sizeof(gatt_write_op_data));
+    data->cb = op.write_cb;
+    data->cb_data = op.write_cb_data;
+    BTA_GATTC_WriteCharValue(conn_id, op.handle, op.write_type,
+                             std::move(op.value), GATT_AUTH_REQ_NONE,
+                             gatt_write_op_finished, data);
+
+  } else if (op.type == GATT_WRITE_DESC) {
+    gatt_write_op_data* data =
+        (gatt_write_op_data*)osi_malloc(sizeof(gatt_write_op_data));
+    data->cb = op.write_cb;
+    data->cb_data = op.write_cb_data;
+    BTA_GATTC_WriteCharDescr(conn_id, op.handle, std::move(op.value),
+                             GATT_AUTH_REQ_NONE, gatt_write_op_finished, data);
+  } else if (op.type == GATT_SERV_SEARCH) {
+    BTA_GATTC_ServiceSearchRequest(conn_id, op.p_srvc_uuid);
+  }
+}
+
+void GattOpsQueue::Clean(uint16_t conn_id) {
+  APPL_TRACE_DEBUG("%s: conn_id=0x%x", __func__, conn_id);
+
+  gatt_op_queue.erase(conn_id);
+  gatt_op_queue_executing.erase(conn_id);
+}
+
+void GattOpsQueue::ReadCharacteristic(uint16_t client_id,
+                                      uint16_t conn_id, uint16_t handle,
+                                      BAP_GATT_READ_OP_CB cb, void* cb_data) {
+  gatt_op_queue[conn_id].push_back({.type = GATT_READ_CHAR,
+                                    .client_id = client_id,
+                                    .handle = handle,
+                                    .read_cb = cb,
+                                    .read_cb_data = cb_data});
+  gatt_execute_next_op(conn_id);
+}
+
+void GattOpsQueue::ReadDescriptor(uint16_t client_id,
+                                  uint16_t conn_id, uint16_t handle,
+                                  BAP_GATT_READ_OP_CB cb, void* cb_data) {
+  gatt_op_queue[conn_id].push_back({.type = GATT_READ_DESC,
+                                    .client_id = client_id,
+                                    .handle = handle,
+                                    .read_cb = cb,
+                                    .read_cb_data = cb_data});
+  gatt_execute_next_op(conn_id);
+}
+
+void GattOpsQueue::WriteCharacteristic(uint16_t client_id,
+                                       uint16_t conn_id, uint16_t handle,
+                                       std::vector<uint8_t> value,
+                                       tGATT_WRITE_TYPE write_type,
+                                       BAP_GATT_WRITE_OP_CB cb, void* cb_data) {
+  gatt_op_queue[conn_id].push_back({.type = GATT_WRITE_CHAR,
+                                    .client_id = client_id,
+                                    .handle = handle,
+                                    .write_type = write_type,
+                                    .write_cb = cb,
+                                    .write_cb_data = cb_data,
+                                    .value = std::move(value)});
+  gatt_execute_next_op(conn_id);
+}
+
+void GattOpsQueue::WriteDescriptor(uint16_t client_id,
+                                   uint16_t conn_id, uint16_t handle,
+                                   std::vector<uint8_t> value,
+                                   tGATT_WRITE_TYPE write_type,
+                                   BAP_GATT_WRITE_OP_CB cb, void* cb_data) {
+  gatt_op_queue[conn_id].push_back({.type = GATT_WRITE_DESC,
+                                    .client_id = client_id,
+                                    .handle = handle,
+                                    .write_type = write_type,
+                                    .write_cb = cb,
+                                    .write_cb_data = cb_data,
+                                    .value = std::move(value)});
+  gatt_execute_next_op(conn_id);
+}
+
+void GattOpsQueue::ServiceSearch(uint16_t client_id,
+                                 uint16_t conn_id, Uuid* srvc_uuid) {
+  gatt_op_queue[conn_id].push_back({.type = GATT_SERV_SEARCH,
+                                    .client_id = client_id,
+                                    .p_srvc_uuid = srvc_uuid});
+  gatt_execute_next_op(conn_id);
+}
+
+uint16_t GattOpsQueue::ServiceSearchComplete(uint16_t conn_id,
+                                          tGATT_STATUS status) {
+  auto map_ptr = gatt_op_queue.find(conn_id);
+  if (map_ptr == gatt_op_queue.end() || map_ptr->second.empty()) {
+    APPL_TRACE_DEBUG("%s: no more operations queued for conn_id %d", __func__,
+                     conn_id);
+    return 0;
+  }
+
+  std::list<gatt_operation>& gatt_ops = map_ptr->second;
+
+  gatt_operation gatt_op = gatt_ops.front();
+  gatt_ops.pop_front();
+  mark_as_not_executing(conn_id);
+  gatt_execute_next_op(conn_id);
+  return gatt_op.client_id;
+}
+
+void GattOpsQueue::CongestionCallback(uint16_t conn_id, bool congested) {
+  congestion_queue[conn_id] = congested;
+  if(!congested) {
+    gatt_execute_next_op(conn_id);
+  }
+}
+
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/bap/gattc_ops_queue.h b/le_audio/system/bt/bta/bap/gattc_ops_queue.h
new file mode 100644
index 0000000..4dd9525
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/gattc_ops_queue.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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 <vector>
+
+#include <list>
+#include <unordered_map>
+#include <unordered_set>
+#include "bta_gatt_api.h"
+
+typedef void (*BAP_GATT_READ_OP_CB)(uint16_t client_id,uint16_t conn_id,
+                                tGATT_STATUS status, uint16_t handle,
+                                uint16_t len, uint8_t* value,
+                                void* data);
+
+typedef void (*BAP_GATT_WRITE_OP_CB)(uint16_t client_id, uint16_t conn_id,
+                                 tGATT_STATUS status,
+                                 uint16_t handle, void* data);
+
+/* BTA GATTC implementation does not allow for multiple commands queuing. So one
+ * client making calls to BTA_GATTC_ReadCharacteristic, BTA_GATTC_ReadCharDescr,
+ * BTA_GATTC_WriteCharValue, BTA_GATTC_WriteCharDescr must wait for the callacks
+ * before scheduling next operation.
+ *
+ * Methods below can be used as replacement to BTA_GATTC_* in BTA app. They do
+ * queue the commands if another command is currently being executed.
+ *
+ * If you decide to use those methods in your app, make sure to not mix it with
+ * existing BTA_GATTC_* API.
+ */
+
+namespace bluetooth {
+namespace bap {
+
+class GattOpsQueue {
+ public:
+  static void Clean(uint16_t conn_id);
+  static void ReadCharacteristic(uint16_t client_id,
+                           uint16_t conn_id, uint16_t handle,
+                           BAP_GATT_READ_OP_CB cb, void* cb_data);
+  static void ReadDescriptor(uint16_t client_id,
+                       uint16_t conn_id, uint16_t handle,
+                       BAP_GATT_READ_OP_CB cb, void* cb_data);
+  static void WriteCharacteristic(uint16_t client_id,
+                            uint16_t conn_id, uint16_t handle,
+                            std::vector<uint8_t> value,
+                            tGATT_WRITE_TYPE write_type,
+                            BAP_GATT_WRITE_OP_CB cb, void* cb_data);
+  static void WriteDescriptor(uint16_t client_id,
+                       uint16_t conn_id, uint16_t handle,
+                        std::vector<uint8_t> value,
+                        tGATT_WRITE_TYPE write_type, BAP_GATT_WRITE_OP_CB cb,
+                        void* cb_data);
+  static void ServiceSearch(uint16_t client_id,
+                            uint16_t conn_id, Uuid* p_srvc_uuid);
+
+  static uint16_t ServiceSearchComplete(uint16_t conn_id, tGATT_STATUS status);
+
+  static void CongestionCallback(uint16_t conn_id, bool congested);
+
+
+  /* Holds pending GATT operations */
+  struct gatt_operation {
+    uint8_t type;
+    uint16_t client_id;
+    uint16_t handle;
+    BAP_GATT_READ_OP_CB read_cb;
+    void* read_cb_data;
+    BAP_GATT_WRITE_OP_CB write_cb;
+    void* write_cb_data;
+
+    /* write-specific fields */
+    tGATT_WRITE_TYPE write_type;
+    std::vector<uint8_t> value;
+
+    /* discovery specific */
+    Uuid* p_srvc_uuid;
+  };
+
+ private:
+  static bool is_congested;
+
+  static void mark_as_not_executing(uint16_t conn_id);
+  static void gatt_execute_next_op(uint16_t conn_id);
+  static void gatt_read_op_finished(uint16_t conn_id, tGATT_STATUS status,
+                              uint16_t handle, uint16_t len,
+                              uint8_t* value, void* data);
+  static void gatt_write_op_finished(uint16_t conn_id, tGATT_STATUS status,
+                                      uint16_t handle, void* data);
+
+  // maps connection id to operations waiting for execution
+  static std::unordered_map<uint16_t, std::list<gatt_operation>> gatt_op_queue;
+
+  // maps connection id to congestion status of each device
+  static std::unordered_map<uint16_t, bool> congestion_queue;
+
+  // contain connection ids that currently execute operations
+  static std::unordered_set<uint16_t> gatt_op_queue_executing;
+};
+
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/bap/gatts_ops_queue.cc b/le_audio/system/bt/bta/bap/gatts_ops_queue.cc
new file mode 100644
index 0000000..b84fe65
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/gatts_ops_queue.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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 "gatts_ops_queue.h"
+
+#include <list>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace bluetooth {
+namespace bap {
+
+using gatts_operation = GattsOpsQueue::gatts_operation;
+using bluetooth::Uuid;
+
+constexpr uint8_t GATT_NOTIFY = 1;
+
+std::unordered_map<uint16_t, std::list<gatts_operation>>
+                                       GattsOpsQueue::gatts_op_queue;
+std::unordered_set<uint16_t> GattsOpsQueue::gatts_op_queue_executing;
+std::unordered_map<uint16_t, bool> GattsOpsQueue::congestion_queue;
+
+void GattsOpsQueue::mark_as_not_executing(uint16_t conn_id) {
+  gatts_op_queue_executing.erase(conn_id);
+}
+
+void GattsOpsQueue::gatts_execute_next_op(uint16_t conn_id) {
+  APPL_TRACE_DEBUG("%s: conn_id=0x%x", __func__, conn_id);
+
+  if (gatts_op_queue.empty()) {
+    APPL_TRACE_DEBUG("%s: op queue is empty", __func__);
+    return;
+  }
+
+  auto ptr = congestion_queue.find(conn_id);
+
+  if (ptr != congestion_queue.end()) {
+    bool is_congested = ptr->second;
+    APPL_TRACE_DEBUG("%s: congestion queue exist, conn_id: %d, is_congested: %d",
+                                               __func__, conn_id, is_congested);
+    if(is_congested) {
+      APPL_TRACE_DEBUG("%s: lower layer is congested", __func__);
+      return;
+    }
+  }
+
+  auto map_ptr = gatts_op_queue.find(conn_id);
+
+  if (map_ptr == gatts_op_queue.end()) {
+    APPL_TRACE_DEBUG("%s: Queue is null", __func__);
+    return;
+  }
+
+  if (map_ptr->second.empty()) {
+    APPL_TRACE_DEBUG("%s: queue is empty for conn_id: %d", __func__,
+                     conn_id);
+    return;
+  }
+
+  if (gatts_op_queue_executing.count(conn_id)) {
+    APPL_TRACE_DEBUG("%s: can't enqueue next op, already executing", __func__);
+    return;
+  }
+
+  std::list<gatts_operation>& gatts_ops = map_ptr->second;
+  gatts_operation& op = gatts_ops.front();
+  APPL_TRACE_DEBUG("%s: op.type=%d, attr_id=%d",
+                              __func__, op.type, op.attr_id);
+
+  if(op.type == GATT_NOTIFY) {
+    if(GATTS_CheckStatusForApp(conn_id,op.need_confirm) == GATT_SUCCESS) {
+      BTA_GATTS_HandleValueIndication(conn_id, op.attr_id, op.value, op.need_confirm);
+      gatts_op_queue_executing.insert(conn_id);
+    }
+  }
+}
+
+void GattsOpsQueue::Clean(uint16_t conn_id) {
+  APPL_TRACE_DEBUG("%s: conn_id=0x%x", __func__, conn_id);
+
+  gatts_op_queue.erase(conn_id);
+  gatts_op_queue_executing.erase(conn_id);
+}
+
+void GattsOpsQueue::SendNotification(uint16_t conn_id,
+                                     uint16_t handle,
+                                     std::vector<uint8_t> value,
+                                     bool need_confirm) {
+  gatts_op_queue[conn_id].push_back({.type = GATT_NOTIFY,
+                                   .attr_id = handle,
+                                   .value = value,
+                                   .need_confirm = need_confirm});
+  gatts_execute_next_op(conn_id);
+}
+
+void GattsOpsQueue::NotificationCallback(uint16_t conn_id){
+  auto map_ptr = gatts_op_queue.find(conn_id);
+  if (map_ptr == gatts_op_queue.end() || map_ptr->second.empty()) {
+    APPL_TRACE_DEBUG("%s: no more operations queued for conn_id %d",
+                                            __func__, conn_id);
+    return;
+  }
+
+  std::list<gatts_operation>& gatts_ops = map_ptr->second;
+  gatts_operation op = gatts_ops.front();
+  gatts_ops.pop_front();
+  mark_as_not_executing(conn_id);
+  gatts_execute_next_op(conn_id);
+}
+
+void GattsOpsQueue::CongestionCallback(uint16_t conn_id, bool congested) {
+  APPL_TRACE_DEBUG("%s: conn_id: %d, congested: %d",
+                                         __func__, conn_id,congested);
+
+  congestion_queue[conn_id] = congested;
+  if(!congested) {
+    gatts_execute_next_op(conn_id);
+  }
+}
+
+}
+} // namespace ends
diff --git a/le_audio/system/bt/bta/bap/gatts_ops_queue.h b/le_audio/system/bt/bta/bap/gatts_ops_queue.h
new file mode 100644
index 0000000..6c0d5a8
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/gatts_ops_queue.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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 <vector>
+
+#include <list>
+#include <unordered_map>
+#include <unordered_set>
+#include "bta_gatt_api.h"
+namespace bluetooth {
+namespace bap {
+
+class GattsOpsQueue {
+ public:
+  static void Clean(uint16_t conn_id);
+  static void SendNotification(uint16_t conn_id, uint16_t handle, std::vector<uint8_t> value, bool need_confirm);
+  static void NotificationCallback(uint16_t conn_id);
+  static void CongestionCallback(uint16_t conn_id, bool congested);
+
+  /* Holds pending GATT operations */
+  struct gatts_operation {
+        uint8_t type;
+        uint16_t attr_id;
+        std::vector<uint8_t> value;
+        bool need_confirm;
+  };
+
+ private:
+  static bool is_congested;
+  static void mark_as_not_executing(uint16_t conn_id);
+  static void gatts_execute_next_op(uint16_t conn_id);
+
+  // maps connection id to operations waiting for execution
+  static std::unordered_map<uint16_t, std::list<gatts_operation>> gatts_op_queue;
+
+  // maps connection id to congestion status of each device
+  static std::unordered_map<uint16_t, bool> congestion_queue;
+
+  // contain connection ids that currently execute operations
+  static std::unordered_set<uint16_t> gatts_op_queue_executing;
+
+}; // Class GattsOpsQueue ends
+
+}
+} // namespace ends
diff --git a/le_audio/system/bt/bta/bap/pacs_client.cc b/le_audio/system/bt/bta/bap/pacs_client.cc
new file mode 100644
index 0000000..2123dcf
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/pacs_client.cc
@@ -0,0 +1,1862 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+#include "bta_gatt_api.h"
+#include "bta_pacs_client_api.h"
+#include "gattc_ops_queue.h"
+#include <map>
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include "stack/btm/btm_int.h"
+#include "device/include/controller.h"
+#include "osi/include/properties.h"
+
+#include <vector>
+#include "btif/include/btif_bap_config.h"
+#include "osi/include/log.h"
+#include "btif_util.h"
+#include <hardware/bt_bap_uclient.h>
+#include "btif_bap_codec_utils.h"
+
+namespace bluetooth {
+namespace bap {
+namespace pacs {
+
+//using bluetooth::bap::pacs::PacsClientCallbacks;
+using base::Closure;
+using bluetooth::bap::GattOpsQueue;
+
+Uuid PACS_UUID               = Uuid::FromString("1850");
+Uuid PACS_SINK_PAC_UUID      = Uuid::FromString("2BC9");
+Uuid PACS_SINK_LOC_UUID      = Uuid::FromString("2BCA");
+Uuid PACS_SRC_PAC_UUID       = Uuid::FromString("2BCB");
+Uuid PACS_SRC_LOC_UUID       = Uuid::FromString("2BCC");
+Uuid PACS_AVA_AUDIO_UUID     = Uuid::FromString("2BCD");
+Uuid PACS_SUP_AUDIO_UUID     = Uuid::FromString("2BCE");
+
+class PacsClientImpl;
+PacsClientImpl* instance;
+
+typedef uint8_t codec_type_t[5];
+
+constexpr uint8_t SINK_PAC        = 0x01;
+constexpr uint8_t SRC_PAC         = 0x02;
+constexpr uint8_t SINK_LOC        = 0x04;
+constexpr uint8_t SRC_LOC         = 0x08;
+constexpr uint8_t AVAIL_CONTEXTS  = 0x10;
+constexpr uint8_t SUPP_CONTEXTS   = 0x20;
+
+constexpr uint8_t LTV_TYPE_SUP_FREQS              = 0x01;
+constexpr uint8_t LTV_TYPE_SUP_FRAME_DUR          = 0x02;
+constexpr uint8_t LTV_TYPE_CHNL_COUNTS            = 0x03;
+constexpr uint8_t LTV_TYPE_OCTS_PER_FRAME         = 0x04;
+constexpr uint8_t LTV_TYPE_MAX_SUP_FRAMES_PER_SDU = 0x05;
+
+constexpr uint8_t LTV_TYPE_PREF_AUD_CONTEXT       = 0x01;
+constexpr uint8_t LTV_TYPE_VS_META_DATA           = 0xFF;//TODO
+constexpr uint16_t QTI_ID                         = 0x000A;
+
+constexpr uint8_t LTV_TYPE_VS_META_DATA_LC3Q      = 0x10;
+
+//constexpr uint16_t SAMPLE_RATE_NONE        = 0x0;
+constexpr uint16_t SAMPLE_RATE_8K          = 0x1 << 0;
+//constexpr uint16_t SAMPLE_RATE_11K         = 0x1 << 1;
+constexpr uint16_t SAMPLE_RATE_16K         = 0x1 << 2;
+//constexpr uint16_t SAMPLE_RATE_22K         = 0x1 << 3;
+constexpr uint16_t SAMPLE_RATE_24K         = 0x1 << 4;
+constexpr uint16_t SAMPLE_RATE_32K         = 0x1 << 5;
+constexpr uint16_t SAMPLE_RATE_441K        = 0x1 << 6;
+constexpr uint16_t SAMPLE_RATE_48K         = 0x1 << 7;
+constexpr uint16_t SAMPLE_RATE_882K        = 0x1 << 8;
+constexpr uint16_t SAMPLE_RATE_96K         = 0x1 << 9;
+constexpr uint16_t SAMPLE_RATE_176K        = 0x1 << 10;
+constexpr uint16_t SAMPLE_RATE_192K        = 0x1 << 11;
+//constexpr uint16_t SAMPLE_RATE_384K        = 0x1 << 12;
+
+constexpr uint8_t CODEC_ID_LC3     = 0x06;
+constexpr uint8_t DISCOVER_SUCCESS = 0x00;
+constexpr uint8_t DISCOVER_FAIL    = 0xFF;
+
+std::map<uint8_t, CodecSampleRate> freq_map = {
+  {SAMPLE_RATE_8K,    CodecSampleRate::CODEC_SAMPLE_RATE_8000  },
+  {SAMPLE_RATE_16K,   CodecSampleRate::CODEC_SAMPLE_RATE_16000 },
+  {SAMPLE_RATE_24K,   CodecSampleRate::CODEC_SAMPLE_RATE_24000 },
+  {SAMPLE_RATE_32K,   CodecSampleRate::CODEC_SAMPLE_RATE_32000 },
+  {SAMPLE_RATE_441K,  CodecSampleRate::CODEC_SAMPLE_RATE_44100 },
+  {SAMPLE_RATE_48K,   CodecSampleRate::CODEC_SAMPLE_RATE_48000 },
+  {SAMPLE_RATE_882K,  CodecSampleRate::CODEC_SAMPLE_RATE_88200 },
+  {SAMPLE_RATE_96K,   CodecSampleRate::CODEC_SAMPLE_RATE_96000 },
+  {SAMPLE_RATE_176K,  CodecSampleRate::CODEC_SAMPLE_RATE_176400},
+  {SAMPLE_RATE_192K,  CodecSampleRate::CODEC_SAMPLE_RATE_192000}
+};
+
+// ltv type to length
+std::map<uint8_t, uint8_t> ltv_info = {
+  {LTV_TYPE_SUP_FREQS,              0x03},
+  {LTV_TYPE_SUP_FRAME_DUR,          0x02},
+  {LTV_TYPE_CHNL_COUNTS,            0x02},
+  {LTV_TYPE_OCTS_PER_FRAME,         0x05},
+  {LTV_TYPE_MAX_SUP_FRAMES_PER_SDU, 0x02},
+  {LTV_TYPE_PREF_AUD_CONTEXT,       0x03}
+};
+
+enum class ProfleOP {
+  CONNECT,
+  DISCONNECT
+};
+
+struct ProfileOperation {
+  uint16_t client_id;
+  ProfleOP type;
+};
+
+enum class DevState {
+  IDLE = 0,
+  CONNECTING,
+  CONNECTED,
+  DISCONNECTING
+};
+
+struct SinkPacsData {
+  uint16_t sink_pac_handle;
+  uint16_t sink_pac_ccc_handle;
+  std::vector<CodecConfig> sink_pac_records;
+  bool read_sink_pac_record;
+};
+
+struct SrcPacsData {
+  uint16_t src_pac_handle;
+  uint16_t src_pac_ccc_handle;
+  std::vector<CodecConfig> src_pac_records;
+  bool read_src_pac_record;
+};
+
+void pacs_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
+void encryption_callback(const RawAddress*, tGATT_TRANSPORT, void*,
+                         tBTM_STATUS);
+
+struct PacsDevice {
+  RawAddress address;
+  /* This is true only during first connection to profile, until we store the
+   * device */
+  bool first_connection;
+  bool service_changed_rcvd;
+
+  /* we are making active attempt to connect to this device, 'direct connect'.
+   * This is true only during initial phase of first connection. */
+  bool connecting_actively;
+
+  uint16_t conn_id;
+  std::vector<SinkPacsData>sink_info;
+  std::vector<SrcPacsData>src_info;
+  uint16_t sink_loc_handle;
+  uint16_t sink_loc_ccc_handle;
+  uint16_t src_loc_handle;
+  uint16_t src_loc_ccc_handle;
+  uint16_t avail_contexts_handle;
+  uint16_t avail_contexts_ccc_handle;
+  uint16_t supp_contexts_handle;
+  uint16_t supp_contexts_ccc_handle;
+  uint16_t srv_changed_ccc_handle;
+  uint8_t chars_read;
+  uint8_t chars_to_be_read;
+  std::vector<CodecConfig> consolidated_sink_pac_records;
+  std::vector<CodecConfig> consolidated_src_pac_records;
+  uint32_t sink_locations;
+  uint32_t src_locations;
+  uint32_t available_contexts;
+  uint32_t supported_contexts;
+  bool discovery_completed;
+  bool notifications_enabled;
+  DevState state;
+  bool is_congested;
+  std::vector<ProfileOperation> profile_queue;
+  std::vector<uint16_t> connected_client_list; //list client requested for connection
+  PacsDevice(const RawAddress& address) : address(address) {}
+  PacsDevice() {
+    first_connection = false;
+    service_changed_rcvd = false;
+    conn_id = 0;
+    sink_loc_handle = 0;
+    sink_loc_ccc_handle = 0;
+    src_loc_handle = 0;
+    src_loc_ccc_handle = 0;
+    avail_contexts_handle = 0;
+    avail_contexts_ccc_handle = 0;
+    supp_contexts_handle = 0;
+    supp_contexts_ccc_handle = 0;
+    srv_changed_ccc_handle = 0;
+    chars_read = 0;
+    sink_locations = 0;
+    src_locations = 0;
+    available_contexts = 0;
+    supported_contexts = 0;
+    discovery_completed = false;
+    notifications_enabled = false;
+    state = static_cast<DevState>(0);
+    is_congested = false;
+  }
+};
+
+class PacsDevices {
+ public:
+  void Add(PacsDevice device) {
+    if (FindByAddress(device.address) != nullptr) return;
+
+    devices.push_back(device);
+  }
+
+  void Remove(const RawAddress& address) {
+    for (auto it = devices.begin(); it != devices.end();) {
+      if (it->address != address) {
+        ++it;
+        continue;
+      }
+
+      it = devices.erase(it);
+      return;
+    }
+  }
+
+  PacsDevice* FindByAddress(const RawAddress& address) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&address](const PacsDevice& device) {
+                               return device.address == address;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  PacsDevice* FindByConnId(uint16_t conn_id) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&conn_id](const PacsDevice& device) {
+                               return device.conn_id == conn_id;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  size_t size() { return (devices.size()); }
+
+  std::vector<PacsDevice> devices;
+};
+
+class PacsClientImpl : public PacsClient {
+ public:
+  ~PacsClientImpl() override = default;
+
+  PacsClientImpl() : gatt_client_id(BTA_GATTS_INVALID_IF) {};
+
+  bool Register(PacsClientCallbacks *callback) {
+    // looks for client is already registered
+    bool is_client_registered = false;
+    for (auto it : callbacks) {
+      PacsClientCallbacks *pac_callback = it.second;
+      if (callback == pac_callback) {
+        is_client_registered = true;
+        break;
+      }
+    }
+
+    LOG(WARNING) << __func__ << " is_client_registered: "
+                             << is_client_registered
+                             << ", gatt_client_id: " << gatt_client_id;
+    if (is_client_registered) return false;
+
+    if (gatt_client_id == BTA_GATTS_INVALID_IF) {
+      BTA_GATTC_AppRegister(
+        pacs_gattc_callback,
+        base::Bind(
+          [](PacsClientCallbacks *callback, uint8_t client_id, uint8_t status) {
+            if (status != GATT_SUCCESS) {
+              LOG(ERROR) << "Can't start PACS profile - no gatt "
+                            "clients left!";
+              return;
+            }
+
+            if (instance) {
+              LOG(WARNING) << " PACS gatt_client_id: "
+                           << instance->gatt_client_id;
+              instance->gatt_client_id = client_id;
+              instance->callbacks.insert(std::make_pair(
+                          ++instance->pacs_client_id, callback));
+              callback->OnInitialized(0, instance->pacs_client_id);
+            }
+          },
+          callback), true);
+    } else {
+      instance->callbacks.insert(std::make_pair(
+                    ++instance->pacs_client_id, callback));
+      callback->OnInitialized(0, instance->pacs_client_id);
+    }
+    return true;
+  }
+
+  bool Deregister (uint16_t client_id) {
+    bool status = false;
+    auto it = callbacks.find(client_id);
+    if (it != callbacks.end()) {
+      callbacks.erase(it);
+      if(callbacks.empty()) {
+       // deregister with GATT
+       LOG(WARNING) << __func__ << " Gatt de-register from pacs";
+       BTA_GATTC_AppDeregister(gatt_client_id);
+       gatt_client_id = BTA_GATTS_INVALID_IF;
+      }
+      status = true;
+    }
+    return status;
+  }
+
+  uint8_t GetClientCount () {
+    return callbacks.size();
+  }
+
+  void Connect(uint16_t client_id, const RawAddress& address,
+                         bool is_direct) override {
+    LOG(WARNING) << __func__ << " address: " << address;
+    PacsDevice *dev = pacsDevices.FindByAddress(address);
+    ProfileOperation op;
+    op.client_id = client_id;
+    op.type = ProfleOP::CONNECT;
+
+    if (dev == nullptr) {
+      PacsDevice pac_dev(address);
+      pacsDevices.Add(pac_dev);
+      dev = pacsDevices.FindByAddress(address);
+    }
+    if (dev == nullptr) {
+      LOG(ERROR) << __func__ << "dev is null";
+      return;
+    }
+
+    LOG(WARNING) << __func__ << ": state: " << static_cast<int>(dev->state);
+
+    switch(dev->state) {
+      case DevState::IDLE: {
+        BTA_GATTC_Open(gatt_client_id, address, is_direct,
+                                           GATT_TRANSPORT_LE, false);
+        dev->state = DevState::CONNECTING;
+        dev->profile_queue.push_back(op);
+      } break;
+      case DevState::CONNECTING: {
+        dev->profile_queue.push_back(op);
+      } break;
+      case DevState::CONNECTED: {
+        // add it to the client id list if not already done
+        auto iter = std::find_if(dev->connected_client_list.begin(),
+                                 dev->connected_client_list.end(),
+                             [&client_id](uint16_t id) {
+                               return id == client_id;
+                             });
+
+        if (iter == dev->connected_client_list.end())
+          dev->connected_client_list.push_back(client_id);
+
+        // respond immediately as connected
+
+      } break;
+      case DevState::DISCONNECTING: {
+        dev->profile_queue.push_back(op);
+      } break;
+    }
+  }
+
+  void Disconnect(uint16_t client_id, const RawAddress& address) override {
+    PacsDevice* dev = pacsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(INFO) << __func__ <<": Device not connected to profile: " << address;
+      return;
+    }
+
+    ProfileOperation op;
+    op.client_id = client_id;
+    op.type = ProfleOP::DISCONNECT;
+
+    LOG(WARNING) << __func__ << ": address: " << address
+                             << ", state: " << static_cast<int>(dev->state);
+
+    switch(dev->state) {
+      case DevState::CONNECTING: {
+        auto iter = std::find_if(dev->profile_queue.begin(),
+                                 dev->profile_queue.end(),
+                             [&client_id]( ProfileOperation entry) {
+                               return ((entry.type == ProfleOP::CONNECT) &&
+                                      (entry.client_id == client_id));
+                             });
+        // If it is the last client requested for disconnect
+        if (iter != dev->profile_queue.end() &&
+           dev->profile_queue.size() == 1) {
+          if (dev->conn_id) {
+            // Removes all registrations for connection.
+            BTA_GATTC_CancelOpen(dev->conn_id, address, false);
+            GattOpsQueue::Clean(dev->conn_id);
+            BTA_GATTC_Close(dev->conn_id);
+          } else {
+            // clear the connection queue and
+            // move the state to DISCONNECTING to better track
+            dev->profile_queue.clear();
+          }
+          dev->state = DevState::DISCONNECTING;
+          dev->profile_queue.push_back(op);
+        } else {
+           // remove the connection entry from the list
+           // as the same client has requested for disconnection
+           dev->profile_queue.erase(iter);
+        }
+      } break;
+      case DevState::CONNECTED: {
+        auto iter = std::find_if(dev->connected_client_list.begin(),
+                                 dev->connected_client_list.end(),
+                             [&client_id]( uint16_t stored_client_id) {
+                               return stored_client_id == client_id;
+                             });
+        // if it is the last client requested for disconnection
+        if (iter != dev->connected_client_list.end() &&
+           dev->connected_client_list.size() == 1) {
+          if (dev->conn_id) {
+            // Removes all registrations for connection.
+            BTA_GATTC_CancelOpen(dev->conn_id, address, false);
+            GattOpsQueue::Clean(dev->conn_id);
+            BTA_GATTC_Close(dev->conn_id);
+            dev->profile_queue.push_back(op);
+            dev->state = DevState::DISCONNECTING;
+          }
+        } else {
+          // remove the client from connected_client_list
+          dev->connected_client_list.erase(iter);
+          // remove the  pending gatt ops( not the ongoing one )
+          // initiated from client which requested disconnect
+          // TODO and send callback as disconnected
+        }
+      } break;
+      case DevState::DISCONNECTING: {
+        dev->profile_queue.push_back(op);
+      } break;
+      default:
+        break;
+    }
+  }
+
+  void StartDiscovery(uint16_t client_id, const RawAddress& address) override {
+    PacsDevice* dev = pacsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(INFO) << __func__ << ": Device not connected to profile: " << address;
+      return;
+    }
+    LOG(WARNING) << __func__ << " address: " << address
+                             << ", state: " << static_cast<int>(dev->state);
+    switch(dev->state) {
+      case DevState::CONNECTED: {
+        auto iter = std::find_if(dev->connected_client_list.begin(),
+                                 dev->connected_client_list.end(),
+                             [&client_id]( uint16_t stored_client_id) {
+                             LOG(WARNING) << __func__
+                                 << ": client_id: " << client_id
+                                 << ", stored_client_id:" << stored_client_id;
+                               return stored_client_id == client_id;
+                             });
+        // check if the client present in the connected client list
+        if (iter == dev->connected_client_list.end()) {
+           break;
+        }
+
+        LOG(WARNING) << __func__
+                 << ", discovery_completed: " << dev->discovery_completed
+                 << ", notifications_enabled: " << dev->notifications_enabled;
+
+        // check if the discovery is already finished
+        // send back the same results to the other client
+        if (dev->discovery_completed && dev->notifications_enabled) {
+          auto iter = callbacks.find(client_id);
+          if (iter != callbacks.end()) {
+            LOG(WARNING) << __func__ << ": OnSearchComplete";
+            PacsClientCallbacks *callback = iter->second;
+            callback->OnSearchComplete(DISCOVER_SUCCESS,
+                                       dev->address,
+                                       dev->consolidated_sink_pac_records,
+                                       dev->consolidated_src_pac_records,
+                                       dev->sink_locations,
+                                       dev->src_locations,
+                                       dev->available_contexts,
+                                       dev->supported_contexts);
+          }
+          break;
+        }
+
+        // reset it
+        dev->chars_read = 0x00;
+        dev->chars_to_be_read = 0x00;
+        dev->sink_info.clear();
+        dev->src_info.clear();
+        dev->consolidated_sink_pac_records.clear();
+        dev->consolidated_src_pac_records.clear();
+        //TODO
+        //btif_bap_remove_record()
+
+        // queue the request to GATT queue module
+        GattOpsQueue::ServiceSearch(client_id, dev->conn_id, &PACS_UUID);
+      } break;
+
+      default:
+        break;
+    }
+  }
+
+  void GetAudioAvailability(uint16_t client_id, const RawAddress& address) {
+    PacsDevice* dev = pacsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(INFO) << __func__ << ": Device not connected to profile: " << address;
+      return;
+    }
+    LOG(WARNING) << __func__ << ": address: " << address
+                             << ", state: " << static_cast<int>(dev->state);
+
+    switch(dev->state) {
+      case DevState::CONNECTED: {
+        auto iter = std::find_if(dev->connected_client_list.begin(),
+                                 dev->connected_client_list.end(),
+                             [&client_id]( uint16_t stored_client_id) {
+                               return stored_client_id == client_id;
+                             });
+        // check if the client present in the connected client list
+        if (iter == dev->connected_client_list.end()) {
+           break;
+        }
+
+        // check if the discovery is already finished
+        // send back the same results to the other client
+        if (dev->discovery_completed && dev->notifications_enabled) {
+          auto iter = callbacks.find(client_id);
+          if (iter != callbacks.end()) {
+            PacsClientCallbacks *callback = iter->second;
+            callback->OnAudioContextAvailable(dev->address,
+                                              dev->available_contexts);
+          }
+          break;
+        }
+
+        // queue the request to GATT queue module
+        GattOpsQueue::ReadCharacteristic(
+                 client_id, dev->conn_id, dev->avail_contexts_handle,
+                 PacsClientImpl::OnReadAvailableAudioStatic, nullptr);
+      } break;
+      default:
+        LOG(WARNING) << __func__ << " default";
+        break;
+    }
+  }
+
+  void OnGattConnected(tGATT_STATUS status, uint16_t conn_id,
+                       tGATT_IF client_if, RawAddress address,
+                       tBTA_TRANSPORT transport, uint16_t mtu) {
+
+    PacsDevice* dev = pacsDevices.FindByAddress(address);
+    if (!dev) {
+      /* When device is quickly disabled and enabled in settings, this case
+       * might happen */
+      LOG(WARNING) << __func__
+                   <<"Closing connection to non pacs device, address: "
+                   << address;
+      BTA_GATTC_Close(conn_id);
+      return;
+    }
+
+    LOG(WARNING) << __func__ << " address: " << address
+                             << ", state: " << static_cast<int>(dev->state)
+                             << ", status: " << loghex(status);
+
+    if (dev->state == DevState::CONNECTING) {
+      if (status != GATT_SUCCESS) {
+        LOG(ERROR) << __func__ <<  ": Failed to connect to PACS device";
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if(it->type == ProfleOP::CONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              PacsClientCallbacks *callback = iter->second;
+              callback->OnConnectionState(address,
+                                          ConnectionState::DISCONNECTED);
+            }
+            dev->profile_queue.erase(it);
+          } else {
+            it++;
+          }
+        }
+        dev->state = DevState::IDLE;
+        pacsDevices.Remove(address);
+        return;
+      }
+    } else if (dev->state == DevState::DISCONNECTING) {
+      // TODO will this happens ?
+      // it could have called the cancel open to expect the
+      // open cancelled event
+      if (status != GATT_SUCCESS) {
+        LOG(ERROR) << "Failed to connect to PACS device";
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if(it->type == ProfleOP::DISCONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              PacsClientCallbacks *callback = iter->second;
+              callback->OnConnectionState(address,
+                                          ConnectionState::DISCONNECTED);
+            }
+            dev->profile_queue.erase(it);
+          } else {
+            it++;
+          }
+        }
+        dev->state = DevState::IDLE;
+        pacsDevices.Remove(address);
+        return;
+      } else {
+        // gatt connected successfully
+        // if the disconnect entry is found we need to initiate the
+        // gatt disconnect. may be a race condition just after sending
+        // cancel open gatt connected event received
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if(it->type == ProfleOP::DISCONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              // Removes all registrations for connection.
+              BTA_GATTC_CancelOpen(dev->conn_id, address, false);
+              GattOpsQueue::Clean(dev->conn_id);
+              BTA_GATTC_Close(dev->conn_id);
+              break;
+            }
+          } else {
+            it++;
+          }
+        }
+        return;
+      }
+    } else {
+      // return unconditinally
+      return;
+    }
+
+    // success scenario code
+    dev->conn_id = conn_id;
+
+    tACL_CONN* p_acl = btm_bda_to_acl(address, BT_TRANSPORT_LE);
+    if (p_acl != nullptr &&
+        controller_get_interface()->supports_ble_2m_phy() &&
+        HCI_LE_2M_PHY_SUPPORTED(p_acl->peer_le_features)) {
+      LOG(INFO) << address << " set preferred PHY to 2M";
+      BTM_BleSetPhy(address, PHY_LE_2M, PHY_LE_2M, 0);
+    }
+
+    /* verify bond */
+    uint8_t sec_flag = 0;
+    BTM_GetSecurityFlagsByTransport(address, &sec_flag, BT_TRANSPORT_LE);
+
+    if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) {
+      /* if link has been encrypted */
+      OnEncryptionComplete(address, true);
+      return;
+    }
+
+    if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) {
+      /* if bonded and link not encrypted */
+      sec_flag = BTM_BLE_SEC_ENCRYPT;
+      LOG(WARNING) << "trying to encrypt now";
+      BTM_SetEncryption(address, BTA_TRANSPORT_LE, encryption_callback,
+                        nullptr, sec_flag);
+      return;
+    }
+
+    /* otherwise let it go through */
+    OnEncryptionComplete(address, true);
+  }
+
+  void OnGattDisconnected(tGATT_STATUS status, uint16_t conn_id,
+                          tGATT_IF client_if, RawAddress remote_bda,
+                          tBTA_GATT_REASON reason) {
+    PacsDevice* dev = pacsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(ERROR)  << __func__
+                  << ": Skipping unknown device disconnect, conn_id="
+                  << loghex(conn_id);
+      return;
+    }
+
+    LOG(WARNING) << __func__ << " remote_bda: " << remote_bda
+                             << ", state: " << static_cast<int>(dev->state);
+
+    switch(dev->state) {
+      case DevState::CONNECTING: {
+        // sudden disconnection
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if (it->type == ProfleOP::CONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              PacsClientCallbacks *callback = iter->second;
+              callback->OnConnectionState(remote_bda,
+                                          ConnectionState::DISCONNECTED);
+            }
+            it = dev->profile_queue.erase(it);
+          } else {
+            it++;
+          }
+        }
+      } break;
+      case DevState::CONNECTED: {
+        // sudden disconnection
+        for (auto it = dev->connected_client_list.begin();
+                             it != dev->connected_client_list.end();) {
+          // get the callback and update the upper layers
+          auto iter = callbacks.find(*it);
+          if (iter != callbacks.end()) {
+            PacsClientCallbacks *callback = iter->second;
+            callback->OnConnectionState(remote_bda,
+                                        ConnectionState::DISCONNECTED);
+          }
+          it = dev->connected_client_list.erase(it);
+        }
+      } break;
+      case DevState::DISCONNECTING: {
+        for (auto it = dev->profile_queue.begin();
+                             it != dev->profile_queue.end();) {
+          if (it->type == ProfleOP::DISCONNECT) {
+            // get the callback and update the upper layers
+            auto iter = callbacks.find(it->client_id);
+            if (iter != callbacks.end()) {
+              PacsClientCallbacks *callback = iter->second;
+              callback->OnConnectionState(remote_bda,
+                                          ConnectionState::DISCONNECTED);
+            }
+            it = dev->profile_queue.erase(it);
+          } else {
+            it++;
+          }
+        }
+
+        for (auto it = dev->connected_client_list.begin();
+                             it != dev->connected_client_list.end();) {
+          // get the callback and update the upper layers
+          it = dev->connected_client_list.erase(it);
+        }
+        // check if the connection queue is not empty
+        // if not initiate the Gatt connection
+      } break;
+      default:
+        break;
+    }
+
+    if (dev->conn_id) {
+      GattOpsQueue::Clean(dev->conn_id);
+      BTA_GATTC_Close(dev->conn_id);
+      dev->conn_id = 0;
+    }
+
+    dev->state = DevState::IDLE;
+    pacsDevices.Remove(remote_bda);
+  }
+
+  void OnConnectionUpdateComplete(uint16_t conn_id, tBTA_GATTC* p_data) {
+    PacsDevice* dev = pacsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(ERROR) << __func__
+                 << ": Skipping unknown device, conn_id="
+                 << loghex(conn_id);
+      return;
+    }
+  }
+
+  void OnEncryptionComplete(const RawAddress& address, bool success) {
+    PacsDevice* dev = pacsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(ERROR) << __func__ << ": Skipping unknown device" << address;
+      return;
+    }
+
+    if(dev->state != DevState::CONNECTING) {
+      LOG(ERROR) << __func__ << ": received in wrong state" << address;
+      return;
+    }
+
+    LOG(WARNING) << __func__ << ": address=" << address  << loghex(success);
+    // encryption failed
+    if (!success) {
+      for (auto it = dev->profile_queue.begin();
+                           it != dev->profile_queue.end();) {
+        if (it->type == ProfleOP::CONNECT) {
+          // get the callback and update the upper layers
+          auto iter = callbacks.find(it->client_id);
+          if (iter != callbacks.end()) {
+            PacsClientCallbacks *callback = iter->second;
+            callback->OnConnectionState(address,
+                                        ConnectionState::DISCONNECTED);
+          }
+          // change the type to disconnect
+          it->type = ProfleOP::DISCONNECT;
+        } else {
+          it++;
+        }
+      }
+      dev->state = DevState::DISCONNECTING;
+      // Removes all registrations for connection.
+      BTA_GATTC_CancelOpen(dev->conn_id, address, false);
+      BTA_GATTC_Close(dev->conn_id);
+    } else {
+      for (auto it = dev->profile_queue.begin();
+                           it != dev->profile_queue.end();) {
+        if (it->type == ProfleOP::CONNECT) {
+          // get the callback and update the upper layers
+          auto iter = callbacks.find(it->client_id);
+          if (iter != callbacks.end()) {
+            dev->connected_client_list.push_back(it->client_id);
+            PacsClientCallbacks *callback = iter->second;
+            LOG(WARNING) << __func__ << " calling OnConnectionState";
+            callback->OnConnectionState(address, ConnectionState::CONNECTED);
+          }
+          dev->profile_queue.erase(it);
+        } else {
+          it++;
+        }
+      }
+      dev->state = DevState::CONNECTED;
+    }
+  }
+
+  void OnServiceChangeEvent(const RawAddress& address) {
+    PacsDevice* dev = pacsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(ERROR) << __func__ << ": Skipping unknown device: " << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": address: " << address;
+    dev->first_connection = true;
+    dev->service_changed_rcvd = true;
+    GattOpsQueue::Clean(dev->conn_id);
+  }
+
+  void OnServiceDiscDoneEvent(const RawAddress& address) {
+    PacsDevice* dev = pacsDevices.FindByAddress(address);
+    if (!dev) {
+      LOG(ERROR) << __func__ << ": Skipping unknown device: " << address;
+      return;
+    }
+
+    LOG(WARNING) << __func__ << " service_changed_rcvd: "
+                << dev->service_changed_rcvd;
+    if (dev->service_changed_rcvd) {
+      // queue the request to GATT queue module with dummu client id
+      GattOpsQueue::ServiceSearch(0XFF, dev->conn_id, &PACS_UUID);
+    }
+  }
+
+  void RegisterForNotification(uint16_t client_id, uint16_t conn_id,
+                               PacsDevice* dev, uint16_t ccc_handle,
+                               uint16_t handle) {
+    if(handle && ccc_handle) {
+      /* Register and enable Notification */
+      tGATT_STATUS register_status;
+      register_status = BTA_GATTC_RegisterForNotifications(
+          conn_id, dev->address, handle);
+      if (register_status != GATT_SUCCESS) {
+        LOG(ERROR) << __func__
+                   << ": BTA_GATTC_RegisterForNotifications failed, status="
+                   << loghex(register_status);
+      }
+      std::vector<uint8_t> value(2);
+      uint8_t* ptr = value.data();
+      UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
+      GattOpsQueue::WriteDescriptor(
+          client_id, conn_id, ccc_handle,
+          std::move(value), GATT_WRITE, nullptr, nullptr);
+    }
+  }
+
+  void OnServiceSearchComplete(uint16_t conn_id, tGATT_STATUS status) {
+    PacsDevice* dev = pacsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(ERROR) << __func__ << ": Skipping unknown device, conn_id = "
+               << loghex(conn_id);
+      return;
+    }
+
+    LOG(WARNING) << __func__ << ": conn_id = " << loghex(conn_id);
+
+    uint16_t client_id = GattOpsQueue::ServiceSearchComplete(conn_id,
+                                          status);
+    LOG(WARNING) << __func__ << ": client_id = " << loghex(client_id);
+    auto iter = callbacks.find(client_id);
+    if (status != GATT_SUCCESS) {
+      /* close connection and report service discovery complete with error */
+      LOG(ERROR) << __func__ << ": Service discovery failed";
+      if (iter != callbacks.end()) {
+        PacsClientCallbacks *callback = iter->second;
+        callback->OnSearchComplete(DISCOVER_FAIL,
+                                   dev->address,
+                                   dev->consolidated_sink_pac_records,
+                                   dev->consolidated_src_pac_records,
+                                   dev->sink_locations,
+                                   dev->src_locations,
+                                   dev->available_contexts,
+                                   dev->supported_contexts);
+      }
+      return;
+    }
+
+    const std::vector<gatt::Service>* services = BTA_GATTC_GetServices(conn_id);
+
+    const gatt::Service* service = nullptr;
+    if (services) {
+      for (const gatt::Service& tmp : *services) {
+        if (tmp.uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER)) {
+          LOG(INFO) << __func__ << ": Found UUID_CLASS_GATT_SERVER, handle="
+                    << loghex(tmp.handle);
+          const gatt::Service* service_changed_service = &tmp;
+          find_server_changed_ccc_handle(conn_id, service_changed_service);
+        } else if (tmp.uuid == PACS_UUID) {
+          LOG(INFO) << __func__ << ": Found PACS service, handle="
+                   << loghex(tmp.handle);
+          service = &tmp;
+        }
+      }
+    } else {
+      LOG(ERROR) << __func__
+                 << ": no services found for conn_id: " << loghex(conn_id);
+      return;
+    }
+
+    if (!service) {
+      LOG(ERROR) << __func__ << ": No PACS service found";
+      if (iter != callbacks.end()) {
+        PacsClientCallbacks *callback = iter->second;
+        callback->OnSearchComplete(DISCOVER_FAIL,
+                                   dev->address,
+                                   dev->consolidated_sink_pac_records,
+                                   dev->consolidated_src_pac_records,
+                                   dev->sink_locations,
+                                   dev->src_locations,
+                                   dev->available_contexts,
+                                   dev->supported_contexts);
+      }
+      return;
+    }
+
+    for (const gatt::Characteristic& charac : service->characteristics) {
+      LOG(INFO) << __func__ << ": uuid: " << charac.uuid;
+      if (charac.uuid == PACS_SINK_PAC_UUID) {
+        LOG(INFO) << __func__ << ": sink pac uuid found. ";
+
+        SinkPacsData info;
+        memset(&info, 0, sizeof(info));
+        info.sink_pac_handle = charac.value_handle;
+        info.sink_pac_ccc_handle = find_ccc_handle(conn_id, charac.value_handle);
+        dev->sink_info.push_back(info);
+        dev->chars_to_be_read |= SINK_PAC;
+        GattOpsQueue::ReadCharacteristic(
+                 client_id, conn_id, charac.value_handle,
+                 PacsClientImpl::OnReadOnlyPropertiesReadStatic, nullptr);
+
+        if (info.sink_pac_ccc_handle) {
+          RegisterForNotification(client_id, conn_id, dev,
+                                  info.sink_pac_ccc_handle,
+                                  info.sink_pac_handle);
+        }
+
+      } else if (charac.uuid == PACS_SINK_LOC_UUID) {
+        LOG(INFO) << __func__ << ": sink loc uuid found. ";
+        dev->sink_loc_handle = charac.value_handle;
+
+        GattOpsQueue::ReadCharacteristic(
+                 client_id,conn_id, charac.value_handle,
+                 PacsClientImpl::OnReadOnlyPropertiesReadStatic, nullptr);
+
+        dev->sink_loc_ccc_handle =
+            find_ccc_handle(conn_id, charac.value_handle);
+
+        dev->chars_to_be_read |= SINK_LOC;
+        if (dev->sink_loc_ccc_handle) {
+          RegisterForNotification(client_id, conn_id, dev,
+                                  dev->sink_loc_ccc_handle,
+                                  dev->sink_loc_handle);
+        }
+
+      } else if (charac.uuid == PACS_SRC_PAC_UUID) {
+        LOG(INFO) << __func__ << ": src pac uuid found. ";
+
+        SrcPacsData info;
+        memset(&info, 0, sizeof(info));
+        info.src_pac_handle = charac.value_handle;
+        info.src_pac_ccc_handle = find_ccc_handle(conn_id, charac.value_handle);
+        dev->src_info.push_back(info);
+        dev->chars_to_be_read |= SRC_PAC;
+        GattOpsQueue::ReadCharacteristic(
+                 client_id, conn_id, charac.value_handle,
+                 PacsClientImpl::OnReadOnlyPropertiesReadStatic, nullptr);
+
+        if (info.src_pac_ccc_handle) {
+          RegisterForNotification(client_id, conn_id, dev,
+                                  info.src_pac_ccc_handle,
+                                  info.src_pac_handle);
+        }
+
+      } else if (charac.uuid == PACS_SRC_LOC_UUID) {
+        LOG(INFO) << __func__ << ": src loc uuid found. ";
+        dev->src_loc_handle = charac.value_handle;
+
+        GattOpsQueue::ReadCharacteristic(
+                 client_id, conn_id, charac.value_handle,
+                 PacsClientImpl::OnReadOnlyPropertiesReadStatic, nullptr);
+
+         dev->src_loc_ccc_handle =
+             find_ccc_handle(conn_id, charac.value_handle);
+        dev->chars_to_be_read |= SRC_LOC;
+        if (dev->src_loc_ccc_handle) {
+          RegisterForNotification(client_id, conn_id, dev,
+                                  dev->src_loc_ccc_handle,
+                                  dev->src_loc_handle);
+        }
+
+      } else if (charac.uuid == PACS_AVA_AUDIO_UUID) {
+        LOG(INFO) << __func__ << ": avaliable audio uuid found. ";
+        dev->avail_contexts_handle = charac.value_handle;
+
+        GattOpsQueue::ReadCharacteristic(
+                 client_id, conn_id, charac.value_handle,
+                 PacsClientImpl::OnReadOnlyPropertiesReadStatic, nullptr);
+
+         dev->avail_contexts_ccc_handle =
+             find_ccc_handle(conn_id, charac.value_handle);
+        dev->chars_to_be_read |= AVAIL_CONTEXTS;
+        if (dev->avail_contexts_ccc_handle) {
+          RegisterForNotification(client_id, conn_id, dev,
+                                  dev->avail_contexts_ccc_handle,
+                                  dev->avail_contexts_handle);
+        }
+
+      } else if (charac.uuid == PACS_SUP_AUDIO_UUID) {
+        LOG(INFO) << __func__ << ": supported audio uuid found. ";
+        dev->supp_contexts_handle = charac.value_handle;
+
+        GattOpsQueue::ReadCharacteristic(
+                 client_id, conn_id, charac.value_handle,
+                 PacsClientImpl::OnReadOnlyPropertiesReadStatic, nullptr);
+
+        dev->supp_contexts_ccc_handle =
+            find_ccc_handle(conn_id, charac.value_handle);
+        dev->chars_to_be_read |= SUPP_CONTEXTS;
+        if (dev->supp_contexts_ccc_handle) {
+          RegisterForNotification(client_id, conn_id, dev,
+                                  dev->supp_contexts_ccc_handle,
+                                  dev->supp_contexts_handle);
+        }
+      } else {
+         LOG(WARNING) << "Unknown characteristic found:" << charac.uuid;
+      }
+    }
+
+    dev->notifications_enabled = true;
+
+    LOG(INFO) << __func__
+              << ": service_changed_rcvd: " << dev->service_changed_rcvd;
+    if (dev->service_changed_rcvd) {
+      dev->service_changed_rcvd = false;
+    }
+  }
+
+  void OnNotificationEvent(uint16_t conn_id, uint16_t handle, uint16_t len,
+                           uint8_t* value) {
+
+    PacsDevice* dev = pacsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(INFO) << __func__
+                << ": Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    LOG(WARNING) << __func__ << ": conn_id: " << loghex(conn_id);
+
+    if(dev->avail_contexts_handle == handle) {
+      uint8_t* p = value;
+      STREAM_TO_UINT32(dev->available_contexts, p);
+    }
+  }
+
+  void OnCongestionEvent(uint16_t conn_id, bool congested) {
+    PacsDevice* dev = pacsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(INFO) << __func__
+                << ": Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    LOG(WARNING) << __func__ << ": conn_id=" << loghex(conn_id)
+                             << ", congested: " << congested;
+    dev->is_congested = congested;
+    GattOpsQueue::CongestionCallback(conn_id, congested);
+  }
+
+  void OnReadAvailableAudio(uint16_t client_id,
+                                uint16_t conn_id, tGATT_STATUS status,
+                                uint16_t handle, uint16_t len, uint8_t* value,
+                                void* data) {
+    PacsDevice* dev = pacsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(ERROR) << __func__ << ": unknown conn_id: " << loghex(conn_id);
+      return;
+    }
+
+    LOG(WARNING) << __func__ << ": conn_id: " << loghex(conn_id);
+
+    if(dev->avail_contexts_handle == handle) {
+      uint8_t* p = value;
+      STREAM_TO_UINT32(dev->available_contexts, p);
+      // check if all pacs characteristics are read
+      // send out the callback as service discovery completed
+      // get the callback and update the upper layers
+      auto iter = callbacks.find(client_id);
+      if (iter != callbacks.end()) {
+        PacsClientCallbacks *callback = iter->second;
+        callback->OnAudioContextAvailable(dev->address,
+                                          dev->available_contexts);
+      }
+    }
+  }
+
+  bool IsRecordReadable(uint16_t total_len, uint16_t processed_len,
+                        uint16_t req_len) {
+    LOG(WARNING) << __func__ << ": processed_len: " << loghex(processed_len)
+                             << ", req_len: " << loghex(req_len);
+    if((total_len > processed_len) &&
+       ((total_len - processed_len) >= req_len)) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  bool IsLtvValid(uint8_t ltv_type, uint16_t ltv_len) {
+    bool valid = true;
+    for (auto it : ltv_info) {
+      if(ltv_type == it.first &&
+         ltv_len != it.second) {
+        valid = false;
+        break;
+      }
+    }
+    return valid;
+  }
+
+  void ParsePacRecord (PacsDevice *dev, uint16_t handle, uint16_t total_len,
+                       uint8_t *value, void* data) {
+    std::vector<CodecConfig> pac_records;
+    CodecIndex codec_type;
+    uint8_t *p = value;
+    codec_type_t codec_id;
+    bool stop_reading = false;
+    uint8_t codec_cap_len;
+    std::vector<uint8_t> codec_caps;
+    uint8_t meta_data_len;
+    std::vector<uint8_t> meta_data;
+    uint16_t processed_len = 0;
+    uint8_t num_pac_recs;
+    uint16_t context_type;
+
+    SinkPacsData* sinkinfo = FindSinkByHandle(dev, handle);
+    SrcPacsData* srcinfo = FindSrcByHandle(dev, handle);
+
+    // Number_of_PAC_records is 1 byte
+    if (!total_len) {
+      LOG(ERROR) << __func__
+                 << ": zero len record, total_len: ";
+      return;
+    }
+
+    STREAM_TO_UINT8(num_pac_recs, p);
+    processed_len ++;
+
+    LOG(WARNING) << __func__ << ": num_pac_recs: " << loghex(num_pac_recs)
+                             << ", total_len: " << loghex(total_len);
+    while (!stop_reading && num_pac_recs) {
+      // reset context type for before reading record
+      context_type = ucast::CONTENT_TYPE_UNSPECIFIED;
+      // read the complete record
+      // codec_id is of 5 bytes.
+      if (!IsRecordReadable(total_len, processed_len, sizeof(codec_id))) {
+        LOG(ERROR) << __func__ << ": Not valid codec id, Bad pacs record.";
+        break;
+      }
+
+      STREAM_TO_ARRAY(&codec_id, p, static_cast<int> (sizeof(codec_id)));
+
+      processed_len += static_cast<int> (sizeof(codec_id));
+
+      if (codec_id[0] == CODEC_ID_LC3) {
+        LOG(INFO) << __func__ << ": LC3 codec ";
+        codec_type = CodecIndex::CODEC_INDEX_SOURCE_LC3;
+      } else {
+        // TODO to check for vendor codecs
+        break;
+      }
+
+      // codec_cap_len is of 1 byte
+      if (!IsRecordReadable(total_len, processed_len, 1)) {
+        LOG(ERROR) << __func__ << ": Not valid codec id, Bad pacs record.";
+        break;
+      }
+
+      STREAM_TO_UINT8(codec_cap_len, p);
+      processed_len ++;
+
+      LOG(WARNING) << __func__
+                   << ": codec_cap_len: " << loghex(codec_cap_len)
+                   << ": processed_len: " <<  loghex(processed_len);
+
+      if (!codec_cap_len) {
+        LOG(ERROR) << __func__
+                   << ": Invalid codec cap len";
+        break;
+      }
+
+      if (!IsRecordReadable(total_len, processed_len, codec_cap_len)) {
+        LOG(ERROR) << __func__ << ": not enough data, Bad pacs record.";
+        break;
+      }
+
+      codec_caps.resize(codec_cap_len);
+      STREAM_TO_ARRAY(codec_caps.data(), p, codec_cap_len);
+      uint8_t len = codec_cap_len;
+      uint8_t *pp = codec_caps.data();
+
+      // Now look for supported freq LTV entry
+      while (len) {
+        LOG(WARNING) << __func__ << ": len: " << loghex(len);
+
+        if (!IsRecordReadable(total_len, processed_len, 1)) {
+          LOG(ERROR) << __func__ << ": not enough data, Bad pacs record.";
+          break;
+        }
+        uint8_t ltv_len = *pp++;
+        len--;
+        processed_len++;
+
+        LOG(WARNING) << __func__ << ": ltv_len: " << loghex(ltv_len);
+        if (!ltv_len ||
+            !IsRecordReadable(total_len, processed_len, ltv_len)) {
+          LOG(ERROR) << __func__ << ": Not valid ltv length";
+          stop_reading = true;
+          break;
+        }
+
+        processed_len += ltv_len;
+
+        // get type and value
+        uint8_t ltv_type = *pp++;
+        LOG(WARNING) << __func__ << ": ltv_type: " << loghex(ltv_type);
+        if(!IsLtvValid(ltv_type, ltv_len)) {
+          LOG(ERROR) << __func__ << ": No ltv type to length match";
+          stop_reading = true;
+          break;
+        }
+        if(ltv_type == LTV_TYPE_SUP_FREQS) {
+          uint16_t supp_freqs;
+          STREAM_TO_UINT16(supp_freqs, pp);
+          LOG(WARNING) << __func__ << ": supp_freqs: " << supp_freqs;
+
+          for (auto it : freq_map) {
+            if(supp_freqs & it.first) {
+              CodecConfig codec_config;
+              codec_config.codec_type = codec_type;
+              codec_config.sample_rate = it.second;
+              pac_records.push_back(codec_config);
+            }
+          }
+        } else {
+          uint8_t rem_len = ltv_len - 1;
+          LOG(WARNING) << __func__ << ": rem_len: " << loghex(rem_len);
+          while (rem_len--) { pp++; };
+        }
+
+        if (len >= ltv_len) {
+          len -= ltv_len;
+        } else {
+          LOG(ERROR) << __func__ << "wrong len";
+          len = 0;
+        }
+      }
+
+      LOG(WARNING) << __func__ << ": stop_reading: " << stop_reading;
+      if (stop_reading) break;
+
+      // set the default chnl count to mono as it is optional
+      for (auto it = pac_records.begin(); it != pac_records.end(); ++it) {
+        it->channel_mode = CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+      }
+
+      // now check for other LTV values
+      len = codec_cap_len;
+      pp = codec_caps.data();
+      while (len) {
+        LOG(WARNING) << __func__
+                     << ": checking other LTV values,len: " << loghex(len);
+        uint8_t ltv_len = *pp++;
+        len--;
+        LOG(WARNING) << __func__ << ": ltv_len: " << loghex(ltv_len);
+
+        //get type and value
+        uint8_t ltv_type = *pp++;
+        LOG(WARNING) << __func__ << ": ltv_type: " << loghex(ltv_type);
+        if(ltv_type == LTV_TYPE_SUP_FRAME_DUR) {
+          uint8_t supp_frames;
+          STREAM_TO_UINT8(supp_frames, pp);
+          LOG(WARNING) << __func__
+                       << ": pac rec len: " << loghex(pac_records.size());
+          for (auto it = pac_records.begin(); it != pac_records.end();
+                                              ++it) {
+            UpdateCapaSupFrameDurations(&(*it), supp_frames);
+          }
+        } else if (ltv_type == LTV_TYPE_CHNL_COUNTS) {
+          uint8_t chnl_allocation;
+          STREAM_TO_UINT8(chnl_allocation, pp);
+          for (auto it = pac_records.begin(); it != pac_records.end(); ++it) {
+            it->channel_mode =
+                       static_cast<CodecChannelMode> (chnl_allocation);
+          }
+        } else if (ltv_type == LTV_TYPE_OCTS_PER_FRAME) {
+          uint32_t octs_per_frame;
+          STREAM_TO_UINT32(octs_per_frame, pp);
+          for (auto it = pac_records.begin(); it != pac_records.end(); ++it) {
+            UpdateCapaSupOctsPerFrame(&(*it), octs_per_frame);
+          }
+        } else if (ltv_type == LTV_TYPE_MAX_SUP_FRAMES_PER_SDU) {
+          uint32_t max_sup_frames_per_sdu;
+          STREAM_TO_UINT32(max_sup_frames_per_sdu, pp);
+          for (auto it = pac_records.begin(); it != pac_records.end(); ++it) {
+            UpdateCapaMaxSupLc3Frames(&(*it), max_sup_frames_per_sdu);
+          }
+        } else {
+          uint8_t rem_len = ltv_len - 1;
+          LOG(WARNING) << __func__ << ": rem_len: " << loghex(rem_len);
+          while (rem_len--) { pp++;};
+        }
+
+        if(len >= ltv_len) {
+          len -= ltv_len;
+        } else {
+          LOG(ERROR) << __func__ << ": wrong len";
+          len = 0;
+        }
+      }
+
+      //Meta data length 1 byte
+      if (!IsRecordReadable(total_len, processed_len, 1)) {
+        LOG(ERROR) << __func__ << ": Not valid meta data len, Bad pacs record.";
+        break;
+      }
+
+      STREAM_TO_UINT8(meta_data_len, p);
+      processed_len ++;
+      LOG(WARNING) << __func__ << ": meta_data_len: " << loghex(meta_data_len)
+                               << ": processed_len: " << loghex(processed_len);
+
+      if (meta_data_len) {
+        if (!IsRecordReadable(total_len, processed_len, meta_data_len)) {
+          LOG(ERROR) << __func__ << ": not enough data, Bad pacs record.";
+          break;
+        }
+
+        meta_data.resize(meta_data_len);
+        STREAM_TO_ARRAY(meta_data.data(), p, meta_data_len);
+        uint8_t len = meta_data_len;
+        uint8_t *pp = meta_data.data();
+
+        while (len) {
+          LOG(WARNING) << __func__ << ": len: " << loghex(len);
+          uint8_t ltv_len = *pp++;
+          len--;
+          processed_len++;
+
+          LOG(WARNING) << __func__ << ": ltv_len: " << loghex(ltv_len);
+          if (!ltv_len ||
+              !IsRecordReadable(total_len, processed_len, ltv_len)) {
+            LOG(ERROR) << __func__ << ": Not valid ltv length";
+            stop_reading = true;
+            break;
+          }
+
+          processed_len += ltv_len;
+
+          // get type and value
+          uint8_t ltv_type = *pp++;
+
+          LOG(WARNING) << __func__ << ": ltv_type: " << loghex(ltv_type);
+
+          if (!IsLtvValid(ltv_type, ltv_len)) {
+            LOG(ERROR) << __func__ << ": No ltv type to length match";
+            stop_reading = true;
+            break;
+          }
+
+          if (ltv_type == LTV_TYPE_PREF_AUD_CONTEXT) {
+            STREAM_TO_UINT16(context_type, pp);
+            LOG(WARNING) << __func__
+                         << ": ltv_context_type: " << loghex(context_type);
+
+            for (auto it = pac_records.begin(); it != pac_records.end(); ++it) {
+               UpdateCapaPreferredContexts(&(*it), context_type);
+            }
+          } else if (ltv_type == LTV_TYPE_VS_META_DATA) {
+            uint16_t company_id;
+            STREAM_TO_UINT16(company_id, pp);
+
+            //total vs meta data length(meta length -4) in bytes.
+            uint8_t total_vendor_ltv_len = meta_data_len - 4;
+            LOG(WARNING) << __func__
+                         << ": total_vendor_ltv_len: " << loghex(total_vendor_ltv_len);
+
+            if (company_id == QTI_ID) {
+              while (total_vendor_ltv_len) {
+                uint8_t vs_meta_data_len = *pp++;
+                LOG(WARNING) << __func__
+                             << ": vs_meta_data_len: " << loghex(vs_meta_data_len);
+
+                // get type and value
+                uint8_t vs_meta_data_type = *pp++;
+                LOG(WARNING) << __func__
+                             << ": vs_meta_data_type: " << loghex(vs_meta_data_type);
+
+                if (vs_meta_data_type == LTV_TYPE_VS_META_DATA_LC3Q) {
+                  uint8_t vs_meta_data_value[vs_meta_data_len - 1];
+                  STREAM_TO_ARRAY(&vs_meta_data_value, pp,
+                                  static_cast<int> (sizeof(vs_meta_data_value)));
+
+                  for (auto it = pac_records.begin(); it != pac_records.end(); ++it) {
+                    UpdateCapaVendorMetaDataLc3QPref(&(*it), true);
+                    UpdateCapaVendorMetaDataLc3QVer(&(*it), vs_meta_data_value[0]);
+                  }
+                } else {
+                  //TODO check for other ltvs
+                  uint8_t rem_len = vs_meta_data_len - 1;
+                  LOG(WARNING) << __func__ << ": rem_len: " << loghex(rem_len);
+                  while (rem_len--) { pp++;};
+                }
+
+                /* 5bytes (VS length bypte + Meta datatype +
+                           company ID(2 bytes) + Lc3q length) */
+                if(total_vendor_ltv_len >= (vs_meta_data_len + 5)) {
+                  total_vendor_ltv_len -= (vs_meta_data_len + 5);
+                  len = total_vendor_ltv_len;
+                } else {
+                  LOG(ERROR) << __func__ << ": wrong len.";
+                  total_vendor_ltv_len = 0;
+                }
+              }
+            } else {
+              //TODO check for other comany IDs
+              uint8_t rem_len = total_vendor_ltv_len - 1;
+              LOG(WARNING) << __func__ << ": rem_len: " << loghex(rem_len);
+              while (rem_len--) { pp++;};
+            }
+          } else {
+            uint8_t rem_len = ltv_len - 1;
+            LOG(WARNING) << __func__ << ": rem_len: " << loghex(rem_len);
+            while (rem_len--) { pp++;};
+          }
+
+          if (len >= ltv_len) {
+            len -= ltv_len;
+          } else {
+            LOG(ERROR) << __func__ << ": wrong len";
+            len = 0;
+          }
+        }
+      }
+
+      if (sinkinfo != nullptr) {
+        // Now update all records to conf file
+        while (!pac_records.empty()) {
+          CodecConfig record = pac_records.back();
+          sinkinfo->sink_pac_records.push_back(record);
+          pac_records.pop_back();
+          btif_bap_add_record(dev->address, REC_TYPE_CAPABILITY,
+                              context_type, CodecDirection::CODEC_DIR_SINK,
+                              &record);
+        }
+      } else if (srcinfo != nullptr) {
+        // Now update all records to conf file
+        while (!pac_records.empty()) {
+          CodecConfig record = pac_records.back();
+          srcinfo->src_pac_records.push_back(record);
+          pac_records.pop_back();
+          btif_bap_add_record(dev->address, REC_TYPE_CAPABILITY,
+                              context_type, CodecDirection::CODEC_DIR_SRC,
+                              &record);
+        }
+      }
+      num_pac_recs--;
+    }
+
+    if (sinkinfo != nullptr) {
+      sinkinfo->read_sink_pac_record = true;
+      bool all_sink_pacs_read = false;
+      for (auto it = dev->sink_info.begin();
+                             it != dev->sink_info.end(); it ++) {
+        if (it->read_sink_pac_record == true) {
+          all_sink_pacs_read = true;
+          continue;
+        } else {
+          all_sink_pacs_read = false;
+          break;
+        }
+      }
+
+      LOG(WARNING) << __func__
+                   << ": all_sink_pacs_read: " << all_sink_pacs_read;
+      if (all_sink_pacs_read)
+        dev->chars_read |= SINK_PAC;
+
+    } else if (srcinfo != nullptr) {
+      srcinfo->read_src_pac_record = true;
+      bool all_source_pacs_read = false;
+      for (auto it = dev->src_info.begin();
+                             it != dev->src_info.end(); it ++) {
+        if (it->read_src_pac_record == true) {
+          all_source_pacs_read = true;
+          continue;
+        } else {
+          all_source_pacs_read = false;
+          break;
+        }
+      }
+
+      LOG(WARNING) << __func__
+                   << ": all_source_pacs_read: " << all_source_pacs_read;
+      if (all_source_pacs_read)
+        dev->chars_read |= SRC_PAC;
+    }
+  }
+
+  void OnReadOnlyPropertiesRead(uint16_t client_id, uint16_t conn_id,
+                                tGATT_STATUS status, uint16_t handle,
+                                uint16_t len, uint8_t *value, void* data) {
+    PacsDevice* dev = pacsDevices.FindByConnId(conn_id);
+    if (!dev) {
+      LOG(ERROR) << __func__ << "unknown conn_id=" << loghex(conn_id);
+      return;
+    }
+    SinkPacsData* sinkinfo = FindSinkByHandle(dev, handle);
+    SrcPacsData* srcinfo = FindSrcByHandle(dev, handle);
+
+    if (sinkinfo != nullptr || srcinfo != nullptr) {
+      ParsePacRecord(dev, handle, len, value, data);
+
+    } else if (dev->sink_loc_handle == handle) {
+      uint8_t *p = value;
+      STREAM_TO_UINT32(dev->sink_locations, p);
+      dev->chars_read |= SINK_LOC;
+      btif_bap_add_audio_loc(dev->address, CodecDirection::CODEC_DIR_SINK,
+                             dev->sink_locations);
+      LOG(WARNING) << __func__  << ": sink loc: " << loghex(dev->sink_locations);
+
+    } else if(dev->src_loc_handle == handle) {
+      uint8_t *p = value;
+      STREAM_TO_UINT32(dev->src_locations, p);
+      dev->chars_read |= SRC_LOC;
+      btif_bap_add_audio_loc(dev->address, CodecDirection::CODEC_DIR_SRC,
+                             dev->src_locations);
+      LOG(WARNING) << __func__  << ": src loc: " << loghex(dev->src_locations);
+
+    } else if(dev->avail_contexts_handle == handle) {
+      uint8_t* p = value;
+      STREAM_TO_UINT32(dev->available_contexts, p);
+      dev->chars_read |= AVAIL_CONTEXTS;
+
+    } else if(dev->supp_contexts_handle == handle) {
+      uint8_t* p = value;
+      STREAM_TO_UINT32(dev->supported_contexts, p);
+      dev->chars_read |= SUPP_CONTEXTS;
+      btif_bap_add_supp_contexts(dev->address, dev->supported_contexts);
+    }
+
+    LOG(WARNING) << __func__ << ": chars_read: " << loghex(dev->chars_read);
+
+    // check if all pacs characteristics are read
+    // send out the callback as service discovery completed
+    if (dev->chars_read == dev->chars_to_be_read) {
+
+      UpdateConsolidatedsinkPacRecords(dev);
+      UpdateConsolidatedsrcPacRecords(dev);
+
+      // get the callback and update the upper layers
+      auto iter = callbacks.find(client_id);
+      if (iter != callbacks.end()) {
+        dev->discovery_completed = true;
+        PacsClientCallbacks *callback = iter->second;
+        callback->OnSearchComplete(DISCOVER_SUCCESS,
+                                   dev->address,
+                                   dev->consolidated_sink_pac_records,
+                                   dev->consolidated_src_pac_records,
+                                   dev->sink_locations,
+                                   dev->src_locations,
+                                   dev->available_contexts,
+                                   dev->supported_contexts);
+      }
+    }
+  }
+
+  static void OnReadOnlyPropertiesReadStatic(uint16_t client_id,
+                                             uint16_t conn_id,
+                                             tGATT_STATUS status,
+                                             uint16_t handle, uint16_t len,
+                                             uint8_t* value, void* data) {
+    if (instance)
+      instance->OnReadOnlyPropertiesRead(client_id, conn_id, status, handle,
+                                         len, value, data);
+  }
+
+  static void OnReadAvailableAudioStatic(uint16_t client_id,
+                                             uint16_t conn_id,
+                                             tGATT_STATUS status,
+                                             uint16_t handle, uint16_t len,
+                                             uint8_t* value, void* data) {
+    if (instance)
+      instance->OnReadAvailableAudio(client_id, conn_id, status, handle,
+                                     len, value, data);
+  }
+
+
+ private:
+  uint8_t gatt_client_id = BTA_GATTS_INVALID_IF;
+  uint16_t pacs_client_id = 0;
+  PacsDevices pacsDevices;
+  // client id to callbacks mapping
+  std::map<uint16_t, PacsClientCallbacks *> callbacks;
+
+  void find_server_changed_ccc_handle(uint16_t conn_id,
+                                      const gatt::Service* service) {
+    PacsDevice* pacsDevice = pacsDevices.FindByConnId(conn_id);
+    if (!pacsDevice) {
+      LOG(ERROR) << __func__
+               << ": Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+    for (const gatt::Characteristic& charac : service->characteristics) {
+      if (charac.uuid == Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD)) {
+        pacsDevice->srv_changed_ccc_handle =
+            find_ccc_handle(conn_id, charac.value_handle);
+        if (!pacsDevice->srv_changed_ccc_handle) {
+          LOG(ERROR) << __func__
+                     << ": cannot find service changed CCC descriptor";
+          continue;
+        }
+        LOG(INFO) << __func__ << ": service_changed_ccc="
+                  << loghex(pacsDevice->srv_changed_ccc_handle);
+        break;
+      }
+    }
+  }
+
+  // Find the handle for the client characteristics configuration of a given
+  // characteristics
+  uint16_t find_ccc_handle(uint16_t conn_id, uint16_t char_handle) {
+    const gatt::Characteristic* p_char =
+        BTA_GATTC_GetCharacteristic(conn_id, char_handle);
+
+    if (!p_char) {
+      LOG(WARNING) << __func__ << ": No such characteristic: " << char_handle;
+      return 0;
+    }
+
+    for (const gatt::Descriptor& desc : p_char->descriptors) {
+      if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG))
+        return desc.handle;
+    }
+
+    return 0;
+  }
+
+  SinkPacsData *FindSinkByHandle(PacsDevice *dev, uint16_t handle) {
+    LOG(INFO) << __func__ << ": handle:" << loghex(handle);
+    auto iter = std::find_if(dev->sink_info.begin(),
+                             dev->sink_info.end(),
+                         [&handle](SinkPacsData data) {
+                           return (data.sink_pac_handle == handle);
+                         });
+
+    return (iter == dev->sink_info.end()) ? nullptr : &(*iter);
+  }
+
+  SrcPacsData *FindSrcByHandle(PacsDevice *dev, uint16_t handle) {
+    LOG(INFO) << __func__ << ": handle:" << loghex(handle);
+    auto iter = std::find_if(dev->src_info.begin(),
+                             dev->src_info.end(),
+                         [&handle](SrcPacsData data) {
+                           return (data.src_pac_handle == handle);
+                         });
+
+    return (iter == dev->src_info.end()) ? nullptr : &(*iter);
+  }
+
+  void UpdateConsolidatedsinkPacRecords(PacsDevice *dev) {
+    LOG(INFO) << __func__;
+    for (auto it = dev->sink_info.begin();
+                             it != dev->sink_info.end(); it ++) {
+      for (auto i = it->sink_pac_records.begin();
+                    i != it->sink_pac_records.end(); i ++) {
+        dev->consolidated_sink_pac_records.
+             push_back(static_cast<CodecConfig>(*i));
+      }
+    }
+  }
+
+  void UpdateConsolidatedsrcPacRecords(PacsDevice *dev) {
+    LOG(INFO) << __func__;
+    for (auto it = dev->src_info.begin();
+              it != dev->src_info.end(); it ++) {
+      for (auto i = it->src_pac_records.begin();
+                i != it->src_pac_records.end(); i ++) {
+        dev->consolidated_src_pac_records.
+             push_back(static_cast<CodecConfig>(*i));
+      }
+    }
+  }
+};
+
+const char* get_gatt_event_name(uint32_t event) {
+  switch (event) {
+    CASE_RETURN_STR(BTA_GATTC_DEREG_EVT)
+    CASE_RETURN_STR(BTA_GATTC_OPEN_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CLOSE_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SEARCH_CMPL_EVT)
+    CASE_RETURN_STR(BTA_GATTC_NOTIF_EVT)
+    CASE_RETURN_STR(BTA_GATTC_ENC_CMPL_CB_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CONN_UPDATE_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SRVC_CHG_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SRVC_DISC_DONE_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CONGEST_EVT)
+    default:
+      return "Unknown Event";
+  }
+}
+
+void pacs_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
+  if (p_data == nullptr || !instance) {
+    LOG(ERROR) << __func__ << ": p_data is null or no instance, return";
+    return;
+  }
+  LOG(INFO) << __func__ << ": Event : " << get_gatt_event_name(event);
+
+  switch (event) {
+    case BTA_GATTC_DEREG_EVT:
+      break;
+
+    case BTA_GATTC_OPEN_EVT: {
+      tBTA_GATTC_OPEN& o = p_data->open;
+      instance->OnGattConnected(o.status, o.conn_id, o.client_if, o.remote_bda,
+                                o.transport, o.mtu);
+      break;
+    }
+
+    case BTA_GATTC_CLOSE_EVT: {
+      tBTA_GATTC_CLOSE& c = p_data->close;
+      instance->OnGattDisconnected(c.status, c.conn_id, c.client_if,
+                                   c.remote_bda, c.reason);
+    } break;
+
+    case BTA_GATTC_SEARCH_CMPL_EVT:
+      instance->OnServiceSearchComplete(p_data->search_cmpl.conn_id,
+                                        p_data->search_cmpl.status);
+      break;
+
+    case BTA_GATTC_NOTIF_EVT:
+      if (!p_data->notify.is_notify || p_data->notify.len > GATT_MAX_ATTR_LEN) {
+        LOG(ERROR) << __func__ << ": rejected BTA_GATTC_NOTIF_EVT. is_notify="
+                   << p_data->notify.is_notify
+                   << ", len=" << p_data->notify.len;
+        break;
+      }
+      instance->OnNotificationEvent(p_data->notify.conn_id,
+                                    p_data->notify.handle, p_data->notify.len,
+                                    p_data->notify.value);
+      break;
+
+    case BTA_GATTC_ENC_CMPL_CB_EVT:
+      instance->OnEncryptionComplete(p_data->enc_cmpl.remote_bda, true);
+      break;
+
+    case BTA_GATTC_CONN_UPDATE_EVT:
+      instance->OnConnectionUpdateComplete(p_data->conn_update.conn_id,
+                                           p_data);
+      break;
+
+    case BTA_GATTC_SRVC_CHG_EVT:
+      instance->OnServiceChangeEvent(p_data->remote_bda);
+      break;
+
+    case BTA_GATTC_SRVC_DISC_DONE_EVT:
+      instance->OnServiceDiscDoneEvent(p_data->remote_bda);
+      break;
+    case BTA_GATTC_CONGEST_EVT:
+      instance->OnCongestionEvent(p_data->congest.conn_id,
+                                  p_data->congest.congested);
+      break;
+    default:
+      break;
+  }
+}
+
+void encryption_callback(const RawAddress* address, tGATT_TRANSPORT, void*,
+                         tBTM_STATUS status) {
+  if (instance) {
+    instance->OnEncryptionComplete(*address,
+                                   status == BTM_SUCCESS ? true : false);
+  }
+}
+
+void PacsClient::Initialize(PacsClientCallbacks* callbacks) {
+  if (instance) {
+    instance->Register(callbacks);
+  } else {
+    instance = new PacsClientImpl();
+    instance->Register(callbacks);
+  }
+}
+
+void PacsClient::CleanUp(uint16_t client_id) {
+  if(instance->GetClientCount()) {
+    instance->Deregister(client_id);
+    if(!instance->GetClientCount()) {
+      delete instance;
+      instance = nullptr;
+    }
+  }
+}
+
+PacsClient* PacsClient::Get() {
+  CHECK(instance);
+  return instance;
+}
+
+}  // namespace pacs
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/bap/ucast_client_int.h b/le_audio/system/bt/bta/bap/ucast_client_int.h
new file mode 100644
index 0000000..2d535fa
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/ucast_client_int.h
@@ -0,0 +1,1276 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+
+#pragma once
+
+#include <string>
+#include "state_machine.h"
+#include <list>
+#include "bta_bap_uclient_api.h"
+#include "bta_pacs_client_api.h"
+#include "bta_ascs_client_api.h"
+#include "bt_trace.h"
+#include "uclient_alarm.h"
+#include "btif_util.h"
+
+namespace bluetooth {
+namespace bap {
+namespace ucast {
+
+using bluetooth::bap::pacs::ConnectionState;
+using bluetooth::bap::pacs::PacsClient;
+using bluetooth::bap::ascs::GattState;
+using bluetooth::bap::ascs::AscsClient;
+using bluetooth::bap::ascs::AseParams;
+using bluetooth::bap::ascs::AseCodecConfigParams;
+using bluetooth::bap::ascs::AseCodecConfigOp;
+using bluetooth::bap::cis::CisInterface;
+
+using bluetooth::bap::cis::CigState;
+using bluetooth::bap::cis::CisState;
+
+using bluetooth::bap::alarm::BapAlarm;
+using bluetooth::bap::alarm::BapAlarmCallbacks;
+
+class UstreamManager;
+class UstreamManagers;
+struct UcastAudioStream;
+class UcastAudioStreams;
+class StreamContexts;
+struct StreamContext;
+class StreamTracker;
+
+enum class StreamAttachedState {
+  IDLE        = 0x1 << 0,
+  IDLE_TO_PHY = 0x1 << 1,
+  VIRTUAL     = 0x1 << 2,
+  VIR_TO_PHY  = 0x1 << 3,
+  PHYSICAL    = 0x1 << 4
+};
+
+enum class StreamControlType {
+  None         = 0x00,
+  Connect      = 0X01,
+  Disconnect   = 0x02,
+  Start        = 0x04,
+  Stop         = 0x08,
+  Reconfig     = 0x10,
+  UpdateStream = 0x20
+};
+
+enum class DeviceType {
+  NONE                  = 0x00,
+  EARBUD                = 0X01,  // group member
+  HEADSET_STEREO        = 0x02,  // headset with 1 CIS
+  HEADSET_SPLIT_STEREO  = 0x03   // headset with 2 CIS
+};
+
+enum class IntConnectState {
+  IDLE             = 0x00,
+  PACS_CONNECTING  = 0x01,
+  PACS_DISCOVERING = 0X02,
+  ASCS_CONNECTING  = 0x03,
+  ASCS_DISCOVERING = 0x04,
+  ASCS_DISCOVERED  = 0x05,
+};
+
+enum class AscsPendingCmd {
+  NONE                      = 0x00,
+  CODEC_CONFIG_ISSUED       = 0x01,
+  QOS_CONFIG_ISSUED         = 0x02,
+  ENABLE_ISSUED             = 0x03,
+  START_READY_ISSUED        = 0x04,
+  DISABLE_ISSUED            = 0x05,
+  STOP_READY_ISSUED         = 0x06,
+  RELEASE_ISSUED            = 0x07,
+  UPDATE_METADATA_ISSUED    = 0x08
+};
+
+enum class CisPendingCmd {
+  NONE                      = 0x00,
+  CIG_CREATE_ISSUED         = 0x08,
+  CIS_CREATE_ISSUED         = 0x09,
+  CIS_SETUP_DATAPATH_ISSUED = 0x10,
+  CIS_RMV_DATAPATH_ISSUED   = 0x11,
+  CIS_DESTROY_ISSUED        = 0x12,
+  CIG_REMOVE_ISSUED         = 0x13
+};
+
+enum class GattPendingCmd {
+  NONE                      = 0x00,
+  GATT_CONN_PENDING         = 0x01,
+  GATT_DISC_PENDING         = 0x02
+};
+
+typedef enum {
+  BAP_CONNECT_REQ_EVT = 0X00,
+  BAP_DISCONNECT_REQ_EVT,
+  BAP_START_REQ_EVT,
+  BAP_STOP_REQ_EVT,
+  BAP_RECONFIG_REQ_EVT,
+  BAP_STREAM_UPDATE_REQ_EVT,
+  PACS_CONNECTION_STATE_EVT,
+  PACS_DISCOVERY_RES_EVT,
+  PACS_AUDIO_CONTEXT_RES_EVT,
+  ASCS_CONNECTION_STATE_EVT,
+  ASCS_DISCOVERY_RES_EVT,
+  ASCS_ASE_STATE_EVT,
+  ASCS_ASE_OP_FAILED_EVT,
+  CIS_GROUP_STATE_EVT,
+  CIS_STATE_EVT,
+  BAP_TIME_OUT_EVT,
+} BapEvent;
+
+struct BapConnect {
+  std::vector<RawAddress> bd_addr;
+  bool is_direct;
+  std::vector<StreamConnect> streams;
+};
+
+struct BapDisconnect {
+  RawAddress bd_addr;
+  std::vector<StreamType> streams;
+};
+
+struct BapStart {
+  RawAddress bd_addr;
+  std::vector<StreamType> streams;
+};
+
+struct BapStop {
+  RawAddress bd_addr;
+  std::vector<StreamType> streams;
+};
+
+struct BapReconfig {
+  RawAddress bd_addr;
+  std::vector<StreamReconfig> streams;
+};
+
+struct BapStreamUpdate {
+  RawAddress bd_addr;
+  std::vector<StreamUpdate> update_streams;
+};
+
+struct PacsConnectionState {
+  RawAddress bd_addr;
+  ConnectionState state;
+};
+
+struct AscsConnectionState {
+  RawAddress bd_addr;
+  GattState state;
+};
+
+struct AscsDiscovery {
+  int status;
+  RawAddress bd_addr;
+  std::vector<AseParams> sink_ases_list;
+  std::vector<AseParams> src_ases_list;
+};
+
+struct AscsState {
+  RawAddress bd_addr;
+  AseParams ase_params;
+};
+
+struct AscsOpFailed {
+  RawAddress bd_addr;
+  ascs::AseOpId ase_op_id;
+  std::vector<ascs::AseOpStatus> ase_list;
+};
+
+struct CisGroupState {
+  uint8_t cig_id;
+  CigState state;
+};
+
+struct CisStreamState {
+  uint8_t cig_id;
+  uint8_t cis_id;
+  uint8_t direction;
+  CisState state;
+};
+
+struct PacsDiscovery {
+  int status;
+  RawAddress bd_addr;
+  std::vector<CodecConfig> sink_pac_records;
+  std::vector<CodecConfig> src_pac_records;
+  uint32_t sink_locations;
+  uint32_t src_locations;
+  uint32_t available_contexts;
+  uint32_t supported_contexts;
+};
+
+struct PacsAvailableContexts {
+  RawAddress bd_addr;
+  uint32_t available_contexts;
+};
+
+struct IntStrmTracker {
+ IntStrmTracker(StreamType strm_type, uint8_t ase_id, uint8_t cig_id,
+                uint8_t cis_id, CodecConfig &codec_config,
+                QosConfig &qos_config)
+     : strm_type(strm_type), ase_id(ase_id) , cig_id(cig_id) ,
+       cis_id(cis_id), codec_config(codec_config),
+       qos_config(qos_config) {
+        attached_state = StreamAttachedState::IDLE;
+       }
+  StreamType strm_type;
+  uint8_t ase_id;
+  uint8_t cig_id;
+  uint8_t cis_id;
+  CodecConfig codec_config;
+  QosConfig qos_config;
+  StreamAttachedState attached_state;
+};
+
+class IntStrmTrackers {
+ public:
+  std::vector<IntStrmTracker *> FindByCigId(uint8_t cig_id) {
+    std::vector<IntStrmTracker *> trackers;
+    for (auto i = int_strm_trackers.begin();
+                         i != int_strm_trackers.end();i++) {
+      if((*i)->cig_id  == cig_id) {
+        LOG(WARNING) << __func__ << " tracker found";
+        trackers.push_back(*i);
+      }
+    }
+    return trackers;
+  }
+
+  std::vector<IntStrmTracker *> FindByCigIdAndDir(uint8_t cig_id,
+                                                  uint8_t direction) {
+    std::vector<IntStrmTracker *> trackers;
+    for (auto i = int_strm_trackers.begin();
+              i != int_strm_trackers.end();i++) {
+      if((*i)->cig_id  == cig_id &&
+         (*i)->strm_type.direction  == direction) {
+        trackers.push_back(*i);
+      }
+    }
+    return trackers;
+  }
+
+  std::vector<IntStrmTracker *> FindByCisId(uint8_t cig_id, uint8_t cis_id) {
+    std::vector<IntStrmTracker *> trackers;
+    for (auto i = int_strm_trackers.begin();
+              i != int_strm_trackers.end();i++) {
+      if((*i)->cig_id  == cig_id && (*i)->cis_id  == cis_id) {
+        trackers.push_back(*i);
+      }
+    }
+    return trackers;
+  }
+
+  IntStrmTracker *FindByIndex(uint8_t i) {
+    IntStrmTracker *tracker = int_strm_trackers.at(i);
+    return tracker;
+  }
+
+  IntStrmTracker *FindByAseId(uint8_t ase_id) {
+    auto iter = std::find_if(int_strm_trackers.begin(), int_strm_trackers.end(),
+                         [&ase_id](IntStrmTracker *tracker) {
+                            return tracker->ase_id == ase_id;
+                         });
+
+    return (iter == int_strm_trackers.end()) ? nullptr : (*iter);
+  }
+
+  IntStrmTracker *FindOrAddBytrackerType(StreamType strm_type,
+                        uint8_t ase_id, uint8_t cig_id,  uint8_t cis_id,
+                        CodecConfig &codec_config, QosConfig &qos_config) {
+
+    auto iter = std::find_if(int_strm_trackers.begin(), int_strm_trackers.end(),
+                  [&strm_type, &cig_id, &cis_id](IntStrmTracker *tracker) {
+                     return ((tracker->strm_type.type == strm_type.type) &&
+                             (tracker->strm_type.direction ==
+                              strm_type.direction) &&
+                             (tracker->cig_id == cig_id) &&
+                             (tracker->cis_id == cis_id));
+                });
+
+    if (iter == int_strm_trackers.end()) {
+      IntStrmTracker *tracker = new IntStrmTracker(strm_type,
+                        ase_id, cig_id, cis_id, codec_config, qos_config);
+      int_strm_trackers.push_back(tracker);
+      return tracker;
+    } else {
+      return (*iter);
+    }
+  }
+
+  void Remove(StreamType strm_type, uint8_t cig_id,  uint8_t cis_id) {
+    for (auto it = int_strm_trackers.begin(); it != int_strm_trackers.end();) {
+      if (((*it)->strm_type.type = strm_type.type) &&
+          ((*it)->strm_type.direction = strm_type.direction) &&
+          ((*it)->cig_id = cig_id) && ((*it)->cis_id = cis_id)) {
+        delete(*it);
+        it = int_strm_trackers.erase(it);
+      } else {
+        it++;
+      }
+    }
+  }
+
+  void RemoveVirtualAttachedTrackers() {
+    LOG(WARNING) << __func__;
+    for (auto it = int_strm_trackers.begin(); it != int_strm_trackers.end();) {
+      if ((*it)->attached_state == StreamAttachedState::VIRTUAL) {
+        delete(*it);
+        it = int_strm_trackers.erase(it);
+        LOG(WARNING) << __func__
+                     << ": Removed virtual attached tracker";
+      } else {
+        it++;
+      }
+    }
+  }
+
+  size_t size() { return (int_strm_trackers.size()); }
+
+  std::vector<IntStrmTracker *> *GetTrackerList() {
+    return &int_strm_trackers;
+  }
+
+  std::vector<IntStrmTracker *> GetTrackerListByDir(uint8_t direction) {
+    std::vector<IntStrmTracker *> trackers;
+    for (auto i = int_strm_trackers.begin();
+              i != int_strm_trackers.end();i++) {
+      if((*i)->strm_type.direction == direction) {
+        trackers.push_back(*i);
+      }
+    }
+    return trackers;
+  }
+
+ private:
+  std::vector<IntStrmTracker *> int_strm_trackers;
+};
+
+union BapEventData {
+  BapConnect connect_req;
+  BapDisconnect disc_req;
+  BapStart start_req;
+  BapStop stop_req;
+  BapReconfig reconfig_req;
+  PacsConnectionState connection_state_rsp;
+  PacsDiscovery pacs_discovery_rsp;
+  PacsAvailableContexts pacs_audio_context_rsp;
+};
+
+enum class TimeoutVal { //in milli seconds(1sec = 1000ms)
+  ConnectingTimeout = 10000,
+  StartingTimeout = 2000,
+  StoppingTimeout = 2000,
+  DisconnectingTimeout = 1000,
+  ReconfiguringTimeout = 2000,
+  UpdatingTimeout = 1000
+};
+
+enum class MaxTimeoutVal { //in milli seconds(1sec = 1000ms)
+  ConnectingTimeout = 10000,
+  StartingTimeout = 4000,
+  StoppingTimeout = 4000,
+  DisconnectingTimeout = 4000,
+  ReconfiguringTimeout = 8000,
+  UpdatingTimeout = 4000
+};
+
+enum class TimeoutReason {
+  STATE_TRANSITION = 1,
+};
+
+struct BapTimeout {
+  RawAddress bd_addr;
+  StreamTracker* tracker;
+  TimeoutReason reason;
+  int transition_state;
+};
+
+class StreamTracker : public bluetooth::common::StateMachine {
+ public:
+  enum {
+    kStateIdle,     //
+    kStateConnecting,  //
+    kStateConnected,   //
+    kStateStarting,  //
+    kStateStreaming,  //
+    kStateStopping, //
+    kStateDisconnecting, //
+    kStateReconfiguring, //
+    kStateUpdating
+  };
+
+  class StateIdle : public State {
+   public:
+    StateIdle(StreamTracker& sm)
+        : State(sm, kStateIdle), tracker_(sm),
+          strm_mgr_(sm.GetStreamManager()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Idle"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    StreamTracker &tracker_;
+    UstreamManager *strm_mgr_;
+  };
+
+  class StateConnecting : public State {
+   public:
+    StateConnecting(StreamTracker& sm)
+        : State(sm, kStateConnecting), tracker_(sm),
+         strm_mgr_(sm.GetStreamManager()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Connecting"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+    void DeriveDeviceType(PacsDiscovery *pacs_discovery);
+    bool AttachStreamsToContext(std::vector<IntStrmTracker *> *all_trackers,
+                                std::vector<UcastAudioStream *> *streams,
+                                uint8_t cis_count,
+                                std::vector<AseCodecConfigOp> *ase_ops);
+    alarm_t* state_transition_timer;
+    BapTimeout timeout;
+
+   private:
+    StreamTracker &tracker_;
+    UstreamManager *strm_mgr_;
+    PacsDiscovery pacs_discovery_;
+    AscsDiscovery ascs_discovery_;
+    IntStrmTrackers int_strm_trackers_;
+   };
+
+  class StateConnected : public State {
+   public:
+    StateConnected(StreamTracker& sm)
+        : State(sm, kStateConnected), tracker_(sm),
+          strm_mgr_(sm.GetStreamManager()){}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Connected"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    StreamTracker &tracker_;
+    UstreamManager *strm_mgr_;
+  };
+
+  class StateStarting : public State {
+   public:
+    StateStarting(StreamTracker& sm)
+        : State(sm, kStateStarting), tracker_(sm),
+          strm_mgr_(sm.GetStreamManager()){}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Starting"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+    bool CheckAndUpdateStreamingState();
+    alarm_t* state_transition_timer;
+    PacsAvailableContexts pacs_contexts;
+    BapTimeout timeout;
+
+   private:
+    StreamTracker &tracker_;
+    UstreamManager *strm_mgr_;
+    IntStrmTrackers int_strm_trackers_;
+  };
+
+  class StateStreaming : public State {
+   public:
+    StateStreaming(StreamTracker& sm)
+        : State(sm, kStateStreaming), tracker_(sm),
+          strm_mgr_(sm.GetStreamManager()){}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Streaming"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    StreamTracker &tracker_;
+    UstreamManager *strm_mgr_;
+  };
+
+  class StateStopping : public State {
+   public:
+    StateStopping(StreamTracker& sm)
+        : State(sm, kStateStopping), tracker_(sm),
+          strm_mgr_(sm.GetStreamManager()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Stopping"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+    bool TerminateCisAndCig(UcastAudioStream *stream);
+    bool CheckAndUpdateStoppedState();
+    alarm_t* state_transition_timer;
+    BapTimeout timeout;
+
+   private:
+    StreamTracker &tracker_;
+    UstreamManager *strm_mgr_;
+    IntStrmTrackers int_strm_trackers_;
+  };
+
+  class StateDisconnecting : public State {
+   public:
+    StateDisconnecting(StreamTracker& sm)
+        : State(sm, kStateDisconnecting), tracker_(sm),
+          strm_mgr_(sm.GetStreamManager()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Disconnecting"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+    bool TerminateGattConnection();
+    void ContinueDisconnection(UcastAudioStream *stream);
+    bool CheckAndUpdateDisconnectedState();
+    alarm_t* state_transition_timer;
+    BapTimeout timeout;
+
+   private:
+    StreamTracker &tracker_;
+    UstreamManager *strm_mgr_;
+    IntStrmTrackers int_strm_trackers_;
+  };
+
+  class StateReconfiguring: public State {
+   public:
+    StateReconfiguring(StreamTracker& sm)
+        : State(sm, kStateReconfiguring), tracker_(sm),
+          strm_mgr_(sm.GetStreamManager()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Reconfiguring"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+    alarm_t* state_transition_timer;
+    BapTimeout timeout;
+
+   private:
+    StreamTracker &tracker_;
+    UstreamManager *strm_mgr_;
+    IntStrmTrackers int_strm_trackers_;
+  };
+
+  class StateUpdating: public State {
+   public:
+    StateUpdating(StreamTracker& sm)
+        : State(sm, kStateUpdating), tracker_(sm),
+          strm_mgr_(sm.GetStreamManager()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    const char* GetState() { return "Updating"; }
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+    alarm_t* state_transition_timer;
+    BapTimeout timeout;
+
+   private:
+    StreamTracker &tracker_;
+    UstreamManager *strm_mgr_;
+    IntStrmTrackers int_strm_trackers_;
+  };
+
+  StreamTracker(int init_state_id, UstreamManager *strm_mgr,
+                std::vector<StreamConnect> *connect_streams,
+                std::vector<StreamReconfig> *reconfig_streams,
+                std::vector<StreamType> *streams,
+                StreamControlType ops_type):
+                    init_state_id_(init_state_id),
+                    strm_mgr_(strm_mgr) {
+
+    state_idle_ = new StateIdle(*this);
+    state_connecting_ = new StateConnecting(*this);
+    state_connected_ = new StateConnected(*this);
+    state_starting_ = new StateStarting(*this);
+    state_streaming_ = new StateStreaming(*this);
+    state_stopping_ = new StateStopping(*this);
+    state_disconnecting_ = new StateDisconnecting(*this);
+    state_reconfiguring_ = new StateReconfiguring(*this);
+    state_updating_ = new StateUpdating(*this);
+    pacs_disc_succeded_ = false;
+
+    AddState(state_idle_);
+    AddState(state_connecting_);
+    AddState(state_connected_);
+    AddState(state_starting_);
+    AddState(state_streaming_);
+    AddState(state_stopping_);
+    AddState(state_disconnecting_);
+    AddState(state_reconfiguring_);
+    AddState(state_updating_);
+
+    switch(init_state_id) {
+      case kStateIdle:
+        SetInitialState(state_idle_);
+        break;
+      case kStateConnected:
+        SetInitialState(state_connected_);
+        break;
+      case kStateStreaming:
+        SetInitialState(state_streaming_);
+        break;
+      case kStateDisconnecting:
+        SetInitialState(state_disconnecting_);
+        break;
+      default:
+        SetInitialState(state_idle_);
+    }
+
+    str_ops_type = ops_type;
+
+    if(ops_type == StreamControlType::Connect) {
+      conn_streams = *connect_streams;
+    } else if(ops_type == StreamControlType::Reconfig) {
+      reconf_streams = *reconfig_streams;
+    } else if(ops_type != StreamControlType::UpdateStream) {
+      other_streams = *streams;
+    }
+  }
+
+  void PauseRemoteDevInteraction(bool pause);
+  bool decoupleStream(StreamType *stream_info);
+
+  uint8_t ChooseBestCodec(StreamType stream_type,
+                              std::vector<CodecQosConfig> *codec_qos_configs,
+                              PacsDiscovery *pacs_discovery);
+
+  bool ChooseBestQos(QosConfig *src_config,
+                     ascs::AseCodecConfigParams *rem_qos_prefs,
+                     QosConfig *dst_config,
+                     int stream_state, uint8_t stream_direction);
+
+  bool HandlePacsConnectionEvent(void *p_data);
+
+  bool HandlePacsAudioContextEvent(PacsAvailableContexts *pacs_contexts);
+
+  bool HandleCisEventsInStreaming(void* p_data);
+
+  bool HandleStreamUpdate (int cur_state);
+
+  bool CheckAndUpdateStreamingState(IntStrmTrackers *int_strm_trackers);
+
+  bool HandleAscsConnectionEvent(void *p_data);
+
+  void HandleCigStateEvent(uint32_t event, void *p_data,
+                           IntStrmTrackers *int_strm_trackers);
+
+  void HandleAseStateEvent(void *p_data, StreamControlType control_type,
+                           IntStrmTrackers *int_strm_trackers);
+
+  void HandleAseOpFailedEvent(void *p_data);
+
+  bool ValidateAseUpdate(void* p_data, IntStrmTrackers *int_strm_trackers,
+                         int exp_strm_state);
+
+  bool HandleDisconnect(void* p_data, int cur_state);
+
+  bool HandleRemoteDisconnect(uint32_t event, void* p_data, int cur_state);
+
+  bool StreamCanbeDisconnected(StreamContext *cur_context, uint8_t ase_id);
+
+  bool HandleInternalDisconnect(bool release);
+
+  bool HandleStop(void* p_data, int cur_state);
+
+  bool HandleRemoteStop(uint32_t event, void* p_data, int cur_state);
+
+  bool HandleRemoteReconfig(uint32_t event, void* p_data, int cur_state);
+
+  bool PrepareCodecConfigPayload(std::vector<AseCodecConfigOp> *ase_ops,
+                                 UcastAudioStream *stream);
+
+  void CheckAndSendQosConfig(IntStrmTrackers *int_strm_trackers);
+
+  void CheckAndSendEnable(IntStrmTrackers *int_strm_trackers);
+
+  bool HandleAbruptStop(uint32_t event, void* p_data);
+
+  alarm_t* SetTimer(const char* alarmname, BapTimeout* timeout,
+                                           TimeoutReason reason, uint64_t ms);
+
+  void ClearTimer(alarm_t* timer, const char* alarmname);
+
+  void OnTimeout(void* data);
+
+  StreamControlType GetControlType() {
+    return str_ops_type;
+  }
+
+  UstreamManager *GetStreamManager() {
+    return strm_mgr_;
+  }
+
+  bool UpdatePacsDiscovery(PacsDiscovery disc_res) {
+    pacs_disc_succeded_ = true;
+    pacs_discovery_ = disc_res;
+    return true;
+  }
+
+  PacsDiscovery *GetPacsDiscovery() {
+    if(pacs_disc_succeded_) {
+      return &pacs_discovery_;
+    } else {
+      return nullptr;
+    }
+  }
+
+  bool UpdateControlType(StreamControlType ops_type) {
+    str_ops_type = ops_type;
+    return true;
+  }
+
+  bool UpdateStreams(std::vector<StreamType> *streams) {
+    other_streams = *streams;
+    return true;
+  }
+
+  bool UpdateConnStreams(
+          std::vector<StreamConnect> *connect_streams) {
+    conn_streams = *connect_streams;
+    return true;
+  }
+
+  bool UpdateReconfStreams(
+         std::vector<StreamReconfig> *reconfig_streams) {
+    reconf_streams = *reconfig_streams;
+    return true;
+  }
+
+  bool UpdateMetaUpdateStreams(
+         std::vector<StreamUpdate> *meta_streams) {
+    meta_update_streams = *meta_streams;
+    return true;
+  }
+
+  std::vector<StreamType> *GetStreams() {
+    return &other_streams;
+  }
+  std::vector<StreamConnect> *GetConnStreams() {
+    return &conn_streams;
+  }
+  std::vector<StreamReconfig> *GetReconfStreams() {
+    return &reconf_streams;
+  }
+
+  std::vector<StreamUpdate> *GetMetaUpdateStreams() {
+    return &meta_update_streams;
+  }
+
+  const char* GetEventName(uint32_t event) {
+    switch (event) {
+      CASE_RETURN_STR(BAP_CONNECT_REQ_EVT)
+      CASE_RETURN_STR(BAP_DISCONNECT_REQ_EVT)
+      CASE_RETURN_STR(BAP_START_REQ_EVT)
+      CASE_RETURN_STR(BAP_STOP_REQ_EVT)
+      CASE_RETURN_STR(BAP_RECONFIG_REQ_EVT)
+      CASE_RETURN_STR(BAP_STREAM_UPDATE_REQ_EVT)
+      CASE_RETURN_STR(PACS_CONNECTION_STATE_EVT)
+      CASE_RETURN_STR(PACS_DISCOVERY_RES_EVT)
+      CASE_RETURN_STR(PACS_AUDIO_CONTEXT_RES_EVT)
+      CASE_RETURN_STR(ASCS_CONNECTION_STATE_EVT)
+      CASE_RETURN_STR(ASCS_DISCOVERY_RES_EVT)
+      CASE_RETURN_STR(ASCS_ASE_STATE_EVT)
+      CASE_RETURN_STR(ASCS_ASE_OP_FAILED_EVT)
+      CASE_RETURN_STR(CIS_GROUP_STATE_EVT)
+      CASE_RETURN_STR(CIS_STATE_EVT)
+      CASE_RETURN_STR(BAP_TIME_OUT_EVT)
+      default:
+       return "Unknown Event";
+    }
+  }
+
+ private:
+  int init_state_id_;
+  UstreamManager *strm_mgr_;
+  std::vector<StreamConnect> conn_streams;
+  std::vector<StreamType> other_streams;
+  std::vector<StreamReconfig> reconf_streams;
+  std::vector<StreamUpdate> meta_update_streams;
+  StreamControlType str_ops_type;
+  bool pacs_disc_succeded_;
+  PacsDiscovery pacs_discovery_;
+  StateIdle *state_idle_;
+  StateConnecting *state_connecting_;
+  StateConnected *state_connected_;
+  StateStarting *state_starting_;
+  StateStreaming *state_streaming_;
+  StateStopping *state_stopping_;
+  StateDisconnecting *state_disconnecting_;
+  StateReconfiguring *state_reconfiguring_;
+  StateUpdating *state_updating_;
+};
+
+struct StreamOpConnect {
+  StreamType stream_type;
+  //std::vector<CodecConfig> codec_configs;
+  //std::vector<QosConfig> qos_configs;
+};
+
+struct StreamOpReconfig {
+  StreamType stream_type;
+  //std::vector<CodecConfig> codec_configs;
+  //std::vector<QosConfig> qos_configs;
+};
+
+union StreamOpData {
+  StreamOpConnect connect_op;
+  StreamType stream_type;
+  StreamOpReconfig reconfig_op;
+};
+
+struct StreamOpNode {
+  bool busy;
+  bool is_client_originated;
+  StreamControlType ops_type;
+  StreamOpData ops_data;
+};
+
+struct StreamIdType {
+  uint8_t ase_id;
+  uint8_t ase_direction;
+  bool virtual_attach;
+  uint8_t cig_id;
+  uint8_t cis_id;
+};
+
+struct StreamContext {
+ StreamContext(StreamType strm_type)
+     : stream_type(strm_type) {
+       stream_state = StreamState::DISCONNECTED;
+       attached_state = StreamAttachedState::IDLE;
+     }
+  StreamType stream_type;
+  StreamAttachedState attached_state;
+  std::vector<StreamIdType> stream_ids;
+  StreamState stream_state;
+  IntConnectState connection_state;
+  CodecConfig codec_config;
+  QosConfig qos_config;
+  QosConfig req_qos_config;
+};
+
+class StreamContexts {
+ public:
+
+  StreamContext *FindByType(StreamType stream_type);
+
+  StreamContext *FindOrAddByType(StreamType stream_type);
+
+  void Remove(StreamType stream_type);
+
+  bool IsAseAttached(StreamType stream_type);
+
+  std::vector<StreamContext *> FindByAseAttachedState(uint16_t ase_id,
+                           StreamAttachedState state);
+
+
+  StreamContext* FindByAseId(uint16_t ase_id);
+
+  std::vector<StreamContext *> *GetAllContexts() {
+    return &strm_contexts;
+  }
+
+  size_t size() { return (strm_contexts.size()); }
+
+ private:
+  std::vector<StreamContext *> strm_contexts;
+};
+
+
+class StreamOpsQueue {
+ public:
+  bool Add(StreamOpNode op_node);
+
+  bool AddFirst(StreamOpNode op_node);
+
+  StreamOpNode *GetNextNode();
+
+  bool Remove(StreamType stream_type);
+
+  StreamOpNode* FindByContext(StreamType stream_type);
+
+  bool ChangeOpType(StreamType stream_type, StreamControlType new_ops_type);
+
+  size_t size() { return (queue.size()); }
+
+  std::vector<StreamOpNode> queue;
+};
+
+class StreamTrackers {
+ public:
+  StreamTracker *FindOrAddByType(int init_state_id, UstreamManager *strm_mgr,
+                                 std::vector<StreamConnect> *connect_streams,
+                                 std::vector<StreamReconfig> *reconfig_streams,
+                                 std::vector<StreamType> *streams,
+                                 StreamControlType ops_type);
+
+  bool Remove(std::vector<StreamType> streams,
+              StreamControlType ops_type);
+
+  void RemoveByStates(std::vector<int> state_ids);
+
+  std::map<StreamTracker * , std::vector<StreamType> > GetTrackersByType(
+                                        std::vector<StreamType> *streams);
+
+  StreamTracker *FindByStreamsType(std::vector<StreamType> *streams);
+
+  std::vector<StreamTracker *> GetTrackersByStates(
+                                         std::vector<int> *state_ids);
+
+  bool ChangeOpType( StreamType stream_type,
+                     StreamControlType new_ops_type);
+
+  bool IsStreamTrackerValid(StreamTracker* Tracker,
+                                     std::vector<int> *state_ids);
+
+  size_t size() { return (stream_trackers.size()); }
+
+  std::vector<StreamTracker *> stream_trackers;
+};
+
+struct UcastAudioStream {
+  UcastAudioStream(uint8_t ase_id, uint8_t ase_state, uint8_t ase_direction)
+     : ase_id(ase_id) , ase_state(ase_state) {
+    ase_state = ascs::ASE_STATE_INVALID;
+    cig_state = CigState::INVALID;
+    cis_state = CisState::INVALID;
+    direction = ase_direction;
+    cig_id = 0XFF;
+    cis_id = 0xFF;
+    cis_retry_count = 0;
+    overall_state = StreamTracker::kStateIdle;
+    ase_pending_cmd = AscsPendingCmd::NONE;
+    cis_pending_cmd = CisPendingCmd::NONE;
+  }
+  bool is_active;
+  uint8_t ase_id;
+  uint8_t ase_state;
+  AseParams ase_params;
+  AseCodecConfigParams pref_qos_params;
+  uint8_t cig_id;
+  CigState cig_state;
+  uint8_t cis_id;
+  CisState cis_state;
+  uint8_t cis_retry_count;
+  int overall_state; // stream tracker state
+  StreamControlType control_type;
+  AscsPendingCmd  ase_pending_cmd;
+  CisPendingCmd cis_pending_cmd;
+  uint16_t audio_context;
+  uint8_t direction;
+  uint32_t audio_location;
+  CodecConfig codec_config;
+  QosConfig qos_config;
+  QosConfig req_qos_config;
+};
+
+class UcastAudioStreams {
+ public:
+
+  std::vector<UcastAudioStream *> FindByCigId(uint8_t cig_id, int state) {
+    std::vector<UcastAudioStream *> streams;
+    for (auto i = audio_streams.begin(); i != audio_streams.end();i++) {
+      if((*i)->cig_id  == cig_id &&
+         (*i)->overall_state  == state) {
+        streams.push_back(*i);
+      }
+    }
+    return streams;
+  }
+
+  std::vector<UcastAudioStream *> FindByCisId(uint8_t cig_id, uint8_t cis_id) {
+    std::vector<UcastAudioStream *> streams;
+    for (auto i = audio_streams.begin(); i != audio_streams.end();i++) {
+      if((*i)->cig_id  == cig_id && (*i)->cis_id  == cis_id) {
+        streams.push_back(*i);
+      }
+    }
+    return streams;
+  }
+
+  UcastAudioStream *FindByStreamType(uint16_t audio_context,
+                                     uint8_t direction) {
+    auto it = audio_streams.begin();
+    while (it != audio_streams.end()) {
+      if((*it)->audio_context  == audio_context &&
+         (*it)->direction & direction) {
+        break;
+      }
+      it++;
+    }
+    return (it == audio_streams.end()) ? nullptr : (*it);
+  }
+
+  UcastAudioStream *FindByCisIdAndDir(uint8_t cig_id, uint8_t cis_id,
+                                      uint8_t dir) {
+    auto it = audio_streams.begin();
+    while (it != audio_streams.end()) {
+      if((*it)->cig_id  == cig_id && (*it)->cis_id  == cis_id &&
+         (*it)->direction & dir) {
+        break;
+      }
+      it++;
+    }
+    return (it == audio_streams.end()) ? nullptr : (*it);
+  }
+
+  UcastAudioStream *FindByAseId(uint8_t ase_id) {
+    auto it = audio_streams.begin();
+    while (it != audio_streams.end()) {
+      if((*it)->ase_id  == ase_id) {
+        break;
+      }
+      it++;
+    }
+    return (it == audio_streams.end()) ? nullptr : (*it);
+  }
+
+  UcastAudioStream *FindOrAddByAseId(uint8_t ase_id, uint8_t ase_state,
+                                     uint8_t ase_direction) {
+    auto iter = std::find_if(audio_streams.begin(), audio_streams.end(),
+                         [&ase_id, &ase_direction](UcastAudioStream *stream) {
+                            return (stream->ase_id == ase_id &&
+                                    stream->direction == ase_direction);
+                         });
+
+    if (iter == audio_streams.end()) {
+      UcastAudioStream *stream = new UcastAudioStream(ase_id, ase_state,
+                                                      ase_direction);
+      stream->overall_state = StreamTracker::kStateIdle;
+      audio_streams.push_back(stream);
+      auto it = std::find_if(audio_streams.begin(), audio_streams.end(),
+                         [&ase_id, &ase_direction](UcastAudioStream* stream) {
+                            return (stream->ase_id == ase_id &&
+                                    stream->direction == ase_direction);
+                         });
+      return (it == audio_streams.end()) ? nullptr : (*it);
+    } else {
+      return (*iter);
+    }
+  }
+
+  std::vector<UcastAudioStream *> GetStreamsByStates(
+                                  std::vector<int> state_ids,
+                                  uint8_t directions) {
+    std::vector<UcastAudioStream *> streams;
+    for (auto i = audio_streams.begin(); i != audio_streams.end();i++) {
+      for (auto j = state_ids.begin(); j != state_ids.end();j++) {
+        if(((*i)->overall_state == *j) && ((*i)->direction & directions)) {
+          streams.push_back(*i);
+        }
+      }
+    }
+    return streams;
+  }
+
+  void Remove(uint8_t ase_id) {
+    for (auto it = audio_streams.begin(); it != audio_streams.end();) {
+      if ((*it)->ase_id == ase_id)  {
+        delete(*it);
+        it = audio_streams.erase(it);
+      } else {
+        it++;
+      }
+    }
+  }
+
+  std::vector<UcastAudioStream *> *GetAllStreams() {
+    return &audio_streams;
+  }
+
+  size_t size() { return (audio_streams.size()); }
+  // UcastAudioStream
+ private:
+  std::vector<UcastAudioStream *> audio_streams;
+};
+
+struct GattPendingData {
+  GattPendingData() {
+        ascs_pending_cmd = GattPendingCmd::NONE;
+        pacs_pending_cmd = GattPendingCmd::NONE;
+  }
+  GattPendingCmd ascs_pending_cmd;
+  GattPendingCmd pacs_pending_cmd;
+};
+
+class UstreamManager {
+ public:
+   UstreamManager(const RawAddress& address, PacsClient *pacs_client,
+                  uint16_t pacs_client_id,
+                  AscsClient *ascs_client, CisInterface *cis_intf,
+                  UcastClientCallbacks* callbacks,
+                  BapAlarm *bap_alarm)
+       : address(address) , pacs_client(pacs_client),
+         pacs_client_id(pacs_client_id),
+         ascs_client(ascs_client), cis_intf(cis_intf),
+         ucl_callbacks(callbacks), bap_alarm(bap_alarm) {
+     pacs_state = ConnectionState::DISCONNECTED;
+     gatt_pending_data.pacs_pending_cmd = GattPendingCmd::NONE;
+     gatt_pending_data.ascs_pending_cmd = GattPendingCmd::NONE;
+     ascs_state = GattState::DISCONNECTED;
+     dev_type = DeviceType::NONE;
+   }
+
+   bool PushEventToTracker(uint32_t event, void *p_data,
+                           std::vector<int> *state_ids);
+
+   std::map<int , std::vector<StreamType> > SplitContextOnState(
+                             std::vector<StreamType> *streams);
+
+   void ProcessEvent(uint32_t event, void* p_data);
+
+   uint16_t GetConnId();
+
+   std::list<uint16_t> GetCigId();
+
+   std::list<uint16_t> GetCisId();
+
+   void ReportStreamState (std::vector<StreamStateInfo> stream_info);
+
+   RawAddress &GetAddress() { return address; };
+
+   PacsClient *GetPacsClient() {
+     return pacs_client;
+   }
+
+   uint16_t GetPacsClientId() {
+     return pacs_client_id;
+   }
+
+   GattPendingData *GetGattPendingData() {
+     return &gatt_pending_data;
+   }
+
+   bool UpdatePacsState(ConnectionState state) {
+     pacs_state = state;
+     return true;
+   }
+
+   bool UpdateAscsState(GattState state) {
+     ascs_state = state;
+     return true;
+   }
+
+   ConnectionState GetPacsState() {
+     return pacs_state;
+   }
+
+   GattState GetAscsState() {
+     return ascs_state;
+   }
+
+   AscsClient *GetAscsClient() {
+     return ascs_client;
+   }
+
+   CisInterface *GetCisInterface() {
+     return cis_intf;
+   }
+
+   BapAlarm *GetBapAlarm() {
+     return bap_alarm;
+   }
+
+   UcastAudioStreams *GetAudioStreams() {
+     return  &audio_streams;
+   }
+
+   StreamTrackers *GetStreamTrackers() {
+     return &stream_trackers;
+   }
+
+   StreamContexts *GetStreamContexts() {
+     return &stream_contexts;
+   }
+
+   UcastClientCallbacks *GetUclientCbacks() {
+     return ucl_callbacks;
+   }
+
+   void UpdateDevType(DeviceType device_type) {
+     dev_type = device_type;
+   }
+
+   DeviceType GetDevType() {
+     return dev_type;
+   }
+
+  const char* GetEventName(uint32_t event) {
+    switch (event) {
+      CASE_RETURN_STR(BAP_CONNECT_REQ_EVT)
+      CASE_RETURN_STR(BAP_DISCONNECT_REQ_EVT)
+      CASE_RETURN_STR(BAP_START_REQ_EVT)
+      CASE_RETURN_STR(BAP_STOP_REQ_EVT)
+      CASE_RETURN_STR(BAP_RECONFIG_REQ_EVT)
+      CASE_RETURN_STR(BAP_STREAM_UPDATE_REQ_EVT)
+      CASE_RETURN_STR(PACS_CONNECTION_STATE_EVT)
+      CASE_RETURN_STR(PACS_DISCOVERY_RES_EVT)
+      CASE_RETURN_STR(PACS_AUDIO_CONTEXT_RES_EVT)
+      CASE_RETURN_STR(ASCS_CONNECTION_STATE_EVT)
+      CASE_RETURN_STR(ASCS_DISCOVERY_RES_EVT)
+      CASE_RETURN_STR(ASCS_ASE_STATE_EVT)
+      CASE_RETURN_STR(ASCS_ASE_OP_FAILED_EVT)
+      CASE_RETURN_STR(CIS_GROUP_STATE_EVT)
+      CASE_RETURN_STR(CIS_STATE_EVT)
+      CASE_RETURN_STR(BAP_TIME_OUT_EVT)
+      default:
+       return "Unknown Event";
+    }
+  }
+
+ private:
+   RawAddress address;
+   PacsClient *pacs_client;
+   uint16_t pacs_client_id;
+   AscsClient *ascs_client;
+   ConnectionState pacs_state;
+   CisInterface *cis_intf;
+   GattState ascs_state;
+   StreamOpsQueue ops_queue;
+   UcastAudioStreams audio_streams;
+   StreamTrackers stream_trackers;
+   StreamContexts stream_contexts;
+   GattPendingData gatt_pending_data;
+   UcastClientCallbacks* ucl_callbacks;
+   BapAlarm *bap_alarm;
+   DeviceType dev_type;
+};
+
+class UstreamManagers {
+ public:
+  UstreamManager* FindByAddress(const RawAddress& address);
+
+  UstreamManager* FindorAddByAddress(const RawAddress& address,
+                  PacsClient *pacs_client, uint16_t pacs_client_id,
+                  AscsClient *ascs_client, CisInterface *cis_intf,
+                  UcastClientCallbacks* callbacks, BapAlarm* bap_alarm);
+
+
+  std::vector<UstreamManager *> *GetAllManagers();
+
+  void Remove(const RawAddress& address);
+
+  size_t size() { return (strm_mgrs.size()); }
+
+  std::vector<UstreamManager *> strm_mgrs;
+};
+
+}  // namespace ucast
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/bap/uclient_alarm.cc b/le_audio/system/bt/bta/bap/uclient_alarm.cc
new file mode 100644
index 0000000..cca6efa
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/uclient_alarm.cc
@@ -0,0 +1,99 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 "uclient_alarm.h"
+#include "bt_trace.h"
+#define LOG_TAG "uclient_alarm"
+
+namespace bluetooth {
+namespace bap {
+namespace alarm {
+
+class BapAlarmImpl;
+BapAlarmImpl *instance;
+
+static void alarm_handler(void* data);
+
+class BapAlarmImpl : public BapAlarm {
+  public:
+    BapAlarmImpl(BapAlarmCallbacks* callback):
+       callbacks(callback)  { }
+
+    ~BapAlarmImpl() override = default;
+
+    void CleanUp () { }
+
+    alarm_t* Create(const char* name) {
+      return alarm_new(name);
+    }
+
+    void Delete(alarm_t* alarm) {
+      alarm_free(alarm);
+    }
+
+    void Start(alarm_t* alarm, period_ms_t interval_ms,
+                              void* data) {
+      alarm_set_on_mloop(alarm, interval_ms, alarm_handler, data);
+    }
+
+    void Stop(alarm_t* alarm) {
+      alarm_cancel(alarm);
+    }
+
+    bool IsScheduled(const alarm_t* alarm) {
+      return alarm_is_scheduled(alarm);
+    }
+
+    void Timeout(void* data) {
+      if (callbacks)
+        callbacks->OnTimeout(data); // Call uclient_main
+    }
+
+  private:
+    BapAlarmCallbacks *callbacks;
+};
+
+void BapAlarm::Initialize(
+                   BapAlarmCallbacks* callbacks) {
+  if (instance) {
+    LOG(ERROR) << "Already initialized!";
+  } else {
+    instance = new BapAlarmImpl(callbacks);
+  }
+}
+
+void BapAlarm::CleanUp() {
+  BapAlarmImpl* ptr = instance;
+  instance = nullptr;
+  ptr->CleanUp();
+  delete ptr;
+}
+
+BapAlarm* BapAlarm::Get() {
+  return instance;
+}
+
+static void alarm_handler(void* data) {
+  if (instance)
+    instance->Timeout(data);
+}
+
+} //alarm
+} //bap
+} //bluetooth
diff --git a/le_audio/system/bt/bta/bap/uclient_alarm.h b/le_audio/system/bt/bta/bap/uclient_alarm.h
new file mode 100644
index 0000000..3e03d94
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/uclient_alarm.h
@@ -0,0 +1,63 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "osi/include/alarm.h"
+#include <raw_address.h>
+
+namespace bluetooth {
+namespace bap {
+namespace alarm {
+
+class BapAlarmCallbacks {
+  public:
+    virtual ~BapAlarmCallbacks() = default;
+
+    /** Callback for timer timeout */
+    virtual void OnTimeout(void* data) = 0;
+};
+
+class BapAlarm {
+  public:
+    virtual ~BapAlarm() = default;
+
+    static void Initialize(BapAlarmCallbacks* callbacks);
+    static void CleanUp();
+    static BapAlarm* Get();
+
+    virtual alarm_t* Create(const char* name) = 0;
+
+    virtual void Delete(alarm_t* alarm) = 0;
+
+    virtual void Start(alarm_t* alarm, period_ms_t interval_ms,
+                              void* data) = 0;
+
+    virtual void Stop(alarm_t* alarm) = 0;
+
+    virtual bool IsScheduled(const alarm_t* alarm) = 0;
+
+    virtual void Timeout(void* data) = 0;
+};
+
+} //alarm
+} //bap
+} //bluetooth
diff --git a/le_audio/system/bt/bta/bap/uclient_main.cc b/le_audio/system/bt/bta/bap/uclient_main.cc
new file mode 100644
index 0000000..0df1c39
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/uclient_main.cc
@@ -0,0 +1,548 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 "bta_bap_uclient_api.h"
+#include "ucast_client_int.h"
+#include "bta_pacs_client_api.h"
+#include "bta_ascs_client_api.h"
+#include <hardware/bt_pacs_client.h>
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include "bta_closure_api.h"
+#include "bt_trace.h"
+
+namespace bluetooth {
+namespace bap {
+namespace ucast {
+
+using base::Bind;
+using base::Unretained;
+using base::Closure;
+using bluetooth::Uuid;
+
+using bluetooth::bap::pacs::PacsClient;
+using bluetooth::bap::pacs::ConnectionState;
+using bluetooth::bap::pacs::CodecConfig;
+using bluetooth::bap::pacs::PacsClientCallbacks;
+
+using bluetooth::bap::ascs::AscsClient;
+using bluetooth::bap::ascs::GattState;
+using bluetooth::bap::ascs::AscsClientCallbacks;
+using bluetooth::bap::ascs::AseOpId;
+using bluetooth::bap::ascs::AseOpStatus;
+using bluetooth::bap::ascs::AseParams;
+
+using bluetooth::bap::ucast::UstreamManagers;
+using bluetooth::bap::ucast::UstreamManager;
+
+using bluetooth::bap::ucast::BapEventData;
+using bluetooth::bap::ucast::BapEvent;
+using bluetooth::bap::ucast::BapConnect;
+using bluetooth::bap::ucast::BapDisconnect;
+using bluetooth::bap::ucast::BapStart;
+using bluetooth::bap::ucast::BapStop;
+using bluetooth::bap::ucast::BapReconfig;
+using bluetooth::bap::ucast::PacsConnectionState;
+using bluetooth::bap::ucast::PacsDiscovery;
+using bluetooth::bap::ucast::PacsAvailableContexts;
+
+using bluetooth::bap::ucast::CisGroupState;
+using bluetooth::bap::ucast::CisStreamState;
+using bluetooth::bap::cis::CigState;
+using bluetooth::bap::cis::CisState;
+using bluetooth::bap::cis::CisInterface;
+
+using bluetooth::bap::alarm::BapAlarm;
+using bluetooth::bap::alarm::BapAlarmCallbacks;
+
+class UcastClientImpl;
+UcastClientImpl* instance = nullptr;
+
+class CisInterfaceCallbacksImpl : public CisInterfaceCallbacks {
+  public:
+    ~CisInterfaceCallbacksImpl() = default;
+        /** Callback for connection state change */
+    void OnCigState(uint8_t cig_id, CigState state) {
+      do_in_bta_thread(FROM_HERE, Bind(&CisInterfaceCallbacks::OnCigState,
+                                       Unretained(UcastClient::Get()), cig_id,
+                                       state));
+
+    }
+
+    void OnCisState(uint8_t cig_id, uint8_t cis_id,
+                   uint8_t direction, CisState state) {
+      do_in_bta_thread(FROM_HERE, Bind(&CisInterfaceCallbacks::OnCisState,
+                                       Unretained(UcastClient::Get()), cig_id,
+                                       cis_id, direction, state));
+    }
+};
+
+class PacsClientCallbacksImpl : public PacsClientCallbacks {
+  public:
+    ~PacsClientCallbacksImpl() = default;
+    void OnInitialized(int status, int client_id) override {
+      LOG(WARNING) << __func__ << ": status =" << loghex(status);
+      do_in_bta_thread(FROM_HERE, Bind(&PacsClientCallbacks::OnInitialized,
+                                       Unretained(UcastClient::Get()), status,
+                                       client_id));
+    }
+
+    void OnConnectionState(const RawAddress& address,
+                       bluetooth::bap::pacs::ConnectionState state) override {
+      LOG(WARNING) << __func__ << ": address=" << address;
+      do_in_bta_thread(FROM_HERE, Bind(&PacsClientCallbacks::OnConnectionState,
+                                       Unretained(UcastClient::Get()),
+                                       address, state));
+    }
+
+    void OnAudioContextAvailable(const RawAddress& address,
+                          uint32_t available_contexts) override {
+      do_in_bta_thread(FROM_HERE,
+                       Bind(&PacsClientCallbacks::OnAudioContextAvailable,
+                            Unretained(UcastClient::Get()),
+                            address, available_contexts));
+    }
+
+    void OnSearchComplete(int status, const RawAddress& address,
+                          std::vector<CodecConfig> sink_pac_records,
+                          std::vector<CodecConfig> src_pac_records,
+                          uint32_t sink_locations,
+                          uint32_t src_locations,
+                          uint32_t available_contexts,
+                          uint32_t supported_contexts) override {
+      do_in_bta_thread(FROM_HERE, Bind(&PacsClientCallbacks::OnSearchComplete,
+                                       Unretained(UcastClient::Get()),
+                                       status, address,
+                                       sink_pac_records,
+                                       src_pac_records,
+                                       sink_locations,
+                                       src_locations,
+                                       available_contexts,
+                                       supported_contexts));
+    }
+};
+
+class AscsClientCallbacksImpl : public AscsClientCallbacks {
+  public:
+    ~AscsClientCallbacksImpl() = default;
+    void OnAscsInitialized(int status, int client_id) override {
+      do_in_bta_thread(FROM_HERE, Bind(&AscsClientCallbacks::OnAscsInitialized,
+                                       Unretained(UcastClient::Get()), status,
+                                       client_id));
+    }
+
+    void OnConnectionState(const RawAddress& address,
+                       bluetooth::bap::ascs::GattState state) override {
+      DVLOG(2) << __func__ << " address: " << address;
+      do_in_bta_thread(FROM_HERE, Bind(&AscsClientCallbacks::OnConnectionState,
+                                       Unretained(UcastClient::Get()),
+                                       address, state));
+    }
+
+    void OnAseOpFailed(const RawAddress& address,
+                             AseOpId ase_op_id,
+                             std::vector<AseOpStatus> status) {
+      do_in_bta_thread(FROM_HERE,
+                       Bind(&AscsClientCallbacks::OnAseOpFailed,
+                            Unretained(UcastClient::Get()),
+                            address, ase_op_id, status));
+
+    }
+
+    void OnAseState(const RawAddress& address,
+                          AseParams ase) override {
+      do_in_bta_thread(FROM_HERE,
+                       Bind(&AscsClientCallbacks::OnAseState,
+                            Unretained(UcastClient::Get()),
+                            address, ase));
+    }
+
+    void OnSearchComplete(int status, const RawAddress& address,
+                          std::vector<AseParams> sink_ase_list,
+                          std::vector<AseParams> src_ase_list) override {
+      do_in_bta_thread(FROM_HERE, Bind(&AscsClientCallbacks::OnSearchComplete,
+                                       Unretained(UcastClient::Get()),
+                                       status, address, sink_ase_list,
+                                       src_ase_list));
+    }
+};
+
+class BapAlarmCallbacksImpl : public BapAlarmCallbacks {
+  public:
+    ~BapAlarmCallbacksImpl() = default;
+    /** Callback for timer timeout */
+    void OnTimeout(void* data) {
+      do_in_bta_thread(FROM_HERE, Bind(&BapAlarmCallbacks::OnTimeout,
+                                       Unretained(UcastClient::Get()), data));
+    }
+};
+
+class UcastClientImpl : public UcastClient {
+ public:
+  ~UcastClientImpl() override = default;
+
+  // APIs exposed for upper layers
+  void Connect(std::vector<RawAddress> address, bool is_direct,
+               std::vector<StreamConnect> streams) override {
+    if(address.size() == 1) {
+      UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address[0],
+                                      pacs_client, pacs_client_id,
+                                      ascs_client, cis_intf,
+                                      ucl_callbacks, bap_alarm);
+      // hand over the request to stream manager
+      BapConnect data = { .bd_addr = address, .is_direct = is_direct,
+                          .streams = streams};
+      mgr->ProcessEvent(BAP_CONNECT_REQ_EVT, &data);
+    }
+  }
+
+  void Disconnect(const RawAddress& address,
+                  std::vector<StreamType> streams) override {
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+
+    // hand over the request to stream manager
+    BapDisconnect data = { .bd_addr = address,
+                          .streams = streams};
+    mgr->ProcessEvent(BAP_DISCONNECT_REQ_EVT, &data);
+  }
+
+  void Start(const RawAddress& address,
+             std::vector<StreamType> streams) override {
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+
+    // hand over the request to stream manager
+    BapStart data = { .bd_addr = address,
+                      .streams = streams};
+    mgr->ProcessEvent(BAP_START_REQ_EVT, &data);
+  }
+
+  void Stop(const RawAddress& address,
+            std::vector<StreamType> streams) override {
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+
+    // hand over the request to stream manager
+    BapStop data = { .bd_addr = address,
+                     .streams = streams};
+    mgr->ProcessEvent(BAP_STOP_REQ_EVT, &data);
+
+  }
+
+  void Reconfigure(const RawAddress& address,
+                   std::vector<StreamReconfig> streams) override  {
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+
+    // hand over the request to stream manager
+    BapReconfig data = { .bd_addr = address,
+                         .streams = streams};
+    mgr->ProcessEvent(BAP_RECONFIG_REQ_EVT, &data);
+  }
+
+  void UpdateStream(const RawAddress& address,
+                   std::vector<StreamUpdate> update_streams) override  {
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+
+    // hand over the request to stream manager
+    BapStreamUpdate data = { .bd_addr = address,
+                            .update_streams = update_streams};
+    mgr->ProcessEvent(BAP_STREAM_UPDATE_REQ_EVT, &data);
+  }
+
+  // To be called from device specific stream manager
+  bool ReportStreamState(const RawAddress& address) {
+    //TODO to check
+    return true;
+
+  }
+
+  // PACS client related callbacks
+  // to be forwarded to device specific stream manager
+  void OnInitialized(int status, int client_id) override {
+    LOG(WARNING) << __func__ << ": actual client_id = " << loghex(client_id);
+    pacs_client_id = client_id;
+  }
+
+  void OnConnectionState(const RawAddress& address,
+                         ConnectionState state) override {
+    LOG(WARNING) << __func__ << ": address=" << address;
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+    // hand over the request to stream manager
+    PacsConnectionState data = { .bd_addr = address,
+                                 .state = state
+                               };
+    mgr->ProcessEvent(PACS_CONNECTION_STATE_EVT, &data);
+  }
+
+  void OnAudioContextAvailable(const RawAddress& address,
+                        uint32_t available_contexts) override {
+    LOG(WARNING) << __func__ << ": address=" << address;
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+    // hand over the request to stream manager
+    PacsAvailableContexts data = {
+                           .bd_addr = address,
+                           .available_contexts = available_contexts,
+                         };
+    mgr->ProcessEvent(PACS_AUDIO_CONTEXT_RES_EVT, &data);
+  }
+
+  void OnSearchComplete(int status, const RawAddress& address,
+                        std::vector<CodecConfig> sink_pac_records,
+                        std::vector<CodecConfig> src_pac_records,
+                        uint32_t sink_locations,
+                        uint32_t src_locations,
+                        uint32_t available_contexts,
+                        uint32_t supported_contexts) override {
+    LOG(WARNING) << __func__ << ": address=" << address;
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+    // hand over the request to stream manager
+    PacsDiscovery data = {
+                           .status = status,
+                           .bd_addr = address,
+                           .sink_pac_records = sink_pac_records,
+                           .src_pac_records = src_pac_records,
+                           .sink_locations = sink_locations,
+                           .src_locations = src_locations,
+                           .available_contexts = available_contexts,
+                           .supported_contexts = supported_contexts
+                         };
+    mgr->ProcessEvent(PACS_DISCOVERY_RES_EVT, &data);
+  }
+
+  // ASCS client related callbacks
+  // to be forwarded to device specific stream manager
+  void OnAscsInitialized(int status, int client_id) override {
+
+  }
+
+  void OnConnectionState(const RawAddress& address,
+                     bluetooth::bap::ascs::GattState state) override {
+    LOG(WARNING) << __func__ << ": address=" << address;
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+    // hand over the request to stream manager
+    AscsConnectionState data = { .bd_addr = address,
+                                 .state = state
+                               };
+    mgr->ProcessEvent(ASCS_CONNECTION_STATE_EVT, &data);
+  }
+
+  void OnAseOpFailed(const RawAddress& address,
+                     AseOpId ase_op_id,
+                     std::vector<AseOpStatus> status) {
+
+    LOG(WARNING) << __func__ << ": address=" << address;
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+    // hand over the request to stream manager
+    AscsOpFailed data = {
+                           .bd_addr = address,
+                           .ase_op_id = ase_op_id,
+                           .ase_list = status
+                        };
+    mgr->ProcessEvent(ASCS_ASE_OP_FAILED_EVT, &data);
+  }
+
+  void OnAseState(const RawAddress& address,
+                        AseParams ase_params) override {
+    LOG(WARNING) << __func__ << ": address=" << address;
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+    // hand over the request to stream manager
+    AscsState data = {
+                           .bd_addr = address,
+                           .ase_params = ase_params
+                     };
+    mgr->ProcessEvent(ASCS_ASE_STATE_EVT, &data);
+  }
+
+  void OnSearchComplete(int status, const RawAddress& address,
+                                std::vector<AseParams> sink_ase_list,
+                                std::vector<AseParams> src_ase_list) override {
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(address,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+    // hand over the request to stream manager
+    AscsDiscovery data = {
+                           .status = status,
+                           .bd_addr = address,
+                           .sink_ases_list = sink_ase_list,
+                           .src_ases_list = src_ase_list
+                         };
+    mgr->ProcessEvent(ASCS_DISCOVERY_RES_EVT, &data);
+  }
+
+  // cis callbacks
+  void OnCigState(uint8_t cig_id, CigState state) override {
+    std::vector<UstreamManager *> *mgrs_list =  strm_mgrs.GetAllManagers();
+    // hand over the request to stream manager
+    CisGroupState data = {
+                      .cig_id = cig_id,
+                      .state = state
+                    };
+
+    for (auto it = mgrs_list->begin(); it != mgrs_list->end(); it++) {
+      (*it)->ProcessEvent(CIS_GROUP_STATE_EVT, &data);
+    }
+  }
+
+  void OnCisState(uint8_t cig_id, uint8_t cis_id, uint8_t direction,
+                                         CisState state) override {
+    std::vector<UstreamManager *> *mgrs_list =  strm_mgrs.GetAllManagers();
+    // hand over the request to stream manager
+    CisStreamState data = {
+                           .cig_id = cig_id,
+                           .cis_id = cis_id,
+                           .direction = direction,
+                           .state = state
+                         };
+
+    for (auto it = mgrs_list->begin(); it != mgrs_list->end(); it++) {
+      (*it)->ProcessEvent(CIS_STATE_EVT, &data);
+    }
+  }
+
+  void OnTimeout(void* data) override {
+    LOG(ERROR) << __func__;
+    BapTimeout* data_ = (BapTimeout *)data;
+    UstreamManager *mgr = strm_mgrs.FindorAddByAddress(data_->bd_addr,
+                                    pacs_client, pacs_client_id,
+                                    ascs_client, cis_intf,
+                                    ucl_callbacks, bap_alarm);
+    // hand over the request to stream manager
+    mgr->ProcessEvent(BAP_TIME_OUT_EVT, data);
+  }
+
+  bool Init(UcastClientCallbacks *callback) {
+    // register callbacks with CIS, ASCS client, PACS client
+    pacs_callbacks = new PacsClientCallbacksImpl;
+    PacsClient::Initialize(pacs_callbacks);
+    pacs_client = PacsClient::Get();
+
+    ascs_callbacks = new AscsClientCallbacksImpl;
+    AscsClient::Init(ascs_callbacks);
+    ascs_client = AscsClient::Get();
+
+    cis_callbacks = new CisInterfaceCallbacksImpl;
+    CisInterface::Initialize(cis_callbacks);
+    cis_intf = CisInterface::Get();
+
+    bap_alarm_cb = new BapAlarmCallbacksImpl;
+    BapAlarm::Initialize(bap_alarm_cb);
+    bap_alarm = BapAlarm::Get();
+
+    pacs_client_id = 0;
+    if(ucl_callbacks != nullptr) {
+      // flag an error
+      return false;
+    } else {
+      ucl_callbacks = callback;
+      return true;
+    }
+  }
+
+  bool CleanUp() {
+    if(ucl_callbacks != nullptr) {
+      ucl_callbacks = nullptr;
+      //call clean ups for each clients(ascs, pacs, cis and bap_alarm)
+      LOG(ERROR) << __func__
+                 <<": Cleaning up pacs, ascs clients, cis intf and bap_alarm.";
+      pacs_client->CleanUp(pacs_client_id);
+      ascs_client->CleanUp(0x01);
+      cis_intf->CleanUp();
+      bap_alarm->CleanUp();
+      pacs_client = nullptr;
+      ascs_client = nullptr;
+      cis_intf = nullptr;
+      bap_alarm = nullptr;
+      // remove all stream managers and other clean ups
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+ private:
+  UcastClientCallbacks* ucl_callbacks;
+  UstreamManagers strm_mgrs;
+  PacsClient *pacs_client;
+  AscsClient *ascs_client;
+  PacsClientCallbacks *pacs_callbacks;
+  AscsClientCallbacks *ascs_callbacks;
+  CisInterface *cis_intf;
+  CisInterfaceCallbacks *cis_callbacks;
+  uint16_t pacs_client_id;
+  BapAlarm* bap_alarm;
+  BapAlarmCallbacks* bap_alarm_cb;
+};
+
+void UcastClient::Initialize(UcastClientCallbacks* callbacks) {
+  if (!instance) {
+    instance = new UcastClientImpl();
+    instance->Init(callbacks);
+  } else {
+    LOG(ERROR) << __func__ << " 2nd client registration ignored";
+  }
+}
+
+void UcastClient::CleanUp() {
+  if(instance && instance->CleanUp()) {
+    delete instance;
+    instance = nullptr;
+  }
+}
+
+UcastClient* UcastClient::Get() {
+  CHECK(instance);
+  return instance;
+}
+
+}  // namespace ucast
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/bap/uclient_strm_mgr.cc b/le_audio/system/bt/bta/bap/uclient_strm_mgr.cc
new file mode 100644
index 0000000..ea06937
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/uclient_strm_mgr.cc
@@ -0,0 +1,1013 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 "bta_bap_uclient_api.h"
+#include "ucast_client_int.h"
+#include "bt_trace.h"
+
+namespace bluetooth {
+namespace bap {
+namespace ucast {
+
+using namespace std;
+using bluetooth::bap::ucast::UstreamManagers;
+using bluetooth::bap::ucast::UstreamManager;
+
+std::map<StreamState, int> state_map = {
+   {StreamState::DISCONNECTED,  StreamTracker::kStateIdle} ,
+   {StreamState::CONNECTING,    StreamTracker::kStateConnecting},
+   {StreamState::CONNECTED,     StreamTracker::kStateConnected},
+   {StreamState::STARTING,      StreamTracker::kStateStarting},
+   {StreamState::STREAMING,     StreamTracker::kStateStreaming},
+   {StreamState::STOPPING,      StreamTracker::kStateStopping},
+   {StreamState::DISCONNECTING, StreamTracker::kStateDisconnecting},
+   {StreamState::RECONFIGURING, StreamTracker::kStateReconfiguring}
+};
+
+StreamContext *StreamContexts::FindByType(StreamType stream_type) {
+  auto iter = std::find_if(strm_contexts.begin(), strm_contexts.end(),
+                       [&stream_type](StreamContext *context) {
+                       return ((context->stream_type.type == stream_type.type)
+                            && (context->stream_type.direction ==
+                                stream_type.direction));
+                       });
+
+  if (iter == strm_contexts.end()) {
+    return nullptr;
+  } else {
+    return (*iter);
+  }
+}
+
+std::vector<StreamContext *> StreamContexts::FindByAseAttachedState(
+                             uint16_t ase_id, StreamAttachedState state) {
+  std::vector<StreamContext *> contexts_list;
+  for(auto i = strm_contexts.begin(); i != strm_contexts.end();i++) {
+    if(static_cast<uint8_t>((*i)->attached_state) &
+       static_cast<uint8_t>(state)) {
+      for(auto j = (*i)->stream_ids.begin(); j != (*i)->stream_ids.end();j++) {
+        if(j->ase_id  == ase_id) {
+          contexts_list.push_back(*i);
+          break;
+        }
+      }
+    }
+  }
+  return contexts_list;
+}
+
+StreamContext *StreamContexts::FindOrAddByType(StreamType stream_type) {
+  auto iter = std::find_if(strm_contexts.begin(), strm_contexts.end(),
+                       [&stream_type](StreamContext *context) {
+                       return ((context->stream_type.type ==
+                                stream_type.type) &&
+                               (context->stream_type.direction ==
+                                stream_type.direction));
+                       });
+
+  if (iter == strm_contexts.end()) {
+    StreamContext *context = new StreamContext(stream_type);
+    strm_contexts.push_back(context);
+    return context;
+  } else {
+    return (*iter);
+  }
+}
+
+void StreamContexts::Remove(StreamType stream_type) {
+  for (auto it = strm_contexts.begin(); it != strm_contexts.end();) {
+    if (((*it)->stream_type.type = stream_type.type) &&
+        ((*it)->stream_type.direction = stream_type.direction)) {
+      delete(*it);
+      it = strm_contexts.erase(it);
+    } else {
+      it++;
+    }
+  }
+}
+
+bool StreamContexts::IsAseAttached(StreamType stream_type) {
+  return false;
+
+}
+
+StreamContext* StreamContexts::FindByAseId(uint16_t ase_id) {
+  auto iter = std::find_if(strm_contexts.begin(), strm_contexts.end(),
+                       [&ase_id](StreamContext *context) {
+                         auto it =  std::find_if(context->stream_ids.begin(),
+                                                 context->stream_ids.end(),
+                                            [&ase_id](StreamIdType id) {
+                                            return (id.ase_id == ase_id);
+                                            });
+                         if (it != context->stream_ids.end()) {
+                           return true;
+                         } else return false;
+
+                       });
+
+  if (iter == strm_contexts.end()) {
+    return nullptr;
+  } else {
+    return (*iter);
+  }
+}
+
+StreamTracker* StreamTrackers::FindOrAddByType(int init_state_id,
+                     UstreamManager *strm_mgr,
+                     std::vector<StreamConnect> *connect_streams,
+                     std::vector<StreamReconfig> *reconfig_streams,
+                     std::vector<StreamType> *streams,
+                     StreamControlType ops_type) {
+  bool found = false;
+  auto iter = stream_trackers.begin();
+  while (iter != stream_trackers.end() && !found) {
+    if((*iter)->GetControlType()  == ops_type) {
+      if(ops_type == StreamControlType::Connect) {
+        // compare connection streams
+        std::vector<StreamConnect> *conn_strms = (*iter)->GetConnStreams();
+        if(conn_strms->size() == connect_streams->size()) {
+          uint8_t len = connect_streams->size();
+          for (uint8_t i = 0; i < len ; i++) {
+            StreamConnect src = conn_strms->at(i);
+            StreamConnect dst = connect_streams->at(i);
+            if((src.stream_type.type == dst.stream_type.type) &&
+               (src.stream_type.direction == dst.stream_type.direction)) {
+              LOG(WARNING) << __func__ << " StreamConnect found";
+              found = true;
+              break;
+            }
+          }
+        }
+      } else if(ops_type == StreamControlType::Reconfig) {
+        // compare connection streams
+        std::vector<StreamReconfig> *reconf_strms = (*iter)->GetReconfStreams();
+        if(reconf_strms->size() == reconfig_streams->size()) {
+          uint8_t len = reconfig_streams->size();
+          for (uint8_t i = 0; i < len ; i++) {
+            StreamReconfig src = reconf_strms->at(i);
+            StreamReconfig dst = reconfig_streams->at(i);
+            if((src.stream_type.type == dst.stream_type.type) &&
+               (src.stream_type.direction == dst.stream_type.direction)) {
+              LOG(WARNING) << __func__ << " StreamReconfig found";
+              found = true;
+              break;
+            }
+          }
+        }
+      } else if(ops_type != StreamControlType::None &&
+                ops_type != StreamControlType::UpdateStream) {
+        // compare connection streams
+        std::vector<StreamType> *strms = (*iter)->GetStreams();
+        if(strms->size() == streams->size()) {
+          uint8_t len = streams->size();
+          for (uint8_t i = 0; i < len ; i++) {
+            StreamType src = strms->at(i);
+            StreamType dst = streams->at(i);
+            if((src.type == dst.type) &&
+               (src.direction == dst.direction)) {
+              LOG(WARNING) << __func__ << " StreamType found";
+              found = true;
+              break;
+            }
+          }
+        }
+      }
+    }
+    iter++;
+  }
+
+  if (iter == stream_trackers.end()) {
+    StreamTracker *tracker =  new StreamTracker(init_state_id, strm_mgr,
+                                   connect_streams, reconfig_streams,
+                                   streams, ops_type);
+    stream_trackers.push_back(tracker);
+    return tracker;
+  } else {
+    return (*iter);
+  }
+}
+
+bool StreamTrackers::Remove(std::vector<StreamType> streams,
+            StreamControlType ops_type) {
+  return true;
+}
+
+std::vector<StreamTracker *> StreamTrackers::GetTrackersByStates(
+                                   std::vector<int> *state_ids) {
+  vector<StreamTracker *> trackers;
+  for (auto i = stream_trackers.begin();
+                       i != stream_trackers.end();i++) {
+    for (auto j = state_ids->begin(); j != state_ids->end();j++) {
+      if((*i)->StateId()  == *j) {
+        LOG(WARNING) << __func__ << " tracker found";
+        trackers.push_back(*i);
+      }
+    }
+  }
+  return trackers;
+}
+
+void StreamTrackers::RemoveByStates(std::vector<int> state_ids) {
+  for (auto i = stream_trackers.begin();
+                       i != stream_trackers.end();) {
+    bool found = false;
+    for (auto j = state_ids.begin(); j != state_ids.end();j++) {
+      if((*i)->StateId()  == *j) {
+        LOG(WARNING) << __func__ << " tracker found";
+        found = true;
+        break;
+      }
+    }
+    if(found) {
+      delete(*i);
+      i = stream_trackers.erase(i);
+    } else {
+      i++;
+    }
+  }
+}
+
+
+std::map<StreamTracker * , std::vector<StreamType> >
+                           StreamTrackers::GetTrackersByType(
+                           std::vector<StreamType> *streams) {
+  std::vector<StreamType> req_types = *streams;
+  std::vector<StreamType> all_types;
+  std::map<StreamTracker * , std::vector<StreamType> > tracker_and_type_map;
+  for (auto iter = stream_trackers.begin(); iter != stream_trackers.end();
+                                            iter++) {
+    all_types.clear();
+    if((*iter)->GetControlType() == StreamControlType::Connect) {
+      // compare connection streams
+      std::vector<StreamConnect> *conn_strms = (*iter)->GetConnStreams();
+      uint8_t len = conn_strms->size();
+      for (uint8_t i = 0; i < len ; i++) {
+        StreamConnect src = conn_strms->at(i);
+        all_types.push_back(src.stream_type);
+      }
+    } else if((*iter)->GetControlType() == StreamControlType::Reconfig) {
+      // compare connection streams
+      std::vector<StreamReconfig> *reconf_strms = (*iter)->GetReconfStreams();
+      uint8_t len = reconf_strms->size();
+      for (uint8_t i = 0; i < len ; i++) {
+        StreamReconfig src = reconf_strms->at(i);
+        all_types.push_back(src.stream_type);
+      }
+    } else if((*iter)->GetControlType() == StreamControlType::UpdateStream) {
+      // compare connection streams
+      std::vector<StreamUpdate> *update_streams = (*iter)->GetMetaUpdateStreams();
+      uint8_t len = update_streams->size();
+      for (uint8_t i = 0; i < len ; i++) {
+        StreamUpdate src = update_streams->at(i);
+        all_types.push_back(src.stream_type);
+      }
+    } else if((*iter)->GetControlType() != StreamControlType::None) {
+      // compare connection streams
+      std::vector<StreamType> *strms = (*iter)->GetStreams();
+      uint8_t len = strms->size();
+      for (uint8_t i = 0; i < len ; i++) {
+        StreamType src = strms->at(i);
+        all_types.push_back(src);
+      }
+    }
+    uint8_t count = 0;
+    std::vector<StreamType> filtered_types;
+    if(all_types.size() <= req_types.size()) {
+      filtered_types.clear();
+      for (auto it = all_types.begin(); it != all_types.end(); it++) {
+        for (auto it_2 = req_types.begin(); it_2 != req_types.end();) {
+          if (((it_2)->type == it->type) &&
+              ((it_2)->direction == it->direction)) {
+            filtered_types.push_back(*it_2);
+            tracker_and_type_map[*iter] = filtered_types;
+            it_2 = req_types.erase(it_2);
+            count++;
+          } else {
+            it_2++;
+          }
+        }
+      }
+      if(all_types.size() != count) {
+        LOG(ERROR) << __func__ << " invalid request";
+      }
+    } else {
+      LOG(ERROR) << __func__ << " invalid request";
+    }
+  }
+  if(req_types.size()) {
+    tracker_and_type_map[nullptr] = req_types;
+  }
+  return tracker_and_type_map;
+}
+
+
+StreamTracker *StreamTrackers::FindByStreamsType(
+                               std::vector<StreamType> *streams) {
+  bool found = false;
+  auto iter = stream_trackers.begin();
+  while (iter != stream_trackers.end()) {
+    if((*iter)->GetControlType() == StreamControlType::Connect) {
+      // compare connection streams
+      std::vector<StreamConnect> *conn_strms = (*iter)->GetConnStreams();
+      if(conn_strms->size() == streams->size()) {
+        uint8_t len = streams->size();
+        for (uint8_t i = 0; i < len ; i++) {
+          StreamConnect src = conn_strms->at(i);
+          StreamType dst = streams->at(i);
+          if((src.stream_type.type == dst.type) &&
+             (src.stream_type.direction == dst.direction)) {
+            LOG(WARNING) << __func__ << " StreamConnect found";
+            found = true;
+            break;
+          }
+        }
+      }
+    } else if((*iter)->GetControlType() == StreamControlType::Reconfig) {
+      // compare connection streams
+      std::vector<StreamReconfig> *reconf_strms = (*iter)->GetReconfStreams();
+      if(reconf_strms->size() == streams->size()) {
+        uint8_t len = streams->size();
+        for (uint8_t i = 0; i < len ; i++) {
+          StreamReconfig src = reconf_strms->at(i);
+          StreamType dst = streams->at(i);
+          if((src.stream_type.type == dst.type) &&
+             (src.stream_type.direction == dst.direction)) {
+            LOG(WARNING) << __func__ << " StreamReconfig found";
+            found = true;
+            break;
+          }
+        }
+      }
+    } else if((*iter)->GetControlType() == StreamControlType::UpdateStream) {
+      // compare connection streams
+      std::vector<StreamUpdate> *update_strms = (*iter)->GetMetaUpdateStreams();
+      if(update_strms->size() == streams->size()) {
+        uint8_t len = streams->size();
+        for (uint8_t i = 0; i < len ; i++) {
+          StreamUpdate src = update_strms->at(i);
+          StreamType dst = streams->at(i);
+          if((src.stream_type.type == dst.type) &&
+             (src.stream_type.direction == dst.direction)) {
+            LOG(WARNING) << __func__ << " StreamUpdate found";
+            found = true;
+            break;
+          }
+        }
+      }
+    } else if((*iter)->GetControlType() != StreamControlType::None) {
+      // compare connection streams
+      std::vector<StreamType> *strms = (*iter)->GetStreams();
+      if(strms->size() == streams->size()) {
+        uint8_t len = streams->size();
+        for (uint8_t i = 0; i < len ; i++) {
+          StreamType src = strms->at(i);
+          StreamType dst = streams->at(i);
+          if((src.type == dst.type) &&
+             (src.direction == dst.direction)) {
+            LOG(WARNING) << __func__ << " StreamType found";
+            found = true;
+            break;
+          }
+        }
+      }
+    }
+    if(found) break;
+    iter++;
+  }
+
+  if (iter == stream_trackers.end()) {
+    return nullptr;
+  } else {
+    return (*iter);
+  }
+}
+
+bool StreamTrackers::ChangeOpType( StreamType stream_type,
+                   StreamControlType new_ops_type) {
+  return true;
+}
+
+bool StreamTrackers::IsStreamTrackerValid(StreamTracker* tracker,
+                                           std::vector<int> *state_ids) {
+  vector<StreamTracker *> trackers_ = GetTrackersByStates(state_ids);
+  LOG(WARNING) << __func__;
+  if(trackers_.empty()) return false;
+
+  for (auto it = trackers_.begin(); it != trackers_.end(); it++) {
+    if ((*it) == tracker) {
+      LOG(WARNING) << __func__ <<": Cached Tracker is valid";
+      return true;
+    }
+  }
+  return false;
+}
+
+bool UstreamManager::PushEventToTracker(uint32_t event, void *p_data,
+                                        std::vector<int> *state_ids) {
+  vector<StreamTracker *> trackers = stream_trackers.GetTrackersByStates(
+                                                    state_ids);
+  if(trackers.empty()) return false;
+
+  for (auto it = trackers.begin(); it != trackers.end(); it++) {
+    (*it)->ProcessEvent(event, p_data);
+  }
+  return true;
+}
+
+
+std::map<int , std::vector<StreamType> > UstreamManager::SplitContextOnState(
+                             std::vector<StreamType> *streams) {
+  StreamContexts *contexts = GetStreamContexts();
+  std::vector<StreamType> req_types = *streams;
+  std::map<int , std::vector<StreamType> > state_and_type_map;
+
+  for (auto it = req_types.begin(); it != req_types.end();) {
+    StreamContext *context = contexts->FindOrAddByType(*it);
+    if (context) {
+      int state = state_map[context->stream_state];
+      state_and_type_map[state].push_back(*it);
+      it = req_types.erase(it);
+    } else {
+      it++;
+    }
+  }
+  return state_and_type_map;
+}
+
+void UstreamManager::ProcessEvent(uint32_t event, void *p_data) {
+  LOG(WARNING) << __func__  <<": Event: " << GetEventName(event)
+                            <<", bt_addr: " << GetAddress();
+
+  std::vector<int> stable_state_ids = {
+                                        StreamTracker::kStateConnected,
+                                        StreamTracker::kStateStreaming,
+                                        StreamTracker::kStateIdle
+                                      };
+
+  std::vector<int> transient_state_ids = {
+                                           StreamTracker::kStateConnecting,
+                                           StreamTracker::kStateReconfiguring,
+                                           StreamTracker::kStateDisconnecting,
+                                           StreamTracker::kStateStarting,
+                                           StreamTracker::kStateStopping,
+                                           StreamTracker::kStateUpdating,
+                                         };
+  StreamContexts *contexts = GetStreamContexts();
+
+  switch (event) {
+
+    case BAP_CONNECT_REQ_EVT: {
+      BapConnect *evt_data = (BapConnect *) p_data;
+      std::vector<StreamConnect> conn_streams = evt_data->streams;
+      LOG(WARNING) << __func__  << ": size: " << conn_streams.size();
+
+      for (auto it = conn_streams.begin(); it != conn_streams.end();) {
+        StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+        if(context && context->stream_state != StreamState::DISCONNECTED) {
+          LOG(WARNING) << __func__  << ": Stream is not in disconnected state";
+          it = conn_streams.erase(it);
+        } else {
+          it++;
+        }
+      }
+
+      if(!conn_streams.size()) {
+        LOG(ERROR) << __func__  << ": All streams are not in disconnected state";
+        break;
+      }
+
+      // validate the combinations media Tx or voice TX|RX
+      StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                            StreamTracker::kStateIdle,
+                            this, &conn_streams, nullptr, nullptr,
+                            StreamControlType::Connect);
+
+      if(tracker) {
+        tracker->Start();
+        tracker->ProcessEvent(event, p_data);
+      }
+    } break;
+
+    case BAP_DISCONNECT_REQ_EVT: {
+      BapDisconnect *evt_data = (BapDisconnect *) p_data;
+      std::vector<StreamType> disc_streams = evt_data->streams;
+      BapDisconnect int_evt_data;
+
+      for (auto it = disc_streams.begin(); it != disc_streams.end();) {
+        StreamContext *context = contexts->FindOrAddByType(*it);
+        if(!context || context->stream_state == StreamState::DISCONNECTED) {
+          LOG(WARNING) << __func__  << " Stream is already disconnected";
+          it = disc_streams.erase(it);
+        } else {
+          it++;
+        }
+      }
+
+      if(!disc_streams.size()) {
+        LOG(ERROR) << __func__  << " All streams are already disconnected";
+        break;
+      }
+
+      LOG(WARNING) << __func__  << " disc streams size " << disc_streams.size();
+
+      // validate the combinations media Tx or voice TX|RX
+      std::map<StreamTracker * , std::vector<StreamType> >
+         tracker_and_type_list =
+                    stream_trackers.GetTrackersByType(&disc_streams);
+
+
+      // check if all streams disconnection or subset of streams
+      // create the new tracker
+      for (auto itr = tracker_and_type_list.begin();
+                itr != tracker_and_type_list.end(); itr++) {
+        if(itr->first == nullptr) {
+          std::map<int , std::vector<StreamType> >
+             list = SplitContextOnState(&itr->second);
+          for (auto itr_2 = list.begin(); itr_2 != list.end(); itr_2++) {
+            StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                                itr_2->first,
+                                this, nullptr, nullptr, &itr_2->second,
+                                StreamControlType::Disconnect);
+            LOG(ERROR) << __func__ << " new tracker start ";
+            tracker->Start();
+            int_evt_data.streams = itr_2->second;
+            tracker->ProcessEvent(event, &int_evt_data);
+          }
+        } else {
+          LOG(ERROR) << __func__ << " existing tracker start ";
+          StreamTracker *tracker = itr->first;
+          int_evt_data.streams = itr->second;
+          tracker->ProcessEvent(event, &int_evt_data);
+        }
+      }
+    } break;
+
+    case BAP_START_REQ_EVT: {
+      BapStart *evt_data = (BapStart *) p_data;
+      std::vector<StreamType> start_streams = evt_data->streams;
+      LOG(WARNING) << __func__ << " start streams size " << start_streams.size();
+
+      for (auto it = start_streams.begin(); it != start_streams.end();) {
+        StreamContext *context = contexts->FindOrAddByType(*it);
+        if(!context || context->stream_state != StreamState::CONNECTED) {
+          LOG(WARNING) << __func__  << " Stream is not in connected state";
+          it = start_streams.erase(it);
+        } else {
+          it++;
+        }
+      }
+
+      if(!start_streams.size()) {
+        LOG(WARNING) << __func__  << " Not eligible for stream start";
+        break;
+      }
+
+      // validate the combinations media Tx or voice TX|RX
+      StreamTracker *tracker = stream_trackers.FindByStreamsType(
+                                                      &start_streams);
+      // create new tracker
+      if(tracker == nullptr) {
+        StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                                      StreamTracker::kStateConnected,
+                                      this, nullptr, nullptr, &start_streams,
+                                      StreamControlType::Start);
+        tracker->Start();
+        tracker->ProcessEvent(event, p_data);
+      } else {
+        tracker->ProcessEvent(event, p_data);
+      }
+    } break;
+
+    case BAP_STOP_REQ_EVT: {
+      BapStop *evt_data = (BapStop *) p_data;
+      std::vector<StreamType> stop_streams = evt_data->streams;
+      LOG(WARNING) << __func__  << " stop streams size " << stop_streams.size();
+
+      for (auto it = stop_streams.begin(); it != stop_streams.end();) {
+        StreamContext *context = contexts->FindOrAddByType(*it);
+        if(!context || (context->stream_state != StreamState::STREAMING &&
+                       context->stream_state != StreamState::STARTING)) {
+          LOG(WARNING) << __func__  << " Stream is not in streaming state";
+          it = stop_streams.erase(it);
+        } else {
+          it++;
+        }
+      }
+
+      if(!stop_streams.size()) {
+        LOG(WARNING) << __func__  << " Not eligible for stream stop";
+        break;
+      }
+
+      StreamTracker *tracker = stream_trackers.FindByStreamsType(
+                                                      &stop_streams);
+      // create the new tracker
+      if(tracker == nullptr) {
+        // validate the combinations media Tx or voice TX|RX
+        StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                              StreamTracker::kStateStreaming,
+                              this, nullptr, nullptr, &stop_streams,
+                              StreamControlType::Stop);
+        tracker->Start();
+        tracker->ProcessEvent(event, p_data);
+      } else {
+        tracker->ProcessEvent(event, p_data);
+      }
+    } break;
+
+    case BAP_STREAM_UPDATE_REQ_EVT: {
+      BapStreamUpdate *evt_data = (BapStreamUpdate *) p_data;
+      std::vector<StreamUpdate> update_streams = evt_data->update_streams;
+      std::vector<StreamType> streams;
+
+      for (auto it = update_streams.begin(); it != update_streams.end();) {
+        StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+        if(!context || (context->stream_state != StreamState::STREAMING &&
+                        context->stream_state != StreamState::STARTING)) {
+          LOG(WARNING) << __func__  << " Stream is not in proper state";
+          it = update_streams.erase(it);
+        } else {
+          it++;
+        }
+      }
+
+      if(!update_streams.size()) {
+        LOG(WARNING) << __func__  << " All streams are not in proper state";
+        break;
+      }
+
+      for (auto it = update_streams.begin();
+                           it != update_streams.end(); it++) {
+        StreamType type = it->stream_type;
+        streams.push_back(type);
+      }
+
+      LOG(WARNING) << __func__  << " update streams size " << streams.size();
+
+      StreamTracker *tracker = stream_trackers.FindByStreamsType(
+                                                  &streams);
+      // create the new tracker
+      if(tracker == nullptr) {
+        // validate the combinations media Tx or voice TX|RX
+        StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                              StreamTracker::kStateStreaming,
+                              this, nullptr, nullptr, nullptr,
+                              StreamControlType::UpdateStream);
+        tracker->Start();
+        tracker->ProcessEvent(event, p_data);
+      } else {
+        tracker->ProcessEvent(event, p_data);
+      }
+    } break;
+
+    case BAP_RECONFIG_REQ_EVT: {
+      BapReconfig *evt_data = (BapReconfig *) p_data;
+      std::vector<StreamReconfig> reconf_streams = evt_data->streams;
+      std::vector<StreamType> streams;
+
+      for (auto it = reconf_streams.begin(); it != reconf_streams.end();) {
+        StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+        if(!context || context->stream_state != StreamState::CONNECTED) {
+          LOG(WARNING) << __func__  << " Stream is not in Connected state";
+          it = reconf_streams.erase(it);
+        } else {
+          it++;
+        }
+      }
+
+      if(!reconf_streams.size()) {
+        LOG(WARNING) << __func__  << " All streams are not connected";
+        break;
+      }
+
+      for (auto it = reconf_streams.begin();
+                           it != reconf_streams.end(); it++) {
+        StreamType type = it->stream_type;
+        streams.push_back(type);
+      }
+
+      LOG(WARNING) << __func__  << " reconf streams size " << streams.size();
+
+      StreamTracker *tracker = stream_trackers.FindByStreamsType(
+                                                  &streams);
+      // create the new tracker
+      if(tracker == nullptr) {
+        // validate the combinations media Tx or voice TX|RX
+        StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                              StreamTracker::kStateConnected,
+                              this, nullptr, &reconf_streams, nullptr,
+                              StreamControlType::Reconfig);
+        tracker->Start();
+        tracker->ProcessEvent(event, p_data);
+      } else {
+        tracker->ProcessEvent(event, p_data);
+      }
+    } break;
+
+    case PACS_CONNECTION_STATE_EVT: {
+      PacsConnectionState *pacs_state =  (PacsConnectionState *) p_data;
+      UpdatePacsState(pacs_state->state);
+      int state = StreamTracker::kStateIdle;
+      if (pacs_state->state != ConnectionState::DISCONNECTED) {
+        if(PushEventToTracker(event, p_data, &transient_state_ids)) {
+          break;
+        } else {
+
+          std::vector<StreamContext *> *context_list = contexts->GetAllContexts();
+          std::vector<StreamType> streams;
+
+          for (auto it = context_list->begin(); it != context_list->end(); it++) {
+             if((*it)->stream_state != StreamState::DISCONNECTED) {
+              streams.push_back((*it)->stream_type);
+              state = state_map[(*it)->stream_state];
+            }
+          }
+
+          StreamTracker *tracker = stream_trackers.FindByStreamsType(
+                                                          &streams);
+          // create the new tracker
+          if(tracker == nullptr) {
+            // validate the combinations media Tx or voice TX|RX
+            StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                                  state,
+                                  this, nullptr, nullptr, &streams,
+                                  StreamControlType::Disconnect);
+            tracker->Start();
+            tracker->ProcessEvent(event, p_data);
+          } else {
+            tracker->ProcessEvent(event, p_data);
+          }
+        }
+      } else {
+
+        std::vector<StreamContext *> *context_list = contexts->GetAllContexts();
+        std::vector<StreamType> disc_streams;
+
+        for (auto it = context_list->begin(); it != context_list->end(); it++) {
+           if((*it)->stream_state != StreamState::DISCONNECTED) {
+            disc_streams.push_back((*it)->stream_type);
+          }
+        }
+
+        LOG(WARNING) << __func__  << ": size: " << disc_streams.size();
+
+        // validate the combinations media Tx or voice TX|RX
+        std::map<StreamTracker * , std::vector<StreamType> >
+           tracker_and_type_list =
+                      stream_trackers.GetTrackersByType(&disc_streams);
+
+        // check if all streams disconnection or subset of streams
+        // create the new tracker
+        for (auto itr = tracker_and_type_list.begin();
+                  itr != tracker_and_type_list.end(); itr++) {
+          if(itr->first == nullptr) {
+            std::map<int , std::vector<StreamType> >
+               list = SplitContextOnState(&itr->second);
+            for (auto itr_2 = list.begin(); itr_2 != list.end(); itr_2++) {
+              StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                                  itr_2->first,
+                                  this, nullptr, nullptr, &itr_2->second,
+                                  StreamControlType::Disconnect);
+              LOG(ERROR) << __func__ << " new tracker start ";
+              tracker->Start();
+              tracker->ProcessEvent(event, p_data);
+            }
+          } else {
+            LOG(ERROR) << __func__ << " existing tracker start ";
+            StreamTracker *tracker = itr->first;
+            tracker->ProcessEvent(event, p_data);
+          }
+        }
+      }
+    } break;
+
+    case PACS_AUDIO_CONTEXT_RES_EVT: {
+      // update the same event to upper layers also
+      PacsAvailableContexts *contexts = (PacsAvailableContexts *) p_data;
+      ucl_callbacks->OnStreamAvailable(
+                          address,
+                          (contexts->available_contexts >> 16),
+                          (contexts->available_contexts & 0xFFFF));
+      std::vector<int> state_ids = { StreamTracker::kStateStarting,
+                                     StreamTracker::kStateUpdating};
+      PushEventToTracker(event, p_data, &state_ids);
+    } break;
+
+    case PACS_DISCOVERY_RES_EVT:
+    case ASCS_DISCOVERY_RES_EVT: {
+      // validate the combinations media Tx or voice TX|RX
+      std::vector<int> state_ids = { StreamTracker::kStateConnecting,
+                                     StreamTracker::kStateReconfiguring };
+      PushEventToTracker(event, p_data, &state_ids);
+    } break;
+
+    case ASCS_CONNECTION_STATE_EVT: {
+      AscsConnectionState *ascs_state =  (AscsConnectionState *) p_data;
+      UpdateAscsState(ascs_state->state);
+      PushEventToTracker(event, p_data, &transient_state_ids);
+    } break;
+
+    case ASCS_ASE_OP_FAILED_EVT: {
+      if(PushEventToTracker(event, p_data, &transient_state_ids)) {
+        break;
+      }
+    } break;
+
+    case ASCS_ASE_STATE_EVT: {
+      if(PushEventToTracker(event, p_data, &transient_state_ids)) {
+        break;
+      }
+      // create strm trackers based on ase ID and push it to
+      // newly created tracker.
+      // TODO Handle Releasing , disabling, codec configured states
+      // initiated from remote device
+
+      AscsState *ascs =  ((AscsState *) p_data);
+      UcastAudioStream *audio_stream = nullptr;
+      // find out the stream for the given ase id
+      uint8_t ase_id = ascs->ase_params.ase_id;
+      std::vector<StreamType> streams;
+      int state = StreamTracker::kStateIdle;
+
+      UcastAudioStreams *audio_strms = GetAudioStreams();
+      audio_stream = audio_strms->FindByAseId(ase_id);
+      if(audio_stream) {
+        StreamType type = {
+                           .type = audio_stream->audio_context,
+                           .direction = audio_stream->direction
+                          };
+        streams.push_back(type);
+        state = audio_stream->overall_state;
+      }
+
+      LOG(WARNING) << __func__  << " size " << streams.size();
+
+      if (ascs->ase_params.ase_state == ascs::ASE_STATE_RELEASING) {
+
+        StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                                 state,
+                                 this, nullptr, nullptr, &streams,
+                                 StreamControlType::Disconnect);
+        tracker->Start();
+        tracker->ProcessEvent(event, p_data);
+      } else if (ascs->ase_params.ase_state == ascs::ASE_STATE_DISABLING) {
+
+        StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                                 state,
+                                 this, nullptr, nullptr, &streams,
+                                 StreamControlType::Stop);
+        tracker->Start();
+        tracker->ProcessEvent(event, p_data);
+      } else if (ascs->ase_params.ase_state ==
+                                      ascs::ASE_STATE_CODEC_CONFIGURED) {
+        // TODO reconfig from remote device
+      }  else if (ascs->ase_params.ase_state ==
+                                      ascs::ASE_STATE_QOS_CONFIGURED) {
+        // this can happen when CIS is lost and detected on remote side
+        // first so it will immediately transition to QOS configured.
+        StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                                 state,
+                                 this, nullptr, nullptr, &streams,
+                                 StreamControlType::Stop);
+        tracker->Start();
+        tracker->ProcessEvent(event, p_data);
+      }
+    } break;
+
+    case CIS_GROUP_STATE_EVT: {
+      // validate the combinations media Tx or voice TX|RX
+      std::vector<int> state_ids = { StreamTracker::kStateStarting,
+                                     StreamTracker::kStateStopping,
+                                     StreamTracker::kStateDisconnecting };
+      PushEventToTracker(event, p_data, &state_ids);
+    } break;
+
+    case CIS_STATE_EVT: {
+      CisStreamState *data = (CisStreamState *) p_data;
+
+      if(PushEventToTracker(event, p_data, &transient_state_ids)) {
+        break;
+      }
+
+      // find out the stream for the given ase id
+      int state = StreamTracker::kStateIdle;
+      std::vector<StreamType> streams;
+
+      UcastAudioStreams *audio_strms = GetAudioStreams();
+      std::vector<UcastAudioStream *> cis_streams = audio_strms->FindByCisId(
+                                                 data->cig_id, data->cis_id);
+
+
+      if(cis_streams.empty()) break;
+
+      for (auto it = cis_streams.begin(); it != cis_streams.end(); it++) {
+        StreamType type = {
+                           .type = (*it)->audio_context,
+                           .direction = (*it)->direction
+                          };
+        streams.push_back(type);
+        state = (*it)->overall_state;
+      }
+
+      LOG(WARNING) << __func__  << " size " << streams.size();
+
+      // create strm trackers based on CIS ID and push it to
+      // newly created tracker.
+      // CIS disconnected
+      if(data->state == CisState::READY) {
+        StreamTracker *tracker = stream_trackers.FindOrAddByType(
+                                 state,
+                                 this, nullptr, nullptr, &streams,
+                                 StreamControlType::Stop);
+        tracker->Start();
+        tracker->ProcessEvent(event, p_data);
+      }
+    } break;
+
+    case BAP_TIME_OUT_EVT: {
+      BapTimeout* evt_data = (BapTimeout*) p_data;
+
+      //Get Cached tracker and check if it is valid
+      if (stream_trackers.IsStreamTrackerValid(evt_data->tracker,
+                                                &transient_state_ids)) {
+        PushEventToTracker(event, p_data, &transient_state_ids);
+      } else {
+        LOG(WARNING) << __func__  << ": Tracker is not valid ";
+      }
+    } break;
+
+    default:
+      break;
+  }
+
+  // look for all stream trackers which are moved to stable states
+  // like connected, streaming, idle, and destroy those trackers here
+  stream_trackers.RemoveByStates(stable_state_ids);
+}
+
+// TODO to introduce a queue for serializing the Audio stream establishment
+UstreamManager* UstreamManagers::FindByAddress(const RawAddress& address) {
+  auto iter = std::find_if(strm_mgrs.begin(), strm_mgrs.end(),
+                       [&address](UstreamManager *strm_mgr) {
+                          return strm_mgr->GetAddress() == address;
+                       });
+
+  return (iter == strm_mgrs.end()) ? nullptr : (*iter);
+}
+
+UstreamManager* UstreamManagers::FindorAddByAddress(const RawAddress& address,
+                        PacsClient *pacs_client, uint16_t pacs_client_id,
+                        AscsClient *ascs_client, CisInterface *cis_intf,
+                        UcastClientCallbacks* ucl_callbacks,
+                        BapAlarm *bap_alarm)  {
+  auto iter = std::find_if(strm_mgrs.begin(), strm_mgrs.end(),
+                       [&address](UstreamManager *strm_mgr) {
+                          return strm_mgr->GetAddress() == address;
+                       });
+
+  if (iter == strm_mgrs.end()) {
+    UstreamManager *strm_mgr = new UstreamManager(address, pacs_client,
+                                   pacs_client_id, ascs_client, cis_intf,
+                                   ucl_callbacks, bap_alarm);
+    strm_mgrs.push_back(strm_mgr);
+    return strm_mgr;
+  } else {
+    return (*iter);
+  }
+}
+
+std::vector<UstreamManager *> *UstreamManagers::GetAllManagers() {
+  return &strm_mgrs;
+
+}
+
+void UstreamManagers::Remove(const RawAddress& address) {
+  for (auto it = strm_mgrs.begin(); it != strm_mgrs.end();) {
+    if ((*it)->GetAddress() == address) {
+      delete(*it);
+      it = strm_mgrs.erase(it);
+    } else {
+      it++;
+    }
+  }
+}
+
+}  // namespace ucast
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/bap/uclient_strm_tracker.cc b/le_audio/system/bt/bta/bap/uclient_strm_tracker.cc
new file mode 100644
index 0000000..3272e56
--- /dev/null
+++ b/le_audio/system/bt/bta/bap/uclient_strm_tracker.cc
@@ -0,0 +1,4001 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 "bta_bap_uclient_api.h"
+#include "ucast_client_int.h"
+#include "bt_trace.h"
+#include "btif/include/btif_bap_codec_utils.h"
+#include "osi/include/properties.h"
+#include "uclient_alarm.h"
+
+namespace bluetooth {
+namespace bap {
+namespace ucast {
+
+using bluetooth::bap::pacs::CodecIndex;
+using bluetooth::bap::pacs::CodecBPS;
+using bluetooth::bap::pacs::CodecConfig;
+using bluetooth::bap::pacs::ConnectionState;
+using bluetooth::bap::pacs::CodecSampleRate;
+using bluetooth::bap::pacs::CodecChannelMode;
+using bluetooth::bap::pacs::PacsClient;
+using bluetooth::bap::cis::CisInterface;
+using bluetooth::bap::ascs::AseCodecConfigOp;
+using bluetooth::bap::ascs::AseQosConfigOp;
+using bluetooth::bap::ascs::AseEnableOp;
+using bluetooth::bap::ascs::AseStartReadyOp;
+using bluetooth::bap::ascs::AseStopReadyOp;
+using bluetooth::bap::ascs::AseDisableOp;
+using bluetooth::bap::ascs::AseReleaseOp;
+using bluetooth::bap::ascs::AseUpdateMetadataOp;
+
+using cis::IsoHciStatus;
+using bluetooth::bap::alarm::BapAlarm;
+
+using bluetooth::bap::ucast::CONTENT_TYPE_MEDIA;
+using bluetooth::bap::ucast::CONTENT_TYPE_CONVERSATIONAL;
+using bluetooth::bap::ucast::CONTENT_TYPE_UNSPECIFIED;
+using bluetooth::bap::ucast::CONTENT_TYPE_GAME;
+
+constexpr uint8_t  LTV_TYPE_SAMP_FREQ           = 0X01;
+constexpr uint8_t  LTV_TYPE_FRAME_DUR           = 0x02;
+constexpr uint8_t  LTV_TYPE_CHNL_ALLOC          = 0x03;
+constexpr uint8_t  LTV_TYPE_OCTS_PER_FRAME      = 0X04;
+constexpr uint8_t  LTV_TYPE_FRAMES_PER_SDU      = 0X05;
+constexpr uint8_t  LTV_TYPE_STRM_AUDIO_CONTEXTS = 0x02;
+
+constexpr uint8_t  LTV_LEN_SAMP_FREQ            = 0X02;
+constexpr uint8_t  LTV_LEN_FRAME_DUR            = 0x02;
+constexpr uint8_t  LTV_LEN_CHNL_ALLOC           = 0x05;
+constexpr uint8_t  LTV_LEN_OCTS_PER_FRAME       = 0X03;
+constexpr uint8_t  LTV_LEN_FRAMES_PER_SDU       = 0X02;
+constexpr uint8_t  LTV_LEN_STRM_AUDIO_CONTEXTS  = 0x03;
+
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_8K         = 0X01;
+//constexpr uint8_t  LTV_VAL_SAMP_FREQ_11K        = 0X02;
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_16K        = 0X03;
+//constexpr uint8_t  LTV_VAL_SAMP_FREQ_22K        = 0X04;
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_24K        = 0X05;
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_32K        = 0X06;
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_441K       = 0X07;
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_48K        = 0X08;
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_882K       = 0X09;
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_96K        = 0X0A;
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_176K       = 0X0B;
+constexpr uint8_t  LTV_VAL_SAMP_FREQ_192K       = 0X0C;
+//constexpr uint8_t  LTV_VAL_SAMP_FREQ_384K       = 0X0D;
+
+constexpr uint8_t  LC3_CODEC_ID          = 0x06;
+constexpr uint8_t  ASCS_CLIENT_ID        = 0x01;
+
+constexpr uint8_t  TGT_LOW_LATENCY       = 0x01;
+constexpr uint8_t  TGT_BAL_LATENCY       = 0x02;
+constexpr uint8_t  TGT_HIGH_RELIABLE     = 0x03;
+
+std::map<CodecSampleRate, uint8_t> freq_to_ltv_map = {
+  {CodecSampleRate::CODEC_SAMPLE_RATE_8000,   LTV_VAL_SAMP_FREQ_8K   },
+  {CodecSampleRate::CODEC_SAMPLE_RATE_16000,  LTV_VAL_SAMP_FREQ_16K  },
+  {CodecSampleRate::CODEC_SAMPLE_RATE_24000,  LTV_VAL_SAMP_FREQ_24K  },
+  {CodecSampleRate::CODEC_SAMPLE_RATE_32000,  LTV_VAL_SAMP_FREQ_32K  },
+  {CodecSampleRate::CODEC_SAMPLE_RATE_44100,  LTV_VAL_SAMP_FREQ_441K },
+  {CodecSampleRate::CODEC_SAMPLE_RATE_48000,  LTV_VAL_SAMP_FREQ_48K  },
+  {CodecSampleRate::CODEC_SAMPLE_RATE_88200,  LTV_VAL_SAMP_FREQ_882K },
+  {CodecSampleRate::CODEC_SAMPLE_RATE_96000,  LTV_VAL_SAMP_FREQ_96K  },
+  {CodecSampleRate::CODEC_SAMPLE_RATE_176400, LTV_VAL_SAMP_FREQ_176K },
+  {CodecSampleRate::CODEC_SAMPLE_RATE_192000, LTV_VAL_SAMP_FREQ_192K }
+};
+
+std::list<uint8_t> directions = {
+  cis::DIR_FROM_AIR,
+  cis::DIR_TO_AIR
+};
+
+std::vector<uint32_t> locations = {
+  AUDIO_LOC_LEFT,
+  AUDIO_LOC_RIGHT
+};
+
+// common functions used from Stream tracker state Handlers
+uint8_t StreamTracker::ChooseBestCodec(StreamType stream_type,
+                              std::vector<CodecQosConfig> *codec_qos_configs,
+                              PacsDiscovery *pacs_discovery) {
+  bool codec_found = false;
+  uint8_t index = 0;
+  // check the stream direction, based on direction look for
+  // matching record from preferred list of upper layer and
+  // remote device sink or src pac records
+  std::vector<CodecConfig> *pac_records = nullptr;
+  if(stream_type.direction == ASE_DIRECTION_SINK) {
+    pac_records = &pacs_discovery->sink_pac_records;
+  } else if(stream_type.direction == ASE_DIRECTION_SRC) {
+    pac_records = &pacs_discovery->src_pac_records;
+  }
+
+  if (!pac_records) {
+     LOG(ERROR) << __func__ << "pac_records is null";
+     return 0xFF;
+  }
+  DeviceType dev_type = strm_mgr_->GetDevType();
+  for (auto i = codec_qos_configs->begin(); i != codec_qos_configs->end()
+                                          ; i++, index++) {
+    if(dev_type == DeviceType::EARBUD ||
+       dev_type == DeviceType::HEADSET_STEREO) {
+      if((*i).qos_config.ascs_configs.size() != 1) continue;
+    } else if(dev_type == DeviceType::HEADSET_SPLIT_STEREO) {
+      if((*i).qos_config.ascs_configs.size() != 2) continue;
+    }
+    for (auto j = pac_records->begin();
+                    j != pac_records->end();j++) {
+      CodecConfig *src = &((*i).codec_config);
+      CodecConfig *dst = &(*j);
+      if (IsCodecConfigEqual(src,dst)) {
+        LOG(WARNING) << __func__ << ": Checking for matching Codec";
+        if (GetLc3QPreference(src) &&
+            GetCapaVendorMetaDataLc3QPref(dst)) {
+          LOG(INFO) << __func__ << ": Matching Codec LC3Q Found "
+                   << ", for direction: " << loghex(stream_type.direction);
+          uint8_t lc3qVer = GetCapaVendorMetaDataLc3QVer(dst);
+          UpdateVendorMetaDataLc3QPref(src, true);
+          UpdateVendorMetaDataLc3QVer(src, lc3qVer);
+        } else {
+          LOG(INFO) << __func__ << ": LC3Q not prefered, going with LC3 "
+                   << "for direction: " << loghex(stream_type.direction);
+        }
+        codec_found = true;
+        break;
+      }
+    }
+    if(codec_found) break;
+  }
+  if(codec_found) return index;
+  else return 0xFF;
+}
+
+// fine tuning the QOS params (RTN, MTL, PD) based on
+// remote device preferences
+bool StreamTracker::ChooseBestQos(QosConfig *src_config,
+                                  ascs::AseCodecConfigParams *rem_qos_prefs,
+                                  QosConfig *dst_config,
+                                  int stream_state,
+                                  uint8_t stream_direction) {
+  uint8_t final_rtn = 0xFF;
+  uint16_t final_mtl = 0xFFFF;
+  uint32_t req_pd = (src_config->ascs_configs[0].presentation_delay[0] |
+                     src_config->ascs_configs[0].presentation_delay[1] << 8 |
+                     src_config->ascs_configs[0].presentation_delay[2] << 16);
+
+  uint32_t rem_pd_min = (rem_qos_prefs->pd_min[0] |
+                         rem_qos_prefs->pd_min[1] << 8 |
+                         rem_qos_prefs->pd_min[2] << 16);
+
+  uint32_t rem_pd_max = (rem_qos_prefs->pd_max[0] |
+                         rem_qos_prefs->pd_max[1] << 8 |
+                         rem_qos_prefs->pd_max[2] << 16);
+
+  uint32_t rem_pref_pd_min = (rem_qos_prefs->pref_pd_min[0] |
+                              rem_qos_prefs->pref_pd_min[1] << 8 |
+                              rem_qos_prefs->pref_pd_min[2] << 16);
+
+  uint32_t rem_pref_pd_max = (rem_qos_prefs->pref_pd_max[0] |
+                              rem_qos_prefs->pref_pd_max[1] << 8 |
+                              rem_qos_prefs->pref_pd_max[2] << 16);
+
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  std::vector<UcastAudioStream *> streams = audio_strms->FindByCigId(
+                                 src_config->ascs_configs[0].cig_id,
+                                 stream_state);
+
+  // check if the RTN and MTL is with in the limits
+  if(stream_direction == ASE_DIRECTION_SINK) {
+    if(src_config->cis_configs[0].rtn_m_to_s > rem_qos_prefs->pref_rtn) {
+      final_rtn = rem_qos_prefs->pref_rtn;
+    }
+    if(src_config->cig_config.max_tport_latency_m_to_s > rem_qos_prefs->mtl) {
+      final_mtl = rem_qos_prefs->mtl;
+    }
+  } else if(stream_direction == ASE_DIRECTION_SRC) {
+    if(src_config->cis_configs[0].rtn_s_to_m > rem_qos_prefs->pref_rtn) {
+      final_rtn = rem_qos_prefs->pref_rtn;
+    }
+    if(src_config->cig_config.max_tport_latency_s_to_m > rem_qos_prefs->mtl) {
+      final_mtl = rem_qos_prefs->mtl;
+    }
+  }
+
+  LOG(INFO) << __func__  << " req_pd: " << loghex(req_pd)
+               << " rem_pd_min: " << loghex(rem_pd_min)
+               << " rem_pd_max: " << loghex(rem_pd_max)
+               << " rem_pref_pd_min: " << loghex(rem_pref_pd_min)
+               << " rem_pref_pd_max: " << loghex(rem_pref_pd_max);
+
+  // check if PD is within the limits
+  if(rem_pref_pd_min && rem_pref_pd_max) {
+    if(req_pd < rem_pref_pd_min) {
+      memcpy(&dst_config->ascs_configs[0].presentation_delay,
+             &rem_qos_prefs->pref_pd_min,
+             sizeof(dst_config->ascs_configs[0].presentation_delay));
+    } else if(req_pd > rem_pref_pd_max) {
+      memcpy(&dst_config->ascs_configs[0].presentation_delay,
+             &rem_qos_prefs->pref_pd_max,
+             sizeof(dst_config->ascs_configs[0].presentation_delay));
+    }
+  } else {
+    if(req_pd != rem_pd_min) {
+      memcpy(&dst_config->ascs_configs[0].presentation_delay,
+             &rem_qos_prefs->pd_min,
+             sizeof(dst_config->ascs_configs[0].presentation_delay));
+    }
+  }
+
+  // check if anything to be updated for all streams
+  if(final_rtn == 0xFF && final_mtl == 0XFFFF) {
+    LOG(WARNING) << __func__  << " No fine tuning for QOS params";
+    return true;
+  } else if(final_rtn != 0xFF) {
+    LOG(WARNING) << __func__  << " Updated RTN to " << loghex(final_rtn);
+  } else if(final_mtl != 0XFFFF) {
+    LOG(WARNING) << __func__  << " Updated MTL to " << loghex(final_mtl);
+  }
+
+  for (auto i = streams.begin(); i != streams.end();i++) {
+    UcastAudioStream *stream = (*i);
+    if(stream_direction == ASE_DIRECTION_SINK) {
+      if(final_mtl != 0xFFFF) {
+        stream->qos_config.cig_config.max_tport_latency_m_to_s = final_mtl;
+      }
+      if(final_rtn != 0xFF) {
+        for (auto it = stream->qos_config.cis_configs.begin();
+                          it != stream->qos_config.cis_configs.end(); it++) {
+          (*it).rtn_m_to_s = final_rtn;
+        }
+      }
+    } else if(stream_direction == ASE_DIRECTION_SRC) {
+      if(final_mtl != 0xFFFF) {
+        stream->qos_config.cig_config.max_tport_latency_s_to_m = final_mtl;
+      }
+      if(final_rtn != 0xFF) {
+        for (auto it = stream->qos_config.cis_configs.begin();
+                          it != stream->qos_config.cis_configs.end(); it++) {
+          (*it).rtn_s_to_m = final_rtn;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool StreamTracker::HandlePacsConnectionEvent(void *p_data) {
+  PacsConnectionState *pacs_state =  (PacsConnectionState *) p_data;
+  if(pacs_state->state == ConnectionState::CONNECTED) {
+    LOG(INFO) << __func__ << " PACS server connected";
+  } else if(pacs_state->state == ConnectionState::DISCONNECTED) {
+    HandleInternalDisconnect(false);
+  }
+  return true;
+}
+
+bool StreamTracker::HandlePacsAudioContextEvent(
+                               PacsAvailableContexts *pacs_contexts) {
+  std::vector<StreamUpdate> *update_streams = GetMetaUpdateStreams();
+  uint8_t contexts_supported = 0;
+
+  // check if supported audio contexts has required contexts
+  for(auto it = update_streams->begin(); it != update_streams->end(); it++) {
+    if(it->update_type == StreamUpdateType::STREAMING_CONTEXT) {
+      if(it->stream_type.direction == ASE_DIRECTION_SINK) {
+        if(it->update_value & pacs_contexts->available_contexts) {
+          contexts_supported++;
+        }
+      } else if(it->stream_type.direction == ASE_DIRECTION_SRC) {
+        if((static_cast<uint64_t>(it->update_value) << 16) &
+             pacs_contexts->available_contexts) {
+          contexts_supported++;
+        }
+      }
+    }
+  }
+
+  if(contexts_supported != update_streams->size()) {
+    LOG(ERROR) << __func__  << ": No Matching available Contexts found";
+    return false;
+  } else {
+    return true;
+  }
+}
+
+bool StreamTracker::HandleCisEventsInStreaming(void* p_data) {
+  CisStreamState *data = (CisStreamState *) p_data;
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  if(data->state == CisState::READY) {
+    for(auto it = directions.begin(); it != directions.end(); ++it) {
+      if(data->direction & *it) {
+        // find out the stream here
+        UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                   (data->cig_id, data->cis_id, *it);
+        if(stream) {
+          stream->cis_state = data->state;
+          stream->cis_pending_cmd = CisPendingCmd::NONE;
+        }
+      }
+    }
+    TransitionTo(StreamTracker::kStateStopping);
+  }
+  return true;
+}
+
+bool StreamTracker::HandleAscsConnectionEvent(void *p_data) {
+  AscsConnectionState *ascs_state =  (AscsConnectionState *) p_data;
+  if(ascs_state->state == GattState::CONNECTED) {
+    LOG(INFO) << __func__ << "ASCS server connected";
+  } else if(ascs_state->state == GattState::DISCONNECTED) {
+    // make all streams ASE state ot idle so that further processing
+    // can happen
+    UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+    std::vector<UcastAudioStream *> *strms_list = audio_strms->GetAllStreams();
+
+    for (auto it = strms_list->begin(); it != strms_list->end(); it++) {
+      (*it)->ase_state = ascs::ASE_STATE_IDLE;
+      (*it)->ase_pending_cmd = AscsPendingCmd::NONE;
+      (*it)->overall_state = StreamTracker::kStateIdle;
+    }
+    HandleInternalDisconnect(false);
+  }
+  return true;
+}
+
+bool StreamTracker::ValidateAseUpdate(void* p_data,
+                                      IntStrmTrackers *int_strm_trackers,
+                                      int exp_strm_state) {
+  AscsState *ascs =  ((AscsState *) p_data);
+
+  uint8_t ase_id = ascs->ase_params.ase_id;
+
+  // check if current stream tracker is interested in this ASE update
+  if(int_strm_trackers->FindByAseId(ase_id)
+                             == nullptr) {
+    LOG(INFO) << __func__  << "Not intended for this tracker";
+    return false;
+  }
+
+  // find out the stream for the given ase id
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+  UcastAudioStream *stream = audio_strms->FindByAseId(ase_id);
+
+  LOG(INFO) << __func__  << ": Streams Size = " << audio_strms->size()
+                         << ": ASE Id = " << loghex(ase_id);
+
+  if (stream == nullptr || stream->overall_state != exp_strm_state) {
+    LOG(WARNING) << __func__  << "No Audio Stream found";
+    return false;
+  }
+
+  stream->ase_state = ascs->ase_params.ase_state;
+  stream->ase_params = ascs->ase_params;
+  stream->ase_pending_cmd = AscsPendingCmd::NONE;
+  return true;
+}
+
+bool StreamTracker::HandleRemoteDisconnect(uint32_t event,
+                                           void* p_data, int cur_state) {
+  UpdateControlType(StreamControlType::Disconnect);
+  std::vector<StreamType> streams;
+
+  switch(cur_state) {
+    case StreamTracker::kStateConnecting: {
+      std::vector<StreamConnect> *conn_streams = GetConnStreams();
+
+      for (auto it = conn_streams->begin(); it != conn_streams->end(); it++) {
+        StreamType type = it->stream_type;
+        streams.push_back(type);
+      }
+      UpdateStreams(&streams);
+    } break;
+    case StreamTracker::kStateReconfiguring: {
+      std::vector<StreamReconfig> *reconf_streams = GetReconfStreams();
+
+      for (auto it = reconf_streams->begin();
+                           it != reconf_streams->end(); it++) {
+        StreamType type = it->stream_type;
+        streams.push_back(type);
+      }
+      UpdateStreams(&streams);
+    } break;
+  }
+
+  // update the state to disconnecting
+  TransitionTo(StreamTracker::kStateDisconnecting);
+  ProcessEvent(event, p_data);
+  return true;
+}
+
+bool StreamTracker::StreamCanbeDisconnected(StreamContext *cur_context,
+                                            uint8_t ase_id) {
+  bool can_be_disconnected = false;
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  StreamAttachedState state = (StreamAttachedState)
+               (static_cast<uint8_t> (StreamAttachedState::PHYSICAL) |
+                static_cast<uint8_t> (StreamAttachedState::IDLE_TO_PHY) |
+                static_cast<uint8_t> (StreamAttachedState::VIR_TO_PHY));
+
+  std::vector<StreamContext *> attached_contexts =
+                 contexts->FindByAseAttachedState(ase_id, state);
+
+  LOG(INFO) << __func__ <<": attached_contexts: size : "
+                        << attached_contexts.size();
+  if(cur_context->attached_state == StreamAttachedState::PHYSICAL ||
+     cur_context->attached_state == StreamAttachedState::IDLE_TO_PHY ||
+     cur_context->attached_state == StreamAttachedState::VIR_TO_PHY ) {
+    can_be_disconnected = true;
+  }
+  return can_be_disconnected;
+}
+
+bool StreamTracker::HandleInternalDisconnect(bool release) {
+
+  UpdateControlType(StreamControlType::Disconnect);
+
+  std::vector<StreamType> streams;
+
+  int cur_state = StateId();
+  LOG(WARNING) << __func__ <<": cur_state: " << cur_state
+                           <<", release: " << release;
+  switch(cur_state) {
+    case StreamTracker::kStateConnecting: {
+      std::vector<StreamConnect> *conn_streams = GetConnStreams();
+
+      for (auto it = conn_streams->begin(); it != conn_streams->end(); it++) {
+        StreamType type = it->stream_type;
+        streams.push_back(type);
+      }
+      UpdateStreams(&streams);
+    } break;
+    case StreamTracker::kStateReconfiguring: {
+      std::vector<StreamReconfig> *reconf_streams = GetReconfStreams();
+
+      for (auto it = reconf_streams->begin();
+                           it != reconf_streams->end(); it++) {
+        StreamType type = it->stream_type;
+        streams.push_back(type);
+      }
+      UpdateStreams(&streams);
+    } break;
+  }
+
+  if (release) {
+    StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+    AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+    UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+    std::vector<AseReleaseOp> ase_ops;
+    std::vector<StreamType> *disc_streams = GetStreams();
+
+    for (auto it = disc_streams->begin(); it != disc_streams->end(); it++) {
+      StreamContext *context = contexts->FindOrAddByType(*it);
+
+      for (auto id = context->stream_ids.begin();
+                         id != context->stream_ids.end(); id++) {
+
+        UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+        bool can_be_disconnected = StreamCanbeDisconnected(context, id->ase_id);
+        if (can_be_disconnected &&
+            stream && stream->overall_state == cur_state &&
+            stream->ase_state != ascs::ASE_STATE_IDLE &&
+            stream->ase_state != ascs::ASE_STATE_RELEASING &&
+            stream->ase_pending_cmd != AscsPendingCmd::RELEASE_ISSUED) {
+          LOG(WARNING) << __func__
+                     <<": ASE State : " << loghex(stream->ase_state);
+          AseReleaseOp release_op = {
+                                      .ase_id = stream->ase_id
+                                    };
+          ase_ops.push_back(release_op);
+          stream->ase_pending_cmd = AscsPendingCmd::RELEASE_ISSUED;
+          // change the overall state to Disconnecting
+          stream->overall_state = StreamTracker::kStateDisconnecting;
+        }
+      }
+    }
+
+    // send consolidated release command to ASCS client
+    if(ase_ops.size()) {
+      LOG(WARNING) << __func__  << ": Going For ASCS Release op";
+      ascs_client->Release(ASCS_CLIENT_ID, strm_mgr_->GetAddress(), ase_ops);
+    }
+  }
+  // update the state to disconnecting
+  TransitionTo(StreamTracker::kStateDisconnecting);
+  return true;
+}
+
+bool StreamTracker::HandleRemoteStop(uint32_t event,
+                                           void* p_data, int cur_state) {
+  AscsState *ascs =  ((AscsState *) p_data);
+  uint8_t ase_id = ascs->ase_params.ase_id;
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  UcastAudioStream *stream = audio_strms->FindByAseId(ase_id);
+
+  if(!stream) return false;
+
+  if(stream->direction & cis::DIR_TO_AIR) {
+    LOG(ERROR) << __func__  << ": Invalid State transition to Disabling"
+               << ": ASE Id = " << loghex(ase_id);
+    return false;
+  }
+
+  UpdateControlType(StreamControlType::Stop);
+
+  if(cur_state != StreamTracker::kStateStarting ||
+     cur_state != StreamTracker::kStateStreaming) {
+    return false;
+  }
+  // update the state to stopping
+  TransitionTo(StreamTracker::kStateStopping);
+  ProcessEvent(event, p_data);
+  return true;
+}
+
+bool StreamTracker::HandleAbruptStop(uint32_t event, void* p_data) {
+  AscsState *ascs =  ((AscsState *) p_data);
+  uint8_t ase_id = ascs->ase_params.ase_id;
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  UcastAudioStream *stream = audio_strms->FindByAseId(ase_id);
+
+  if(!stream) return false;
+  stream->ase_pending_cmd = AscsPendingCmd::NONE;
+
+  UpdateControlType(StreamControlType::Stop);
+
+  // update the state to stopping
+  TransitionTo(StreamTracker::kStateStopping);
+  return true;
+}
+
+bool StreamTracker::HandleRemoteReconfig(uint32_t event,
+                                           void* p_data, int cur_state) {
+  UpdateControlType(StreamControlType::Reconfig);
+  std::vector<StreamType> streams;
+
+  if(cur_state != StreamTracker::kStateConnected) {
+    return false;
+  }
+  // update the state to Reconfiguring
+  TransitionTo(StreamTracker::kStateReconfiguring);
+  ProcessEvent(event, p_data);
+  return true;
+}
+
+void StreamTracker::HandleAseOpFailedEvent(void *p_data) {
+  AscsOpFailed *ascs_op =  ((AscsOpFailed *) p_data);
+  std::vector<ascs::AseOpStatus> *ase_list = &ascs_op->ase_list;
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+  if(ascs_op->ase_op_id == ascs::AseOpId::CODEC_CONFIG) {
+    // treat it like internal failure
+    for (auto i = ase_list->begin(); i != ase_list->end();i++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId((i)->ase_id);
+      if(stream) {
+        stream->ase_pending_cmd = AscsPendingCmd::NONE;
+        stream->overall_state = StreamTracker::kStateIdle;
+      }
+    }
+    HandleInternalDisconnect(false);
+  } else {
+    HandleInternalDisconnect(true);
+  }
+}
+
+void StreamTracker::HandleAseStateEvent(void *p_data,
+                                        StreamControlType control_type,
+                                        IntStrmTrackers *int_strm_trackers) {
+  // check the state and if the state is codec configured for all ASEs
+  // then proceed with group creation
+  AscsState *ascs =  reinterpret_cast<AscsState *> (p_data);
+
+  uint8_t ase_id = ascs->ase_params.ase_id;
+
+  // check if current stream tracker is interested in this ASE update
+  if(int_strm_trackers->FindByAseId(ase_id) == nullptr) {
+    LOG(INFO) << __func__ << ": Not intended for this tracker";
+    return;
+  }
+
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+  UcastAudioStream *stream = audio_strms->FindByAseId(ase_id);
+
+  if(stream == nullptr) {
+    return;
+  } else {
+    stream->ase_state = ascs->ase_params.ase_state;
+    stream->ase_params = ascs->ase_params;
+    stream->ase_pending_cmd = AscsPendingCmd::NONE;
+  }
+
+  if(ascs->ase_params.ase_state == ascs::ASE_STATE_CODEC_CONFIGURED) {
+    stream->pref_qos_params = ascs->ase_params.codec_config_params;
+    // find out the stream for the given ase id
+    LOG(INFO) << __func__  << ": Total Num Streams = " << audio_strms->size()
+                           << ": ASE Id = " << loghex(ase_id);
+
+    // decide on best QOS params by comparing the upper layer prefs
+    // and remote dev's preferences
+    int state = StreamTracker::kStateIdle;
+
+    if(control_type == StreamControlType::Connect) {
+      state = StreamTracker::kStateConnecting;
+    } else if(control_type == StreamControlType::Reconfig) {
+      state = StreamTracker::kStateReconfiguring;
+    }
+
+    // check for all trackers codec is configured or not
+    std::vector<IntStrmTracker *> *all_trackers =
+                        int_strm_trackers->GetTrackerList();
+    uint8_t num_codec_configured = 0;
+    for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+      if (!stream) {
+        LOG(ERROR) << __func__  << "stream is null";
+        continue;
+      }
+      if(stream->ase_pending_cmd == AscsPendingCmd::NONE &&
+         (stream->ase_state == ascs::ASE_STATE_CODEC_CONFIGURED ||
+         (control_type == StreamControlType::Reconfig &&
+          stream->ase_state == ascs::ASE_STATE_QOS_CONFIGURED))) {
+        num_codec_configured++;
+      }
+    }
+
+    if(int_strm_trackers->size() != num_codec_configured) {
+      LOG(WARNING) << __func__  << " Codec Not Configured For All Streams";
+      return;
+    }
+
+    // check for all streams together so that final group params
+    // will be decided
+    for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+      if (stream) {
+        ChooseBestQos(&stream->req_qos_config, &stream->pref_qos_params,
+                    &stream->qos_config, state, stream->direction);
+      }
+    }
+    CheckAndSendQosConfig(int_strm_trackers);
+
+  } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_QOS_CONFIGURED) {
+    // TODO update the state as connected using callbacks
+    // make the state transition to connected
+
+    // check for all trackers QOS is configured or not
+    // if so update it as streams are connected
+    std::vector<IntStrmTracker *> *all_trackers =
+                        int_strm_trackers->GetTrackerList();
+    uint8_t num_qos_configured = 0;
+    for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+      if(stream && stream->ase_state == ascs::ASE_STATE_QOS_CONFIGURED &&
+         stream->ase_pending_cmd == AscsPendingCmd::NONE) {
+        num_qos_configured++;
+      }
+    }
+
+    if(int_strm_trackers->size() != num_qos_configured) {
+      LOG(WARNING) << __func__  << " QOS Not Configured For All Streams";
+      return;
+    }
+
+    for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+      if (!stream) {
+        LOG(ERROR) << __func__  << "stream is null";
+        continue;
+      }
+      StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+      StreamContext *context = contexts->FindOrAddByType(
+                                         (*i)->strm_type);
+      if(context->attached_state == StreamAttachedState::IDLE_TO_PHY ||
+         context->attached_state == StreamAttachedState::VIR_TO_PHY) {
+        context->attached_state = StreamAttachedState::PHYSICAL;
+        LOG(INFO) << __func__  << " Attached state made physical";
+      }
+      stream->overall_state = kStateConnected;
+    }
+
+    // update the state to connected
+    TransitionTo(StreamTracker::kStateConnected);
+
+  } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_RELEASING) {
+    HandleRemoteDisconnect(ASCS_ASE_STATE_EVT, p_data, StateId());
+  }
+}
+
+bool StreamTracker::HandleStreamUpdate (int cur_state) {
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+  std::vector<AseUpdateMetadataOp> ase_meta_ops;
+  std::vector<StreamUpdate> *update_streams = GetMetaUpdateStreams();
+
+  for (auto it = update_streams->begin();
+                       it != update_streams->end(); it++) {
+    StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+
+    for (auto id = context->stream_ids.begin();
+              id != context->stream_ids.end(); id++) {
+      std::vector<uint8_t> meta_data;
+      UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+      if(stream && stream->ase_state != ascs::ASE_STATE_ENABLING &&
+         stream->ase_state != ascs::ASE_STATE_STREAMING) {
+        continue;
+      }
+      if(it->update_type == StreamUpdateType::STREAMING_CONTEXT) {
+        uint8_t len = LTV_LEN_STRM_AUDIO_CONTEXTS;
+        uint8_t type = LTV_TYPE_STRM_AUDIO_CONTEXTS;
+        uint16_t value = it->update_value;
+        if(stream) stream->audio_context = value;
+        meta_data.insert(meta_data.end(), &len, &len + 1);
+        meta_data.insert(meta_data.end(), &type, &type + 1);
+        meta_data.insert(meta_data.end(), ((uint8_t *)&value),
+                              ((uint8_t *)&value) + sizeof(uint16_t));
+      }
+
+      AseUpdateMetadataOp meta_op = {
+                            .ase_id = id->ase_id,
+                            .meta_data_len =
+                             static_cast <uint8_t> (meta_data.size()),
+                            .meta_data = meta_data // media or voice
+                          };
+      ase_meta_ops.push_back(meta_op);
+    }
+  }
+
+  // send consolidated update meta command to ASCS client
+  if(ase_meta_ops.size()) {
+    LOG(WARNING) << __func__  << ": Going For ASCS Update MetaData op";
+    ascs_client->UpdateStream(ASCS_CLIENT_ID, strm_mgr_->GetAddress(),
+                              ase_meta_ops);
+  } else {
+    return false;
+  }
+
+  if(cur_state == StreamTracker::kStateUpdating) {
+    for (auto it = update_streams->begin();
+                         it != update_streams->end(); it++) {
+      StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+      for (auto id = context->stream_ids.begin();
+                    id != context->stream_ids.end(); id++) {
+        UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+        if (stream != nullptr) {
+          // change the connection state to disable issued
+          stream->ase_pending_cmd = AscsPendingCmd::UPDATE_METADATA_ISSUED;
+          // change the overall state to Updating
+          stream->overall_state = StreamTracker::kStateUpdating;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool StreamTracker::HandleStop(void* p_data, int cur_state) {
+  if(p_data != nullptr) {
+    BapStop *evt_data = (BapStop *) p_data;
+    UpdateStreams(&evt_data->streams);
+  }
+  UpdateControlType(StreamControlType::Stop);
+
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+  std::vector<AseDisableOp> ase_ops;
+  std::vector<StreamType> *stop_streams = GetStreams();
+
+  for (auto it = stop_streams->begin();
+                       it != stop_streams->end(); it++) {
+    StreamContext *context = contexts->FindOrAddByType(*it);
+
+    for (auto id = context->stream_ids.begin();
+                         id != context->stream_ids.end(); id++) {
+      AseDisableOp disable_op = {
+                          .ase_id = id->ase_id
+      };
+      ase_ops.push_back(disable_op);
+    }
+  }
+
+  // send consolidated disable command to ASCS client
+  if(ase_ops.size()) {
+    LOG(WARNING) << __func__  << ": Going For ASCS Disable op";
+    ascs_client->Disable(ASCS_CLIENT_ID, strm_mgr_->GetAddress(), ase_ops);
+  }
+
+  for (auto it = stop_streams->begin();
+                       it != stop_streams->end(); it++) {
+    StreamContext *context = contexts->FindOrAddByType(*it);
+    for (auto id = context->stream_ids.begin();
+                  id != context->stream_ids.end(); id++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+      if (stream != nullptr && stream->overall_state == cur_state) {
+        // change the connection state to disable issued
+        stream->ase_pending_cmd = AscsPendingCmd::DISABLE_ISSUED;
+        // change the overall state to stopping
+        stream->overall_state = StreamTracker::kStateStopping;
+      }
+    }
+  }
+  TransitionTo(StreamTracker::kStateStopping);
+  return true;
+}
+
+bool StreamTracker::HandleDisconnect(void* p_data, int cur_state) {
+  // expect the disconnection for same set of streams connection
+  // has initiated ex: if connect is issued for media (tx), voice(tx & rx)
+  // then disconnect is expected for media (tx), voice(tx & rx).
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+  BapDisconnect *evt_data = (BapDisconnect *) p_data;
+
+  UpdateControlType(StreamControlType::Disconnect);
+
+  UpdateStreams(&evt_data->streams);
+
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+
+  std::vector<AseReleaseOp> ase_ops;
+  std::vector<StreamType> *disc_streams = GetStreams();
+
+  for (auto it = disc_streams->begin(); it != disc_streams->end(); it++) {
+    StreamContext *context = contexts->FindOrAddByType(*it);
+
+    for (auto id = context->stream_ids.begin();
+                  id != context->stream_ids.end(); id++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+      bool can_be_disconnected = StreamCanbeDisconnected(context, id->ase_id);
+      if(can_be_disconnected &&
+         stream && stream->overall_state != StreamTracker::kStateIdle &&
+         stream->overall_state != StreamTracker::kStateDisconnecting &&
+         stream->ase_pending_cmd != AscsPendingCmd::RELEASE_ISSUED) {
+        AseReleaseOp release_op = {
+                                    .ase_id = id->ase_id
+                                  };
+        ase_ops.push_back(release_op);
+        stream->ase_pending_cmd = AscsPendingCmd::RELEASE_ISSUED;
+        // change the overall state to starting
+        stream->overall_state = StreamTracker::kStateDisconnecting;
+      }
+    }
+  }
+
+  // send consolidated release command to ASCS client
+  if(ase_ops.size()) {
+    LOG(WARNING) << __func__  << ": Going For ASCS Release op";
+    ascs_client->Release(ASCS_CLIENT_ID, strm_mgr_->GetAddress(), ase_ops);
+  }
+
+  TransitionTo(StreamTracker::kStateDisconnecting);
+  return true;
+}
+
+void StreamTracker::CheckAndSendQosConfig(IntStrmTrackers *int_strm_trackers) {
+
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+  // check for all trackers CIG is created or not
+  // if so proceed with QOS config operaiton
+  std::vector<IntStrmTracker *> *all_trackers =
+                      int_strm_trackers->GetTrackerList();
+
+  std::vector<AseQosConfigOp> ase_ops;
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    QosConfig *qos_config = &stream->qos_config;
+    if(!stream || stream->ase_pending_cmd != AscsPendingCmd::NONE) {
+      continue;
+    }
+    if(stream->direction & cis::DIR_TO_AIR) {
+
+      AseQosConfigOp qos_config_op = {
+       .ase_id = (*i)->ase_id,
+       .cig_id = stream->cig_id,
+       .cis_id = stream->cis_id,
+       .sdu_interval = { qos_config->cig_config.sdu_interval_m_to_s[0],
+                         qos_config->cig_config.sdu_interval_m_to_s[1],
+                         qos_config->cig_config.sdu_interval_m_to_s[2] },
+       .framing = qos_config->cig_config.framing,
+       .phy = LE_2M_PHY,
+       .max_sdu_size = qos_config->cis_configs[(*i)->cis_id].max_sdu_m_to_s,
+       .retrans_number = qos_config->cis_configs[(*i)->cis_id].rtn_m_to_s,
+       .trans_latency = qos_config->cig_config.max_tport_latency_m_to_s,
+       .present_delay = {qos_config->ascs_configs[0].presentation_delay[0],
+                         qos_config->ascs_configs[0].presentation_delay[1],
+                         qos_config->ascs_configs[0].presentation_delay[2]}
+      };
+      ase_ops.push_back(qos_config_op);
+
+    } else if(stream->direction & cis::DIR_FROM_AIR) {
+      AseQosConfigOp qos_config_op = {
+       .ase_id = (*i)->ase_id,
+       .cig_id = stream->cig_id,
+       .cis_id = stream->cis_id,
+       .sdu_interval = { qos_config->cig_config.sdu_interval_s_to_m[0],
+                         qos_config->cig_config.sdu_interval_s_to_m[1],
+                         qos_config->cig_config.sdu_interval_s_to_m[2] },
+       .framing = qos_config->cig_config.framing,
+       .phy = LE_2M_PHY,
+       .max_sdu_size = qos_config->cis_configs[(*i)->cis_id].max_sdu_s_to_m,
+       .retrans_number = qos_config->cis_configs[(*i)->cis_id].rtn_s_to_m,
+       .trans_latency = qos_config->cig_config.max_tport_latency_s_to_m,
+       .present_delay = {qos_config->ascs_configs[0].presentation_delay[0],
+                         qos_config->ascs_configs[0].presentation_delay[1],
+                         qos_config->ascs_configs[0].presentation_delay[2]}
+      };
+      ase_ops.push_back(qos_config_op);
+    }
+  }
+
+  if(ase_ops.size()) {
+    LOG(WARNING) << __func__  << ": Going For ASCS QosConfig op";
+    ascs_client->QosConfig(ASCS_CLIENT_ID, strm_mgr_->GetAddress(), ase_ops);
+
+    for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+      if(stream && stream->ase_pending_cmd == AscsPendingCmd::NONE)
+        stream->ase_pending_cmd = AscsPendingCmd::QOS_CONFIG_ISSUED;
+    }
+  }
+}
+
+
+void StreamTracker::CheckAndSendEnable(IntStrmTrackers *int_strm_trackers) {
+
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+  std::vector<StreamType> *start_streams = GetStreams();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<AseEnableOp> ase_ops;
+  // check for all trackers CIG is created or not
+  // if so proceed with QOS config operaiton
+  std::vector<IntStrmTracker *> *all_trackers =
+                      int_strm_trackers->GetTrackerList();
+
+  uint8_t num_cig_created = 0;
+
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if(stream && stream->cig_state == CigState::CREATED) {
+      num_cig_created++;
+    }
+  }
+
+  if(int_strm_trackers->size() != num_cig_created) {
+    LOG(WARNING) << __func__  << " All CIGs are not created";
+    return;
+  }
+
+  for(auto it = start_streams->begin(); it != start_streams->end(); it++) {
+    StreamContext *context = contexts->FindOrAddByType(*it);
+    for (auto id = context->stream_ids.begin();
+              id != context->stream_ids.end(); id++) {
+      std::vector<uint8_t> meta_data;
+      UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+      uint8_t len = LTV_LEN_STRM_AUDIO_CONTEXTS;
+      uint8_t type = LTV_TYPE_STRM_AUDIO_CONTEXTS;
+      uint16_t value = (*it).audio_context;
+      if(stream) stream->audio_context = value;
+      meta_data.insert(meta_data.end(), &len, &len + 1);
+      meta_data.insert(meta_data.end(), &type, &type + 1);
+      meta_data.insert(meta_data.end(), ((uint8_t *)&value),
+                              ((uint8_t *)&value) + sizeof(uint16_t));
+
+      AseEnableOp enable_op = {
+                            .ase_id = id->ase_id,
+                            .meta_data_len =
+                             static_cast <uint8_t> (meta_data.size()),
+                            .meta_data = meta_data // media or voice
+                          };
+      ase_ops.push_back(enable_op);
+    }
+  }
+
+  // send consolidated enable command to ASCS client
+  if(ase_ops.size()) {
+    LOG(WARNING) << __func__  << ": Going For ASCS Enable op";
+    ascs_client->Enable(ASCS_CLIENT_ID, strm_mgr_->GetAddress(), ase_ops);
+
+    for (auto it = start_streams->begin(); it != start_streams->end(); it++) {
+      StreamContext *context = contexts->FindOrAddByType(*it);
+      for (auto id = context->stream_ids.begin();
+                id != context->stream_ids.end(); id++) {
+        UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+        if (stream != nullptr && stream->overall_state ==
+                               StreamTracker::kStateConnected) {
+          // change the connection state to enable issued
+          stream->ase_pending_cmd = AscsPendingCmd::ENABLE_ISSUED;
+          // change the overall state to starting
+          stream->overall_state = StreamTracker::kStateStarting;
+        }
+      }
+    }
+  }
+}
+
+void StreamTracker::HandleCigStateEvent(uint32_t event, void *p_data,
+                                        IntStrmTrackers *int_strm_trackers) {
+  // check if the associated CIG state is created
+  // if so go for Enable Operation
+  CisGroupState *data = ((CisGroupState *) p_data);
+
+  // check if current stream tracker is interested in this CIG update
+  std::vector<IntStrmTracker *> int_trackers =
+                        int_strm_trackers->FindByCigId(data->cig_id);
+  if(int_trackers.empty()) {
+    LOG(INFO) << __func__  << " Not intended for this tracker";
+    return;
+  }
+
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  if(data->state == CigState::CREATED) {
+    for (auto i = int_trackers.begin(); i != int_trackers.end();i++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+      if(stream) {
+        stream->cis_pending_cmd = CisPendingCmd::NONE;
+        stream->cig_state = data->state;
+        stream->cis_state = CisState::READY;
+      }
+    }
+    CheckAndSendEnable(int_strm_trackers);
+  } else if(data->state == CigState::IDLE) {
+    // CIG state is idle means there is some failure
+    LOG(ERROR) << __func__ << ": CIG Creation Failed";
+    for (auto i = int_trackers.begin(); i != int_trackers.end();i++) {
+      UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+      if(stream) {
+        stream->cig_state = CigState::INVALID;
+        stream->cis_state = CisState::INVALID;
+        stream->cis_pending_cmd = CisPendingCmd::NONE;;
+      }
+    }
+    HandleInternalDisconnect(false);
+    return;
+  }
+}
+
+bool StreamTracker::PrepareCodecConfigPayload(
+                                 std::vector<AseCodecConfigOp> *ase_ops,
+                                 UcastAudioStream *stream) {
+  std::vector<uint8_t> codec_params;
+  uint8_t tgt_latency = TGT_HIGH_RELIABLE;
+  // check for sampling freq
+  for (auto it : freq_to_ltv_map) {
+    if(stream->codec_config.sample_rate == it.first) {
+      uint8_t len = LTV_LEN_SAMP_FREQ;
+      uint8_t type = LTV_TYPE_SAMP_FREQ;
+      uint8_t rate = it.second;
+      codec_params.insert(codec_params.end(), &len, &len + 1);
+      codec_params.insert(codec_params.end(), &type, &type + 1);
+      codec_params.insert(codec_params.end(), &rate, &rate + 1);
+      break;
+    }
+  }
+
+  // check for frame duration and fetch 5th byte
+  uint8_t frame_duration = GetFrameDuration(&stream->codec_config);
+  uint8_t len = LTV_LEN_FRAME_DUR;
+  uint8_t type = LTV_TYPE_FRAME_DUR;
+  codec_params.insert(codec_params.end(), &len, &len + 1);
+  codec_params.insert(codec_params.end(), &type, &type + 1);
+  codec_params.insert(codec_params.end(), &frame_duration,
+                                          &frame_duration + 1);
+
+  // audio chnl allcation
+  if(stream->audio_location) {
+    uint8_t len = LTV_LEN_CHNL_ALLOC;
+    uint8_t type = LTV_TYPE_CHNL_ALLOC;
+    uint32_t value = stream->audio_location;
+    codec_params.insert(codec_params.end(), &len, &len + 1);
+    codec_params.insert(codec_params.end(), &type, &type + 1);
+    codec_params.insert(codec_params.end(), ((uint8_t *)&value),
+                           ((uint8_t *)&value) + sizeof(uint32_t));
+  }
+
+  // octets per frame
+  len = LTV_LEN_OCTS_PER_FRAME;
+  type = LTV_TYPE_OCTS_PER_FRAME;
+  uint16_t octs_per_frame = GetOctsPerFrame(&stream->codec_config);
+  codec_params.insert(codec_params.end(), &len, &len + 1);
+  codec_params.insert(codec_params.end(), &type, &type + 1);
+  codec_params.insert(codec_params.end(), ((uint8_t *)&octs_per_frame),
+                        ((uint8_t *)&octs_per_frame) + sizeof(uint16_t));
+
+  // blocks per SDU
+  len = LTV_LEN_FRAMES_PER_SDU;
+  type = LTV_TYPE_FRAMES_PER_SDU;
+  uint8_t blocks_per_sdu = GetLc3BlocksPerSdu(&stream->codec_config);
+  // initialize it to 1 if it doesn't exists
+  if(!blocks_per_sdu) {
+    blocks_per_sdu = 1;
+  }
+  codec_params.insert(codec_params.end(), &len, &len + 1);
+  codec_params.insert(codec_params.end(), &type, &type + 1);
+  codec_params.insert(codec_params.end(), &blocks_per_sdu,
+                                          &blocks_per_sdu + 1);
+
+  if(stream->audio_context == CONTENT_TYPE_MEDIA) {
+    tgt_latency = TGT_HIGH_RELIABLE;
+  } else if(stream->audio_context == CONTENT_TYPE_CONVERSATIONAL) {
+    tgt_latency = TGT_BAL_LATENCY;
+  } else if(stream->audio_context == CONTENT_TYPE_GAME) {
+    tgt_latency = TGT_LOW_LATENCY;
+  }
+
+  AseCodecConfigOp codec_config_op = {
+                            .ase_id = stream->ase_id,
+                            .tgt_latency =  tgt_latency,
+                            .tgt_phy = LE_2M_PHY,
+                            .codec_id = {LC3_CODEC_ID, 0, 0, 0, 0},
+                            .codec_params_len =
+                                static_cast <uint8_t> (codec_params.size()),
+                            .codec_params = codec_params
+  };
+  ase_ops->push_back(codec_config_op);
+  return true;
+}
+
+alarm_t* StreamTracker::SetTimer(const char* alarmname,
+                  BapTimeout* timeout, TimeoutReason reason, uint64_t ms) {
+  alarm_t* timer = nullptr;
+
+  timeout->bd_addr = strm_mgr_->GetAddress();
+  timeout->tracker = this;
+  timeout->reason = reason;
+  timeout->transition_state = StateId();
+
+  BapAlarm* bap_alarm = strm_mgr_->GetBapAlarm();
+  if (bap_alarm != nullptr) {
+    timer = bap_alarm->Create(alarmname);
+    if (timer == nullptr) {
+      LOG(ERROR) << __func__ << ": Not able to create alarm";
+      return nullptr;
+    }
+    LOG(INFO) << __func__ << ": starting " << alarmname;
+    bap_alarm->Start(timer, ms, timeout);
+  }
+  return timer;
+}
+
+void StreamTracker::ClearTimer(alarm_t* timer, const char* alarmname) {
+  BapAlarm* bap_alarm = strm_mgr_->GetBapAlarm();
+
+  if (bap_alarm != nullptr && bap_alarm->IsScheduled(timer)) {
+    LOG(INFO) << __func__ << ": clear " << alarmname;
+    bap_alarm->Stop(timer);
+  }
+}
+
+void StreamTracker::OnTimeout(void* data) {
+  BapTimeout* timeout = (BapTimeout *)data;
+  if (timeout == nullptr) {
+    LOG(INFO) << __func__ << ": timeout data null, return ";
+    return;
+  }
+
+  bool isReleaseNeeded = false;
+  int stream_tracker_id = timeout->transition_state;
+  LOG(INFO) << __func__ << ": stream_tracker_ID: " << stream_tracker_id
+            << ", timeout reason: " << static_cast<int>(timeout->reason);
+
+  if (timeout->reason == TimeoutReason::STATE_TRANSITION) {
+    if (stream_tracker_id == StreamTracker::kStateConnecting) {
+      StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+      std::vector<StreamConnect> *conn_streams = GetConnStreams();
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+      LOG(WARNING) << __func__ << ": audio_strms: " << audio_strms->size()
+                               << ", conn_streams: " << conn_streams->size();
+
+      for (auto it = conn_streams->begin(); it != conn_streams->end(); it++) {
+        StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+        LOG(INFO) << __func__ << ": connection_state: "
+                              << static_cast<int>(context->connection_state);
+
+        if(context->connection_state == IntConnectState::ASCS_DISCOVERED) {
+          for (auto id = context->stream_ids.begin();
+                    id != context->stream_ids.end(); id++) {
+            UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+            if (!stream) {
+              LOG(ERROR) << __func__  << "stream is null";
+              continue;
+            }
+
+            LOG(INFO) << __func__ << ": ase_state: " << stream->ase_state;
+            if (stream->ase_state != ascs::ASE_STATE_IDLE &&
+               stream->ase_state != ascs::ASE_STATE_RELEASING) {
+              LOG(WARNING) << __func__
+                           << ": ascs state is neither idle nor releasing";
+              isReleaseNeeded = true;
+              break;
+            }
+          }
+        }
+      }
+      LOG(INFO) << __func__ << ": isReleaseNeeded: " << isReleaseNeeded;
+      HandleInternalDisconnect(isReleaseNeeded);
+    } else if (stream_tracker_id != StreamTracker::kStateDisconnecting) {
+      //All other transient states
+      HandleInternalDisconnect(true);
+    }
+  }
+  LOG(INFO) << __func__ << ": Exit";
+}
+
+void StreamTracker::StateIdle::OnEnter() {
+  LOG(INFO) << __func__ << ": StreamTracker State: " << GetState();
+
+  UcastClientCallbacks* callbacks = strm_mgr_->GetUclientCbacks();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamStateInfo> strms;
+  StreamControlType control_type = tracker_.GetControlType();
+
+  if(control_type != StreamControlType::Disconnect &&
+     control_type != StreamControlType::Connect) {
+    return;
+  }
+
+  if(control_type == StreamControlType::Disconnect) {
+    std::vector<StreamType> *disc_streams = tracker_.GetStreams();
+    LOG(WARNING) << __func__ << ": Disc Streams Size: "
+                             << disc_streams->size();
+    for (auto it = disc_streams->begin(); it != disc_streams->end(); it++) {
+      StreamStateInfo state;
+      memset(&state, 0, sizeof(state));
+      state.stream_type = *it;
+      state.stream_state = StreamState::DISCONNECTED;
+      strms.push_back(state);
+      StreamContext *context = contexts->FindOrAddByType(*it);
+      context->stream_state = StreamState::DISCONNECTED;
+      context->attached_state = StreamAttachedState::IDLE;
+      LOG(INFO) << __func__  << " Attached state made idle";
+      context->stream_ids.clear();
+    }
+  } else if(control_type == StreamControlType::Connect) {
+    std::vector<StreamConnect> *conn_streams = tracker_.GetConnStreams();
+    uint32_t prev_state = tracker_.PreviousStateId();
+    for (auto it = conn_streams->begin(); it != conn_streams->end(); it++) {
+      StreamStateInfo state;
+      memset(&state, 0, sizeof(state));
+      StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+      context->stream_state = StreamState::DISCONNECTED;
+      context->attached_state = StreamAttachedState::IDLE;
+      LOG(INFO) << __func__  << " Attached state made idle";
+      context->stream_ids.clear();
+      if(prev_state == StreamTracker::kStateConnecting) {
+        state.stream_type = it->stream_type;
+        state.stream_state = StreamState::DISCONNECTED;
+        strms.push_back(state);
+      }
+    }
+  }
+  callbacks->OnStreamState(strm_mgr_->GetAddress(), strms);
+}
+
+void StreamTracker::StateIdle::OnExit() {
+
+}
+
+bool StreamTracker::StateIdle::ProcessEvent(uint32_t event, void* p_data) {
+  LOG(INFO) <<__func__  <<": BD Addr = " << strm_mgr_->GetAddress()
+                        <<": State = " << GetState()
+                        <<": Event = " << tracker_.GetEventName(event);
+
+  GattPendingData *gatt_pending_data = strm_mgr_->GetGattPendingData();
+
+  switch (event) {
+    case BAP_CONNECT_REQ_EVT: {
+       BapConnect *evt_data = (BapConnect *) p_data;
+      // check if the PACS client is connected to remote device
+      PacsClient *pacs_client = strm_mgr_->GetPacsClient();
+      uint16_t pacs_client_id = strm_mgr_->GetPacsClientId();
+      ConnectionState pacs_state = strm_mgr_->GetPacsState();
+      if(pacs_state == ConnectionState::DISCONNECTED ||
+            pacs_state == ConnectionState::DISCONNECTING ||
+            pacs_state == ConnectionState::CONNECTING) {
+        // move the state to connecting and initiate pacs connection
+        pacs_client->Connect(pacs_client_id, strm_mgr_->GetAddress(),
+                             evt_data->is_direct);
+        if(gatt_pending_data->pacs_pending_cmd == GattPendingCmd::NONE) {
+          gatt_pending_data->pacs_pending_cmd =
+                              GattPendingCmd::GATT_CONN_PENDING;
+        }
+        tracker_.TransitionTo(StreamTracker::kStateConnecting);
+      } else if(pacs_state == ConnectionState::CONNECTED) {
+        // pacs is already connected so initiate
+        // pacs service discovry now and move the state to connecting
+        pacs_client->StartDiscovery(pacs_client_id, strm_mgr_->GetAddress());
+        tracker_.TransitionTo(StreamTracker::kStateConnecting);
+      }
+    } break;
+    default:
+      LOG(WARNING) << __func__ << "Unhandled Event" << loghex(event);
+      break;
+  }
+  return true;
+}
+
+void StreamTracker::StateConnecting::OnEnter() {
+  LOG(INFO) << __func__ << ": StreamTracker State: " << GetState();
+
+  UcastClientCallbacks* callbacks = strm_mgr_->GetUclientCbacks();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamStateInfo> strms;
+  std::vector<StreamConnect> *conn_streams = tracker_.GetConnStreams();
+
+  StreamControlType control_type = tracker_.GetControlType();
+
+  if(control_type != StreamControlType::Connect) return;
+
+  ConnectionState pacs_state = strm_mgr_->GetPacsState();
+
+  LOG(INFO) << __func__  << ": Conn Streams Size: " << conn_streams->size();
+
+  for (auto it = conn_streams->begin(); it != conn_streams->end(); it++) {
+    StreamStateInfo state;
+    memset(&state, 0, sizeof(state));
+    StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+    context->stream_state = StreamState::CONNECTING;
+    if(pacs_state == ConnectionState::DISCONNECTED ||
+       pacs_state == ConnectionState::CONNECTING) {
+      context->connection_state = IntConnectState::PACS_CONNECTING;
+    } else if(pacs_state == ConnectionState::CONNECTED) {
+      context->connection_state = IntConnectState::PACS_DISCOVERING;
+    }
+    state.stream_type = it->stream_type;
+    state.stream_state = StreamState::CONNECTING;
+    strms.push_back(state);
+  }
+  callbacks->OnStreamState(strm_mgr_->GetAddress(), strms);
+
+  TimeoutReason reason = TimeoutReason::STATE_TRANSITION;
+  state_transition_timer = tracker_.SetTimer("StateConnectingTimer",
+                       &timeout, reason, ((conn_streams->size()) *
+                       (static_cast<uint64_t>(TimeoutVal::ConnectingTimeout))));
+  if (state_transition_timer == nullptr) {
+    LOG(ERROR) << __func__ << ": StateConnecting: Alarm not allocated.";
+    return;
+  }
+}
+
+void StreamTracker::StateConnecting::OnExit() {
+  tracker_.ClearTimer(state_transition_timer, "StateConnectingTimer");
+}
+
+void StreamTracker::StateConnecting::DeriveDeviceType(
+                                     PacsDiscovery *pacs_discovery) {
+  // derive the device type based on sink pac records
+  std::vector<CodecConfig> *pac_records = &pacs_discovery->sink_pac_records;
+  uint8_t max_chnl_count = 0;
+
+  // chnl count Audio location  Type of device           No of ASEs
+  // 1          Left or Right   Earbud                   1 ASE per Earbud
+  // 1          Left and Right  Stereo Headset ( 2 CIS)  2 ASEs
+  // 2          Left and Right  Stereo Headset ( 1 CIS)  1 ASE
+  // 2          Left or Right   Earbud                   1 ASE per Earbud
+
+  for (auto j = pac_records->begin(); j != pac_records->end();j++) {
+    CodecConfig *dst = &(*j);
+    if(static_cast<uint16_t> (dst->channel_mode) &
+       static_cast<uint16_t> (CodecChannelMode::CODEC_CHANNEL_MODE_STEREO)) {
+      max_chnl_count = 2;
+    } else if(!max_chnl_count &&
+           static_cast<uint16_t> (dst->channel_mode) &
+           static_cast<uint16_t> (CodecChannelMode::CODEC_CHANNEL_MODE_MONO)) {
+      max_chnl_count = 1;
+    }
+  }
+
+  if(pacs_discovery->sink_locations & ucast::AUDIO_LOC_LEFT &&
+     pacs_discovery->sink_locations & ucast::AUDIO_LOC_RIGHT) {
+    if(max_chnl_count == 2) {
+      strm_mgr_->UpdateDevType(DeviceType::HEADSET_STEREO);
+    } else if (max_chnl_count == 1) {
+      strm_mgr_->UpdateDevType(DeviceType::HEADSET_SPLIT_STEREO);
+    }
+  } else if(pacs_discovery->sink_locations & ucast::AUDIO_LOC_LEFT ||
+            pacs_discovery->sink_locations & ucast::AUDIO_LOC_RIGHT) {
+    strm_mgr_->UpdateDevType(DeviceType::EARBUD);
+  }
+}
+
+
+bool StreamTracker::StateConnecting::AttachStreamsToContext(
+                                 std::vector<IntStrmTracker *> *all_trackers,
+                                 std::vector<UcastAudioStream *> *streams,
+                                 uint8_t cis_count,
+                                 std::vector<AseCodecConfigOp> *ase_ops) {
+  PacsDiscovery *pacs_discovery_ = tracker_.GetPacsDiscovery();
+  if (!pacs_discovery_) {
+    return false;
+  }
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  for(uint8_t i = 0; i < all_trackers->size()/cis_count ; i++) {
+    for(uint8_t j = 0; j < cis_count ; j++) {
+      IntStrmTracker *tracker = all_trackers->at(i*cis_count + j);
+      UcastAudioStream *stream = streams->at((i*cis_count + j)% streams->size());
+      StreamContext *context = contexts->FindOrAddByType(
+                                         tracker->strm_type);
+      if(stream->overall_state == StreamTracker::kStateIdle) {
+        stream->audio_context = tracker->strm_type.audio_context;
+        stream->control_type = StreamControlType::Connect;
+        stream->ase_pending_cmd = AscsPendingCmd::NONE;
+        stream->cis_pending_cmd = CisPendingCmd::NONE;
+        stream->codec_config = tracker->codec_config;
+        stream->req_qos_config = tracker->qos_config;
+        stream->qos_config = tracker->qos_config;
+        stream->cig_id = tracker->cig_id;
+        stream->cis_id = tracker->cis_id;
+
+        stream->cig_state = CigState::INVALID;
+        stream->cis_state = CisState::INVALID;
+        stream->overall_state = StreamTracker::kStateConnecting;
+
+        if (stream->direction == ASE_DIRECTION_SINK) {
+          if(cis_count > 1) {
+            stream->audio_location =
+                    pacs_discovery_->sink_locations & locations.at(j);
+          } else {
+            if(pacs_discovery_->sink_locations & ucast::AUDIO_LOC_LEFT &&
+               pacs_discovery_->sink_locations & ucast::AUDIO_LOC_RIGHT) {
+              stream->audio_location = 0;
+            } else if(pacs_discovery_->sink_locations & ucast::AUDIO_LOC_LEFT ||
+                      pacs_discovery_->sink_locations & ucast::AUDIO_LOC_RIGHT) {
+              stream->audio_location = pacs_discovery_->sink_locations;
+            }
+          }
+        } else if (stream->direction == ASE_DIRECTION_SRC) {
+          if(cis_count > 1) {
+            stream->audio_location =
+                    pacs_discovery_->src_locations & locations.at(j);
+          } else {
+            if(pacs_discovery_->src_locations & ucast::AUDIO_LOC_LEFT &&
+               pacs_discovery_->src_locations & ucast::AUDIO_LOC_RIGHT) {
+              stream->audio_location = 0;
+            } else if(pacs_discovery_->src_locations & ucast::AUDIO_LOC_LEFT ||
+                      pacs_discovery_->src_locations & ucast::AUDIO_LOC_RIGHT) {
+              stream->audio_location = pacs_discovery_->src_locations;
+            }
+          }
+        }
+        tracker_.PrepareCodecConfigPayload(ase_ops, stream);
+        tracker->attached_state = context->attached_state =
+                                  StreamAttachedState::IDLE_TO_PHY;
+        LOG(INFO) << __func__
+                     << ": Physically  attached";
+      } else {
+        LOG(INFO) << __func__
+                     << ": Virtually attached";
+        tracker->attached_state = context->attached_state =
+                                  StreamAttachedState::VIRTUAL;
+      }
+      tracker->ase_id = stream->ase_id;
+
+      StreamIdType id = {
+                  .ase_id = stream->ase_id,
+                  .ase_direction = stream->direction,
+                  .virtual_attach = false,
+                  .cig_id = tracker->cig_id,
+                  .cis_id = tracker->cis_id
+      };
+      context->stream_ids.push_back(id);
+    }
+  }
+  return true;
+}
+
+bool StreamTracker::StateConnecting::ProcessEvent(uint32_t event, void* p_data) {
+  LOG(INFO) <<__func__  <<": BD Addr = " << strm_mgr_->GetAddress()
+                        <<": State = " << GetState()
+                        <<": Event = " << tracker_.GetEventName(event);
+
+  std::vector<StreamConnect> *conn_streams = tracker_.GetConnStreams();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  GattPendingData *gatt_pending_data = strm_mgr_->GetGattPendingData();
+
+  uint8_t num_conn_streams = 0;
+  if(conn_streams) {
+     num_conn_streams = conn_streams->size();
+  }
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+
+  switch (event) {
+    case BAP_DISCONNECT_REQ_EVT: {
+
+      // expect the disconnection for same set of streams connection
+      // has initiated ex: if connect is issued for media (tx), voice(tx & rx)
+      // then disconnect is expected for media (tx), voice(tx & rx).
+
+      // based on connection state, issue the relevant commands and move
+      // the state to disconnecting
+      // issue the release opertion if for any stream ASE operation is
+      // initiated
+
+      // upate the control type and streams also
+      BapDisconnect *evt_data = (BapDisconnect *) p_data;
+
+      tracker_.UpdateControlType(StreamControlType::Disconnect);
+
+      tracker_.UpdateStreams(&evt_data->streams);
+
+      StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+      AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+
+      std::vector<AseReleaseOp> ase_ops;
+      std::vector<StreamType> *disc_streams = tracker_.GetStreams();
+
+      LOG(WARNING) << __func__  << ": disc_streams: " << disc_streams->size();
+
+      for (auto it = disc_streams->begin(); it != disc_streams->end(); it++) {
+        StreamContext *context = contexts->FindOrAddByType(*it);
+        if(context->connection_state == IntConnectState::ASCS_DISCOVERED) {
+          for (auto id = context->stream_ids.begin();
+                  id != context->stream_ids.end(); id++) {
+            UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+            if (!stream) {
+              LOG(ERROR) << __func__  << "stream is null";
+              continue;
+            }
+            bool can_be_disconnected =
+                       tracker_.StreamCanbeDisconnected(context, id->ase_id);
+            if(can_be_disconnected &&
+               stream->ase_state == ascs::ASE_STATE_CODEC_CONFIGURED &&
+               stream->ase_pending_cmd != AscsPendingCmd::RELEASE_ISSUED) {
+              AseReleaseOp release_op = {
+                                         .ase_id = stream->ase_id
+                                        };
+              ase_ops.push_back(release_op);
+              stream->ase_pending_cmd = AscsPendingCmd::RELEASE_ISSUED;
+              // change the overall state to starting
+              stream->overall_state = StreamTracker::kStateDisconnecting;
+            }
+          }
+        }
+      }
+
+      LOG(INFO) << __func__  << ": ase_ops size: " << ase_ops.size();
+
+      // send consolidated release command to ASCS client
+      if(ase_ops.size()) {
+        LOG(WARNING) << __func__  << ": Going For ASCS Release op";
+        ascs_client->Release(ASCS_CLIENT_ID, strm_mgr_->GetAddress(), ase_ops);
+      }
+
+      tracker_.TransitionTo(StreamTracker::kStateDisconnecting);
+
+    } break;
+    case PACS_CONNECTION_STATE_EVT: {
+      PacsConnectionState *pacs_state =  (PacsConnectionState *) p_data;
+
+      PacsClient *pacs_client = strm_mgr_->GetPacsClient();
+      uint16_t pacs_client_id = strm_mgr_->GetPacsClientId();
+
+      LOG(WARNING) << __func__
+                   <<": pacs_state: " << static_cast<int>(pacs_state->state);
+      if(pacs_state->state == ConnectionState::CONNECTED) {
+        // now send the PACS discovery
+        gatt_pending_data->pacs_pending_cmd = GattPendingCmd::NONE;
+        pacs_client->StartDiscovery(pacs_client_id, strm_mgr_->GetAddress());
+      } else if(pacs_state->state == ConnectionState::DISCONNECTED) {
+        gatt_pending_data->pacs_pending_cmd = GattPendingCmd::NONE;
+        tracker_.HandleInternalDisconnect(false);
+        return false;
+      }
+
+      for (uint8_t i = 0; i < num_conn_streams ; i++) {
+        StreamConnect conn_stream = conn_streams->at(i);
+        StreamContext *context = contexts->FindOrAddByType(
+                                           conn_stream.stream_type);
+        if(pacs_state->state == ConnectionState::CONNECTED) {
+          context->connection_state = IntConnectState::PACS_DISCOVERING;
+        }
+      }
+    } break;
+
+    case PACS_DISCOVERY_RES_EVT: {
+      PacsDiscovery pacs_discovery_ =  *((PacsDiscovery *) p_data);
+      GattState ascs_state = strm_mgr_->GetAscsState();
+      GattPendingData *gatt_pending_data = strm_mgr_->GetGattPendingData();
+      bool process_pacs_results = false;
+
+      // check if this tracker already passed the pacs discovery stage
+      for (uint8_t i = 0; i < num_conn_streams ; i++) {
+        StreamConnect conn_stream = conn_streams->at(i);
+        StreamContext *context = contexts->FindOrAddByType(
+                                           conn_stream.stream_type);
+        if(context->connection_state == IntConnectState::PACS_DISCOVERING) {
+          process_pacs_results = true;
+          break;
+        }
+      }
+
+      if(!process_pacs_results) break;
+
+      bool context_supported = false;
+      // check the status
+      if(pacs_discovery_.status) {
+        tracker_.HandleInternalDisconnect(false);
+        LOG(ERROR) << __func__  << " PACS discovery failed";
+        return false;
+      }
+
+      tracker_.UpdatePacsDiscovery(pacs_discovery_);
+
+      // Derive the device type based on pacs discovery results
+      DeriveDeviceType((PacsDiscovery *) p_data);
+
+      // check if supported audio contexts has required contexts
+      for (auto it = conn_streams->begin(); it != conn_streams->end(); it++) {
+        StreamType stream = it->stream_type;
+        if(stream.direction == ASE_DIRECTION_SINK) {
+          if(stream.audio_context & pacs_discovery_.supported_contexts) {
+            context_supported = true;
+          }
+        } else if(stream.direction == ASE_DIRECTION_SRC) {
+          if((static_cast<uint64_t>(stream.audio_context) << 16) &
+               pacs_discovery_.supported_contexts) {
+            context_supported = true;
+          }
+        }
+      }
+
+      if(!context_supported) {
+        LOG(ERROR) << __func__  << " No Matching Supported Contexts found";
+        tracker_.HandleInternalDisconnect(false);
+        break;
+      }
+
+      // if not present send the BAP callback as disconnected
+      // compare the codec configs from upper layer to remote dev
+      // sink or src PACS records/capabilities.
+
+      // go for ASCS discovery only when codec configs are decided
+
+      for (uint8_t i = 0; i < num_conn_streams ; i++) {
+        StreamConnect conn_stream = conn_streams->at(i);
+        // TODO for now will pick directly first set of Codec and QOS configs
+
+        uint8_t index = tracker_.ChooseBestCodec(conn_stream.stream_type,
+                                             &conn_stream.codec_qos_config_pair,
+                                             &pacs_discovery_);
+        if(index != 0XFF) {
+          CodecQosConfig entry = conn_stream.codec_qos_config_pair.at(index);
+          CodecConfig codec_config = entry.codec_config;
+          QosConfig qos_config = entry.qos_config;
+
+          StreamContext *context = contexts->FindOrAddByType(
+                                             conn_stream.stream_type);
+          for (auto ascs_config = qos_config.ascs_configs.begin();
+                ascs_config != qos_config.ascs_configs.end(); ascs_config++) {
+            int_strm_trackers_.FindOrAddBytrackerType(conn_stream.stream_type,
+                               0x00, ascs_config->cig_id,
+                               ascs_config->cis_id,
+                               codec_config, qos_config);
+          }
+          context->codec_config = codec_config;
+          context->req_qos_config = qos_config;
+        } else {
+          LOG(ERROR) << __func__  << " No Matching Codec Found For Stream";
+        }
+      }
+
+      // check if any match between upper layer codec and remote dev's
+      // pacs records
+      if(!int_strm_trackers_.size()) {
+        LOG(WARNING) << __func__ << "No Matching codec found for all streams";
+        tracker_.HandleInternalDisconnect(false);
+        return false;
+      }
+
+      if(ascs_state == GattState::CONNECTED) {
+        LOG(WARNING) << __func__  << ": Going For ASCS Service Discovery";
+        // now send the ASCS discovery
+        ascs_client->StartDiscovery(ASCS_CLIENT_ID, strm_mgr_->GetAddress());
+      } else if(ascs_state == GattState::DISCONNECTED) {
+        LOG(WARNING) << __func__  << ": Going For ASCS Conneciton";
+        ascs_client->Connect(ASCS_CLIENT_ID, strm_mgr_->GetAddress(), false);
+        if(gatt_pending_data->ascs_pending_cmd == GattPendingCmd::NONE) {
+          gatt_pending_data->ascs_pending_cmd =
+                              GattPendingCmd::GATT_CONN_PENDING;
+        }
+      }
+
+      for (uint8_t i = 0; i < num_conn_streams ; i++) {
+        StreamConnect conn_stream = conn_streams->at(i);
+        StreamContext *context = contexts->FindOrAddByType(
+                                             conn_stream.stream_type);
+        if(ascs_state == GattState::CONNECTED) {
+          context->connection_state = IntConnectState::ASCS_DISCOVERING;
+        } else if(ascs_state == GattState::DISCONNECTED) {
+          context->connection_state = IntConnectState::ASCS_CONNECTING;
+        }
+      }
+    } break;
+
+    case ASCS_CONNECTION_STATE_EVT: {
+      AscsConnectionState *ascs_state =  (AscsConnectionState *) p_data;
+      AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+
+      if(ascs_state->state == GattState::CONNECTED) {
+        LOG(INFO) << __func__ << " ASCS server connected";
+        // now send the ASCS discovery
+        gatt_pending_data->ascs_pending_cmd = GattPendingCmd::NONE;
+        ascs_client->StartDiscovery(ASCS_CLIENT_ID, strm_mgr_->GetAddress());
+      } else if(ascs_state->state == GattState::DISCONNECTED) {
+        LOG(INFO) << __func__ << " ASCS server Disconnected";
+        gatt_pending_data->ascs_pending_cmd = GattPendingCmd::NONE;
+        tracker_.HandleInternalDisconnect(false);
+        return false;
+      }
+
+      for (uint8_t i = 0; i < num_conn_streams ; i++) {
+        StreamConnect conn_stream = conn_streams->at(i);
+        StreamContext *context = contexts->FindOrAddByType(
+                                             conn_stream.stream_type);
+        if(ascs_state->state == GattState::CONNECTED) {
+          context->connection_state = IntConnectState::ASCS_DISCOVERING;
+        }
+      }
+    } break;
+    case ASCS_DISCOVERY_RES_EVT: {
+      AscsDiscovery ascs_discovery_ =  *((AscsDiscovery *) p_data);
+      std::vector<AseCodecConfigOp> ase_ops;
+      AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+      std::vector<AseParams> sink_ase_list = ascs_discovery_.sink_ases_list;
+      std::vector<AseParams> src_ase_list = ascs_discovery_.src_ases_list;
+      // check the status
+      if(ascs_discovery_.status) {
+        tracker_.HandleInternalDisconnect(false);
+        return false;
+      }
+
+      for (uint8_t i = 0; i < num_conn_streams ; i++) {
+        StreamConnect conn_stream = conn_streams->at(i);
+        StreamContext *context = contexts->FindOrAddByType(
+                                             conn_stream.stream_type);
+        context->connection_state = IntConnectState::ASCS_DISCOVERED;
+      }
+
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+      // create the UcastAudioStream for each ASEs (ase id)
+      // check if the entry is present, if not create and add it to list
+      // find out number of ASEs which are in IDLE state
+      for (auto & ase : sink_ase_list) {
+        audio_strms->FindOrAddByAseId(ase.ase_id,
+                                      ase.ase_state, ASE_DIRECTION_SINK);
+      }
+
+      for (auto & ase : src_ase_list) {
+        audio_strms->FindOrAddByAseId(ase.ase_id,
+                                      ase.ase_state, ASE_DIRECTION_SRC);
+      }
+
+      LOG(INFO) << __func__ << ": total num of audio strms: "
+                            << audio_strms->size();
+
+      std::vector<IntStrmTracker *> sink_int_trackers =
+                   int_strm_trackers_.GetTrackerListByDir(ASE_DIRECTION_SINK);
+
+      std::vector<IntStrmTracker *> src_int_trackers =
+                   int_strm_trackers_.GetTrackerListByDir(ASE_DIRECTION_SRC);
+
+      std::vector<int> state_ids = { StreamTracker::kStateIdle };
+
+      std::vector<UcastAudioStream *> idle_sink_streams =
+                                   audio_strms->GetStreamsByStates(state_ids,
+                                   ASE_DIRECTION_SINK);
+      std::vector<UcastAudioStream *> idle_src_streams =
+                                   audio_strms->GetStreamsByStates(state_ids,
+                                   ASE_DIRECTION_SRC);
+      LOG(INFO) << __func__  << ": Num of Sink Idle Streams = "
+                                <<  idle_sink_streams.size()
+                                << ": Num of Src Idle Streams = "
+                                <<  idle_src_streams.size();
+
+      LOG(INFO) << __func__  << ": Num of Sink Internal Trackers = "
+                                <<  sink_int_trackers.size()
+                                << ": Num of Src Internal Trackers = "
+                                <<  src_int_trackers.size();
+
+      LOG(INFO) << __func__  << ": Num of Conn Streams "
+                                <<  loghex(num_conn_streams);
+
+      // check how many stream connections are requested and
+      // how many streams(ASEs) are available for processing
+      // check if we have sufficient number of streams(ASEs) for
+      // the given set of connection requirement
+      DeviceType dev_type = strm_mgr_->GetDevType();
+      uint8_t cis_count = 0;
+      if(dev_type == DeviceType::EARBUD ||
+         dev_type == DeviceType::HEADSET_STEREO) {
+        cis_count = 1;
+      } else if(dev_type == DeviceType::HEADSET_SPLIT_STEREO) {
+        cis_count = 2;
+      }
+
+      std::vector<int> valid_state_ids = {
+                                       StreamTracker::kStateConnecting,
+                                       StreamTracker::kStateConnected,
+                                       StreamTracker::kStateStreaming,
+                                       StreamTracker::kStateReconfiguring,
+                                       StreamTracker::kStateDisconnecting,
+                                       StreamTracker::kStateStarting,
+                                       StreamTracker::kStateStopping
+                                    };
+
+      if(sink_int_trackers.size()) {
+        if(idle_sink_streams.size() >= sink_int_trackers.size()) {
+          AttachStreamsToContext(&sink_int_trackers, &idle_sink_streams,
+                                 cis_count, &ase_ops);
+        } else {
+          std::vector<IntStrmTracker *> sink_int_trackers_1,
+                                        sink_int_trackers_2;
+            // split the sink_int_trackers into 2 lists now, one list
+            // is equal to idle_sink_streams as physical and other
+            // list as virtually attached
+          if(idle_sink_streams.size()) { // less num of free ASEs
+            for (uint8_t i = 0; i < idle_sink_streams.size() ; i++) {
+              IntStrmTracker *tracker = sink_int_trackers.at(i);
+              sink_int_trackers_1.push_back(tracker);
+            }
+            AttachStreamsToContext(&sink_int_trackers_1, &idle_sink_streams,
+                                   cis_count, &ase_ops);
+            for (uint8_t i = idle_sink_streams.size();
+                         i < sink_int_trackers.size() ; i++) {
+              IntStrmTracker *tracker = sink_int_trackers.at(i);
+              sink_int_trackers_2.push_back(tracker);
+            }
+          }
+
+          std::vector<UcastAudioStream *> all_active_sink_streams =
+                                audio_strms->GetStreamsByStates(valid_state_ids,
+                                ASE_DIRECTION_SINK);
+
+          if(sink_int_trackers_2.size()) {
+            AttachStreamsToContext(&sink_int_trackers_2, &all_active_sink_streams,
+                                 cis_count, &ase_ops);
+          } else if(sink_int_trackers.size()) {
+            AttachStreamsToContext(&sink_int_trackers, &all_active_sink_streams,
+                                 cis_count, &ase_ops);
+          }
+        }
+      }
+
+      // do the same procedure for src trackers as well
+      if(src_int_trackers.size()) {
+        if(idle_src_streams.size() >= src_int_trackers.size()) {
+          AttachStreamsToContext(&src_int_trackers, &idle_src_streams,
+                                 cis_count, &ase_ops);
+        } else {
+          std::vector<IntStrmTracker *> src_int_trackers_1,
+                                        src_int_trackers_2;
+            // split the src_int_trackers into 2 lists now, one list
+            // is equal to idle_src_streams as physical and other
+            // list as virtually attached
+          if(idle_src_streams.size()) { // less num of free ASEs
+            for (uint8_t i = 0; i < idle_src_streams.size() ; i++) {
+              IntStrmTracker *tracker = src_int_trackers.at(i);
+              src_int_trackers_1.push_back(tracker);
+            }
+            AttachStreamsToContext(&src_int_trackers_1, &idle_src_streams,
+                                 cis_count, &ase_ops);
+            for (uint8_t i = idle_src_streams.size();
+                         i < src_int_trackers.size() ; i++) {
+              IntStrmTracker *tracker = src_int_trackers.at(i);
+              src_int_trackers_2.push_back(tracker);
+            }
+          }
+
+          std::vector<UcastAudioStream *> all_active_src_streams =
+                              audio_strms->GetStreamsByStates(valid_state_ids,
+                              ASE_DIRECTION_SRC);
+
+          if(src_int_trackers_2.size()) {
+            AttachStreamsToContext(&src_int_trackers_2, &all_active_src_streams,
+                                 cis_count, &ase_ops);
+          } else if(src_int_trackers.size()) {
+            AttachStreamsToContext(&src_int_trackers, &all_active_src_streams,
+                                 cis_count, &ase_ops);
+          }
+        }
+      }
+
+      // remove all duplicate internal stream trackers
+      int_strm_trackers_.RemoveVirtualAttachedTrackers();
+
+      // if the int strm trackers size is 0 then return as
+      // connected immediately
+      if(!int_strm_trackers_.size()) {
+        // update the state to connected
+        TransitionTo(StreamTracker::kStateConnected);
+        break;
+      }
+
+      if(!ase_ops.empty()) {
+        LOG(WARNING) << __func__  << ": Going For ASCS CodecConfig op";
+        ascs_client->CodecConfig(ASCS_CLIENT_ID, strm_mgr_->GetAddress(),
+                                 ase_ops);
+      } else {
+        tracker_.HandleInternalDisconnect(false);
+        break;
+      }
+
+      // refresh the sink and src trackers
+      sink_int_trackers =
+                   int_strm_trackers_.GetTrackerListByDir(ASE_DIRECTION_SINK);
+
+      src_int_trackers =
+                   int_strm_trackers_.GetTrackerListByDir(ASE_DIRECTION_SRC);
+
+      LOG(INFO) << __func__  << ": Num of new Sink Internal Trackers = "
+                             <<  sink_int_trackers.size()
+                             << ": Num of new Src Internal Trackers = "
+                             <<  src_int_trackers.size();
+
+      LOG(INFO) << __func__  << ": Num of new Sink Idle Streams = "
+                             <<  idle_sink_streams.size()
+                             << ": Num of new Src Idle Streams = "
+                             <<  idle_src_streams.size();
+
+      // update the states to connecting or other internal states
+      if(sink_int_trackers.size()) {
+        for (uint8_t i = 0; i < sink_int_trackers.size() ; i++) {
+          UcastAudioStream *stream = idle_sink_streams.at(i);
+          stream->ase_pending_cmd = AscsPendingCmd::CODEC_CONFIG_ISSUED;
+        }
+      }
+      if(src_int_trackers.size()) {
+        for (uint8_t i = 0; i < src_int_trackers.size() ; i++) {
+          UcastAudioStream *stream = idle_src_streams.at(i);
+          stream->ase_pending_cmd = AscsPendingCmd::CODEC_CONFIG_ISSUED;
+        }
+      }
+    } break;
+
+    case ASCS_ASE_STATE_EVT: {
+      tracker_.HandleAseStateEvent(p_data, StreamControlType::Connect,
+                                   &int_strm_trackers_);
+    } break;
+
+    case ASCS_ASE_OP_FAILED_EVT: {
+      tracker_.HandleAseOpFailedEvent(p_data);
+    } break;
+
+    case BAP_TIME_OUT_EVT: {
+      tracker_.OnTimeout(p_data);
+    } break;
+
+    default:
+      LOG(WARNING) << __func__ << ": Un-handled event: "
+                               << tracker_.GetEventName(event);
+      break;
+  }
+  return true;
+}
+
+
+void StreamTracker::StateConnected::OnEnter() {
+  LOG(INFO) << __func__ << ": StreamTracker State: " << GetState();
+
+  UcastClientCallbacks* callbacks = strm_mgr_->GetUclientCbacks();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamStateInfo> strms;
+  std::vector<StreamConfigInfo> stream_configs;
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  PacsDiscovery *pacs_discovery_ = tracker_.GetPacsDiscovery();
+  StreamControlType control_type = tracker_.GetControlType();
+  std::vector<StreamType> conv_streams;
+
+  if(control_type != StreamControlType::Connect &&
+     control_type != StreamControlType::Stop &&
+     control_type != StreamControlType::Reconfig) {
+    return;
+  }
+
+  if(control_type == StreamControlType::Connect) {
+    std::vector<StreamConnect> *conn_streams = tracker_.GetConnStreams();
+    for (auto it = conn_streams->begin(); it != conn_streams->end(); it++) {
+      StreamType type = it->stream_type;
+      conv_streams.push_back(type);
+    }
+    LOG(WARNING) << __func__  << ": Conn Streams Size " << conn_streams->size();
+  } else if(control_type == StreamControlType::Reconfig) {
+    std::vector<StreamReconfig> *reconf_streams = tracker_.GetReconfStreams();
+    for (auto it = reconf_streams->begin(); it != reconf_streams->end();it++) {
+      StreamType type = it->stream_type;
+      conv_streams.push_back(type);
+    }
+    LOG(WARNING) << __func__  << ": Reconfig Streams size "
+                              << reconf_streams->size();
+  } else {
+    conv_streams =  *tracker_.GetStreams();
+  }
+
+  if(control_type == StreamControlType::Connect ||
+     control_type == StreamControlType::Reconfig) {
+    for (auto it = conv_streams.begin(); it != conv_streams.end(); it++) {
+      StreamContext *context = contexts->FindOrAddByType(*it);
+      UcastAudioStream *stream = audio_strms->FindByStreamType(
+                (*it).type, (*it).direction);
+      // avoid duplicate updates
+      if(context && pacs_discovery_ &&
+         context->stream_state != StreamState::CONNECTED) {
+        StreamConfigInfo config;
+        memset(&config, 0, sizeof(config));
+        config.stream_type = *it;
+        if(stream) {
+          config.codec_config = stream->codec_config;
+          config.qos_config = stream->qos_config;
+          context->qos_config = stream->qos_config;
+        } else {
+          config.codec_config = context->codec_config;
+          config.qos_config = context->req_qos_config;
+          context->qos_config = context->req_qos_config;
+        }
+
+        //Keeping bits_per_sample as 24 always for LC3
+        if (config.codec_config.codec_type ==
+                                CodecIndex::CODEC_INDEX_SOURCE_LC3) {
+          config.codec_config.bits_per_sample =
+                                CodecBPS::CODEC_BITS_PER_SAMPLE_24;
+        }
+
+        if(config.stream_type.direction == ASE_DIRECTION_SINK) {
+          config.audio_location = pacs_discovery_->sink_locations;
+          config.codecs_selectable = pacs_discovery_->sink_pac_records;
+        } else if(config.stream_type.direction == ASE_DIRECTION_SRC) {
+          config.audio_location = pacs_discovery_->src_locations;
+          config.codecs_selectable = pacs_discovery_->src_pac_records;
+        }
+        stream_configs.push_back(config);
+      }
+    }
+
+    if(stream_configs.size()) {
+      callbacks->OnStreamConfig(strm_mgr_->GetAddress(), stream_configs);
+    }
+  }
+
+  for (auto it = conv_streams.begin(); it != conv_streams.end(); it++) {
+    StreamContext *context = contexts->FindOrAddByType(*it);
+    // avoid duplicate updates
+    if( context->stream_state != StreamState::CONNECTED) {
+      StreamStateInfo state;
+      memset(&state, 0, sizeof(state));
+      state.stream_type = *it;
+      state.stream_state = StreamState::CONNECTED;
+      context->stream_state = StreamState::CONNECTED;
+      strms.push_back(state);
+    }
+  }
+
+  if(strms.size()) {
+    callbacks->OnStreamState(strm_mgr_->GetAddress(), strms);
+  }
+}
+
+void StreamTracker::StateConnected::OnExit() {
+
+}
+
+bool StreamTracker::StateConnected::ProcessEvent(uint32_t event, void* p_data) {
+  LOG(INFO) <<__func__  <<": BD Addr = " << strm_mgr_->GetAddress()
+                        <<": State = " << GetState()
+                        <<": Event = " << tracker_.GetEventName(event);
+
+  switch (event) {
+    case BAP_DISCONNECT_REQ_EVT: {
+      tracker_.HandleDisconnect(p_data, StreamTracker::kStateConnected);
+    } break;
+
+    case BAP_START_REQ_EVT: {
+      PacsClient *pacs_client = strm_mgr_->GetPacsClient();
+      uint16_t pacs_client_id = strm_mgr_->GetPacsClientId();
+
+      BapStart *evt_data = (BapStart *) p_data;
+
+      tracker_.UpdateControlType(StreamControlType::Start);
+
+      tracker_.UpdateStreams(&evt_data->streams);
+
+      pacs_client->GetAudioAvailability(pacs_client_id,
+                                        strm_mgr_->GetAddress());
+
+      tracker_.TransitionTo(StreamTracker::kStateStarting);
+    } break;
+
+    case BAP_RECONFIG_REQ_EVT: {
+      BapReconfig *evt_data = (BapReconfig *) p_data;
+
+      tracker_.UpdateControlType(StreamControlType::Reconfig);
+      tracker_.UpdateReconfStreams(&evt_data->streams);
+
+      // check if codec reconfiguration or qos reconfiguration
+      PacsClient *pacs_client = strm_mgr_->GetPacsClient();
+      uint16_t pacs_client_id = strm_mgr_->GetPacsClientId();
+
+      // pacs is already connected so initiate
+      // pacs service discovry now and move the state to reconfiguring
+      pacs_client->StartDiscovery(pacs_client_id, strm_mgr_->GetAddress());
+
+      tracker_.TransitionTo(StreamTracker::kStateReconfiguring);
+
+    } break;
+    case PACS_CONNECTION_STATE_EVT: {
+      tracker_.HandlePacsConnectionEvent(p_data);
+    } break;
+    case ASCS_CONNECTION_STATE_EVT: {
+      tracker_.HandleAscsConnectionEvent(p_data);
+    } break;
+    case ASCS_ASE_STATE_EVT: {
+      AscsState *ascs =  ((AscsState *) p_data);
+      if(ascs->ase_params.ase_state == ascs::ASE_STATE_RELEASING) {
+        tracker_.HandleRemoteDisconnect(ASCS_ASE_STATE_EVT, p_data, StateId());
+      } else if(ascs->ase_params.ase_state ==
+                              ascs::ASE_STATE_CODEC_CONFIGURED) {
+        tracker_.HandleRemoteReconfig(ASCS_ASE_STATE_EVT, p_data, StateId());
+      }
+    } break;
+    default:
+      LOG(WARNING) << __func__ << ": Un-handled event: "
+                               << tracker_.GetEventName(event);
+      break;
+  }
+  return true;
+}
+
+
+void StreamTracker::StateStarting::OnEnter() {
+  LOG(INFO) << __func__ << ": StreamTracker State: " << GetState();
+
+  UcastClientCallbacks* callbacks = strm_mgr_->GetUclientCbacks();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamStateInfo> strms;
+  std::vector<StreamType> *start_streams = tracker_.GetStreams();
+  uint8_t num_ases = 0;
+
+  LOG(WARNING) << __func__  << ": Start Streams Size: "
+                            << start_streams->size();
+
+  StreamControlType control_type = tracker_.GetControlType();
+
+  if(control_type != StreamControlType::Start) return;
+
+  for (auto it = start_streams->begin(); it != start_streams->end(); it++) {
+    StreamStateInfo state;
+    memset(&state, 0, sizeof(state));
+    state.stream_type = *it;
+    state.stream_state = StreamState::STARTING;
+    strms.push_back(state);
+    StreamContext *context = contexts->FindOrAddByType(*it);
+    context->stream_state = StreamState::STARTING;
+
+    for (auto id = context->stream_ids.begin();
+              id != context->stream_ids.end(); id++) {
+      int_strm_trackers_.FindOrAddBytrackerType(*it,
+                                id->ase_id, id->cig_id,
+                                id->cis_id,
+                                context->codec_config, context->qos_config);
+    }
+    num_ases += context->stream_ids.size();
+  }
+  callbacks->OnStreamState(strm_mgr_->GetAddress(), strms);
+  uint64_t tout = num_ases *
+                    (static_cast<uint64_t>(TimeoutVal::StartingTimeout));
+  if(!tout) {
+    tout = static_cast<uint64_t>(MaxTimeoutVal::StartingTimeout);
+  }
+  TimeoutReason reason = TimeoutReason::STATE_TRANSITION;
+  state_transition_timer = tracker_.SetTimer("StateStartingTimer",
+        &timeout, reason, tout);
+  if (state_transition_timer == nullptr) {
+    LOG(ERROR) << __func__  << ": StateStarting: Alarm not allocated.";
+    return;
+  }
+}
+
+void StreamTracker::StateStarting::OnExit() {
+  tracker_.ClearTimer(state_transition_timer, "StateStartingTimer");
+}
+
+bool StreamTracker::CheckAndUpdateStreamingState(
+                          IntStrmTrackers *int_strm_trackers) {
+  //  to check for all internal trackers are moved to
+  // streaming state then update it upper layers
+  std::vector<IntStrmTracker *> *all_trackers =
+                      int_strm_trackers->GetTrackerList();
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  uint8_t num_strms_in_streaming = 0;
+
+  bool pending_cmds = false;
+
+  // check if any pending commands are present
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if(stream && (stream->cis_pending_cmd != CisPendingCmd::NONE ||
+                  stream->ase_pending_cmd != AscsPendingCmd::NONE)) {
+      LOG(WARNING) << __func__  << ": cis_pending_cmd "
+                   << loghex(static_cast <uint8_t>(stream->cis_pending_cmd));
+      LOG(WARNING) << __func__  << ": ase_pending_cmd "
+                   << loghex(static_cast <uint8_t>(stream->ase_pending_cmd));
+      pending_cmds = true;
+      break;
+    }
+  }
+
+  if(pending_cmds) {
+    LOG(WARNING) << __func__  << ": ASCS/CIS Pending commands left";
+    return false;
+  }
+
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if (!stream) {
+      LOG(ERROR) << __func__  << "stream is null";
+      continue;
+    }
+    if(stream->ase_state == ascs::ASE_STATE_STREAMING &&
+       stream->cis_state == CisState::ESTABLISHED) {
+      num_strms_in_streaming++;
+    }
+  }
+
+  if(int_strm_trackers->size() != num_strms_in_streaming) {
+    LOG(WARNING) << __func__  << ": Not all streams moved to streaming";
+    return false;
+  }
+
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if (!stream) {
+      LOG(ERROR) << __func__  << "stream is null";
+      continue;
+    }
+    stream->overall_state = StreamTracker::kStateStreaming;
+  }
+
+  // all streams are moved to streaming state
+  TransitionTo(StreamTracker::kStateStreaming);
+  return true;
+}
+
+bool StreamTracker::StateStarting::ProcessEvent(uint32_t event, void* p_data) {
+  LOG(INFO) <<__func__  <<": BD Addr = " << strm_mgr_->GetAddress()
+                        <<": State = " << GetState()
+                        <<": Event = " << tracker_.GetEventName(event);
+
+  switch (event) {
+    case BAP_DISCONNECT_REQ_EVT: {
+      tracker_.HandleDisconnect(p_data, StreamTracker::kStateStarting);
+    } break;
+    case BAP_STOP_REQ_EVT: {
+      tracker_.HandleStop(p_data, StreamTracker::kStateStarting);
+    } break;
+    case BAP_STREAM_UPDATE_REQ_EVT: {
+      BapStreamUpdate *evt_data = (BapStreamUpdate *) p_data;
+      tracker_.UpdateMetaUpdateStreams(&evt_data->update_streams);
+      if(tracker_.HandlePacsAudioContextEvent(&pacs_contexts)) {
+        tracker_.HandleStreamUpdate(StreamTracker::kStateStarting);
+      }
+    } break;
+    case PACS_CONNECTION_STATE_EVT: {
+      tracker_.HandlePacsConnectionEvent(p_data);
+    } break;
+    case PACS_AUDIO_CONTEXT_RES_EVT: {
+      // check for all stream start requests, stream contexts are
+      // part of available contexts
+      pacs_contexts = *((PacsAvailableContexts *) p_data);
+      StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+      std::vector<IntStrmTracker *> *all_trackers =
+                            int_strm_trackers_.GetTrackerList();
+      bool ignore_event = false;
+
+      std::vector<StreamType> *start_streams = tracker_.GetStreams();
+      uint8_t contexts_supported = 0;
+
+      // check if supported audio contexts has required contexts
+      for(auto it = start_streams->begin(); it != start_streams->end(); it++) {
+        if(it->direction == ASE_DIRECTION_SINK) {
+          if(it->audio_context & pacs_contexts.available_contexts) {
+            contexts_supported++;
+          }
+        } else if(it->direction == ASE_DIRECTION_SRC) {
+          if((static_cast<uint64_t>(it->audio_context) << 16) &
+               pacs_contexts.available_contexts) {
+            contexts_supported++;
+          }
+        }
+      }
+
+      if(contexts_supported != start_streams->size()) {
+        LOG(ERROR) << __func__  << ": No Matching available Contexts found";
+        tracker_.TransitionTo(StreamTracker::kStateConnected);
+        break;
+      }
+
+      for (auto it = start_streams->begin(); it != start_streams->end(); it++) {
+        StreamContext *context = contexts->FindOrAddByType(*it);
+        for (auto id = context->stream_ids.begin();
+                  id != context->stream_ids.end(); id++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+          if (stream != nullptr && stream->overall_state ==
+                                 StreamTracker::kStateStarting) {
+            ignore_event = true;
+            break;
+          }
+        }
+      }
+
+      if(ignore_event) break;
+
+      // Now create the groups
+      for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+        UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+        if (!stream) {
+          LOG(ERROR) << __func__  << "stream is null";
+          continue;
+        }
+        QosConfig *qos_config = &stream->qos_config;
+        CisInterface *cis_intf = strm_mgr_->GetCisInterface();
+        IsoHciStatus status =  cis_intf->CreateCig(strm_mgr_->GetAddress(),
+                                                   false,
+                                                   qos_config->cig_config,
+                                                   qos_config->cis_configs);
+
+        LOG(WARNING) << __func__  << ": status: "
+                     << loghex(static_cast<uint8_t>(status));
+        if( status == IsoHciStatus::ISO_HCI_SUCCESS) {
+          stream->cig_state = CigState::CREATED;
+          stream->cis_state = CisState::READY;
+        } else if (status == IsoHciStatus::ISO_HCI_IN_PROGRESS) {
+          stream->cis_pending_cmd = CisPendingCmd::CIG_CREATE_ISSUED;
+        } else {
+          LOG(ERROR) << __func__  << " CIG Creation Failed";
+        }
+      }
+      tracker_.CheckAndSendEnable(&int_strm_trackers_);
+    } break;
+
+    case CIS_GROUP_STATE_EVT: {
+      tracker_.HandleCigStateEvent(event, p_data, &int_strm_trackers_);
+    } break;
+
+    case ASCS_CONNECTION_STATE_EVT: {
+      tracker_.HandleAscsConnectionEvent(p_data);
+    } break;
+    case ASCS_ASE_STATE_EVT: {
+      // to handle remote driven operations
+      // check the state and if the state is Enabling
+      // proceed with cis creation
+      AscsState *ascs =  ((AscsState *) p_data);
+
+      if(!tracker_.ValidateAseUpdate(p_data, &int_strm_trackers_,
+                                     StreamTracker::kStateStarting)) {
+        break;
+      }
+
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+      if (ascs->ase_params.ase_state == ascs::ASE_STATE_ENABLING) {
+        // change the connection state to ENABLING
+        CisInterface *cis_intf = strm_mgr_->GetCisInterface();
+
+        // check for Enabling notification is received for all ASEs
+        std::vector<IntStrmTracker *> *all_trackers =
+                            int_strm_trackers_.GetTrackerList();
+
+        uint8_t num_enabling_notify = 0;
+
+        for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+          if (!stream) {
+            LOG(ERROR) << __func__  << "stream is null";
+            continue;
+          }
+          if(stream->ase_state == ascs::ASE_STATE_ENABLING) {
+            num_enabling_notify++;
+          }
+        }
+
+        if(int_strm_trackers_.size() != num_enabling_notify) {
+          LOG(WARNING) << __func__
+                       << "Enabling notification is not received for all strms";
+          break;
+        }
+
+        // As it single group use cases, always single group start request
+        // will come to BAP layer
+        IsoHciStatus status;
+        std::vector<uint8_t> cis_ids;
+        uint8_t cigId;
+        for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+          if (std::find(cis_ids.begin(), cis_ids.end(),
+                        stream->cis_id) == cis_ids.end()) {
+            cis_ids.push_back(stream->cis_id);
+            cigId = stream->cig_id;
+          }
+        }
+        if(cis_ids.size()) {
+          LOG(WARNING) << __func__ << ": Going For CIS Creation ";
+          status = cis_intf->CreateCis(cigId,
+                                       cis_ids,
+                                       strm_mgr_->GetAddress());
+        }
+        for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+          if (!stream) {
+            LOG(ERROR) << __func__ << "stream is null";
+            continue;
+          }
+          stream->cis_retry_count = 0;
+          if( status == IsoHciStatus::ISO_HCI_SUCCESS) {
+            stream->cis_state = CisState::ESTABLISHED;
+          } else if (status == IsoHciStatus::ISO_HCI_IN_PROGRESS) {
+            // change the connection state to CIS create issued
+            stream->cis_pending_cmd = CisPendingCmd::CIS_CREATE_ISSUED;
+          } else {
+            LOG(WARNING) << __func__ << "CIS create Failed";
+          }
+        }
+      } else if (ascs->ase_params.ase_state == ascs::ASE_STATE_STREAMING) {
+        tracker_.CheckAndUpdateStreamingState(&int_strm_trackers_);
+
+      } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_RELEASING) {
+        tracker_.HandleRemoteDisconnect(ASCS_ASE_STATE_EVT, p_data, StateId());
+
+      } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_DISABLING) {
+        tracker_.HandleRemoteStop(ASCS_ASE_STATE_EVT, p_data, StateId());
+      }
+    } break;
+
+    case ASCS_ASE_OP_FAILED_EVT: {
+      tracker_.HandleAseOpFailedEvent(p_data);
+    } break;
+
+    case CIS_STATE_EVT: {
+      CisStreamState *data = (CisStreamState *) p_data;
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+      // check if current stream tracker is interested in this CIG update
+      std::vector<IntStrmTracker *> int_trackers =
+                   int_strm_trackers_.FindByCisId(data->cig_id, data->cis_id);
+      if(int_trackers.empty()) {
+        LOG(INFO) << __func__  << ": Not intended for this tracker";
+        break;
+      }
+
+      if(data->state == CisState::ESTABLISHED) {
+        // find out the CIS is bidirectional or from air direction
+        // cis, send Receiver start ready as set up data path
+        // is already completed during CIG creation
+        if(data->direction & cis::DIR_FROM_AIR) {
+          // setup the datapath for RX
+          // find out the stream here
+          UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                     (data->cig_id, data->cis_id,
+                                      cis::DIR_FROM_AIR);
+          LOG(WARNING) << __func__  << " DIR_FROM_AIR "
+                       << loghex(static_cast <uint8_t> (cis::DIR_FROM_AIR));
+
+          if(stream && int_strm_trackers_.FindByAseId(stream->ase_id)) {
+            LOG(INFO) << __func__  << ": Stream Direction "
+                      << loghex(static_cast <uint8_t> (stream->direction));
+
+            LOG(INFO) << __func__  << ": Stream ASE Id "
+                      << loghex(static_cast <uint8_t> (stream->ase_id));
+
+            AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+            AseStartReadyOp start_ready_op = {
+                                              .ase_id = stream->ase_id
+                                             };
+            std::vector<AseStartReadyOp> ase_ops;
+            ase_ops.push_back(start_ready_op);
+            ascs_client->StartReady(ASCS_CLIENT_ID,
+                         strm_mgr_->GetAddress(), ase_ops);
+            stream->cis_state = data->state;
+            stream->cis_pending_cmd = CisPendingCmd::NONE;
+            stream->ase_pending_cmd = AscsPendingCmd::START_READY_ISSUED;
+          }
+        }
+
+        if(data->direction & cis::DIR_TO_AIR) {
+          UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                     (data->cig_id, data->cis_id,
+                                      cis::DIR_TO_AIR);
+          if(stream) {
+            stream->cis_state = data->state;
+            stream->cis_pending_cmd = CisPendingCmd::NONE;
+          }
+        }
+
+        tracker_.CheckAndUpdateStreamingState(&int_strm_trackers_);
+
+      } else if (data->state == CisState::READY) { // CIS creation failed
+        CisInterface *cis_intf = strm_mgr_->GetCisInterface();
+        UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                   (data->cig_id, data->cis_id,
+                                    data->direction);
+        if(stream && stream->cis_retry_count < 2) {
+          std::vector<uint8_t> cisIds = {stream->cis_id};
+          LOG(WARNING) << __func__  << ": Going For Retrial of CIS Creation ";
+          IsoHciStatus status =  cis_intf->CreateCis(
+                                           stream->cig_id,
+                                           cisIds,
+                                           strm_mgr_->GetAddress());
+
+          if( status == IsoHciStatus::ISO_HCI_SUCCESS) {
+            stream->cis_state = CisState::ESTABLISHED;
+            tracker_.CheckAndUpdateStreamingState(&int_strm_trackers_);
+          } else if (status == IsoHciStatus::ISO_HCI_IN_PROGRESS) {
+            // change the connection state to CIS create issued
+            stream->cis_retry_count++;
+            stream->cis_pending_cmd = CisPendingCmd::CIS_CREATE_ISSUED;
+          } else {
+            stream->cis_retry_count = 0;
+            LOG(WARNING) << __func__  << "CIS create Failed";
+          }
+        } else {
+          if(stream) {
+            stream->cis_retry_count = 0;
+            stream->cis_state = data->state;
+            stream->cis_pending_cmd = CisPendingCmd::NONE;
+          }
+        }
+      } else {  // transient states
+        UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                   (data->cig_id, data->cis_id,
+                                    data->direction);
+        if(stream) stream->cis_state = data->state;
+      }
+    } break;
+
+    case BAP_TIME_OUT_EVT: {
+      tracker_.OnTimeout(p_data);
+    } break;
+
+    default:
+      LOG(WARNING) << __func__ << ": Un-handled event: "
+                               << tracker_.GetEventName(event);
+      break;
+  }
+  return true;
+}
+
+void StreamTracker::StateUpdating::OnEnter() {
+  LOG(INFO) << __func__ << ": StreamTracker State: " << GetState();
+
+  UcastClientCallbacks* callbacks = strm_mgr_->GetUclientCbacks();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamStateInfo> strms;
+  std::vector<StreamUpdate> *update_streams = tracker_.GetMetaUpdateStreams();
+  uint8_t num_ases = 0;
+
+  LOG(WARNING) << __func__  << ": Start Streams Size "
+                            << update_streams->size();
+
+  StreamControlType control_type = tracker_.GetControlType();
+
+  if(control_type != StreamControlType::UpdateStream) return;
+
+  for (auto it = update_streams->begin(); it != update_streams->end(); it++) {
+    StreamStateInfo state;
+    memset(&state, 0, sizeof(state));
+    state.stream_type = it->stream_type;
+    state.stream_state = StreamState::UPDATING;
+    strms.push_back(state);
+    StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+    context->stream_state = StreamState::UPDATING;
+    for (auto id = context->stream_ids.begin();
+              id != context->stream_ids.end(); id++) {
+      int_strm_trackers_.FindOrAddBytrackerType(it->stream_type,
+                                id->ase_id, id->cig_id,
+                                id->cis_id,
+                                context->codec_config, context->qos_config);
+    }
+    num_ases += context->stream_ids.size();
+  }
+  callbacks->OnStreamState(strm_mgr_->GetAddress(), strms);
+
+  uint64_t tout = num_ases *
+                    (static_cast<uint64_t>(TimeoutVal::UpdatingTimeout));
+  if(!tout) {
+    tout = static_cast<uint64_t>(MaxTimeoutVal::UpdatingTimeout);
+  }
+  TimeoutReason reason = TimeoutReason::STATE_TRANSITION;
+  state_transition_timer = tracker_.SetTimer("StateUpdatingTimer",
+                                             &timeout, reason, tout);
+  if (state_transition_timer == nullptr) {
+    LOG(ERROR) << __func__  << ": StateUpdating: Alarm not allocated.";
+    return;
+  }
+}
+
+void StreamTracker::StateUpdating::OnExit() {
+  tracker_.ClearTimer(state_transition_timer, "StateUpdatingTimer");
+}
+
+bool StreamTracker::StateUpdating::ProcessEvent(uint32_t event, void* p_data) {
+  LOG(INFO) <<__func__  <<": BD Addr = " << strm_mgr_->GetAddress()
+                        <<": State = " << GetState()
+                        <<": Event = " << tracker_.GetEventName(event);
+
+  switch (event) {
+    case BAP_DISCONNECT_REQ_EVT: {
+      tracker_.HandleDisconnect(p_data, StreamTracker::kStateUpdating);
+    } break;
+    case BAP_STOP_REQ_EVT: {
+      tracker_.HandleStop(p_data, StreamTracker::kStateUpdating);
+    } break;
+    case PACS_CONNECTION_STATE_EVT: {
+      tracker_.HandlePacsConnectionEvent(p_data);
+    } break;
+    case ASCS_CONNECTION_STATE_EVT: {
+      tracker_.HandleAscsConnectionEvent(p_data);
+    } break;
+    case PACS_AUDIO_CONTEXT_RES_EVT: {
+      // check for all stream start requests, stream contexts are
+      // part of available contexts
+      PacsAvailableContexts *pacs_contexts = (PacsAvailableContexts *) p_data;
+      if(!tracker_.HandlePacsAudioContextEvent(pacs_contexts) ||
+         !tracker_.HandleStreamUpdate(StreamTracker::kStateUpdating)) {
+        tracker_.TransitionTo(StreamTracker::kStateStreaming);
+      }
+    } break;
+    case ASCS_ASE_STATE_EVT: {
+      AscsState *ascs =  ((AscsState *) p_data);
+      if(!tracker_.ValidateAseUpdate(p_data, &int_strm_trackers_,
+                                     StreamTracker::kStateUpdating)) {
+        break;
+      }
+      if(ascs->ase_params.ase_state == ascs::ASE_STATE_STREAMING) {
+        tracker_.CheckAndUpdateStreamingState(&int_strm_trackers_);
+      } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_RELEASING) {
+        tracker_.HandleRemoteDisconnect(ASCS_ASE_STATE_EVT, p_data, StateId());
+      } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_DISABLING) {
+        tracker_.HandleRemoteStop(ASCS_ASE_STATE_EVT, p_data, StateId());
+      } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_QOS_CONFIGURED) {
+        // this can happen when CIS is lost and detected on remote side
+        // first so it will immediately transition to QOS configured.
+        tracker_.HandleAbruptStop(ASCS_ASE_STATE_EVT, p_data);
+      }
+    } break;
+    case CIS_STATE_EVT: {
+      // handle sudden CIS Disconnection
+      tracker_.HandleCisEventsInStreaming(p_data);
+    } break;
+    default:
+      LOG(WARNING) << __func__ << ": Un-handled event: "
+                               << tracker_.GetEventName(event);
+      break;
+  }
+  return true;
+}
+
+void StreamTracker::StateStreaming::OnEnter() {
+  LOG(INFO) << __func__ << ": StreamTracker State: " << GetState();
+
+  UcastClientCallbacks* callbacks = strm_mgr_->GetUclientCbacks();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamStateInfo> strms;
+  std::vector<StreamType> *start_streams = tracker_.GetStreams();
+  std::vector<StreamUpdate> *update_streams = tracker_.GetMetaUpdateStreams();
+  LOG(WARNING) << __func__  << ": Start Streams Size "
+                            << start_streams->size();
+
+  LOG(WARNING) << __func__  << ": Update Streams Size "
+                            << update_streams->size();
+
+  StreamControlType control_type = tracker_.GetControlType();
+
+  if(control_type == StreamControlType::Start) {
+    for (auto it = start_streams->begin(); it != start_streams->end(); it++) {
+      StreamStateInfo state;
+      memset(&state, 0, sizeof(state));
+      state.stream_type = *it;
+      state.stream_state = StreamState::STREAMING;
+      strms.push_back(state);
+      StreamContext *context = contexts->FindOrAddByType(*it);
+      context->stream_state = StreamState::STREAMING;
+    }
+  } else if(control_type == StreamControlType::UpdateStream) {
+    for (auto it = update_streams->begin(); it != update_streams->end(); it++) {
+      StreamStateInfo state;
+      memset(&state, 0, sizeof(state));
+      state.stream_type = it->stream_type;
+      state.stream_state = StreamState::STREAMING;
+      strms.push_back(state);
+      StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+      context->stream_state = StreamState::STREAMING;
+    }
+  }
+  if(strms.size()) {
+    callbacks->OnStreamState(strm_mgr_->GetAddress(), strms);
+  }
+}
+
+void StreamTracker::StateStreaming::OnExit() {
+
+}
+
+bool StreamTracker::StateStreaming::ProcessEvent(uint32_t event, void* p_data) {
+  LOG(INFO) <<__func__  <<": BD Addr = " << strm_mgr_->GetAddress()
+                        <<": State = " << GetState()
+                        <<": Event = " << tracker_.GetEventName(event);
+
+  switch (event) {
+    case BAP_DISCONNECT_REQ_EVT: {
+      tracker_.HandleDisconnect(p_data, StreamTracker::kStateStreaming);
+    } break;
+    case BAP_STOP_REQ_EVT: {
+      tracker_.HandleStop(p_data, StreamTracker::kStateStreaming);
+    } break;
+    case BAP_STREAM_UPDATE_REQ_EVT: {
+      PacsClient *pacs_client = strm_mgr_->GetPacsClient();
+      uint16_t pacs_client_id = strm_mgr_->GetPacsClientId();
+      BapStreamUpdate *evt_data = (BapStreamUpdate *) p_data;
+      tracker_.UpdateControlType(StreamControlType::UpdateStream);
+      tracker_.UpdateMetaUpdateStreams(&evt_data->update_streams);
+      pacs_client->GetAudioAvailability(pacs_client_id,
+                                        strm_mgr_->GetAddress());
+      tracker_.TransitionTo(StreamTracker::kStateUpdating);
+    } break;
+    case PACS_CONNECTION_STATE_EVT: {
+      tracker_.HandlePacsConnectionEvent(p_data);
+    } break;
+    case ASCS_CONNECTION_STATE_EVT: {
+      tracker_.HandleAscsConnectionEvent(p_data);
+    } break;
+    case ASCS_ASE_STATE_EVT: {
+      AscsState *ascs =  ((AscsState *) p_data);
+      uint8_t ase_id = ascs->ase_params.ase_id;
+      // find out the stream for the given ase id
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+      UcastAudioStream *stream = audio_strms->FindByAseId(ase_id);
+      if (stream) {
+        stream->ase_state = ascs->ase_params.ase_state;
+        stream->ase_params = ascs->ase_params;
+        stream->ase_pending_cmd = AscsPendingCmd::NONE;
+        if(ascs->ase_params.ase_state == ascs::ASE_STATE_RELEASING) {
+          tracker_.HandleRemoteDisconnect(ASCS_ASE_STATE_EVT, p_data, StateId());
+        } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_DISABLING) {
+          tracker_.HandleRemoteStop(ASCS_ASE_STATE_EVT, p_data, StateId());
+        } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_QOS_CONFIGURED){
+          // this can happen when CIS is lost and detected on remote side
+          // first so it will immediately transition to QOS configured.
+          tracker_.HandleAbruptStop(ASCS_ASE_STATE_EVT, p_data);
+        }
+      }
+    } break;
+    case CIS_STATE_EVT: {
+      // handle sudden CIS Disconnection
+      tracker_.HandleCisEventsInStreaming(p_data);
+    } break;
+    default:
+      LOG(WARNING) << __func__ << ": Un-handled event: "
+                               << tracker_.GetEventName(event);
+      break;
+  }
+  return true;
+}
+
+void StreamTracker::StateStopping::OnEnter() {
+  LOG(INFO) << __func__ << ": StreamTracker State: " << GetState();
+
+  UcastClientCallbacks* callbacks = strm_mgr_->GetUclientCbacks();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamStateInfo> strms;
+  std::vector<StreamType> *stop_streams = tracker_.GetStreams();
+  uint8_t num_ases = 0;
+
+  LOG(WARNING) << __func__  << ": Stop Streams Size : "
+                            << stop_streams->size();
+  StreamControlType control_type = tracker_.GetControlType();
+
+  if(control_type != StreamControlType::Stop) return;
+
+  for (auto it = stop_streams->begin();
+                       it != stop_streams->end(); it++) {
+    StreamStateInfo state;
+    memset(&state, 0, sizeof(state));
+    state.stream_type = *it;
+    state.stream_state = StreamState::STOPPING;
+    strms.push_back(state);
+    StreamContext *context = contexts->FindOrAddByType(*it);
+    context->stream_state = StreamState::STOPPING;
+
+    for (auto id = context->stream_ids.begin();
+              id != context->stream_ids.end(); id++) {
+      int_strm_trackers_.FindOrAddBytrackerType(*it,
+                                id->ase_id, id->cig_id,
+                                id->cis_id,
+                                context->codec_config, context->qos_config);
+    }
+    num_ases += context->stream_ids.size();
+  }
+  callbacks->OnStreamState(strm_mgr_->GetAddress(), strms);
+
+  uint64_t tout = num_ases *
+                    (static_cast<uint64_t>(TimeoutVal::StoppingTimeout));
+  if(!tout) {
+    tout = static_cast<uint64_t>(MaxTimeoutVal::StoppingTimeout);
+  }
+
+  TimeoutReason reason = TimeoutReason::STATE_TRANSITION;
+  state_transition_timer = tracker_.SetTimer("StateStoppingTimer",
+                &timeout, reason, tout);
+  if (state_transition_timer == nullptr) {
+    LOG(ERROR) << __func__  << ": StateStopping: Alarm not allocated.";
+    return;
+  }
+}
+
+void StreamTracker::StateStopping::OnExit() {
+  tracker_.ClearTimer(state_transition_timer, "StateStoppingTimer");
+}
+
+bool StreamTracker::StateStopping::TerminateCisAndCig(UcastAudioStream *stream) {
+
+  CisInterface *cis_intf = strm_mgr_->GetCisInterface();
+  uint8_t num_strms_in_qos_configured = 0;
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+  std::vector<IntStrmTracker *> all_trackers =
+                      int_strm_trackers_.FindByCigIdAndDir(stream->cig_id,
+                                                           stream->direction);
+
+  for(auto i = all_trackers.begin(); i != all_trackers.end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if (!stream) {
+      LOG(ERROR) << __func__  << "stream is null";
+      continue;
+    }
+    if(stream->ase_state == ascs::ASE_STATE_QOS_CONFIGURED) {
+      num_strms_in_qos_configured++;
+    }
+  }
+
+  if(all_trackers.size() != num_strms_in_qos_configured) {
+    LOG(WARNING) << __func__  << "Not All Streams Moved to QOS Configured";
+    return false;
+  }
+
+  for (auto i = all_trackers.begin(); i != all_trackers.end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if (!stream) {
+      LOG(ERROR) << __func__  << "stream is null";
+      continue;
+    }
+    if(stream->cis_pending_cmd == CisPendingCmd::NONE &&
+       stream->cis_state == CisState::ESTABLISHED &&
+       stream->ase_state == ascs::ASE_STATE_QOS_CONFIGURED) {
+      LOG(WARNING) << __func__  << ": Going For CIS Disconnect ";
+      IsoHciStatus status = cis_intf->DisconnectCis(stream->cig_id,
+                                                    stream->cis_id,
+                                                    stream->direction);
+      if(status == IsoHciStatus::ISO_HCI_SUCCESS) {
+        stream->cis_pending_cmd = CisPendingCmd::NONE;
+        stream->cis_state = CisState::READY;
+      } else if(status == IsoHciStatus::ISO_HCI_IN_PROGRESS) {
+        stream->cis_pending_cmd = CisPendingCmd::CIS_DESTROY_ISSUED;
+      } else {
+        LOG(WARNING) << __func__  << ": CIS Disconnect Failed";
+      }
+    }
+
+    if(stream->cis_state == CisState::READY) {
+      if(stream->cig_state == CigState::CREATED &&
+         stream->cis_pending_cmd == CisPendingCmd::NONE) {
+        LOG(WARNING) << __func__  << ": Going For CIG Removal";
+        IsoHciStatus status = cis_intf->RemoveCig(strm_mgr_->GetAddress(),
+                                stream->cig_id);
+        if( status == IsoHciStatus::ISO_HCI_SUCCESS) {
+          stream->cig_state = CigState::INVALID;
+          stream->cis_state = CisState::INVALID;
+        } else if (status == IsoHciStatus::ISO_HCI_IN_PROGRESS) {
+          stream->cis_pending_cmd = CisPendingCmd::CIG_REMOVE_ISSUED;
+        } else {
+          LOG(WARNING) << __func__  << ": CIG removal Failed";
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool StreamTracker::StateStopping::CheckAndUpdateStoppedState() {
+  //  to check for all internal trackers are moved to
+  // cis destroyed state then update the callback
+  uint8_t num_strms_in_stopping = 0;
+  bool pending_cmds = false;
+
+  std::vector<IntStrmTracker *> *all_trackers =
+                      int_strm_trackers_.GetTrackerList();
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+  // check if any pending commands are present
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if(stream && (stream->cis_pending_cmd != CisPendingCmd::NONE ||
+                  stream->ase_pending_cmd != AscsPendingCmd::NONE)) {
+      pending_cmds = true;
+      break;
+    }
+  }
+
+  if(pending_cmds) return false;
+
+  for(auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if(stream && (stream->cig_state == CigState::IDLE ||
+       stream->cig_state == CigState::INVALID) &&
+       (stream->cis_state == CisState::READY ||
+        stream->cis_state == CisState::INVALID) &&
+       stream->ase_state == ascs::ASE_STATE_QOS_CONFIGURED) {
+      num_strms_in_stopping++;
+    }
+  }
+
+  if(int_strm_trackers_.size() != num_strms_in_stopping) {
+    LOG(WARNING) << __func__  << "Not All Streams Moved to Stopped State";
+    return false;
+  }
+
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if (!stream) {
+      LOG(ERROR) << __func__  << "stream is null";
+      continue;
+    }
+    stream->overall_state = StreamTracker::kStateConnected;
+  }
+
+  tracker_.TransitionTo(StreamTracker::kStateConnected);
+  return true;
+}
+
+bool StreamTracker::StateStopping::ProcessEvent(uint32_t event, void* p_data) {
+  LOG(INFO) <<__func__  <<": BD Addr = " << strm_mgr_->GetAddress()
+                        <<": State = " << GetState()
+                        <<": Event = " << tracker_.GetEventName(event);
+
+  switch (event) {
+    case BAP_DISCONNECT_REQ_EVT:{
+      tracker_.HandleDisconnect(p_data, StreamTracker::kStateStopping);
+    } break;
+    case PACS_CONNECTION_STATE_EVT: {
+      tracker_.HandlePacsConnectionEvent(p_data);
+    } break;
+    case ASCS_CONNECTION_STATE_EVT: {
+      tracker_.HandleAscsConnectionEvent(p_data);
+    } break;
+    case ASCS_ASE_STATE_EVT: {
+      // to handle remote driven operations
+      AscsState *ascs =  ((AscsState *) p_data);
+
+      if(!tracker_.ValidateAseUpdate(p_data, &int_strm_trackers_,
+                                     StreamTracker::kStateStopping)) {
+        break;
+      }
+
+      // find out the stream for the given ase id
+      uint8_t ase_id = ascs->ase_params.ase_id;
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+      UcastAudioStream *stream = audio_strms->FindByAseId(ase_id);
+      if (!stream) {
+        LOG(ERROR) << __func__  << "stream is null";
+        break;
+      }
+
+      if(ascs->ase_params.ase_state == ascs::ASE_STATE_DISABLING) {
+        if(stream->direction & cis::DIR_FROM_AIR) {
+          LOG(INFO) << __func__  << " Sending Stop Ready ";
+          AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+          AseStopReadyOp stop_ready_op = {
+                                .ase_id = stream->ase_id
+          };
+          std::vector<AseStopReadyOp> ase_ops;
+          ase_ops.push_back(stop_ready_op);
+          ascs_client->StopReady(ASCS_CLIENT_ID,
+                       strm_mgr_->GetAddress(), ase_ops);
+          stream->ase_pending_cmd = AscsPendingCmd::STOP_READY_ISSUED;
+        } else {
+          LOG(ERROR) << __func__  << ": Invalid State transition to Disabling"
+                     << ": ASE Id = " << loghex(ase_id);
+        }
+
+      } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_QOS_CONFIGURED) {
+
+        stream->ase_pending_cmd = AscsPendingCmd::NONE;
+        // stopped state then issue CIS disconnect
+        TerminateCisAndCig(stream);
+        CheckAndUpdateStoppedState();
+
+      } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_RELEASING) {
+        tracker_.HandleRemoteDisconnect(ASCS_ASE_STATE_EVT, p_data, StateId());
+      }
+    } break;
+
+    case ASCS_ASE_OP_FAILED_EVT: {
+      tracker_.HandleAseOpFailedEvent(p_data);
+    } break;
+
+    case CIS_STATE_EVT: {
+      CisStreamState *data = (CisStreamState *) p_data;
+      CisInterface *cis_intf = strm_mgr_->GetCisInterface();
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+      // check if current stream tracker is interested in this CIG update
+      std::vector<IntStrmTracker *> int_trackers =
+                   int_strm_trackers_.FindByCisId(data->cig_id, data->cis_id);
+      if(int_trackers.empty()) {
+        LOG(INFO) << __func__  << "Not intended for this tracker";
+        break;
+      }
+      if(data->state == CisState::ESTABLISHED) {
+        for(auto it = directions.begin(); it != directions.end(); ++it) {
+          if(data->direction & *it) {
+            // find out the stream here
+            UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                       (data->cig_id, data->cis_id, *it);
+            if(stream) {
+              stream->cis_state = data->state;
+              stream->cis_pending_cmd = CisPendingCmd::NONE;
+              if(int_strm_trackers_.FindByAseId(stream->ase_id)) {
+                TerminateCisAndCig(stream);
+              }
+            }
+          }
+        }
+        CheckAndUpdateStoppedState();
+
+      } else if(data->state == CisState::READY) {
+        for(auto it = directions.begin(); it != directions.end(); ++it) {
+          if(data->direction & *it) {
+            // find out the stream here
+            UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                       (data->cig_id, data->cis_id, *it);
+            if(stream) {
+              stream->cis_state = data->state;
+              stream->cis_pending_cmd = CisPendingCmd::NONE;
+              if(stream->cig_state == CigState::CREATED &&
+                 stream->cis_pending_cmd == CisPendingCmd::NONE) {
+                IsoHciStatus status = cis_intf->RemoveCig(
+                                        strm_mgr_->GetAddress(),
+                                        stream->cig_id);
+                if( status == IsoHciStatus::ISO_HCI_SUCCESS) {
+                  stream->cig_state = CigState::INVALID;
+                  stream->cis_state = CisState::INVALID;
+                } else if (status == IsoHciStatus::ISO_HCI_IN_PROGRESS) {
+                  stream->cis_pending_cmd = CisPendingCmd::CIG_REMOVE_ISSUED;
+                } else {
+                  LOG(WARNING) << __func__  << ": CIG removal Failed";
+                }
+              }
+            }
+          }
+        }
+        CheckAndUpdateStoppedState();
+      } else {  // transient states
+        for(auto it = directions.begin(); it != directions.end(); ++it) {
+          if(data->direction & *it) {
+            // find out the stream here
+            UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                       (data->cig_id, data->cis_id, *it);
+            if(stream) stream->cis_state = data->state;
+          }
+        }
+      }
+    } break;
+
+    case CIS_GROUP_STATE_EVT: {
+      CisGroupState *data = ((CisGroupState *) p_data);
+      // check if current stream tracker is interested in this CIG update
+      std::vector<IntStrmTracker *> int_trackers =
+                            int_strm_trackers_.FindByCigId(data->cig_id);
+      if(int_trackers.empty()) {
+        LOG(INFO) << __func__  << "Not intended for this tracker";
+        break;
+      }
+
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+      if(data->state == CigState::CREATED) {
+        for (auto i = int_trackers.begin(); i != int_trackers.end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+          if (stream) {
+            // check if this is a CIG created event due to CIG create
+            // issued during starting state
+            stream->cis_pending_cmd = CisPendingCmd::NONE;
+            stream->cig_state = data->state;
+            stream->cis_state = CisState::READY;
+            TerminateCisAndCig(stream);
+          }
+        }
+        CheckAndUpdateStoppedState();
+
+      } else if(data->state == CigState::IDLE) {
+        for (auto i = int_trackers.begin(); i != int_trackers.end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+          if (stream) {
+            stream->cig_state = CigState::INVALID;
+            stream->cis_state = CisState::INVALID;
+            stream->cis_pending_cmd = CisPendingCmd::NONE;
+          }
+        }
+        CheckAndUpdateStoppedState();
+      }
+    } break;
+    case BAP_TIME_OUT_EVT: {
+      tracker_.OnTimeout(p_data);
+    } break;
+
+    default:
+      LOG(WARNING) << __func__ << ": Un-handled event: "
+                               << tracker_.GetEventName(event);
+      break;
+  }
+  return true;
+}
+
+bool StreamTracker::StateDisconnecting::TerminateGattConnection() {
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  GattPendingData *gatt_pending_data = strm_mgr_->GetGattPendingData();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamContext *> *all_contexts = contexts->GetAllContexts();
+  bool any_context_active = false;
+  bool disc_issued = false;
+  std::vector<int> ids = { StreamTracker::kStateIdle };
+  std::vector<UcastAudioStream *> idle_streams =
+                               audio_strms->GetStreamsByStates(
+                               ids, ASE_DIRECTION_SINK | ASE_DIRECTION_SRC);
+
+  LOG(WARNING) << __func__ <<": Total Streams size: " << audio_strms->size()
+                           <<": Idle Streams size: " << idle_streams.size();
+
+  // check if any of the contexts stream state is connected
+  for (auto it = all_contexts->begin(); it != all_contexts->end(); it++) {
+    if((*it)->stream_state != StreamState::DISCONNECTING &&
+       (*it)->stream_state != StreamState::DISCONNECTED) {
+      LOG(INFO) << __func__ <<": Other contexts are active,not to disc Gatt";
+      any_context_active = true;
+      break;
+    }
+  }
+  if(!any_context_active &&
+     (!audio_strms->size() || audio_strms->size() == idle_streams.size())) {
+
+    // check if gatt connection can be tear down for ascs & pacs clients
+    // all streams are in idle state
+    AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+    PacsClient *pacs_client = strm_mgr_->GetPacsClient();
+    uint16_t pacs_client_id = strm_mgr_->GetPacsClientId();
+
+    ConnectionState pacs_state = strm_mgr_->GetPacsState();
+    if((pacs_state == ConnectionState::CONNECTED &&
+        gatt_pending_data->pacs_pending_cmd == GattPendingCmd::NONE) ||
+       (gatt_pending_data->pacs_pending_cmd ==
+                                GattPendingCmd::GATT_CONN_PENDING)) {
+      LOG(WARNING) << __func__  << " Issue PACS server disconnect ";
+      pacs_client->Disconnect(pacs_client_id, strm_mgr_->GetAddress());
+      gatt_pending_data->pacs_pending_cmd = GattPendingCmd::GATT_DISC_PENDING;
+      disc_issued = true;
+    }
+
+    GattState ascs_state = strm_mgr_->GetAscsState();
+    if((ascs_state == GattState::CONNECTED &&
+        gatt_pending_data->ascs_pending_cmd == GattPendingCmd::NONE) ||
+       (gatt_pending_data->ascs_pending_cmd ==
+                                GattPendingCmd::GATT_CONN_PENDING)) {
+      LOG(WARNING) << __func__  << " Issue ASCS server disconnect ";
+      ascs_client->Disconnect(ASCS_CLIENT_ID, strm_mgr_->GetAddress());
+      gatt_pending_data->ascs_pending_cmd = GattPendingCmd::GATT_DISC_PENDING;
+      disc_issued = true;
+    }
+  }
+  return disc_issued;
+}
+
+void StreamTracker::StateDisconnecting::OnEnter() {
+  LOG(INFO) << __func__ << ": StreamTracker State: " << GetState();
+
+  // check the previous state i.e connecting, starting, stopping
+  // or reconfiguring
+
+  UcastClientCallbacks* callbacks = strm_mgr_->GetUclientCbacks();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamStateInfo> strms;
+  uint8_t num_ases = 0;
+
+  std::vector<StreamType> *disc_streams = tracker_.GetStreams();
+  LOG(WARNING) << __func__  << ": Disconection Streams Size: "
+                            << disc_streams->size();
+
+  StreamControlType control_type = tracker_.GetControlType();
+
+  if(control_type != StreamControlType::Disconnect) {
+    return;
+  }
+
+  for (auto it = disc_streams->begin(); it != disc_streams->end(); it++) {
+    StreamStateInfo state;
+    memset(&state, 0, sizeof(state));
+    state.stream_type = *it;
+    state.stream_state = StreamState::DISCONNECTING;
+    strms.push_back(state);
+    StreamContext *context = contexts->FindOrAddByType(*it);
+    context->stream_state = StreamState::DISCONNECTING;
+    if(context->connection_state == IntConnectState::ASCS_DISCOVERED) {
+      for (auto id = context->stream_ids.begin();
+              id != context->stream_ids.end(); id++) {
+        bool can_be_disconnected = tracker_.
+                       StreamCanbeDisconnected(context, id->ase_id);
+        if(can_be_disconnected) {
+          int_strm_trackers_.FindOrAddBytrackerType(*it,
+                             id->ase_id, id->cig_id,
+                             id->cis_id,
+                             context->codec_config, context->qos_config);
+        }
+      }
+    }
+    num_ases += context->stream_ids.size();
+  }
+  callbacks->OnStreamState(strm_mgr_->GetAddress(), strms);
+
+  uint64_t tout = num_ases *
+                    (static_cast<uint64_t>(TimeoutVal::DisconnectingTimeout));
+  if(!tout ||tout > static_cast<uint64_t>(MaxTimeoutVal::DisconnectingTimeout)) {
+    tout = static_cast<uint64_t>(MaxTimeoutVal::DisconnectingTimeout);
+  }
+
+  TimeoutReason reason = TimeoutReason::STATE_TRANSITION;
+  state_transition_timer = tracker_.SetTimer("StateDisconnectingTimer",
+                &timeout, reason, tout);
+  if (state_transition_timer == nullptr) {
+    LOG(ERROR) << __func__  << ": StateDisconnecting: Alarm not allocated.";
+    return;
+  }
+
+  bool gatt_disc_pending = TerminateGattConnection();
+  // check if there are no internal stream trackers, then update to
+  // upper layer as completely disconnected
+  if(!int_strm_trackers_.size() && !gatt_disc_pending) {
+    tracker_.TransitionTo(StreamTracker::kStateIdle);
+  }
+}
+
+void StreamTracker::StateDisconnecting::ContinueDisconnection
+                                (UcastAudioStream *stream) {
+
+  // check ase state, return if state is not releasing or
+  if(stream->ase_state != ascs::ASE_STATE_IDLE &&
+     stream->ase_state != ascs::ASE_STATE_CODEC_CONFIGURED &&
+     stream->ase_state != ascs::ASE_STATE_RELEASING) {
+    LOG(WARNING) << __func__  << " Return as ASE is not moved to Right state";
+    return;
+  }
+
+  CisInterface *cis_intf = strm_mgr_->GetCisInterface();
+
+  // check if there is no pending CIS command then issue relevant
+  // CIS command based on CIS state
+  if(stream->cis_pending_cmd != CisPendingCmd::NONE) {
+    LOG(INFO) << __func__  << ": cis_pending_cmd is not NONE ";
+    return;
+  }
+
+  if(stream->cis_state == CisState::ESTABLISHED) {
+    LOG(WARNING) << __func__  << ": Going For CIS disconnect ";
+    IsoHciStatus status = cis_intf->DisconnectCis(stream->cig_id,
+                                                  stream->cis_id,
+                                                  stream->direction);
+    if(status == IsoHciStatus::ISO_HCI_SUCCESS) {
+      stream->cis_pending_cmd = CisPendingCmd::NONE;
+      stream->cis_state = CisState::READY;
+    } else if(status == IsoHciStatus::ISO_HCI_IN_PROGRESS) {
+      stream->cis_pending_cmd = CisPendingCmd::CIS_DESTROY_ISSUED;
+    } else {
+      LOG(WARNING) << __func__  << ": CIS Disconnect Failed";
+    }
+  }
+
+  if(stream->cis_state == CisState::READY) {
+    if(stream->cig_state == CigState::CREATED &&
+       stream->cis_pending_cmd == CisPendingCmd::NONE) {
+      LOG(WARNING) << __func__  << ": Going For CIG Removal";
+      IsoHciStatus status = cis_intf->RemoveCig(strm_mgr_->GetAddress(),
+                              stream->cig_id);
+      if( status == IsoHciStatus::ISO_HCI_SUCCESS) {
+        stream->cig_state = CigState::INVALID;
+        stream->cis_state = CisState::INVALID;
+      } else if (status == IsoHciStatus::ISO_HCI_IN_PROGRESS) {
+        stream->cis_pending_cmd = CisPendingCmd::CIG_REMOVE_ISSUED;
+      } else {
+        LOG(WARNING) << __func__  << ": CIG removal Failed";
+      }
+    }
+  }
+}
+
+bool StreamTracker::StateDisconnecting::CheckAndUpdateDisconnectedState() {
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  bool pending_cmds = false;
+
+  std::vector<IntStrmTracker *> *all_trackers =
+                      int_strm_trackers_.GetTrackerList();
+
+  // check if any pending commands are present
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if(stream && (stream->cis_pending_cmd != CisPendingCmd::NONE ||
+                  stream->ase_pending_cmd != AscsPendingCmd::NONE)) {
+      pending_cmds = true;
+      break;
+    }
+  }
+
+  if(pending_cmds) {
+    LOG(WARNING) << __func__  << " Pending ASCS/CIS cmds present ";
+    return false;
+  }
+
+  TerminateGattConnection();
+
+  // check it needs to wait for ASCS & PACS disconnection also
+  GattPendingData *gatt_pending_data = strm_mgr_->GetGattPendingData();
+  if(gatt_pending_data->ascs_pending_cmd != GattPendingCmd::NONE ||
+     gatt_pending_data->pacs_pending_cmd != GattPendingCmd::NONE) {
+    LOG(WARNING) << __func__  << " Pending Gatt disc present ";
+    return false;
+  }
+
+  // check for all trackers moved to idle and
+  // CIG state is idle if so update it as streams are disconnected
+  uint8_t num_strms_disconnected = 0;
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if (!stream) {
+      LOG(ERROR) << __func__  << "stream is null";
+      continue;
+    }
+    if((stream->ase_state == ascs::ASE_STATE_IDLE ||
+       stream->ase_state == ascs::ASE_STATE_CODEC_CONFIGURED) &&
+       (stream->cig_state == CigState::IDLE ||
+       stream->cig_state == CigState::INVALID) &&
+       (stream->cis_state == CisState::READY ||
+        stream->cis_state == CisState::INVALID)) {
+      num_strms_disconnected++;
+    }
+  }
+
+  if(int_strm_trackers_.size() != num_strms_disconnected) {
+    LOG(WARNING) << __func__  << "Not disconnected for all streams";
+    return false;
+  } else {
+    LOG(ERROR) << __func__  << "Disconnected for all streams";
+  }
+
+  for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+    UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+    if (stream) {
+      stream->overall_state = StreamTracker::kStateIdle;
+    }
+  }
+
+  // update the state to idle
+  tracker_.TransitionTo(StreamTracker::kStateIdle);
+  return true;
+}
+
+void StreamTracker::StateDisconnecting::OnExit() {
+  tracker_.ClearTimer(state_transition_timer, "StateDisconnectingTimer");
+}
+
+bool StreamTracker::StateDisconnecting::ProcessEvent(uint32_t event,
+                                                     void* p_data) {
+  LOG(INFO) <<__func__  <<": BD Addr = " << strm_mgr_->GetAddress()
+                        <<": State = " << GetState()
+                        <<": Event = " << tracker_.GetEventName(event);
+
+  switch (event) {
+    case PACS_CONNECTION_STATE_EVT: {
+      PacsConnectionState *pacs_state =  (PacsConnectionState *) p_data;
+      GattPendingData *gatt_pending_data = strm_mgr_->GetGattPendingData();
+      if(pacs_state->state == ConnectionState::DISCONNECTED) {
+        gatt_pending_data->pacs_pending_cmd = GattPendingCmd::NONE;
+      }
+      CheckAndUpdateDisconnectedState();
+    } break;
+    case ASCS_CONNECTION_STATE_EVT: {
+      AscsConnectionState *ascs_state =  (AscsConnectionState *) p_data;
+      GattPendingData *gatt_pending_data = strm_mgr_->GetGattPendingData();
+      if(ascs_state->state == GattState::DISCONNECTED) {
+        // make all streams ASE state to idle so that further processing
+        // can happen
+        gatt_pending_data->ascs_pending_cmd = GattPendingCmd::NONE;
+        UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+        std::vector<UcastAudioStream *> *strms_list =
+                                             audio_strms->GetAllStreams();
+
+        for (auto it = strms_list->begin(); it != strms_list->end(); it++) {
+
+          (*it)->ase_state = ascs::ASE_STATE_IDLE;
+          (*it)->ase_pending_cmd = AscsPendingCmd::NONE;
+          (*it)->overall_state = StreamTracker::kStateIdle;
+          ContinueDisconnection(*it);
+        }
+      }
+      CheckAndUpdateDisconnectedState();
+    } break;
+    case ASCS_ASE_STATE_EVT: {  // to handle remote driven operations
+
+      // check for state releasing
+      // based on prev state do accordingly
+      AscsState *ascs =  ((AscsState *) p_data);
+
+      uint8_t ase_id = ascs->ase_params.ase_id;
+
+      // check if current stream tracker is interested in this ASE update
+      if(int_strm_trackers_.FindByAseId(ase_id)
+                                 == nullptr) {
+        LOG(INFO) << __func__  << "Not intended for this tracker";
+        break;
+      }
+
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+      UcastAudioStream *stream = audio_strms->FindByAseId(ase_id);
+
+      if(stream == nullptr) {
+        break;
+      } else {
+        stream->ase_state = ascs->ase_params.ase_state;
+        stream->ase_params = ascs->ase_params;
+      }
+
+      if(ascs->ase_params.ase_state == ascs::ASE_STATE_RELEASING) {
+        // find out the stream for the given ase id
+        LOG(WARNING) << __func__  << " ASE Id " << loghex(ase_id);
+        stream->ase_pending_cmd = AscsPendingCmd::NONE;
+        ContinueDisconnection(stream);
+
+      } else if( ascs->ase_params.ase_state ==
+                                    ascs::ASE_STATE_CODEC_CONFIGURED) {
+        // check if this is a codec config notification due to codec config
+        // issued during connecting state
+        if((tracker_.PreviousStateId() == StreamTracker::kStateConnecting ||
+            tracker_.PreviousStateId() == StreamTracker::kStateReconfiguring) &&
+           stream->ase_pending_cmd == AscsPendingCmd::CODEC_CONFIG_ISSUED &&
+           stream->ase_state == ascs::ASE_STATE_CODEC_CONFIGURED) {
+          // mark int conn state as codec configured and issue release command
+          std::vector<AseReleaseOp> ase_ops;
+          AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+          AseReleaseOp release_op = {
+                              .ase_id = stream->ase_id
+          };
+          ase_ops.push_back(release_op);
+          stream->ase_pending_cmd = AscsPendingCmd::RELEASE_ISSUED;
+          ascs_client->Release(ASCS_CLIENT_ID,
+                       strm_mgr_->GetAddress(), ase_ops);
+          break; // break the switch case
+        } else {
+          stream->ase_pending_cmd = AscsPendingCmd::NONE;
+          stream->overall_state = StreamTracker::kStateIdle;
+          ContinueDisconnection(stream);
+          CheckAndUpdateDisconnectedState();
+        }
+      } else if(ascs->ase_params.ase_state == ascs::ASE_STATE_IDLE) {
+        // check for all trackers moved to idle and
+        // CIG state is idle if so update it as streams are disconnected
+        stream->ase_pending_cmd = AscsPendingCmd::NONE;
+        stream->overall_state = StreamTracker::kStateIdle;
+        ContinueDisconnection(stream);
+        CheckAndUpdateDisconnectedState();
+      }
+    } break;
+
+    case ASCS_ASE_OP_FAILED_EVT: {
+      AscsOpFailed *ascs_op =  ((AscsOpFailed *) p_data);
+      std::vector<ascs::AseOpStatus> *ase_list = &ascs_op->ase_list;
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+
+      if(ascs_op->ase_op_id == ascs::AseOpId::RELEASE) {
+        // treat it like internal failure
+        for (auto i = ase_list->begin(); i != ase_list->end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((i)->ase_id);
+          if(stream) {
+            stream->ase_state = ascs::ASE_STATE_IDLE;
+            stream->ase_pending_cmd = AscsPendingCmd::NONE;
+            stream->overall_state = StreamTracker::kStateIdle;
+            ContinueDisconnection(stream);
+          }
+        }
+        CheckAndUpdateDisconnectedState();
+      }
+    } break;
+
+    case CIS_GROUP_STATE_EVT: {
+      // check if the associated CIG state is created
+      // if so go for QOS config operation
+      CisGroupState *data = ((CisGroupState *) p_data);
+
+      // check if current stream tracker is interested in this CIG update
+      std::vector<IntStrmTracker *> int_trackers =
+                            int_strm_trackers_.FindByCigId(data->cig_id);
+      if(int_trackers.empty()) {
+        LOG(INFO) << __func__  << "Not intended for this tracker";
+        break;
+      }
+
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+      if(data->state == CigState::CREATED) {
+        for (auto i = int_trackers.begin(); i != int_trackers.end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+          if (stream) {
+            stream->cis_pending_cmd = CisPendingCmd::NONE;
+            stream->cig_state = data->state;
+            stream->cis_state = CisState::READY;
+            // check if this is a CIG created event due to CIG create
+            // issued during starting state
+            ContinueDisconnection(stream);
+          }
+        }
+        CheckAndUpdateDisconnectedState();
+
+      } else if(data->state == CigState::IDLE) {
+        for (auto i = int_trackers.begin(); i != int_trackers.end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+          if (!stream) {
+            LOG(ERROR) << __func__  << "stream is null";
+            continue;
+          }
+          stream->cig_state = CigState::INVALID;
+          stream->cis_state = CisState::INVALID;
+          stream->cis_pending_cmd = CisPendingCmd::NONE;
+        }
+        CheckAndUpdateDisconnectedState();
+      }
+    } break;
+    case CIS_STATE_EVT: {
+      CisStreamState *data = (CisStreamState *) p_data;
+      UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+      // check if current stream tracker is interested in this CIG update
+      std::vector<IntStrmTracker *> int_trackers =
+                   int_strm_trackers_.FindByCisId(data->cig_id, data->cis_id);
+      if(int_trackers.empty()) {
+        LOG(INFO) << __func__  << "Not intended for this tracker";
+        break;
+      }
+
+      // go for CIS destroy or CIG removal based on CIS state
+      if(data->state == CisState::ESTABLISHED) {
+        for(auto it = directions.begin(); it != directions.end(); ++it) {
+          if(data->direction & *it) {
+            UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                       (data->cig_id, data->cis_id, *it);
+            if(stream) {
+              stream->cis_state = data->state;
+              stream->cis_pending_cmd = CisPendingCmd::NONE;
+              ContinueDisconnection(stream);
+            }
+          }
+        }
+      } else if(data->state == CisState::READY) {
+        for(auto it = directions.begin(); it != directions.end(); ++it) {
+          if(data->direction & *it) {
+            UcastAudioStream *stream = audio_strms->FindByCisIdAndDir
+                                       (data->cig_id, data->cis_id, *it);
+            if(stream) {
+              stream->cis_state = data->state;
+              stream->cis_pending_cmd = CisPendingCmd::NONE;
+              ContinueDisconnection(stream);
+            }
+          }
+        }
+        CheckAndUpdateDisconnectedState();
+      }
+    } break;
+
+    case BAP_TIME_OUT_EVT: {
+      BapTimeout* timeout = static_cast <BapTimeout *> (p_data);
+      if (timeout == nullptr) {
+        LOG(INFO) << __func__ << ": timeout data null, return ";
+        break;
+      }
+
+      int stream_tracker_id = timeout->transition_state;
+      LOG(INFO) << __func__ << ": stream_tracker_ID: " << stream_tracker_id
+                << ", timeout reason: " << static_cast<int>(timeout->reason);
+
+      std::vector<IntStrmTracker *> *int_trackers =
+                            int_strm_trackers_.GetTrackerList();
+      if (timeout->reason == TimeoutReason::STATE_TRANSITION) {
+        UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+        for (auto i = int_trackers->begin(); i != int_trackers->end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+          if(stream) {
+            stream->ase_state = ascs::ASE_STATE_IDLE;
+            stream->ase_pending_cmd = AscsPendingCmd::NONE;
+            stream->overall_state = StreamTracker::kStateIdle;
+            ContinueDisconnection(stream);
+          }
+        }
+        CheckAndUpdateDisconnectedState();
+      }
+    } break;
+    default:
+      LOG(WARNING) << __func__ << ": Un-handled event: "
+                               << tracker_.GetEventName(event);
+      break;
+  }
+  return true;
+}
+
+void StreamTracker::StateReconfiguring::OnEnter() {
+  LOG(INFO) << __func__ << ": StreamTracker State: " << GetState();
+  UcastClientCallbacks* callbacks = strm_mgr_->GetUclientCbacks();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  std::vector<StreamStateInfo> strms;
+  std::vector<StreamReconfig> *reconfig_streams = tracker_.GetReconfStreams();
+  uint8_t num_ases = 0;
+
+  LOG(WARNING) << __func__  << ": Reconfig Streams Size: "
+                            << reconfig_streams->size();
+
+  StreamControlType control_type = tracker_.GetControlType();
+
+  if(control_type != StreamControlType::Reconfig) return;
+
+  for (auto it = reconfig_streams->begin();
+                       it != reconfig_streams->end(); it++) {
+    StreamStateInfo state;
+    memset(&state, 0, sizeof(state));
+    state.stream_type = it->stream_type;
+    state.stream_state = StreamState::RECONFIGURING;
+    strms.push_back(state);
+    StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+    context->connection_state = IntConnectState::PACS_DISCOVERING;
+    context->stream_state = StreamState::RECONFIGURING;
+    num_ases += context->stream_ids.size();
+  }
+  callbacks->OnStreamState(strm_mgr_->GetAddress(), strms);
+
+  uint64_t tout = num_ases *
+                    (static_cast<uint64_t>(TimeoutVal::ReconfiguringTimeout));
+  if(!tout ||tout > static_cast<uint64_t>(MaxTimeoutVal::ReconfiguringTimeout)){
+    tout = static_cast<uint64_t>(MaxTimeoutVal::ReconfiguringTimeout);
+  }
+
+  TimeoutReason reason = TimeoutReason::STATE_TRANSITION;
+  state_transition_timer = tracker_.SetTimer("StateReconfiguringTimer",
+            &timeout, reason, tout);
+  if (state_transition_timer == nullptr) {
+    LOG(ERROR) << __func__  << ": state_transition_timer: Alarm not allocated.";
+    return;
+  }
+}
+
+void StreamTracker::StateReconfiguring::OnExit() {
+  tracker_.ClearTimer(state_transition_timer, "StateReconfiguringTimer");
+}
+
+bool StreamTracker::StateReconfiguring::ProcessEvent(uint32_t event,
+                                                     void* p_data) {
+  LOG(INFO) <<__func__  <<": BD Addr = " << strm_mgr_->GetAddress()
+                        <<": State = " << GetState()
+                        <<": Event = " << tracker_.GetEventName(event);
+
+  std::vector<StreamReconfig> *reconf_streams = tracker_.GetReconfStreams();
+  StreamContexts *contexts = strm_mgr_->GetStreamContexts();
+  uint8_t num_reconf_streams = 0;
+  if(reconf_streams) {
+     num_reconf_streams = reconf_streams->size();
+  }
+  UcastAudioStreams *audio_strms = strm_mgr_->GetAudioStreams();
+  AscsClient *ascs_client = strm_mgr_->GetAscsClient();
+
+  switch (event) {
+    case BAP_DISCONNECT_REQ_EVT: {
+      tracker_.HandleDisconnect(p_data, StreamTracker::kStateReconfiguring);
+    } break;
+    case PACS_DISCOVERY_RES_EVT: {
+      PacsDiscovery pacs_discovery_ =  *((PacsDiscovery *) p_data);
+      GattState ascs_state = strm_mgr_->GetAscsState();
+      uint8_t qos_reconfigs = 0;
+
+      bool process_pacs_results = false;
+
+      // check if this tracker already passed the pacs discovery stage
+      for (auto it = reconf_streams->begin();
+                        it != reconf_streams->end(); it++) {
+        StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+        if (context->connection_state == IntConnectState::PACS_DISCOVERING) {
+          context->connection_state = IntConnectState::ASCS_DISCOVERED;
+          process_pacs_results = true;
+        }
+      }
+
+      if(!process_pacs_results) break;
+
+      // check the status
+      if(pacs_discovery_.status) {
+        // send the BAP callback as connected as discovery failed
+        // during reconfiguring
+        tracker_.TransitionTo(StreamTracker::kStateConnected);
+        return false;
+      }
+
+      tracker_.UpdatePacsDiscovery(pacs_discovery_);
+
+      // check if supported audio contexts has required contexts
+      for (auto it = reconf_streams->begin();
+                           it != reconf_streams->end();) {
+        bool context_supported = false;
+        StreamType stream = it->stream_type;
+        if(stream.direction == ASE_DIRECTION_SINK) {
+          if(stream.audio_context & pacs_discovery_.supported_contexts) {
+            context_supported = true;
+          }
+        } else if(stream.direction == ASE_DIRECTION_SRC) {
+          if((static_cast<uint64_t>(stream.audio_context) << 16) &
+               pacs_discovery_.supported_contexts) {
+            context_supported = true;
+          }
+        }
+        if(context_supported) {
+          it++;
+        } else {
+          it = reconf_streams->erase(it);
+          // TODO to update the disconnected callback
+        }
+      }
+
+      if(reconf_streams->empty()) {
+        LOG(ERROR) << __func__  << " No Matching Sup Contexts found";
+        LOG(ERROR) << __func__  << " Moving back to Connected state";
+        tracker_.TransitionTo(StreamTracker::kStateConnected);
+        break;
+      }
+
+      // check physical allocation for all reconfig requests
+      uint8_t num_phy_attached = 0;
+      uint8_t num_same_config_applied = 0;
+      // if not present send the BAP callback as disconnected
+      // compare the codec configs from upper layer to remote dev
+      // sink or src PACS records/capabilities.
+      for (auto it = reconf_streams->begin();
+                           it != reconf_streams->end(); it++) {
+        uint8_t index = tracker_.ChooseBestCodec(it->stream_type,
+                                 &it->codec_qos_config_pair,
+                                 &pacs_discovery_);
+        if(index != 0XFF) {
+          CodecQosConfig entry = it->codec_qos_config_pair.at(index);
+          StreamContext *context = contexts->FindOrAddByType(
+                                             it->stream_type);
+          if(context->attached_state == StreamAttachedState::PHYSICAL) {
+            num_phy_attached++;
+            // check if same config is already applied
+            if(IsCodecConfigEqual(&context->codec_config,&entry.codec_config)) {
+              num_same_config_applied++;
+            }
+          }
+        } else {
+          LOG(ERROR) << __func__  << " Matching Codec not found";
+        }
+      }
+
+      if(reconf_streams->size() == num_phy_attached &&
+         num_phy_attached == num_same_config_applied) {
+        // update the state to connected
+        LOG(INFO) << __func__  << " Making state to Connected as Nothing to do";
+        TransitionTo(StreamTracker::kStateConnected);
+        break;
+      }
+
+      if(ascs_state != GattState::CONNECTED) {
+        break;
+      }
+
+      for (auto it = reconf_streams->begin();
+                           it != reconf_streams->end(); it++) {
+        uint8_t index = tracker_.ChooseBestCodec(it->stream_type,
+                                 &it->codec_qos_config_pair,
+                                 &pacs_discovery_);
+        if(index != 0XFF) {
+          CodecQosConfig entry = it->codec_qos_config_pair.at(index);
+          StreamContext *context = contexts->FindOrAddByType(
+                                             it->stream_type);
+          CodecConfig codec_config = entry.codec_config;
+          QosConfig qos_config = entry.qos_config;
+
+          if(context->attached_state == StreamAttachedState::VIRTUAL) {
+            std::vector<StreamContext *> phy_attached_contexts;
+            for (auto id = context->stream_ids.begin();
+                      id != context->stream_ids.end(); id++) {
+              std::vector<StreamContext *> phy_attached_contexts;
+              phy_attached_contexts = contexts->FindByAseAttachedState(
+                                    id->ase_id, StreamAttachedState::PHYSICAL);
+              for (auto context_id = phy_attached_contexts.begin();
+                        context_id != phy_attached_contexts.end();
+                        context_id++) {
+                LOG(INFO) << __func__ << ":Attached state made virtual";
+                (*context_id)->attached_state = StreamAttachedState::VIRTUAL;
+              }
+            }
+            LOG(INFO) << __func__ << ":Attached state made virtual to phy";
+            context->attached_state = StreamAttachedState::VIR_TO_PHY;
+            context->codec_config = codec_config;
+            context->req_qos_config = qos_config;
+          } else if (context->attached_state == StreamAttachedState::PHYSICAL) {
+            LOG(INFO) << __func__ << ":Attached state is physical";
+            // check if same config is already applied
+            if(IsCodecConfigEqual(&context->codec_config,&entry.codec_config)) {
+              if(it->reconf_type == StreamReconfigType::CODEC_CONFIG) {
+                it->reconf_type = StreamReconfigType::QOS_CONFIG;
+              }
+            } else {
+              context->codec_config = codec_config;
+            }
+            context->req_qos_config = qos_config;
+          }
+
+          uint8_t ascs_config_index = 0;
+          for (auto id = context->stream_ids.begin();
+                    id != context->stream_ids.end(); id++) {
+            int_strm_trackers_.FindOrAddBytrackerType(it->stream_type,
+                        id->ase_id,
+                        qos_config.ascs_configs[ascs_config_index].cig_id,
+                        qos_config.ascs_configs[ascs_config_index].cis_id,
+                        codec_config,
+                        qos_config);
+            UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+            if (!stream) {
+              LOG(ERROR) << __func__  << "stream is null";
+              continue;
+            }
+            stream->cig_id = id->cig_id =
+                            qos_config.ascs_configs[ascs_config_index].cig_id;
+            stream->cis_id = id->cis_id =
+                            qos_config.ascs_configs[ascs_config_index].cis_id;
+            stream->cig_state = CigState::INVALID;
+            stream->cis_state = CisState::INVALID;
+            stream->codec_config = codec_config;
+            stream->req_qos_config = qos_config;
+            stream->qos_config = qos_config;
+            stream->audio_context = it->stream_type.audio_context;
+            ascs_config_index++;
+          }
+        } else {
+          LOG(ERROR) << __func__  << " Matching Codec not found";
+        }
+      }
+      for (auto it = reconf_streams->begin();
+                it != reconf_streams->end(); it++) {
+        if (it->reconf_type == StreamReconfigType::QOS_CONFIG) {
+          qos_reconfigs++;
+        }
+      }
+
+      if(qos_reconfigs == num_reconf_streams) {
+        // now create the group
+        std::vector<IntStrmTracker *> *all_trackers =
+                            int_strm_trackers_.GetTrackerList();
+        // check for all streams together so that final group params
+        // will be decided.
+        for (auto i = all_trackers->begin(); i != all_trackers->end();i++) {
+          UcastAudioStream *stream = audio_strms->FindByAseId((*i)->ase_id);
+          if (!stream) {
+            LOG(ERROR) << __func__  << "stream is null";
+            continue;
+          }
+          tracker_.ChooseBestQos(&stream->req_qos_config,
+                                 &stream->pref_qos_params,
+                                 &stream->qos_config,
+                                 StreamTracker::kStateReconfiguring,
+                                 stream->direction);
+        }
+        tracker_.CheckAndSendQosConfig(&int_strm_trackers_);
+      } else {
+        // now send the ASCS codec config
+        std::vector<AseCodecConfigOp> ase_ops;
+        for (auto it = reconf_streams->begin();
+                             it != reconf_streams->end(); it++) {
+          if(it->reconf_type == StreamReconfigType::CODEC_CONFIG) {
+            StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+            for (auto id = context->stream_ids.begin();
+                      id != context->stream_ids.end(); id++) {
+              UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+              if (stream) {
+                tracker_.PrepareCodecConfigPayload(&ase_ops, stream);
+              }
+            }
+          }
+        }
+
+        if(!ase_ops.empty()) {
+          LOG(WARNING) << __func__  << ": Going For ASCS CodecConfig op";
+          ascs_client->CodecConfig(ASCS_CLIENT_ID,
+                       strm_mgr_->GetAddress(), ase_ops);
+        }
+
+        // update the states to connecting or other internal states
+        for (auto it = reconf_streams->begin();
+                             it != reconf_streams->end(); it++) {
+          if(it->reconf_type == StreamReconfigType::CODEC_CONFIG) {
+            StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+            for (auto id = context->stream_ids.begin();
+                      id != context->stream_ids.end(); id++) {
+              UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+              if (stream) {
+                stream->ase_pending_cmd = AscsPendingCmd::CODEC_CONFIG_ISSUED;
+                stream->overall_state = StreamTracker::kStateReconfiguring;
+              }
+            }
+          } else {
+            StreamContext *context = contexts->FindOrAddByType(it->stream_type);
+            for (auto id = context->stream_ids.begin();
+                      id != context->stream_ids.end(); id++) {
+              UcastAudioStream *stream = audio_strms->FindByAseId(id->ase_id);
+              if (stream) {
+                stream->overall_state = StreamTracker::kStateReconfiguring;
+              }
+            }
+          }
+        }
+      }
+    } break;
+
+    case ASCS_ASE_STATE_EVT: {
+      tracker_.HandleAseStateEvent(p_data, StreamControlType::Reconfig,
+                                   &int_strm_trackers_);
+    } break;
+
+    case ASCS_ASE_OP_FAILED_EVT: {
+      tracker_.HandleAseOpFailedEvent(p_data);
+    } break;
+
+    case PACS_CONNECTION_STATE_EVT: {
+      tracker_.HandlePacsConnectionEvent(p_data);
+    } break;
+
+    case ASCS_CONNECTION_STATE_EVT: {
+      tracker_.HandleAscsConnectionEvent(p_data);
+    } break;
+
+    case BAP_TIME_OUT_EVT: {
+      tracker_.OnTimeout(p_data);
+    } break;
+
+    default:
+      LOG(WARNING) << __func__ << ": Un-handled event: "
+                               << tracker_.GetEventName(event);
+      break;
+  }
+  return true;
+}
+
+}  // namespace ucast
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/cc/bta_cc_main.cc b/le_audio/system/bt/bta/cc/bta_cc_main.cc
new file mode 100644
index 0000000..eedebcc
--- /dev/null
+++ b/le_audio/system/bt/bta/cc/bta_cc_main.cc
@@ -0,0 +1,2334 @@
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+/*
+ * Copyright (C) 2003-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
+ */
+/******************************************************************************
+ *
+ *  This is the main implementation file for the BTA  LE audio call Gateway.
+ *
+ ******************************************************************************/
+
+#include "bta_api.h"
+#include "btif_util.h"
+#include "bt_target.h"
+#include "bta_cc_api.h"
+#include "gatts_ops_queue.h"
+#include "btm_int.h"
+#include "device/include/controller.h"
+
+#include "osi/include/properties.h"
+#include "osi/include/alarm.h"
+#include "osi/include/allocator.h"
+#include "osi/include/osi.h"
+#include "bta_sys.h"
+
+#include <vector>
+#include <iostream>
+#include <string.h>
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/location.h>
+#include <hardware/bluetooth.h>
+
+#include <base/strings/string_number_conversions.h>
+
+#include <vector>
+#include <string.h>
+#include <algorithm>
+#include <map>
+
+#define MAX_URI_SIZE 50
+#define STANDARD_BEARER_UCI "un000"
+#define MS_IN_SEC 1000
+#define CCS_DEFAULT_INDEX_VAL 0
+#define DEFAULT_INDICIES_COUNT 1
+
+using bluetooth::Uuid;
+using bluetooth::bap::GattsOpsQueue;
+class CallControllerImpl;
+static CallControllerImpl *cc_instance;
+static bool gIsTerminatedInitiatedFromClient = false;
+static int gTerminateIntiatedIndex = 0;
+static bool gIsActiveCC = false;
+
+//GTBS UUID (4B: TBS, 4C: GTBS)
+Uuid CALL_CONTROL_SERVER_UUID = Uuid::FromString("0000184C-0000-1000-8000-00805F9B34FB");
+
+Uuid GTBS_CALL_BEARER_NAME_UUID = Uuid::FromString("00002bb3-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_BEARER_UCI = Uuid::FromString("00002bb4-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_BEARER_TECHNOLOGY = Uuid::FromString("00002bb5-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_BEARER_URI_SCHEMES = Uuid::FromString("00002bb6-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_SIGNAL_STRENGTH = Uuid::FromString("00002bb7-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_SIGNAL_STRENGTH_REPORTINTERVAL = Uuid::FromString("00002bb8-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_BEARER_LIST_CURRENT_CALLS = Uuid::FromString("00002bb9-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_CONTENT_CONTROLID = Uuid::FromString("00002bba-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_CALL_STATUS_FLAGS = Uuid::FromString("00002bbb-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_INCOMINGCALL_TARGET_URI = Uuid::FromString("00002bbc-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_CALL_STATE_UUID = Uuid::FromString("00002bbd-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_CALL_CONTROL_POINT_OPS = Uuid::FromString("00002bbe-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_CALL_CONTROL_POINT_OPTIONAL_OPS = Uuid::FromString("00002bbf-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_CALL_TERMINATION_REASON = Uuid::FromString("00002bc0-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_INCOMING_CALL = Uuid::FromString("00002bc1-0000-1000-8000-00805F9B34FB");
+Uuid GTBS_CALL_FRIENDLY_NAME = Uuid::FromString("00002bc2-0000-1000-8000-00805F9B34FB");
+
+
+Uuid GTBS_DESCRIPTOR_UUID = Uuid::FromString("00002902-0000-1000-8000-00805f9b34fb");
+
+//global varibale
+CcsControlServiceInfo_t ccsControlServiceInfo;
+tCCS_CALL_STATE CallStateInfo;
+std::map<uint8_t, tCCS_CALL_STATE> CallStatelist;
+std::map<uint8_t, tCCS_BEARER_LIST_CURRENT_CALLS> BlccInfolist;
+tCCS_CALL_CONTROL_POINT CallControllerOps;
+tCCS_CALL_CONTROL_RESPONSE CallControllerResp;
+tCCS_BEARER_LIST_CURRENT_CALLS BlccInfo;
+tCCS_BEARER_PROVIDER_INFO  BearerProviderInfo;
+tCCS_CONTENT_CONTROL_ID CcidInfo;
+tCCS_STATUS_FLAGS StatusFlags;
+tCCS_INCOMING_CALL IncomingCallInfo;
+tCCS_INCOMING_CALL_URI IncomingCallTargetUri;
+tCCS_TERM_REASON TerminationReason;
+tCCS_FRIENDLY_NAME FriendlyName;
+tCCS_SUPP_OPTIONAL_OPCODES SupportedOptionalOpcodes;
+
+void BTCcCback(tBTA_GATTS_EVT event, tBTA_GATTS* param);
+void ReverseByteOrder(unsigned char s[], int length);
+
+typedef base::Callback<void(uint8_t status, int server_if,
+                             std::vector<btgatt_db_element_t> service)>
+                            OnGtbsServiceAdded;
+
+static void OnGtbsServiceAddedCb(uint8_t status, int serverIf,
+                              std::vector<btgatt_db_element_t> service);
+
+const char* bta_cc_event_str(uint32_t event) {
+  switch (event) {
+        CASE_RETURN_STR(CCS_NONE_EVENT)
+      CASE_RETURN_STR(CCS_INIT_EVENT)
+      CASE_RETURN_STR(CCS_CLEANUP_EVENT)
+      CASE_RETURN_STR(CCS_CALL_STATE_UPDATE)
+      CASE_RETURN_STR(CCS_BEARER_NAME_UPDATE)
+      CASE_RETURN_STR(CCS_BEARER_UCI_UPDATE)
+      CASE_RETURN_STR(CCS_BEARER_URI_SCHEMES_SUPPORTED)
+      CASE_RETURN_STR(CCS_UPDATE)
+      CASE_RETURN_STR(CCS_OPT_OPCODES)
+      CASE_RETURN_STR(CCS_BEARER_CURRENT_CALL_LIST_UPDATE)
+      CASE_RETURN_STR(CCS_BEARER_SIGNAL_STRENGTH_UPDATE)
+      CASE_RETURN_STR(CCS_SIGNAL_STRENGTH_REPORT_INTERVAL)
+      CASE_RETURN_STR(CCS_STATUS_FLAGS_UPDATE)
+      CASE_RETURN_STR(CCS_INCOMING_CALL_UPDATE)
+      CASE_RETURN_STR(CCS_INCOMING_TARGET_URI_UPDATE)
+      CASE_RETURN_STR(CCS_TERMINATION_REASON_UPDATE)
+      CASE_RETURN_STR(CCS_BEARER_TECHNOLOGY_UPDATE)
+      CASE_RETURN_STR(CCS_CCID_UPDATE)
+      CASE_RETURN_STR(CCS_ACTIVE_DEVICE_UPDATE)
+      CASE_RETURN_STR(CCS_CALL_CONTROL_RESPONSE)
+      CASE_RETURN_STR(CCS_NOTIFY_ALL)
+      CASE_RETURN_STR(CCS_WRITE_RSP)
+      CASE_RETURN_STR(CCS_READ_RSP)
+      CASE_RETURN_STR(CCS_DESCRIPTOR_WRITE_RSP)
+      CASE_RETURN_STR(CCS_DESCRIPTOR_READ_RSP)
+      CASE_RETURN_STR(CCS_CONNECTION)
+      CASE_RETURN_STR(CCS_DISCONNECTION)
+      CASE_RETURN_STR(CCS_CONNECTION_UPDATE)
+      CASE_RETURN_STR(CCS_CONGESTION_UPDATE)
+      CASE_RETURN_STR(CCS_PHY_UPDATE)
+      CASE_RETURN_STR(CCS_MTU_UPDATE)
+      CASE_RETURN_STR(CCS_SET_ACTIVE_DEVICE)
+      CASE_RETURN_STR(CCS_CONNECTION_CLOSE_EVENT)
+      CASE_RETURN_STR(CCS_BOND_STATE_CHANGE_EVENT)
+    default:
+      return (char*)"Unknown bta cc event";
+  }
+}
+
+
+class CallControllerDevices {
+ private:
+   CallActiveDevice activeDevice;
+   //int max_connection;
+ public:
+  bool Add(CallControllerDeviceList device) {
+    if (devices.size() == MAX_CCS_CONNECTION) {
+      return false;
+    }
+    if (FindByAddress(device.peer_bda) != nullptr) return false;
+
+    device.DeviceStateHandlerPointer[CCS_DISCONNECTED] = DeviceStateDisconnectedHandler;
+    device.DeviceStateHandlerPointer[CCS_CONNECTED] = DeviceStateConnectionHandler;
+    device.signal_strength_report_interval = 0;
+    std::string alarmName = device.peer_bda.ToString();
+    alarmName.append("-CC_SSReportingTimer");
+
+    device.signal_strength_reporting_timer = alarm_new_periodic(alarmName.c_str());
+    devices.push_back(device);
+    return true;
+  }
+
+  void Remove(RawAddress& address) {
+    for (auto it = devices.begin(); it != devices.end();) {
+      if (it->peer_bda != address) {
+        ++it;
+        continue;
+      }
+      if (it == devices.end()) {
+          LOG(ERROR) << __func__ <<"no matching device";
+        return;
+      }
+      //Cancel SSReporting timer
+      if (it->signal_strength_report_interval != 0 &&
+          it->signal_strength_reporting_timer != nullptr) {
+          alarm_cancel(it->signal_strength_reporting_timer);
+        alarm_free(it->signal_strength_reporting_timer);
+      }
+
+      it = devices.erase(it);
+
+      return;
+    }
+  }
+
+  void RemoveDevices() {
+    for (auto it = devices.begin(); it != devices.end();) {
+       it = devices.erase(it);
+    }
+    return;
+  }
+
+  CallControllerDeviceList* FindByAddress(const RawAddress& address) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&address](const CallControllerDeviceList& device) {
+                               return device.peer_bda == address;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  CallControllerDeviceList* FindByConnId(uint16_t conn_id) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&conn_id](const CallControllerDeviceList& device) {
+                               return device.conn_id == conn_id;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  size_t size() { return (devices.size()); }
+
+  std::vector<CallControllerDeviceList> GetRemoteDevices() {
+    return devices;
+  }
+  std::vector<CallControllerDeviceList> FindNotifyDevices(uint16_t handle) {
+    std::vector<CallControllerDeviceList> notify_devices;
+    for (size_t it = 0; it != devices.size(); it++){
+      if(ccsControlServiceInfo.bearer_provider_name_handle == handle &&
+        devices[it].bearer_provider_name_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.bearer_technology_handle == handle &&
+        devices[it].bearer_technology_changed_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.bearer_signal_strength_handle == handle &&
+        devices[it].bearer_signal_strength_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.bearer_list_currentcalls_handle == handle &&
+        devices[it].bearer_current_calls_list_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.call_status_flags_handle == handle &&
+        devices[it].status_flags_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.incoming_call_target_beareruri_handle == handle &&
+        devices[it].incoming_call_target_URI_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.call_state_handle == handle &&
+        devices[it].call_state_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.call_control_point_handle == handle &&
+        devices[it].call_control_point_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.call_termination_reason_handle == handle &&
+        devices[it].call_termination_reason_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.incoming_call_handle == handle &&
+        devices[it].incoming_call_state_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(ccsControlServiceInfo.call_friendly_name_handle == handle &&
+        devices[it].call_friendly_name_notify) {
+        notify_devices.push_back(devices[it]);
+      }
+    }
+    return notify_devices;
+  }
+  void AddSetActiveDevice(tCCS_SET_ACTIVE_DEVICE *device) {
+    if (device->set_id == activeDevice.set_id) {
+      activeDevice.address.push_back(device->address);
+    } else {
+      activeDevice.address.clear();
+      activeDevice.set_id = device->set_id;
+      activeDevice.address.push_back(device->address);
+    }
+  }
+
+  bool FindActiveDevice(CallControllerDeviceList *remoteDevice) {
+    bool flag = false;
+    for (auto& it : activeDevice.address) {
+      if(remoteDevice->peer_bda == it) {
+        flag = true;
+        break;
+      }
+    }
+    return flag;
+  }
+  static void SSReportingTimerExpired(void *data) {
+      LOG(INFO) << __func__ ;
+    CallControllerDeviceList* dev = (CallControllerDeviceList*)data;
+    if (dev == nullptr) {
+        LOG(ERROR) << __func__ << "no valid dev handle";
+        return;
+    }
+
+    //send Notification for Signal Strength
+    tcc_resp_t *rsp = new tcc_resp_t();
+    if (rsp == NULL) {
+       LOG(ERROR) << __func__ << "Allocation error!";
+       return;
+    }
+    std::vector<uint8_t> _data;
+    _data.clear();
+    rsp->remoteDevice = dev;
+    rsp->event = CCS_NOTIFY_ALL;
+    rsp->handle = ccsControlServiceInfo.bearer_signal_strength_handle;
+    _data.push_back(BearerProviderInfo.signal);
+    rsp->oper.CallControllerOp.data = std::move(_data);
+    //force the Notification on timer expiry
+    rsp->force = true;
+
+    if (dev->bearer_signal_strength_notify) {
+       LOG(INFO) << "Caling device state handler for SSReporting";
+       dev->DeviceStateHandlerPointer[dev->state](CCS_NOTIFY_ALL, rsp);
+    } else {
+       LOG(INFO) << "SSReporting Interval set without Desc write for SSR";
+    }
+  }
+
+  static void SetupSSReportingInterval (CallControllerDeviceList* dev) {
+    LOG(INFO) << __func__ ;
+    if (alarm_is_scheduled(dev->signal_strength_reporting_timer)) {
+       alarm_cancel(dev->signal_strength_reporting_timer);
+    }
+    if (dev->signal_strength_report_interval != 0) {
+       alarm_set(dev->signal_strength_reporting_timer,
+                       (period_ms_t)dev->signal_strength_report_interval*MS_IN_SEC,
+                       SSReportingTimerExpired,
+                       (void*)dev);
+    }
+  }
+
+  bool UpdateSSReportingInterval(const RawAddress& address, uint8_t ssrInterval) {
+      bool ret = false;
+      CallControllerDeviceList *dev = FindByAddress(address);
+
+      if (dev != nullptr) {
+         dev->signal_strength_report_interval = ssrInterval;
+         SetupSSReportingInterval(dev);
+         ret = true;
+      }
+      return ret;
+  }
+
+  std::vector<CallControllerDeviceList> devices;
+};
+
+
+class CallControllerImpl : public CallController {
+  bluetooth::call_control::CallControllerCallbacks* callbacks;
+  Uuid app_uuid;
+  int max_clients;
+  bool inband_ring_support;
+
+  public:
+     CallControllerDevices remoteDevices;
+     virtual ~CallControllerImpl() = default;
+
+
+  CallControllerImpl(bluetooth::call_control::CallControllerCallbacks* callback,
+              Uuid uuid, int max_ccs_clients, bool inband_ringing_enabled)
+        :callbacks(callback),
+     app_uuid(uuid),
+     max_clients(max_ccs_clients),
+     inband_ring_support(inband_ringing_enabled)  {
+    // HandleCcsEvent(CCS_INIT_EVENT, &app_uuid);
+    LOG(INFO) << "max_clients " << max_clients;
+     if (inband_ring_support) {
+       StatusFlags.supported_flags =  (StatusFlags.supported_flags & 0x0000) | INBAND_RINGTONE_FEATURE_BIT;
+     } else {
+       StatusFlags.supported_flags = 0x0000;
+     }
+     LOG(INFO) << "CallControllerImpl gatts app register";
+     BTA_GATTS_AppRegister(app_uuid, BTCcCback, true);
+
+  }
+
+ void Disconnect(const RawAddress& bd_addr) {
+    LOG(INFO) << __func__;
+    tCCS_CONNECTION_CLOSE ConnectClosingOp;
+    ConnectClosingOp.addr = bd_addr;
+    HandleCcsEvent(CCS_CONNECTION_CLOSE_EVENT, &ConnectClosingOp);
+ }
+
+ void SetActiveDevice(const RawAddress& address, int setId) {
+    LOG(INFO) << __func__ ;
+    tCCS_SET_ACTIVE_DEVICE SetActiveDeviceOp;
+    SetActiveDeviceOp.set_id = setId;
+    SetActiveDeviceOp.address = address;
+    HandleCcsEvent(CCS_ACTIVE_DEVICE_UPDATE, &SetActiveDeviceOp);
+  }
+
+  void CallState(int len, std::vector<uint8_t> call_state_list) {
+    tCCS_CALL_STATE CallStateOp;
+    for (int k=0; k<len; k++) {
+        LOG(INFO) << __func__ << " " << k <<" : index: " << (unsigned)call_state_list[3*k + 0]
+                          << " state: " << (unsigned) call_state_list[3*k + 1]
+                          << " flags: " << (unsigned) call_state_list[3*k + 2];
+        CallStateOp.index = call_state_list[3*k + 0];
+        CallStateOp.state = call_state_list[3*k + 1];
+        CallStateOp.flags = call_state_list[3*k + 2];
+
+        BlccInfo.call_flags = CallStateOp.flags;
+        BlccInfo.call_index = CallStateOp.index;
+        BlccInfo.call_state = CallStateOp.state;
+
+        if (CallStateOp.state == CCS_STATE_INCOMING) {
+            int len = strlen((char *)IncomingCallInfo.incoming_uri);
+            memcpy(BlccInfo.call_uri, IncomingCallInfo.incoming_uri, len);
+            BlccInfo.list_length = 3 + len;
+        } else {
+            BlccInfo.list_length = 3;
+        }
+
+        if (CallStateOp.state == CCS_STATE_DISCONNECTED) {
+            //clear off the Incoming call related things as well
+            if (IncomingCallInfo.index == CallStateOp.index) {
+                IncomingCallInfo.index = 0;
+                memset(IncomingCallInfo.incoming_uri, 0, MAX_URI_LENGTH);
+            }
+            if (IncomingCallTargetUri.index == CallStateOp.index) {
+                IncomingCallTargetUri.index = 0;
+                memset(IncomingCallTargetUri.incoming_target_uri, 0, MAX_URI_LENGTH);
+            }
+
+            TerminationReason.index = CallStateOp.index;
+            if (gIsTerminatedInitiatedFromClient &&
+                 CallStateOp.index == gIsTerminatedInitiatedFromClient) {
+                TerminationReason.reason = CC_TERM_END_FROM_CLIENT;
+            } else {
+                TerminationReason.reason = CC_TERM_END_FROM_SERVER;
+            }
+            gIsTerminatedInitiatedFromClient = false;
+            gTerminateIntiatedIndex = 0;
+            HandleCcsEvent(CCS_INCOMING_CALL_UPDATE, &IncomingCallInfo);
+            HandleCcsEvent(CCS_INCOMING_TARGET_URI_UPDATE, &IncomingCallTargetUri);
+            HandleCcsEvent(CCS_TERMINATION_REASON_UPDATE, &TerminationReason);
+
+            //erase the disconnected Indicies
+            BlccInfolist.erase(CallStateOp.index);
+            CallStatelist.erase(CallStateOp.index);
+        } else {
+            //keep appending the data
+            std::map<uint8_t, tCCS_BEARER_LIST_CURRENT_CALLS>::iterator i = BlccInfolist.find(BlccInfo.call_index);
+            if (i != BlccInfolist.end()) {
+                LOG(INFO) << __func__ << " update existing Blcc";
+                i->second = BlccInfo;
+            } else {
+                BlccInfolist.insert({BlccInfo.call_index, BlccInfo});
+            }
+            std::map<uint8_t, tCCS_CALL_STATE>::iterator j = CallStatelist.find(CallStateOp.index);
+            if (j != CallStatelist.end()) {
+                j->second = CallStateOp;
+            } else {
+                CallStatelist.insert({CallStateOp.index, CallStateOp});
+            }
+            //clear the term reason
+            if (CallStateOp.index == TerminationReason.index) {
+                TerminationReason.index = 0;
+                TerminationReason.reason = CC_TERM_INVALID_ORIG_URI;
+            }
+        }
+    }
+    HandleCcsEvent(CCS_CALL_STATE_UPDATE, &CallStatelist);
+    HandleCcsEvent(CCS_BEARER_CURRENT_CALL_LIST_UPDATE, &BlccInfolist);
+ }
+
+ void BearerInfoName(uint8_t* name) {
+   LOG(INFO) << __func__ << name;
+   tCCS_BEARER_PROVIDER_INFO BearerProviderOp;
+   BearerProviderOp.length = strlen((char *)name);
+   memcpy(BearerProviderOp.name, name, BearerProviderOp.length+1);
+   HandleCcsEvent(CCS_BEARER_NAME_UPDATE, &BearerProviderOp);
+ }
+
+ void UpdateBearerTechnology(int tech_type) {
+   LOG(INFO) << __func__ << " tech type: " <<tech_type;
+   tCCS_BEARER_PROVIDER_INFO bearerProviderOp;
+   BearerProviderInfo.technology_type = tech_type;
+
+   HandleCcsEvent(CCS_BEARER_TECHNOLOGY_UPDATE, &bearerProviderOp);
+ }
+ void UpdateBearerSignalStrength(int signal) {
+   LOG(INFO) << __func__<< " signal: " << signal;
+   tCCS_BEARER_PROVIDER_INFO BearerProviderOp;
+   BearerProviderOp.signal = signal;
+   HandleCcsEvent(CCS_BEARER_SIGNAL_STRENGTH_UPDATE, &BearerProviderOp);
+ }
+
+ void UpdateStatusFlags(uint8_t status_flag) {
+   LOG(INFO) << __func__ << " status_flag: " <<status_flag;
+   tCCS_STATUS_FLAGS StatusFlagsOp;
+   StatusFlagsOp.supported_flags = status_flag;
+   HandleCcsEvent(CCS_STATUS_FLAGS_UPDATE, &StatusFlagsOp);
+ }
+
+ void CallControlOptionalOpSupported(int feature) {
+   LOG(INFO) << __func__ << " feature: " << feature;
+   tCCS_SUPP_OPTIONAL_OPCODES opSupportedFeatures;
+   opSupportedFeatures.supp_opcode = (uint16_t)0x00FF&feature;
+
+   HandleCcsEvent(CCS_OPT_OPCODES, &opSupportedFeatures);
+ }
+
+ void UpdateSupportedBearerList(uint8_t* list) {
+   LOG(INFO) << __func__ << " list: " <<list;
+   tCCS_BEARER_PROVIDER_INFO BearerProviderOp;
+   BearerProviderOp.bearer_list_len = strlen((char *)list);
+   LOG(INFO) << __func__ << " list len:" << BearerProviderOp.bearer_list_len;
+   memcpy(BearerProviderOp.bearer_schemes_list, list, BearerProviderOp.bearer_list_len+1);
+   HandleCcsEvent(CCS_BEARER_URI_SCHEMES_SUPPORTED, &BearerProviderOp);
+ }
+
+ void UpdateIncomingCall(int index, uint8_t* Uri) {
+    LOG(INFO) << __func__ << " Uri: " << Uri;
+    std::map<uint8_t, tCCS_BEARER_LIST_CURRENT_CALLS>::iterator it;
+    tCCS_INCOMING_CALL IncomingCall;
+    int len = strlen((char *)Uri);
+    memcpy(IncomingCall.incoming_uri, Uri, len+1);
+    IncomingCall.index = index;
+    for (it = BlccInfolist.begin(); it != BlccInfolist.end(); it++) {
+        tCCS_BEARER_LIST_CURRENT_CALLS blccObj = it->second;
+       if (blccObj.call_index == index) {
+          memcpy(blccObj.call_uri, Uri, strlen((char *)Uri)+1);
+          break;
+       }
+    }
+    HandleCcsEvent(CCS_INCOMING_CALL_UPDATE, &IncomingCall);
+    //update Target URI as well with same Info
+    UpdateIncomingCallTargetUri(index, Uri);
+ }
+
+ void UpdateIncomingCallTargetUri(int index, uint8_t* target_uri) {
+    LOG(INFO) << __func__ << " target_uri: " << target_uri;
+    tCCS_INCOMING_CALL_URI IncomingcallUri;
+    int len = strlen((char *)target_uri);
+    memcpy(IncomingcallUri.incoming_target_uri, target_uri, len+1);
+    IncomingcallUri.index = index;
+    HandleCcsEvent(CCS_INCOMING_TARGET_URI_UPDATE, &IncomingcallUri);
+ }
+
+ void ContentControlId(uint32_t ccid) {
+    LOG(INFO) << __func__;
+    tCCS_CONTENT_CONTROL_ID ContentControlIdOp;
+    ContentControlIdOp.ccid = ccid;
+    HandleCcsEvent(CCS_CCID_UPDATE, &ContentControlIdOp);
+ }
+
+ int GetDialingCallIndex() {
+    int retIndex = 0;
+    std::map<uint8_t, tCCS_CALL_STATE>::iterator it;
+     for (it = CallStatelist.begin(); it != CallStatelist.end(); it++) {
+        tCCS_CALL_STATE obj = it->second;
+        if (obj.state == CCS_STATE_DIALING || obj.state == CCS_STATE_ALERTING) {
+           LOG(INFO) << __func__ << " call state match: " << obj.index;
+           retIndex = obj.index;
+           break;
+         }
+    }
+    return retIndex;
+ }
+ void CallControlResponse(uint8_t op, uint8_t index, uint32_t status,  const RawAddress& address) {
+   LOG(INFO) << __func__;
+   tCCS_CALL_CONTROL_RESPONSE CallControlResponse;
+   CallControllerResp.opcode = op;
+   CallControllerResp.response_status = status;
+   CallControllerResp.remote_address = address;
+   CallControllerResp.index = index;
+   if (status == CCS_STATUS_SUCCESS && op == CALL_ORIGINATE) {
+       //get proper call Index for call orignate status
+       CallControllerResp.index = GetDialingCallIndex();
+   }
+   HandleCcsEvent(CCS_CALL_CONTROL_RESPONSE, &CallControlResponse);
+ }
+
+ void CallControlInitializedCallback(uint8_t state) {
+    LOG(INFO) << __func__ << " state" << state;
+    callbacks->CallControlInitializedCallback(state);
+    if (state == 0) {
+        //Initialize local char values
+        memcpy(BearerProviderInfo.uci, STANDARD_BEARER_UCI, strlen(STANDARD_BEARER_UCI));
+    }
+ }
+
+ void ConnectionStateCallback(uint8_t state,  const RawAddress&  address) {
+    LOG(INFO) << __func__ << " state: " << state;
+    callbacks->ConnectionStateCallback(state, address);
+ }
+
+ void CallControlPointChange(uint8_t op, uint8_t* indices, int count, char* uri, const RawAddress&  address) {
+     LOG(INFO) << __func__ << " op: " <<op << " count :" << count;
+     std::vector<uint8_t> uri_data;
+     if (uri != NULL) {
+         LOG(INFO) << __func__ <<" uri=" << uri;
+         uri_data.insert(uri_data.begin(), uri, uri + strlen(uri));
+     }
+     std::vector<int32_t> call_indicies;
+     for (int i=0; i<count; i++) {
+         call_indicies.push_back(indices[i]);
+
+     }
+     callbacks->CallControlCallback(op, call_indicies, count, uri_data, address);
+ }
+};
+
+void CallController::CleanUp() {
+  HandleCcsEvent(CCS_CLEANUP_EVENT, NULL);
+  delete cc_instance;
+  cc_instance = nullptr;
+ }
+
+CallController* CallController::Get() {
+  CHECK(cc_instance);
+  return cc_instance;
+}
+
+void  CallController::Initialize(bluetooth::call_control::CallControllerCallbacks* callbacks,
+                        Uuid uuid, int max_ccs_clients, bool inband_ringing_enabled) {
+  if (cc_instance) {
+  LOG(ERROR) << "Already initialized!";
+  } else {
+     cc_instance = new CallControllerImpl(callbacks, uuid, max_ccs_clients, inband_ringing_enabled);
+  }
+  char activeCC[PROPERTY_VALUE_MAX] = "false";
+  if(osi_property_get("persist.vendor.service.bt.activeCC", activeCC, "false") &&
+        !strcmp(activeCC, "true")) {
+    gIsActiveCC = true;
+  }
+}
+
+bool CallController::IsCcServiceRunnig() { return cc_instance; }
+
+static std::vector<btgatt_db_element_t> CcAddService(int server_if) {
+
+  std::vector<btgatt_db_element_t> ccs_services;
+  ccs_services.clear();
+  //service
+  btgatt_db_element_t service = {};
+  service.uuid = CALL_CONTROL_SERVER_UUID;
+  service.type = BTGATT_DB_PRIMARY_SERVICE;
+  ccs_services.push_back(service);
+  ccsControlServiceInfo.ccs_service_uuid = service.uuid;
+
+  btgatt_db_element_t bearer_provider_name_char = {};
+  bearer_provider_name_char.uuid = GTBS_CALL_BEARER_NAME_UUID;
+  bearer_provider_name_char.type = BTGATT_DB_CHARACTERISTIC;
+  bearer_provider_name_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  bearer_provider_name_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(bearer_provider_name_char);
+  ccsControlServiceInfo.bearer_provider_name_uuid = bearer_provider_name_char.uuid;
+
+  btgatt_db_element_t bearer_provider_name_desc = {};
+  bearer_provider_name_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  bearer_provider_name_desc.type = BTGATT_DB_DESCRIPTOR;
+  bearer_provider_name_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(bearer_provider_name_desc);
+
+  btgatt_db_element_t bearer_technology_char = {};
+  bearer_technology_char.uuid = GTBS_BEARER_TECHNOLOGY;
+  bearer_technology_char.type = BTGATT_DB_CHARACTERISTIC;
+  bearer_technology_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  bearer_technology_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(bearer_technology_char);
+  ccsControlServiceInfo.bearer_technology_uuid = bearer_technology_char.uuid;
+
+  btgatt_db_element_t bearer_technology_desc = {};
+  bearer_technology_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  bearer_technology_desc.type = BTGATT_DB_DESCRIPTOR;
+  bearer_technology_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(bearer_technology_desc);
+
+  btgatt_db_element_t gtbs_cc_optional_opcode_char = {};
+  gtbs_cc_optional_opcode_char.uuid = GTBS_CALL_CONTROL_POINT_OPTIONAL_OPS;
+  gtbs_cc_optional_opcode_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_cc_optional_opcode_char.properties = GATT_CHAR_PROP_BIT_READ;
+  gtbs_cc_optional_opcode_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(gtbs_cc_optional_opcode_char);
+  ccsControlServiceInfo.call_control_point_opcode_supported_uuid = gtbs_cc_optional_opcode_char.uuid;
+
+  btgatt_db_element_t gtbs_call_state_char = {};
+  gtbs_call_state_char.uuid = GTBS_CALL_STATE_UUID;
+  gtbs_call_state_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_call_state_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  gtbs_call_state_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(gtbs_call_state_char);
+  ccsControlServiceInfo.call_state_uuid = gtbs_call_state_char.uuid;
+
+  btgatt_db_element_t gtbs_call_state_desc = {};
+  gtbs_call_state_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_call_state_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_call_state_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_call_state_desc);
+
+  btgatt_db_element_t gtbs_call_control_point_char = {};
+  gtbs_call_control_point_char.uuid = GTBS_CALL_CONTROL_POINT_OPS;
+  gtbs_call_control_point_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_call_control_point_char.properties = GATT_CHAR_PROP_BIT_WRITE|GATT_CHAR_PROP_BIT_NOTIFY|GATT_CHAR_PROP_BIT_WRITE_NR;
+  gtbs_call_control_point_char.permissions = GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_call_control_point_char);
+  ccsControlServiceInfo.call_control_point_uuid = gtbs_call_control_point_char.uuid;
+
+  btgatt_db_element_t gtbs_call_control_point_desc = {};
+  gtbs_call_control_point_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_call_control_point_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_call_control_point_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_call_control_point_desc);
+
+  btgatt_db_element_t gtbs_bearer_uci_char = {};
+  gtbs_bearer_uci_char.uuid = GTBS_BEARER_UCI;
+  gtbs_bearer_uci_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_bearer_uci_char.properties = GATT_CHAR_PROP_BIT_READ;
+  gtbs_bearer_uci_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(gtbs_bearer_uci_char);
+  ccsControlServiceInfo.bearer_uci_uuid= gtbs_bearer_uci_char.uuid;
+
+  btgatt_db_element_t gtbs_bearer_URI_schemes_char = {};
+  gtbs_bearer_URI_schemes_char.uuid = GTBS_BEARER_URI_SCHEMES;
+  gtbs_bearer_URI_schemes_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_bearer_URI_schemes_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  gtbs_bearer_URI_schemes_char.permissions = GATT_PERM_READ;
+
+  ccs_services.push_back(gtbs_bearer_URI_schemes_char);
+  ccsControlServiceInfo.bearer_uri_schemes_supported_uuid = gtbs_bearer_URI_schemes_char.uuid;
+
+  btgatt_db_element_t gtbs_bearer_URI_schemes_desc = {};
+  gtbs_bearer_URI_schemes_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_bearer_URI_schemes_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_bearer_URI_schemes_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_bearer_URI_schemes_desc);
+
+  btgatt_db_element_t gtbs_signal_strength_char = {};
+  gtbs_signal_strength_char.uuid = GTBS_SIGNAL_STRENGTH;
+  gtbs_signal_strength_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_signal_strength_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  gtbs_signal_strength_char.permissions = GATT_PERM_READ;
+
+  ccs_services.push_back(gtbs_signal_strength_char);
+  ccsControlServiceInfo.bearer_signal_strength_uuid = gtbs_signal_strength_char.uuid;
+
+  btgatt_db_element_t gtbs_signal_strength_desc = {};
+  gtbs_signal_strength_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_signal_strength_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_signal_strength_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_signal_strength_desc);
+
+ btgatt_db_element_t gtbs_signal_strength_report_interval_char = {};
+  gtbs_signal_strength_report_interval_char.uuid = GTBS_SIGNAL_STRENGTH_REPORTINTERVAL;
+  gtbs_signal_strength_report_interval_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_signal_strength_report_interval_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_WRITE|GATT_CHAR_PROP_BIT_WRITE_NR;
+  gtbs_signal_strength_report_interval_char.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_signal_strength_report_interval_char);
+  ccsControlServiceInfo.bearer_signal_strength_report_interval_uuid = gtbs_signal_strength_report_interval_char.uuid;
+
+  btgatt_db_element_t gtbs_list_current_calls_char = {};
+  gtbs_list_current_calls_char.uuid = GTBS_BEARER_LIST_CURRENT_CALLS;
+  gtbs_list_current_calls_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_list_current_calls_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  gtbs_list_current_calls_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(gtbs_list_current_calls_char);
+  ccsControlServiceInfo.bearer_list_currentcalls_uuid = gtbs_list_current_calls_char.uuid;
+
+  btgatt_db_element_t gtbs_list_current_calls_desc = {};
+  gtbs_list_current_calls_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_list_current_calls_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_list_current_calls_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_list_current_calls_desc);
+
+  btgatt_db_element_t gtbs_call_status_flags_char = {};
+  gtbs_call_status_flags_char.uuid = GTBS_CALL_STATUS_FLAGS;
+  gtbs_call_status_flags_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_call_status_flags_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  gtbs_call_status_flags_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(gtbs_call_status_flags_char);
+  ccsControlServiceInfo.call_status_flags_uuid = gtbs_call_status_flags_char.uuid;
+
+  btgatt_db_element_t gtbs_call_status_flags_desc = {};
+  gtbs_call_status_flags_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_call_status_flags_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_call_status_flags_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_call_status_flags_desc);
+
+  btgatt_db_element_t gtbs_incomingcall_target_bearer_URI_char = {};
+  gtbs_incomingcall_target_bearer_URI_char.uuid = GTBS_INCOMINGCALL_TARGET_URI;
+  gtbs_incomingcall_target_bearer_URI_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_incomingcall_target_bearer_URI_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  gtbs_incomingcall_target_bearer_URI_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(gtbs_incomingcall_target_bearer_URI_char);
+  ccsControlServiceInfo.incoming_call_target_beareruri_uuid = gtbs_incomingcall_target_bearer_URI_char.uuid;
+
+  btgatt_db_element_t gtbs_incomingcall_target_bearer_URI_desc = {};
+  gtbs_incomingcall_target_bearer_URI_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_incomingcall_target_bearer_URI_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_incomingcall_target_bearer_URI_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_incomingcall_target_bearer_URI_desc);
+
+  btgatt_db_element_t gtbs_incomingcall_char = {};
+  gtbs_incomingcall_char.uuid = GTBS_INCOMING_CALL;
+  gtbs_incomingcall_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_incomingcall_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  gtbs_incomingcall_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(gtbs_incomingcall_char);
+  ccsControlServiceInfo.incoming_call_uuid = gtbs_incomingcall_char.uuid;
+
+  btgatt_db_element_t gtbs_incomingcall_desc = {};
+  gtbs_incomingcall_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_incomingcall_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_incomingcall_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_incomingcall_desc);
+
+  btgatt_db_element_t gtbs_call_termination_reason_char = {};
+  gtbs_call_termination_reason_char.uuid = GTBS_CALL_TERMINATION_REASON;
+  gtbs_call_termination_reason_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_call_termination_reason_char.properties = GATT_CHAR_PROP_BIT_NOTIFY;
+  ccs_services.push_back(gtbs_call_termination_reason_char);
+  ccsControlServiceInfo.call_termination_reason_uuid = gtbs_call_termination_reason_char.uuid;
+
+  btgatt_db_element_t gtbs_call_termination_reason_desc = {};
+  gtbs_call_termination_reason_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_call_termination_reason_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_call_termination_reason_desc.permissions = GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_call_termination_reason_desc);
+
+  btgatt_db_element_t gtbs_call_friendly_name_char = {};
+  gtbs_call_friendly_name_char.uuid = GTBS_CALL_FRIENDLY_NAME;
+  gtbs_call_friendly_name_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_call_friendly_name_char.properties = GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY;
+  gtbs_call_friendly_name_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(gtbs_call_friendly_name_char);
+  ccsControlServiceInfo.call_friendly_name_uuid = gtbs_call_friendly_name_char.uuid;
+
+  btgatt_db_element_t gtbs_call_friendly_name_desc = {};
+  gtbs_call_friendly_name_desc.uuid = GTBS_DESCRIPTOR_UUID;
+  gtbs_call_friendly_name_desc.type = BTGATT_DB_DESCRIPTOR;
+  gtbs_call_friendly_name_desc.permissions =  GATT_PERM_READ|GATT_PERM_WRITE;
+  ccs_services.push_back(gtbs_call_friendly_name_desc);
+
+  btgatt_db_element_t gtbs_ccid_char = {};
+  gtbs_ccid_char.uuid = GTBS_CONTENT_CONTROLID;
+  gtbs_ccid_char.type = BTGATT_DB_CHARACTERISTIC;
+  gtbs_ccid_char.properties = GATT_CHAR_PROP_BIT_READ;
+  gtbs_ccid_char.permissions = GATT_PERM_READ;
+  ccs_services.push_back(gtbs_ccid_char);
+  ccsControlServiceInfo.gtbs_ccid_uuid = gtbs_ccid_char.uuid;
+
+  return ccs_services;
+}
+
+static void OnGtbsServiceAddedCb(uint8_t status, int serverIf,
+                                std::vector<btgatt_db_element_t> service) {
+
+  if (service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER) ||
+      service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
+    LOG(INFO) << "%s: Attempt to register restricted service"<< __func__;
+    return;
+  }
+
+   for(int i=0; i< (int)service.size(); i++) {
+
+    if (service[i].uuid == CALL_CONTROL_SERVER_UUID) {
+      LOG(INFO) << __func__ << " GTBS service added attr handle: " << service[i].attribute_handle;
+    } else if(service[i].uuid ==  GTBS_CALL_BEARER_NAME_UUID) {
+      ccsControlServiceInfo.bearer_provider_name_handle = service[i++].attribute_handle;
+      ccsControlServiceInfo.bearer_provider_name_desc  = service[i].attribute_handle;
+      LOG(INFO) << __func__ << " bearer_provider_name_attr: "
+                << ccsControlServiceInfo.bearer_provider_name_handle
+                << " bearer_provider_name_desc: "
+                << ccsControlServiceInfo.bearer_provider_name_desc;
+    } else if(service[i].uuid == GTBS_BEARER_TECHNOLOGY) {
+      ccsControlServiceInfo.bearer_technology_handle = service[i++].attribute_handle;
+      ccsControlServiceInfo.bearer_technology_desc = service[i].attribute_handle;
+      LOG(INFO) << __func__ << " bearer_technology_handle: "
+                << ccsControlServiceInfo.bearer_technology_handle
+                << " bearer_technology_desc: "
+                << ccsControlServiceInfo.bearer_technology_desc;
+    } else if(service[i].uuid == GTBS_CALL_CONTROL_POINT_OPTIONAL_OPS) {
+      ccsControlServiceInfo.call_control_point_opcode_supported_handle =
+          service[i].attribute_handle;
+      LOG(INFO) << __func__ << " call_control_point_opcode_supported_handle register: "
+                << ccsControlServiceInfo.call_control_point_opcode_supported_handle;
+    } else if (service[i].uuid == GTBS_CALL_STATE_UUID) {
+
+      ccsControlServiceInfo.call_state_handle = service[i++].attribute_handle;
+      ccsControlServiceInfo.call_state_desc = service[i].attribute_handle;
+      LOG(INFO) << __func__ << " call_state_handle: "
+                << ccsControlServiceInfo.call_state_handle
+                << " call_state_handle desc: "
+                << ccsControlServiceInfo.call_state_desc;
+    } else if(service[i].uuid == GTBS_CALL_CONTROL_POINT_OPS) {
+      ccsControlServiceInfo.call_control_point_handle = service[i++].attribute_handle;
+      ccsControlServiceInfo.call_control_point_desc =  service[i].attribute_handle;
+       LOG(INFO) << __func__ << " call_control_point_handle: "
+                 << ccsControlServiceInfo.call_control_point_handle
+                 << " call_control_point_desc: "
+                 << ccsControlServiceInfo.call_control_point_desc;
+    } else if(service[i].uuid == GTBS_BEARER_UCI)  {
+      ccsControlServiceInfo.bearer_uci_handle = service[i].attribute_handle;
+       LOG(INFO) << __func__ << " bearer_uci_handle: "
+                 << ccsControlServiceInfo.bearer_uci_handle;
+
+    } else if(service[i].uuid == GTBS_BEARER_URI_SCHEMES)  {
+      ccsControlServiceInfo.bearer_uri_schemes_supported_handle =
+           service[i++].attribute_handle;
+      ccsControlServiceInfo.bearer_uri_schemes_supported_desc =
+           service[i].attribute_handle;
+      LOG(INFO) << __func__ << " bearer_uri_schemes_supported_handle: "
+                << ccsControlServiceInfo.bearer_uri_schemes_supported_handle
+                << " bearer_uri_schemes_supported_desc: "
+                << ccsControlServiceInfo.bearer_uri_schemes_supported_desc;
+
+    } else if(service[i].uuid == GTBS_SIGNAL_STRENGTH)  {
+      ccsControlServiceInfo.bearer_signal_strength_handle =
+           service[i++].attribute_handle;
+      ccsControlServiceInfo.bearer_signal_strength_desc =
+           service[i].attribute_handle;
+      LOG(INFO) << __func__ << " bearer_signal_strength_handle: "
+                << ccsControlServiceInfo.bearer_signal_strength_handle
+                << " bearer_signal_strength_desc: "
+                << ccsControlServiceInfo.bearer_signal_strength_desc;
+
+    } else if(service[i].uuid == GTBS_SIGNAL_STRENGTH_REPORTINTERVAL)  {
+      ccsControlServiceInfo.bearer_signal_strength_report_interval_handle =
+              service[i].attribute_handle;
+
+      LOG(INFO) << __func__ << " bearer_signal_strength_report_interval_handle: "
+                << ccsControlServiceInfo.bearer_signal_strength_report_interval_handle;
+
+    } else if(service[i].uuid == GTBS_BEARER_LIST_CURRENT_CALLS)  {
+      ccsControlServiceInfo.bearer_list_currentcalls_handle =
+            service[i++].attribute_handle;
+      ccsControlServiceInfo.bearer_list_currentcalls_desc =
+            service[i].attribute_handle;
+      LOG(INFO) << __func__ << " bearer_list_currentcalls_handle: "
+                << ccsControlServiceInfo.bearer_list_currentcalls_handle
+                << " bearer_list_currentcalls_desc: "
+                << ccsControlServiceInfo.bearer_list_currentcalls_desc;
+
+    } else if(service[i].uuid == GTBS_CALL_STATUS_FLAGS)  {
+      ccsControlServiceInfo.call_status_flags_handle = service[i++].attribute_handle;
+      ccsControlServiceInfo.call_status_flags_desc = service[i].attribute_handle;
+      LOG(INFO) << __func__ << " call_status_flags_handle: "
+                << ccsControlServiceInfo.call_status_flags_handle
+                << " call_status_flags_desc: "
+                << ccsControlServiceInfo.call_status_flags_desc;
+
+    } else if(service[i].uuid == GTBS_INCOMINGCALL_TARGET_URI)  {
+      ccsControlServiceInfo.incoming_call_target_beareruri_handle =
+           service[i++].attribute_handle;
+      ccsControlServiceInfo.incoming_call_target_bearerURI_desc =
+           service[i].attribute_handle;
+      LOG(INFO) << __func__ << " incoming_call_target_beareruri_handle: "
+                << ccsControlServiceInfo.incoming_call_target_beareruri_handle
+                << " incoming_call_target_bearerURI_desc: "
+                << ccsControlServiceInfo.incoming_call_target_bearerURI_desc;
+    } else if(service[i].uuid == GTBS_INCOMING_CALL)  {
+      ccsControlServiceInfo.incoming_call_handle = service[i++].attribute_handle;
+      ccsControlServiceInfo.incoming_call_desc = service[i].attribute_handle;
+      LOG(INFO) << __func__ << " incoming_call_handle: "
+                << ccsControlServiceInfo.incoming_call_handle
+                << " incoming_call_desc: "
+                << ccsControlServiceInfo.incoming_call_desc;
+    } else if(service[i].uuid == GTBS_CONTENT_CONTROLID)  {
+      ccsControlServiceInfo.ccid_handle = service[i].attribute_handle;
+      LOG(INFO) << __func__ << " ccid_handle: " << ccsControlServiceInfo.ccid_handle;
+      //Declare the CC Initialization
+      cc_instance->CallControlInitializedCallback(0);
+    } else if(service[i].uuid == GTBS_CALL_TERMINATION_REASON)  {
+      ccsControlServiceInfo.call_termination_reason_handle =
+           service[i++].attribute_handle;
+      ccsControlServiceInfo.call_termination_reason_desc =
+           service[i].attribute_handle;
+      LOG(INFO) << __func__ << " call_termination_reason_handle: "
+                << ccsControlServiceInfo.call_termination_reason_handle
+                << " call_termination_reason_desc: "
+                << ccsControlServiceInfo.call_termination_reason_desc;
+    } else if(service[i].uuid == GTBS_CALL_FRIENDLY_NAME)  {
+      ccsControlServiceInfo.call_friendly_name_handle = service[i++].attribute_handle;
+      ccsControlServiceInfo.call_friendly_name_desc = service[i].attribute_handle;
+      LOG(INFO) << __func__ << " call_friendly_name_handle: "
+                << ccsControlServiceInfo.call_friendly_name_handle
+                << " call_friendly_name_desc: "
+                << ccsControlServiceInfo.call_friendly_name_desc;
+    }
+  }
+}
+
+void PrintData(uint8_t data[], uint16_t len) {
+    for (int i=0; i<len; i++) {
+        LOG(INFO) << __func__ << " data[" << i << "] = " << std::hex << std::setfill('0') << std::setw(2) << data[i] << std::endl;
+    }
+}
+
+void ReverseByteOrder(unsigned char s[], int length)
+{
+    char revbytes_enabled[PROPERTY_VALUE_MAX] = "false";
+    osi_property_get("persist.bluetooth.ccp_rev", revbytes_enabled, "false");
+    bool revNeeded = strncmp(revbytes_enabled, "true", 4) == 0;;
+    if (revNeeded) {
+        int tmp, i, j;
+
+        for (i = 0, j = length-1; i < j; i++, j--)
+        {
+            tmp = s[i];
+            s[i] = s[j];
+            s[j] = tmp;
+        }
+    }
+}
+
+void HandleCcsEvent(uint32_t event, void* param) {
+  LOG(INFO) << __func__ << " event: " << bta_cc_event_str(event);
+  tBTA_GATTS* p_data = NULL;
+  tcc_resp_t *rsp = new tcc_resp_t();
+  if (rsp == NULL) {
+    LOG(INFO) << __func__ << " ccs handle return rsp not allocated ";
+    return;
+  }
+  std::vector<uint8_t> _data;
+  _data.clear();
+  uint8_t status = BT_STATUS_SUCCESS;
+  rsp->event = CCS_NONE_EVENT;
+  bool isCallControllerOpUsed = false;
+  switch (event) {
+
+    case CCS_INIT_EVENT:
+    {
+      Uuid aap_uuid = bluetooth::Uuid::GetRandom();
+      BTA_GATTS_AppRegister(aap_uuid, BTCcCback, true);
+      break;
+    }
+
+    case CCS_CLEANUP_EVENT:
+    {
+      //unregister APP
+      BTA_GATTS_AppDeregister(ccsControlServiceInfo.server_if);
+      cc_instance->remoteDevices.RemoveDevices();
+      break;
+    }
+    case BTA_GATTS_REG_EVT:
+    {
+       p_data = (tBTA_GATTS*)param;
+       if (p_data->reg_oper.status == BT_STATUS_SUCCESS) {
+           ccsControlServiceInfo.server_if = p_data->reg_oper.server_if;
+         std::vector<btgatt_db_element_t> service;
+         service = CcAddService(ccsControlServiceInfo.server_if);
+         if (service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER) ||
+                 service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
+           LOG(INFO) << __func__ << " service app register uuid is not valid";
+           break;
+         }
+         LOG(INFO) << __func__ << " service app register";
+         BTA_GATTS_AddService(ccsControlServiceInfo.server_if, service, base::Bind(&OnGtbsServiceAddedCb));
+       }
+       break;
+    }
+
+    case BTA_GATTS_DEREG_EVT:
+    {
+      break;
+    }
+
+    case BTA_GATTS_CONF_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      uint16_t conn_id = p_data->req_data.conn_id;
+      uint8_t status = p_data->req_data.status;
+      LOG(INFO) << __func__ << "conn_id :" << conn_id << "status:" << status;
+      if (status == BT_STATUS_SUCCESS) {
+          LOG(INFO) << __func__ << "Notification callback for conn_id :" << conn_id;
+          GattsOpsQueue::NotificationCallback(conn_id);
+      }
+      break;
+    }
+
+    case BTA_GATTS_CONGEST_EVT:
+    {
+      p_data = (tBTA_GATTS*)param;
+      CallControllerDeviceList *remoteDevice;
+      remoteDevice = cc_instance->remoteDevices.FindByConnId(p_data->congest.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " connection entry not found conn_id: "
+          << p_data->congest.conn_id;
+        break;
+      }
+     // rsp->ConngestionOp.status = p_data->req_data.status;
+      rsp->remoteDevice = remoteDevice;
+      rsp->oper.CongestionOp.congested = p_data->congest.congested;
+      rsp->event = CCS_CONGESTION_UPDATE;
+      break;
+    }
+    case BTA_GATTS_MTU_EVT: {
+      p_data = (tBTA_GATTS*)param;
+
+      CallControllerDeviceList *remoteDevice;
+      remoteDevice = cc_instance->remoteDevices.FindByConnId(p_data->congest.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " connection entry not found conn_id: "
+          << p_data->congest.conn_id;
+        break;
+      }
+      rsp->event = CCS_MTU_UPDATE;
+      rsp->remoteDevice = remoteDevice;
+      rsp->oper.MtuOp.mtu = p_data->req_data.p_data->mtu;
+      break;
+    }
+    case BTA_GATTS_CONNECT_EVT: {
+
+      p_data = (tBTA_GATTS*)param;
+      LOG(INFO) << __func__ << " remote devices connected";
+      /*
+      #if (!defined(BTA_SKIP_BLE_START_ENCRYPTION) || BTA_SKIP_BLE_START_ENCRYPTION == FALSE)
+        btif_gatt_check_encrypted_link(p_data->conn.remote_bda,
+                                     p_data->conn.transport);
+      #endif*/
+      CallControllerDeviceList remoteDevice;
+      memset(&remoteDevice, 0, sizeof(remoteDevice));
+      if(cc_instance->remoteDevices.FindByAddress(p_data->conn.remote_bda)) {
+      LOG(INFO) << __func__ << " remote devices already there is connected list";
+        status = BT_STATUS_FAIL;
+        return;
+      }
+      remoteDevice.peer_bda = p_data->conn.remote_bda;
+      remoteDevice.conn_id = p_data->conn.conn_id;
+      if(cc_instance->remoteDevices.Add(remoteDevice) == false) {
+        LOG(INFO) << __func__ << " remote device is not added : max connection reached";
+        //<TBD> need to check disconnection required
+        break;
+      }
+      remoteDevice.state = CCS_DISCONNECTED;
+
+      LOG(INFO) << __func__ << " remote devices connected conn_id: "<< remoteDevice.conn_id <<
+         "bd_addr " << remoteDevice.peer_bda;
+
+      rsp->event = CCS_CONNECTION;
+      rsp->remoteDevice = cc_instance->remoteDevices.FindByAddress(p_data->conn.remote_bda);
+      if (rsp->remoteDevice == NULL) {
+          LOG(INFO)<<__func__ << " remote dev is null";
+        break;
+      }
+      break;
+    }
+
+    case BTA_GATTS_DISCONNECT_EVT: {
+      LOG(INFO) << __func__ << " remote devices disconnected";
+      p_data = (tBTA_GATTS*)param;
+      CallControllerDeviceList *remoteDevice;
+      remoteDevice = cc_instance->remoteDevices.FindByConnId(p_data->conn_update.conn_id);
+      if((!remoteDevice) ) {
+        status = BT_STATUS_FAIL;
+        break;
+      }
+
+      rsp->remoteDevice->peer_bda = remoteDevice->peer_bda;
+      rsp->event = CCS_DISCONNECTION;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case BTA_GATTS_STOP_EVT:
+      //Do nothing
+      break;
+
+    case BTA_GATTS_DELELTE_EVT:
+      //Do nothing
+      break;
+
+    case BTA_GATTS_READ_CHARACTERISTIC_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      std::vector<uint8_t> value;
+      CallControllerDeviceList *remoteDevice =
+          cc_instance->remoteDevices.FindByConnId(p_data->req_data.conn_id);
+      LOG(INFO) << __func__ << " charateristcs read handle " <<
+          p_data->req_data.p_data->read_req.handle <<" trans_id : " <<
+              p_data->req_data.trans_id;
+
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore read operation";
+        status = BT_STATUS_FAIL;
+        break;
+      }
+
+      LOG(INFO) <<" offset: " << p_data->req_data.p_data->read_req.offset <<
+          " long : " << p_data->req_data.p_data->read_req.is_long;
+
+      tGATTS_RSP rsp_struct;
+      rsp_struct.attr_value.auth_req  = 0;
+      rsp_struct.attr_value.handle = p_data->req_data.p_data->read_req.handle;
+      rsp_struct.attr_value.offset = p_data->req_data.p_data->read_req.offset;
+
+      if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.call_state_handle) {
+        std::vector<uint8_t> loc_desc_value;
+        std::map<uint8_t, tCCS_CALL_STATE>::iterator it;
+        for (it = CallStatelist.begin(); it != CallStatelist.end(); it++){
+            tCCS_CALL_STATE obj = it->second;
+            loc_desc_value.push_back(obj.index);
+            loc_desc_value.push_back(obj.state);
+            loc_desc_value.push_back(obj.flags);
+        }
+        size_t count = std::min((size_t)GATT_MAX_ATTR_LEN, loc_desc_value.size());
+        rsp_struct.attr_value.len = count;
+        memcpy(rsp_struct.attr_value.value, loc_desc_value.data(), rsp_struct.attr_value.len);
+
+
+        LOG(INFO) << __func__ << " CallStateInfo read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.call_control_point_opcode_supported_handle) {
+
+        rsp_struct.attr_value.len = sizeof(SupportedOptionalOpcodes.supp_opcode);
+        memcpy(rsp_struct.attr_value.value, &SupportedOptionalOpcodes.supp_opcode, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " callcontrol_point_opcode_supported_handle read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.bearer_provider_name_handle) {
+        LOG(INFO) << __func__ << " BearerProviderInfo name read " << BearerProviderInfo.name;
+        rsp_struct.attr_value.len = strlen((char *)BearerProviderInfo.name);
+        LOG(INFO) << __func__ << " BearerProviderInfo name len: " <<rsp_struct.attr_value.len;
+        memcpy(rsp_struct.attr_value.value, BearerProviderInfo.name, rsp_struct.attr_value.len);
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.bearer_list_currentcalls_handle) {
+
+        std::vector<uint8_t> loc_desc_value;
+        std::map<uint8_t, tCCS_BEARER_LIST_CURRENT_CALLS>::iterator it;
+        for (it = BlccInfolist.begin(); it != BlccInfolist.end(); it++){
+            tCCS_BEARER_LIST_CURRENT_CALLS obj = it->second;
+            loc_desc_value.push_back(obj.list_length);
+            loc_desc_value.push_back(obj.call_index);
+            loc_desc_value.push_back(obj.call_state);
+            loc_desc_value.push_back(obj.call_flags);
+
+            loc_desc_value.insert(loc_desc_value.end(),
+                obj.call_uri, obj.call_uri+(obj.list_length-3));
+        }
+        size_t count = std::min((size_t)GATT_MAX_ATTR_LEN, loc_desc_value.size());
+        rsp_struct.attr_value.len = count;
+        memcpy(rsp_struct.attr_value.value, loc_desc_value.data(), rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " BlccInfo read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.bearer_technology_handle) {
+        rsp_struct.attr_value.len = sizeof(BearerProviderInfo.technology_type);
+        memcpy(rsp_struct.attr_value.value, &BearerProviderInfo.technology_type, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " technology_type read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.bearer_uci_handle) {
+           rsp_struct.attr_value.len = strlen((char *)BearerProviderInfo.uci);
+           memcpy(rsp_struct.attr_value.value, BearerProviderInfo.uci, rsp_struct.attr_value.len);
+           LOG(INFO) << __func__ << " Bearer UCI read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.bearer_signal_strength_handle) {
+        rsp_struct.attr_value.len = sizeof(BearerProviderInfo.signal);
+        memcpy(rsp_struct.attr_value.value, &BearerProviderInfo.signal, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " signal strength read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.bearer_signal_strength_report_interval_handle) {
+        rsp_struct.attr_value.len = sizeof(BearerProviderInfo.signal_report_interval);
+        memcpy(rsp_struct.attr_value.value, &BearerProviderInfo.signal_report_interval, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " signal_report_interval read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.bearer_uri_schemes_supported_handle) {
+            rsp_struct.attr_value.len = strlen((const char*)BearerProviderInfo.bearer_schemes_list);
+            memcpy(rsp_struct.attr_value.value, BearerProviderInfo.bearer_schemes_list, rsp_struct.attr_value.len);
+            LOG(INFO) << __func__ << " bearer_schemes_list read";
+      }else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.incoming_call_handle) {
+        rsp_struct.attr_value.len = 1 + strlen((char*)IncomingCallInfo.incoming_uri);
+        memcpy(rsp_struct.attr_value.value, &IncomingCallInfo, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " Incoming call read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.incoming_call_target_beareruri_handle) {
+        rsp_struct.attr_value.len = 1 + strlen((char*)IncomingCallTargetUri.incoming_target_uri);
+        memcpy(rsp_struct.attr_value.value, &IncomingCallTargetUri, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " Incoming Call target URI read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.ccid_handle) {
+        rsp_struct.attr_value.len = sizeof(CcidInfo);
+        memcpy(rsp_struct.attr_value.value, &CcidInfo, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " Content Control read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.call_status_flags_handle) {
+        rsp_struct.attr_value.len = sizeof(StatusFlags.supported_flags);
+        memcpy(rsp_struct.attr_value.value, &StatusFlags.supported_flags, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " Status flags read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.call_friendly_name_handle) {
+        rsp_struct.attr_value.len = 1 + strlen((char*)FriendlyName.name);
+        memcpy(rsp_struct.attr_value.value, &FriendlyName, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " Call friendly name read";
+      }else if(p_data->req_data.p_data->read_req.handle ==
+           ccsControlServiceInfo.call_termination_reason_handle) {
+        rsp_struct.attr_value.len = sizeof(TerminationReason);
+        memcpy(rsp_struct.attr_value.value, &TerminationReason, rsp_struct.attr_value.len);
+        LOG(INFO) << __func__ << " Termination Reason read";
+      }
+      else {
+        LOG(INFO) << __func__ << " read request for unknow handle " << p_data->req_data.p_data->read_req.handle;
+        status = BT_STATUS_FAIL;
+        break;
+      }
+      LOG(INFO) << __func__ << " read request handle " << p_data->req_data.p_data->read_req.handle <<
+        "connection id " << p_data->req_data.conn_id;
+      rsp->oper.ReadOp.char_handle = rsp_struct.attr_value.handle;
+      rsp->oper.ReadOp.trans_id = p_data->req_data.trans_id;
+      rsp->oper.ReadOp.status = BT_STATUS_SUCCESS;
+      rsp->event = CCS_READ_RSP;
+      rsp->remoteDevice = remoteDevice;
+
+      memcpy((void*)&rsp->rsp_value, &rsp_struct, sizeof(rsp_struct));
+      break;
+    }
+
+    case BTA_GATTS_READ_DESCRIPTOR_EVT: {
+      LOG(INFO) << __func__ << " read descriptor";
+      p_data = (tBTA_GATTS*)param;
+      LOG(INFO) << __func__ << " charateristcs read desc handle " <<
+          p_data->req_data.p_data->read_req.handle << " offset : "
+          << p_data->req_data.p_data->read_req.offset;
+       CallControllerDeviceList *remoteDevice =
+          cc_instance->remoteDevices.FindByConnId(p_data->req_data.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore write";
+        status = BT_STATUS_FAIL;
+        break;
+      }
+      uint16_t data = 0x00;
+      if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.call_state_desc) {
+        LOG(INFO) << __func__ << " call_state_desc read";
+        data = remoteDevice->call_state_notify;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.call_control_point_desc) {
+        LOG(INFO) << __func__ << " callcontrol_point_desc read";
+        data = remoteDevice->call_control_point_notify;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.bearer_provider_name_desc) {
+        LOG(INFO) << __func__ << " bearer_provider_name_desc read";
+        data = remoteDevice->bearer_provider_name_notify;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.bearer_signal_strength_desc) {
+        LOG(INFO) << __func__ << " bearer_signal_strength desc read";
+        data = remoteDevice->bearer_signal_strength_notify;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.bearer_list_currentcalls_desc) {
+        LOG(INFO) << __func__ << " bearer_list_currentcall read";
+        data = remoteDevice->bearer_current_calls_list_notify;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.bearer_technology_desc) {
+        LOG(INFO) << __func__ << " bearer_technology_desc read";
+        data = remoteDevice->bearer_technology_changed_notify;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.bearer_uci_desc) {
+        LOG(INFO) << __func__ << " bearer_uci_desc read";
+        data = remoteDevice->bearer_uci_notify;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.bearer_uri_schemes_supported_desc) {
+        LOG(INFO) << __func__ << " bearer_uri_schemes_supported_desc read";
+        data = remoteDevice->bearer_uri_schemes_supported_notify;
+      }  else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.call_friendly_name_desc) {
+        LOG(INFO) << __func__ << " call_friendly_name_desc read";
+        data = remoteDevice->call_friendly_name_notify;
+      }  else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.call_control_point_opcode_supported_desc) {
+        LOG(INFO) << __func__ << " call_control_point_opcode_supported_desc read";
+        data = remoteDevice->call_control_point_opcode_supported_notify;
+      }  else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.call_termination_reason_desc) {
+        LOG(INFO) << __func__ << " call_termination_reason_desc read";
+        data = remoteDevice->call_termination_reason_notify;
+      }  else if(p_data->req_data.p_data->read_req.handle ==
+          ccsControlServiceInfo.bearer_signal_strength_report_interval_desc) {
+        LOG(INFO) << __func__ << " bearer_signal_strength_report_interval_desc read";
+        data = remoteDevice->bearer_signal_strength_report_interval_notify;
+      } else {
+        LOG(INFO) << __func__ << " read request for unknown handle " << p_data->req_data.p_data->read_req.handle;
+        status = BT_STATUS_FAIL;
+        break;
+      }
+      rsp->rsp_value.attr_value.auth_req  = 0;
+      rsp->rsp_value.attr_value.handle = p_data->req_data.p_data->read_req.handle;
+      rsp->rsp_value.attr_value.offset = p_data->req_data.p_data->read_req.offset;
+      *(uint16_t *)rsp->rsp_value.attr_value.value = (uint16_t)data;
+      rsp->rsp_value.attr_value.len = sizeof(data);
+      //cc response
+      rsp->oper.ReadDescOp.desc_handle = p_data->req_data.p_data->read_req.handle;
+      rsp->oper.ReadDescOp.trans_id = p_data->req_data.trans_id;
+      rsp->oper.ReadDescOp.status = BT_STATUS_SUCCESS;
+      rsp->remoteDevice = remoteDevice;
+      rsp->event = CCS_DESCRIPTOR_READ_RSP;
+      break;
+    }
+
+    case BTA_GATTS_WRITE_CHARACTERISTIC_EVT: {
+
+      p_data = (tBTA_GATTS*)param;
+      tGATT_WRITE_REQ req = p_data->req_data.p_data->write_req;
+      LOG(INFO) << __func__ << " write characteristics len: " << req.len;
+      PrintData(req.value, req.len);
+      CallControllerDeviceList *remoteDevice =
+          cc_instance->remoteDevices.FindByConnId(p_data->req_data.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore write";
+        status = BT_STATUS_FAIL;
+        break;
+      }
+      if ( status != BT_STATUS_FAIL) {
+        rsp->oper.WriteOp.char_handle = req.handle;
+        rsp->oper.WriteOp.trans_id = p_data->req_data.trans_id;
+        rsp->oper.WriteOp.status = BT_STATUS_SUCCESS;
+        rsp->remoteDevice = remoteDevice;
+        rsp->oper.WriteOp.need_rsp = req.need_rsp;
+        rsp->oper.WriteOp.offset = req.offset;
+        rsp->oper.WriteOp.len = req.len;
+        rsp->oper.WriteOp.data = (uint8_t*) malloc(sizeof(uint8_t)*req.len);
+        memcpy(rsp->oper.WriteOp.data, req.value, req.len);
+        rsp->event = CCS_WRITE_RSP;
+      }
+      break;
+    }
+
+    case BTA_GATTS_WRITE_DESCRIPTOR_EVT: {
+
+      p_data = (tBTA_GATTS* )param;
+      std::vector<uint8_t> write_desc_value;
+      write_desc_value.clear();
+      uint16_t handle = 0;
+      uint16_t req_value = 0;
+      const auto& req = p_data->req_data.p_data->write_req;
+      CallControllerDeviceList *remoteDevice =
+           cc_instance->remoteDevices.FindByConnId(p_data->req_data.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore notification";
+        break;
+      }
+      req_value = *(uint16_t* )req.value;
+      //need to initialized with proper error code
+      int status = BT_STATUS_SUCCESS;
+      LOG(INFO) << __func__ << " write descriptor :" << req.handle <<
+         " is resp: " << req.need_rsp << "is prep: " << req.is_prep << " value " << req_value;
+
+      if(req.handle ==
+          ccsControlServiceInfo.call_state_desc) {
+        LOG(INFO) << __func__ << " call_state_desc descriptor write";
+        remoteDevice->call_state_notify = req_value;
+        std::map<uint8_t, tCCS_CALL_STATE>::iterator it;
+        for (it = CallStatelist.begin(); it != CallStatelist.end(); it++){
+            tCCS_CALL_STATE obj = it->second;
+            write_desc_value.push_back(obj.index);
+            write_desc_value.push_back(obj.state);
+            write_desc_value.push_back(obj.flags);
+        }
+        handle = ccsControlServiceInfo.call_state_handle;
+      } else if(req.handle ==
+        ccsControlServiceInfo.bearer_provider_name_desc) {
+        remoteDevice->bearer_provider_name_notify = req_value;
+        LOG(INFO) << __func__ << " bearer_provider_name_desc descriptor write";
+           write_desc_value.assign(BearerProviderInfo.name,
+            BearerProviderInfo.name + strlen((char*)BearerProviderInfo.name));
+        handle = ccsControlServiceInfo.bearer_provider_name_handle;
+      } else if(req.handle ==
+          ccsControlServiceInfo.call_control_point_desc) {
+        LOG(INFO) << __func__ << " callcontrol_point_desc write";
+        write_desc_value.push_back(CallControllerResp.opcode);
+        write_desc_value.push_back(CallControllerResp.index);
+        write_desc_value.push_back(CallControllerResp.response_status);
+
+        remoteDevice->call_control_point_notify = req_value;
+        handle = ccsControlServiceInfo.call_control_point_handle;
+      } else if(req.handle ==
+          ccsControlServiceInfo.bearer_signal_strength_desc) {
+        LOG(INFO) << __func__ << " bearer_signal_strength desc write";
+        write_desc_value.push_back(BearerProviderInfo.signal);
+        remoteDevice->bearer_signal_strength_notify = req_value;
+        handle = ccsControlServiceInfo.bearer_signal_strength_handle;
+      }  else if(req.handle ==
+           ccsControlServiceInfo.bearer_uri_schemes_supported_desc) {
+            remoteDevice->bearer_uri_schemes_supported_notify = req_value;
+            write_desc_value.assign(BearerProviderInfo.bearer_schemes_list,
+            BearerProviderInfo.bearer_schemes_list + strlen((char*)BearerProviderInfo.bearer_schemes_list));
+            LOG(INFO) << __func__ << " bearer_schemes_list Desc write";
+      } else if(req.handle ==
+          ccsControlServiceInfo.bearer_list_currentcalls_desc) {
+        std::map<uint8_t, tCCS_BEARER_LIST_CURRENT_CALLS>::iterator it;
+        LOG(INFO) << __func__ << " bearer_list_currentcall desc write";
+        for (it = BlccInfolist.begin(); it != BlccInfolist.end(); it++){
+            tCCS_BEARER_LIST_CURRENT_CALLS obj = it->second;
+            write_desc_value.push_back(obj.list_length);
+            write_desc_value.push_back(obj.call_index);
+            write_desc_value.push_back(obj.call_state);
+            write_desc_value.push_back(obj.call_flags);
+            write_desc_value.insert(write_desc_value.end(),
+                obj.call_uri, obj.call_uri+(obj.list_length-3));
+        }
+        remoteDevice->bearer_current_calls_list_notify = req_value;
+        handle = ccsControlServiceInfo.bearer_list_currentcalls_handle;
+
+      } else if(req.handle ==
+          ccsControlServiceInfo.bearer_technology_desc) {
+        LOG(INFO) << __func__ << " bearer_technology_desc write";
+        remoteDevice->bearer_technology_changed_notify = req_value;
+        write_desc_value.push_back(BearerProviderInfo.technology_type);
+        handle = ccsControlServiceInfo.bearer_technology_handle;
+      } else if(req.handle ==
+          ccsControlServiceInfo.call_friendly_name_desc) {
+        LOG(INFO) << __func__ << " call_friendly_name_desc read";
+        remoteDevice->call_friendly_name_notify = req_value;
+        int len = 1 + strlen((char*)FriendlyName.name);
+        write_desc_value.assign((uint8_t*)&FriendlyName, (uint8_t*)&FriendlyName+len);
+        handle = ccsControlServiceInfo.call_friendly_name_handle;
+      } else if(req.handle ==
+          ccsControlServiceInfo.call_termination_reason_desc) {
+        LOG(INFO) << __func__ << " call_termination_reason_desc write";
+        remoteDevice->call_termination_reason_notify = req_value;
+        handle = ccsControlServiceInfo.call_termination_reason_handle;
+        write_desc_value.push_back(TerminationReason.index);
+        write_desc_value.push_back(TerminationReason.reason);
+
+        handle = ccsControlServiceInfo.call_termination_reason_handle;
+    } else if(req.handle ==
+          ccsControlServiceInfo.call_status_flags_desc) {
+        LOG(INFO) << __func__ << " Status Flags Desc write";
+        remoteDevice->status_flags_notify= req_value;
+        write_desc_value.push_back(StatusFlags.supported_flags);
+        handle = ccsControlServiceInfo.call_status_flags_handle;
+
+      } else if(req.handle ==
+          ccsControlServiceInfo.incoming_call_target_bearerURI_desc) {
+        LOG(INFO) << __func__ << " Incoming target bearer desc write";
+        int len = 1 + strlen((char*)IncomingCallTargetUri.incoming_target_uri);
+        write_desc_value.assign((uint8_t*)&IncomingCallTargetUri, (uint8_t*)&IncomingCallTargetUri+len);
+        remoteDevice->incoming_call_target_URI_notify= req_value;
+        handle = ccsControlServiceInfo.incoming_call_target_beareruri_handle;
+
+      } else if(req.handle ==
+          ccsControlServiceInfo.incoming_call_desc) {
+        LOG(INFO) << __func__ << " Incoming Call desc write";
+        remoteDevice->incoming_call_state_notify = req_value;
+        int len = 1 + strlen((char*)IncomingCallInfo.incoming_uri);
+        write_desc_value.assign((uint8_t*)&IncomingCallInfo, (uint8_t*)&IncomingCallInfo+len);
+
+        handle = ccsControlServiceInfo.incoming_call_handle;
+      } else {
+        LOG(INFO) << __func__ << " descriptor write not matched";
+        status = 4; //check error code
+      }
+      rsp->oper.WriteDescOp.desc_handle = req.handle;
+      rsp->oper.WriteDescOp.char_handle = handle;
+      rsp->oper.WriteDescOp.trans_id = p_data->req_data.trans_id;
+      rsp->oper.WriteDescOp.status = status;
+      rsp->oper.WriteDescOp.need_rsp = req.need_rsp;
+      rsp->oper.WriteDescOp.notification = req_value;
+      rsp->oper.WriteDescOp.value = std::move(write_desc_value);
+      rsp->event = CCS_DESCRIPTOR_WRITE_RSP;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case BTA_GATTS_EXEC_WRITE_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      break;
+    }
+
+    case BTA_GATTS_CLOSE_EVT: {
+       //not required
+       break;
+    }
+
+    case BTA_GATTS_PHY_UPDATE_EVT: {
+
+      p_data = (tBTA_GATTS*)param;
+      CallControllerDeviceList *remoteDevice =
+        cc_instance->remoteDevices.FindByConnId(p_data->phy_update.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore phy update "
+             << p_data->phy_update.status;
+        status = BT_STATUS_FAIL;
+        break;
+      }
+      rsp->event = CCS_PHY_UPDATE;
+      rsp->oper.PhyOp.rx_phy = p_data->phy_update.rx_phy;
+      rsp->oper.PhyOp.tx_phy = p_data->phy_update.tx_phy;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case BTA_GATTS_CONN_UPDATE_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      CallControllerDeviceList *remoteDevice =
+        cc_instance->remoteDevices.FindByConnId(p_data->phy_update.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " connection update device not found";
+        break;
+      }
+      LOG(INFO) << __func__ << " connection update status " << p_data->phy_update.status;
+      rsp->event = CCS_CONNECTION_UPDATE;
+      rsp->oper.ConnectionUpdateOp.remoteDevice = remoteDevice;
+      rsp->oper.ConnectionUpdateOp.remoteDevice->latency = p_data->conn_update.latency;
+      rsp->oper.ConnectionUpdateOp.remoteDevice->timeout = p_data->conn_update.timeout;
+      rsp->oper.ConnectionUpdateOp.remoteDevice->interval = p_data->conn_update.interval;
+      rsp->oper.ConnectionUpdateOp.status = p_data->conn_update.status;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case CCS_ACTIVE_DEVICE_UPDATE:
+    {
+      tCCS_SET_ACTIVE_DEVICE *data = (tCCS_SET_ACTIVE_DEVICE *)param;
+      LOG(INFO) << __func__ << " CCS_ACTIVE_DEVICE_UPDATE address " << data->address;
+      if (cc_instance->remoteDevices.FindByAddress(data->address) != NULL) {
+        cc_instance->remoteDevices.AddSetActiveDevice(data);
+      } else {
+          LOG(ERROR) << __func__ << " CCS_ACTIVE_DEVICE_UPDATE failed as given remote is not registered for notif " << data->address;
+      }
+      break;
+    }
+    case CCS_CONNECTION_CLOSE_EVENT:
+    {
+     break;
+    }
+    case CCS_CALL_STATE_UPDATE:
+    {
+      std::map<uint8_t, tCCS_CALL_STATE>::iterator it;
+      for (it = CallStatelist.begin(); it != CallStatelist.end(); it++){
+        tCCS_CALL_STATE obj = it->second;
+        _data.push_back(obj.index);
+        _data.push_back(obj.state);
+        _data.push_back(obj.flags);
+      }
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.call_state_handle;
+      isCallControllerOpUsed = true;
+      break;
+    }
+    case CCS_BEARER_NAME_UPDATE:
+    {
+      LOG(INFO) << __func__ << " CCS_BEARER_NAME_UPDATE";
+      tCCS_BEARER_PROVIDER_INFO *data = (tCCS_BEARER_PROVIDER_INFO*) param;
+      uint16_t len = strlen((char*)data->name);
+      ReverseByteOrder(data->name, len);
+      memcpy(BearerProviderInfo.name, data->name, len);
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.bearer_provider_name_handle;
+      _data.assign(BearerProviderInfo.name,
+          BearerProviderInfo.name + len);
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_OPT_OPCODES:
+    {
+      LOG(INFO) << __func__ << " CCS_OPT_OPCODES";
+      tCCS_SUPP_OPTIONAL_OPCODES *data = (tCCS_SUPP_OPTIONAL_OPCODES*) param;
+      SupportedOptionalOpcodes.supp_opcode = data->supp_opcode;
+
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.call_control_point_opcode_supported_handle;
+
+      _data.push_back(SupportedOptionalOpcodes.supp_opcode);
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_BEARER_CURRENT_CALL_LIST_UPDATE:
+    {
+      LOG(INFO) << __func__ << " CCS_BEARER_CURRENT_CALL_LIST_UPDATE";
+      std::map<uint8_t, tCCS_BEARER_LIST_CURRENT_CALLS>::iterator it;
+      for (it = BlccInfolist.begin(); it != BlccInfolist.end(); it++){
+          tCCS_BEARER_LIST_CURRENT_CALLS obj = it->second;
+          _data.push_back(obj.list_length);
+          _data.push_back(obj.call_index);
+          _data.push_back(obj.call_state);
+          _data.push_back(obj.call_flags);
+          _data.insert(_data.end(),
+              obj.call_uri, obj.call_uri+(obj.list_length-3));
+      }
+
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.bearer_list_currentcalls_handle;
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_BEARER_SIGNAL_STRENGTH_UPDATE:
+    {
+      LOG(INFO) << __func__ << " CCS_BEARER_SIGNAL_STRENGTH_UPDATE";
+      tCCS_BEARER_PROVIDER_INFO *data = (tCCS_BEARER_PROVIDER_INFO *) param;
+      BearerProviderInfo.signal = data->signal;
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.bearer_signal_strength_handle;
+      //control whether notification has to happen based
+      //on reporting Interval time or not
+      rsp->force = false;
+      _data.push_back(BearerProviderInfo.signal);
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_BEARER_TECHNOLOGY_UPDATE:
+    {
+      LOG(INFO) << __func__ << " CCS_BEARER_TECHNOLOGY_UPDATE";
+      tCCS_BEARER_PROVIDER_INFO *data = (tCCS_BEARER_PROVIDER_INFO *) param;
+      BearerProviderInfo.technology_type = data->technology_type;
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.bearer_technology_handle;
+      _data.push_back(BearerProviderInfo.technology_type);
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_BEARER_URI_SCHEMES_SUPPORTED: {
+      LOG(INFO) << __func__ << " CCS_BEARER_URI_SCHEMES_SUPPORTED";
+      tCCS_BEARER_PROVIDER_INFO *data = (tCCS_BEARER_PROVIDER_INFO*) param;
+      BearerProviderInfo.bearer_list_len = data->bearer_list_len;
+      LOG(INFO) << __func__ << " CCS_BEARER_URI_SCHEMES_SUPPORTED: len " <<BearerProviderInfo.bearer_list_len;
+      ReverseByteOrder(data->bearer_schemes_list, BearerProviderInfo.bearer_list_len);
+      memcpy(BearerProviderInfo.bearer_schemes_list, data->bearer_schemes_list, BearerProviderInfo.bearer_list_len);
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.bearer_uri_schemes_supported_handle;
+      isCallControllerOpUsed = true;
+      _data.assign(BearerProviderInfo.bearer_schemes_list,
+          BearerProviderInfo.bearer_schemes_list + BearerProviderInfo.bearer_list_len);
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_INCOMING_CALL_UPDATE:
+    {
+      LOG(INFO) << __func__ << " CCS_INCOMING_CALL_UPDATE";
+      tCCS_INCOMING_CALL* data = (tCCS_INCOMING_CALL *) param;
+      IncomingCallInfo.index = data->index;
+      uint16_t len = strlen((char*)data->incoming_uri);
+      ReverseByteOrder(data->incoming_uri, len);
+      memcpy(IncomingCallInfo.incoming_uri, data->incoming_uri, len);
+
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.incoming_call_handle;
+      _data.push_back(IncomingCallInfo.index);
+      _data.insert(_data.end(), IncomingCallInfo.incoming_uri,
+                   IncomingCallInfo.incoming_uri + len);
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_INCOMING_TARGET_URI_UPDATE:
+    {
+      LOG(INFO) << __func__ << " CCS_INCOMING_TARGET_URI_UPDATE";
+      tCCS_INCOMING_CALL_URI* data = (tCCS_INCOMING_CALL_URI *) param;
+      IncomingCallTargetUri.index = data->index;
+      uint16_t len = strlen((char*)data->incoming_target_uri);
+      LOG(INFO) << __func__ << " CCS_INCOMING_TARGET_URI_UPDATE: urilen " << len;
+      ReverseByteOrder(data->incoming_target_uri, len);
+      memcpy(IncomingCallTargetUri.incoming_target_uri, data->incoming_target_uri, len);
+
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.incoming_call_target_beareruri_handle;
+      _data.push_back(IncomingCallTargetUri.index);
+      _data.insert(_data.end(), IncomingCallTargetUri.incoming_target_uri,
+                   IncomingCallTargetUri.incoming_target_uri + len);
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_TERMINATION_REASON_UPDATE:
+    {
+        LOG(INFO) << __func__ << " CCS_TERMINATION_REASON_UPDATE";
+        tCCS_TERM_REASON* data = (tCCS_TERM_REASON *) param;
+        TerminationReason.index = data->index;
+        TerminationReason.reason = data->reason;
+
+        rsp->event = CCS_NOTIFY_ALL;
+        rsp->handle = ccsControlServiceInfo.call_termination_reason_handle;
+        _data.push_back(TerminationReason.index);
+        _data.push_back(TerminationReason.reason);
+
+        isCallControllerOpUsed = true;
+        break;
+    }
+
+    case CCS_STATUS_FLAGS_UPDATE:
+    {
+      LOG(INFO) << __func__ << " CCS_STATUS_FLAGS_UPDATE";
+      tCCS_STATUS_FLAGS *data = (tCCS_STATUS_FLAGS*) param;
+
+      ReverseByteOrder((unsigned char*)&data->supported_flags, sizeof(data->supported_flags));
+      StatusFlags.supported_flags = data->supported_flags;
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.call_status_flags_handle;
+      _data.push_back(StatusFlags.supported_flags);
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_CCID_UPDATE:
+    {
+      LOG(INFO) << __func__ << " CCS_CCID_UPDATE";
+      tCCS_CONTENT_CONTROL_ID *data = (tCCS_CONTENT_CONTROL_ID *) param;
+      ReverseByteOrder((unsigned char*)&data->ccid, sizeof(data->ccid));
+      CcidInfo.ccid = (uint32_t)data->ccid;
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.ccid_handle;
+      _data.push_back(CcidInfo.ccid);
+      isCallControllerOpUsed = true;
+      break;
+    }
+
+    case CCS_CALL_CONTROL_RESPONSE:
+    {
+      LOG(INFO) << __func__ << " CCS_CALL_CONTROL_RESPONSE";
+
+      rsp->event = CCS_NOTIFY_ALL;
+      rsp->handle = ccsControlServiceInfo.call_control_point_handle;
+      _data.push_back(CallControllerResp.opcode);
+      _data.push_back(CallControllerResp.index);
+      _data.push_back(CallControllerResp.response_status);
+      isCallControllerOpUsed = true;
+      break;
+    }
+    default:
+      LOG(INFO) << __func__ << " event not matched !!";
+      break;
+  }
+
+  if(rsp->event != CCS_NONE_EVENT) {
+    if (isCallControllerOpUsed == true) {
+        rsp->oper.CallControllerOp.data = std::move(_data);
+        LOG(INFO) << __func__ << " After Moving size " << rsp->oper.CallControllerOp.data.size();
+    }
+    CCSHandler(rsp->event, rsp);
+  }
+  if(rsp) {
+    LOG(INFO) << __func__ << "free rsp data";
+    free(rsp);
+  }
+}
+
+
+
+void BTCcCback(tBTA_GATTS_EVT event, tBTA_GATTS* param) {
+
+   HandleCcsEvent((uint32_t)event, param);
+}
+
+
+ bool DeviceStateConnectionHandler(uint32_t event, void* param) {
+  LOG(INFO) << __func__ << " device connected handle " << event;
+  tcc_resp_t *p_data = (tcc_resp_t *) param;
+  switch (event) {
+
+    case CCS_NOTIFY_ALL: {
+      LOG(INFO) << __func__ << " device notify all";
+      if (p_data->handle == ccsControlServiceInfo.bearer_signal_strength_handle) {
+          if (p_data->force == false &&
+               p_data->remoteDevice->signal_strength_report_interval != 0) {
+           LOG(INFO) << __func__ << "Not a timer expired push, dont notify to remote";
+           break;
+        }
+      }
+
+      GattsOpsQueue::SendNotification(p_data->remoteDevice->conn_id, p_data->handle,
+                                      p_data->oper.CallControllerOp.data, false);
+      break;
+    }
+
+    case CCS_READ_RSP:
+
+      LOG(INFO) << __func__ << " device CCS_READ_RSP update " << event;
+      BTA_GATTS_SendRsp(p_data->remoteDevice->conn_id, p_data->oper.ReadOp.trans_id,
+          BT_STATUS_SUCCESS, &p_data->rsp_value);
+      break;
+
+    case CCS_DESCRIPTOR_READ_RSP:
+
+      LOG(INFO) << __func__ << " device CCS_DESCRIPTOR_READ_RSP update " << event;
+      BTA_GATTS_SendRsp(p_data->remoteDevice->conn_id, p_data->oper.ReadDescOp.trans_id,
+          BT_STATUS_SUCCESS, &p_data->rsp_value);
+      break;
+
+    case CCS_DESCRIPTOR_WRITE_RSP:
+    {
+      LOG(INFO) << __func__ << " device CCS_DESCRIPTOR_WRITE_RSP update rsp: " << p_data->oper.WriteDescOp.need_rsp;
+      tGATTS_RSP rsp_struct;
+      rsp_struct.attr_value.handle = p_data->rsp_value.attr_value.handle;
+      rsp_struct.attr_value.offset = p_data->rsp_value.attr_value.offset;
+      if (p_data->remoteDevice->congested == false &&
+            p_data->oper.WriteDescOp.need_rsp) {
+        //send rsp to write
+        LOG(INFO) << __func__ << " gett send rsp";
+        BTA_GATTS_SendRsp(p_data->remoteDevice->conn_id, p_data->oper.WriteDescOp.trans_id,
+            p_data->oper.WriteDescOp.status, &rsp_struct);
+      }
+      if (!p_data->oper.WriteDescOp.status && p_data->oper.WriteDescOp.notification) {
+        //notify update to register device
+        GattsOpsQueue::SendNotification(p_data->remoteDevice->conn_id,
+                                        p_data->oper.WriteDescOp.char_handle,
+                                        p_data->oper.WriteDescOp.value, false);
+      } else {
+        LOG(INFO) << __func__ << " notification disable  handle : " << p_data->oper.WriteDescOp.char_handle;
+      }
+      break;
+    }
+
+    case CCS_WRITE_RSP: {
+      LOG(INFO) << __func__ << " device CCS_WRITE_RSP update " << event;
+      bool need_rsp = p_data->oper.WriteOp.need_rsp;
+      LOG(INFO) << __func__ << " device CCS_WRITE_RSP update " << event << " need_rsp: " <<need_rsp;
+      if (need_rsp) {
+         BTA_GATTS_SendRsp(p_data->remoteDevice->conn_id, p_data->oper.WriteOp.trans_id,
+             BT_STATUS_SUCCESS, &p_data->rsp_value);
+      }
+      break;
+    }
+
+    case CCS_CONNECTION_UPDATE: {
+      LOG(INFO) << __func__ << " device cconnection update " << event;
+      break;
+    }
+
+    case CCS_PHY_UPDATE: {
+      LOG(INFO) << __func__ << " device CCS_PHY_UPDATE update " << event;
+      p_data->remoteDevice->rx_phy = p_data->oper.PhyOp.rx_phy;
+      p_data->remoteDevice->tx_phy = p_data->oper.PhyOp.tx_phy;
+      break;
+    }
+
+    case CCS_MTU_UPDATE: {
+      LOG(INFO) << __func__ << " device CCS_MTU_UPDATE update " << event;
+      p_data->remoteDevice->mtu = p_data->oper.MtuOp.mtu;
+      break;
+    }
+
+    case CCS_CONGESTION_UPDATE: {
+      LOG(INFO) << __func__ << ": device CCS_CONGESTION_UPDATE update: " << event;
+      CcpCongestionUpdate(p_data);
+      break;
+    }
+
+    case CCS_DISCONNECTION: {
+      LOG(INFO) << __func__ << " device CCS_DISCONNECTION remove " << event;
+      cc_instance->remoteDevices.Remove(p_data->remoteDevice->peer_bda);
+      cc_instance->ConnectionStateCallback(CCS_DISCONNECTED, p_data->remoteDevice->peer_bda);
+      break;
+    }
+
+    case CCS_CONNECTION_CLOSE_EVENT: {
+      LOG(INFO) << __func__ << " device connection closing";
+        // Close active connection
+      if (p_data->remoteDevice->conn_id != 0)
+        BTA_GATTS_Close(p_data->remoteDevice->conn_id);
+      else
+        BTA_GATTS_CancelOpen(ccsControlServiceInfo.server_if, p_data->remoteDevice->peer_bda, true);
+        // Cancel pending background connections
+        BTA_GATTS_CancelOpen(ccsControlServiceInfo.server_if, p_data->remoteDevice->peer_bda, false);
+      break;
+    }
+
+    case CCS_BOND_STATE_CHANGE_EVENT:
+      LOG(INFO) << __func__ << " Bond state change";
+      cc_instance->remoteDevices.Remove(p_data->remoteDevice->peer_bda);
+      break;
+
+    default:
+      LOG(INFO) << __func__ << " event not matched";
+      break;
+  }
+
+  return BT_STATUS_SUCCESS;
+}
+
+
+bool DeviceStateDisconnectedHandler(uint32_t event, void* param) {
+  LOG(INFO) << __func__ << " device disconnected handle " << event;
+  tcc_resp_t *p_data = (tcc_resp_t *) param;
+  switch (event) {
+    case CCS_CONNECTION:
+    {
+      LOG(INFO) << __func__ << " connection processing " << event;
+      p_data->remoteDevice->state = CCS_CONNECTED;
+      p_data->remoteDevice->call_state_notify = 0x00;
+      p_data->remoteDevice->bearer_provider_name_notify = 0x00;
+      p_data->remoteDevice->call_control_point_notify = 0x00;
+      p_data->remoteDevice->bearer_technology_changed_notify = 0x00;
+      p_data->remoteDevice->bearer_uci_notify = 0x00;
+      p_data->remoteDevice->bearer_current_calls_list_notify = 0x00;
+      p_data->remoteDevice->call_friendly_name_notify = 0x00;
+      p_data->remoteDevice->call_termination_reason_notify = 0x00;
+      p_data->remoteDevice->congested = false;
+      // p_data->remoteDevice->conn_id;
+
+      p_data->remoteDevice->timeout = 0;
+      p_data->remoteDevice->latency = 0;
+      p_data->remoteDevice->interval = 0;
+      p_data->remoteDevice->rx_phy = 0;
+      p_data->remoteDevice->tx_phy = 0;
+      cc_instance->ConnectionStateCallback(CCS_CONNECTED, p_data->remoteDevice->peer_bda);
+      break;
+    }
+
+    case CCS_CONGESTION_UPDATE: {
+      LOG(INFO) << __func__ << ": device CCS_CONGESTION_UPDATE update: " << event;
+      CcpCongestionUpdate(p_data);
+      break;
+    }
+
+    case CCS_MTU_UPDATE:
+    case CCS_PHY_UPDATE:
+    case CCS_DISCONNECTED:
+    case CCS_READ_RSP:
+    case CCS_DESCRIPTOR_READ_RSP:
+    case CCS_WRITE_RSP:
+    case CCS_DESCRIPTOR_WRITE_RSP:
+    case CCS_NOTIFY_ALL:
+    case CCS_DISCONNECTION:
+
+    default:
+      //ignore event
+      LOG(INFO) << __func__ << " Ignore event " << event;
+      break;
+  }
+  return BT_STATUS_SUCCESS;
+}
+
+bool is_digits(const std::string &str)
+{
+    return str.find_first_not_of("0123456789+") == std::string::npos;
+}
+
+bool IsValidOriginateUri(std::string uri) {
+    bool ret = false;
+    std::vector<std::string> out;
+    char *token;
+    char* rest = const_cast<char*>(uri.c_str());
+    while ((token = strtok_r(rest, ":", &rest)))
+    {
+        out.push_back(std::string(token));
+    }
+    if (out.size() == 2) {
+        if (out[0].compare("tel") == 0 && is_digits(out[1])) {
+            ret = true;
+        }
+    }
+    LOG(INFO) << __func__ << " ret: " << ret;
+    return ret;
+}
+
+bool CCSActiveProfile(RawAddress addr) {
+    bool isCCSActiveProfile = false;
+    int32_t activeProfile;
+    activeProfile = osi_property_get_int32("persist.vendor.qcom.bluetooth.default_profiles",0);
+    if ((activeProfile&0x2000) == 0x2000) {
+        isCCSActiveProfile = true;
+    }
+    LOG(INFO) << __func__ << " activeProfile "<< activeProfile <<" for" << addr;
+    return isCCSActiveProfile;
+}
+
+bool CCSHandler(uint32_t event, void* param) {
+
+  tcc_resp_t *p_data = (tcc_resp_t *)param;
+
+  CallControllerDeviceList *device = p_data->remoteDevice;
+  LOG(INFO) << __func__ << " inactive ccs handle event "<< bta_cc_event_str(event);
+  switch(p_data->event) {
+    case CCS_NOTIFY_ALL: {
+
+      std::vector<CallControllerDeviceList>notifyDevices = cc_instance->remoteDevices.FindNotifyDevices(p_data->handle);
+      std::vector<CallControllerDeviceList>::iterator it;
+      LOG(INFO) << __func__ << " Notify all handle "<< p_data->handle;
+      if (notifyDevices.size() <= 0) {
+         LOG(INFO) << __func__ << " No device register for notification";
+         break;
+      }
+      for (it = notifyDevices.begin(); it != notifyDevices.end(); it++){
+        if (CCSActiveProfile(it->peer_bda)) {
+            LOG(INFO) << __func__ << " Notify all handle device id " << it->conn_id;
+            p_data->remoteDevice = cc_instance->remoteDevices.FindByConnId(it->conn_id);
+            it->DeviceStateHandlerPointer[it->state](p_data->event, p_data);
+        }
+      }
+      break;
+    }
+    case CCS_WRITE_RSP: {
+
+      LOG(INFO) << __func__ << " Push Write response first: " << device->state;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data);
+      //Process the values now
+      uint8_t* data = p_data->oper.WriteOp.data;
+      int len = p_data->oper.WriteOp.len;
+      RawAddress peer_bda = p_data->remoteDevice->peer_bda;
+      std::map<uint8_t, tCCS_CALL_STATE>::iterator it;
+      bool valid_index = false;
+      uint8_t indices[10];
+      char URI[MAX_URI_SIZE];
+      int i;
+      uint32_t handle = p_data->oper.WriteOp.char_handle;
+      if(handle == ccsControlServiceInfo.call_control_point_handle) {
+           CallControllerOps.operation = data[0];
+           LOG(INFO) << __func__ << " operation: " << std::hex << CallControllerOps.operation;
+           if (gIsActiveCC == true && !cc_instance->remoteDevices.FindActiveDevice(p_data->remoteDevice) &&
+               (CallControllerOps.operation == CALL_TERMINATE ||
+               CallControllerOps.operation == CALL_LOCAL_HOLD ||
+               CallControllerOps.operation == CALL_LOCAL_RETRIEVE)) {
+                   LOG(ERROR) << __func__ << "TERM|HOLD|RET|JOIN triggered from non-active Device";
+                cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                                   CCS_DEFAULT_INDEX_VAL,
+                                                                   CCS_OPCODE_UNSUCCESSFUL, peer_bda);
+                return true;
+           }
+           switch(CallControllerOps.operation) {
+            case CALL_ACCEPT:{
+                if (len != 2) {
+                    LOG(ERROR) << __func__ << " Invalid params";
+                    valid_index = false;
+                } else {
+                    indices[0] = data[1];
+                    valid_index = false;
+                    for (i=0,it = CallStatelist.begin(); it != CallStatelist.end(); it++, i++){
+                        tCCS_CALL_STATE obj = it->second;
+                        if (obj.index == indices[0] && obj.state == CCS_STATE_INCOMING) {
+                            valid_index = true;
+                            break;
+                        }
+                    }
+                }
+                if (valid_index == true) {
+                    cc_instance->CallControlPointChange(CallControllerOps.operation, indices,
+                                                      DEFAULT_INDICIES_COUNT, NULL,
+                                                      device->peer_bda);
+                    cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                   indices[0],
+                                                   CCS_STATUS_SUCCESS, peer_bda);
+                } else {
+                    LOG(INFO) << " ACCEPT ignored as there is no valid Index";
+                    cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                   CCS_DEFAULT_INDEX_VAL,
+                                                   CCS_INVALID_INDEX, peer_bda);
+                }
+                break;
+            }
+            case CALL_TERMINATE: {
+               if (len != 2) {
+                   LOG(ERROR) << __func__ << " Invalid params";
+                   valid_index = false;
+               } else {
+                   indices[0] = data[1];
+                   valid_index = false;
+                   for (i=0,it = CallStatelist.begin(); it != CallStatelist.end(); it++, i++) {
+                       tCCS_CALL_STATE obj = it->second;
+                       if (obj.index == indices[0]) {
+                           LOG(INFO) << __func__ << " call index match: " << indices[0];
+                           valid_index = true;
+                           break;
+                       }
+                    }
+               }
+               if (valid_index == true) {
+                    cc_instance->CallControlPointChange(CallControllerOps.operation, indices,
+                                                       DEFAULT_INDICIES_COUNT, NULL,
+                                                       device->peer_bda);
+                    cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                    indices[0],
+                                                    CCS_STATUS_SUCCESS, peer_bda);
+                    gIsTerminatedInitiatedFromClient = true;
+                    gTerminateIntiatedIndex = indices[0];
+               } else {
+                    LOG(INFO) << " TERMINATE ignored as there is no valid Index";
+                    cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                    CCS_DEFAULT_INDEX_VAL,
+                                                    CCS_INVALID_INDEX, peer_bda);
+               }
+             break;
+            }
+            case CALL_LOCAL_HOLD: {
+                if (len != 2) {
+                   LOG(ERROR) << __func__ << " Invalid params";
+                   valid_index = false;
+                } else {
+                    indices[0]= data[1];
+                    valid_index = false;
+                    for (i=0,it = CallStatelist.begin(); it != CallStatelist.end(); it++, i++) {
+                        tCCS_CALL_STATE obj = it->second;
+                        if (obj.index ==  indices[0] &&
+                            (obj.state == CCS_STATE_INCOMING || obj.state == CCS_STATE_ACTIVE)) {
+                            LOG(INFO) << __func__ << " call index match: " << indices[0];
+                            valid_index = true;
+                            break;
+                        }
+                    }
+                }
+                if (valid_index == true) {
+                     cc_instance->CallControlPointChange(CallControllerOps.operation, indices,
+                                                         DEFAULT_INDICIES_COUNT, NULL,
+                                                         device->peer_bda);
+
+                } else {
+                     LOG(INFO) << " CALL_LOCAL_HOLD ignored as there is no valid Index";
+                     cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                      CCS_DEFAULT_INDEX_VAL,
+                                                      CCS_INVALID_INDEX, peer_bda);
+                }
+                break;
+            }
+            case CALL_LOCAL_RETRIEVE: {
+                if (len != 2) {
+                    LOG(ERROR) << __func__ << " Invalid params";
+                    valid_index = false;
+                } else {
+                    indices[0]= data[1];
+                    valid_index = false;
+                    for (i=0,it = CallStatelist.begin(); it != CallStatelist.end(); it++, i++) {
+                        tCCS_CALL_STATE obj = it->second;
+                        if (obj.index ==  indices[0] && (obj.state == CCS_STATE_LOCAL_HELD )) {
+                            LOG(INFO) << __func__ << " call index match: " << indices[0];
+                            valid_index = true;
+                            break;
+                        }
+                    }
+                }
+                if (valid_index == true) {
+                    cc_instance->CallControlPointChange( CallControllerOps.operation, indices,
+                                                      DEFAULT_INDICIES_COUNT, NULL,
+                                                      device->peer_bda);
+
+                } else {
+                    LOG(INFO) << " CALL_LOCAL_RETRIEVE ignored as there is no valid Index";
+                    cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                    CCS_DEFAULT_INDEX_VAL,
+                                                    CCS_INVALID_INDEX, peer_bda);
+                }
+                break;
+            }
+            case CALL_ORIGINATE: {
+                  LOG(INFO) << __func__ << " CALL_ORIGINATE match:";
+                  memset(URI, '\0', sizeof(char)*MAX_URI_SIZE);
+                  strlcpy(URI, (char*)&data[1], len);
+                  LOG(INFO) << __func__ << " ORIGINATE: " << URI;
+                  if (IsValidOriginateUri(URI)) {
+                      cc_instance->CallControlPointChange(CallControllerOps.operation, /*unused*/0,
+                                                       /*count*/0, URI, device->peer_bda);
+                  } else {
+                    cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                   CCS_DEFAULT_INDEX_VAL,
+                                                   CCS_INVALID_OUTGOING_URI, peer_bda);
+                  }
+                  break;
+               }
+            case CALL_JOIN: {
+                  int count = len-1;
+                  uint8_t *start_of_indicies = &(data[1]);
+                  uint8_t indices[MAX_CCS_CONNECTION];
+                  valid_index = true;
+                  bool atleastOneHeldCall = false;
+                  for (int i=0; i<count; i++) {
+                      indices[i] = start_of_indicies[i];
+                      LOG(INFO) << __func__ << " Indicies: " << i << ": " <<indices[i];
+                      std::map<uint8_t, tCCS_CALL_STATE>::iterator j = CallStatelist.find(indices[i]);
+                      if (j == CallStatelist.end()) {
+                        valid_index = false;
+                        break;
+                      } else if (j->second.state == CCS_STATE_LOCAL_HELD) {
+                         atleastOneHeldCall = true;
+                      }
+                  }
+                  if (CallStatelist.size() < 2 || atleastOneHeldCall == false) {
+                      LOG(INFO) << __func__ << " Join is not valid: ";
+                      valid_index = false;
+                  }
+                  if (count > 1 && valid_index == true) {
+                     cc_instance->CallControlPointChange(CallControllerOps.operation,
+                                                        indices, count,
+                                                        NULL, device->peer_bda);
+                  } else {
+                     LOG(INFO) << __func__ << " no valid indices found for JOIN operation ";
+                     cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                     CCS_DEFAULT_INDEX_VAL,
+                                                     CCS_INVALID_INDEX, peer_bda);
+                  }
+                  break;
+                }
+            default:
+                LOG(ERROR) << __func__ << " Unhandled Event: " << CallControllerOps.operation;
+                cc_instance->CallControlResponse(CallControllerOps.operation,
+                                                CCS_DEFAULT_INDEX_VAL,
+                                                CCS_OPCODE_NOT_SUPPORTED, peer_bda);
+            }
+        } else if (handle == ccsControlServiceInfo.bearer_signal_strength_report_interval_handle) {
+            if (len != 1) {
+                LOG(ERROR) << " Invalid input for SSR interval";
+                return BT_STATUS_SUCCESS;
+            }
+            LOG(INFO) << __func__ << " signal strength repo Interval: " << data[0];
+            BearerProviderInfo.signal_report_interval = data[0];
+            cc_instance->remoteDevices.UpdateSSReportingInterval(device->peer_bda, BearerProviderInfo.signal_report_interval);
+        }
+        break;
+    }
+
+    case CCS_CONGESTION_UPDATE: {
+      LOG(INFO) << __func__ << ": device CCS_CONGESTION_UPDATE update: " << event;
+      CcpCongestionUpdate(p_data);
+      break;
+    }
+
+    case CCS_READ_RSP:
+    case CCS_DESCRIPTOR_READ_RSP:
+    case CCS_DESCRIPTOR_WRITE_RSP:
+    case CCS_CONNECTION_UPDATE:
+    case CCS_PHY_UPDATE:
+    case CCS_CONNECTION:
+      LOG(INFO) << __func__ << " calling device state " << device->state;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data);
+      break;
+
+      default:
+      LOG(INFO) << __func__ << " event is not in list";
+      break;
+    }
+
+  return BT_STATUS_SUCCESS;
+
+}
+
+void CcpCongestionUpdate(tcc_resp_t* p_data) {
+  p_data->remoteDevice->congested = p_data->oper.CongestionOp.congested;
+  LOG(INFO) << __func__ << ": conn_id: " << p_data->remoteDevice->conn_id
+                        << ", congested: " << p_data->remoteDevice->congested;
+
+  GattsOpsQueue::CongestionCallback(p_data->remoteDevice->conn_id,
+                                    p_data->remoteDevice->congested);
+}
diff --git a/le_audio/system/bt/bta/csip/bta_csip_act.cc b/le_audio/system/bt/bta/csip/bta_csip_act.cc
new file mode 100644
index 0000000..ff83083
--- /dev/null
+++ b/le_audio/system/bt/bta/csip/bta_csip_act.cc
@@ -0,0 +1,1686 @@
+/******************************************************************************
+
+Copyright (c) 2020, The Linux Foundation. All rights reserved.
+*
+*****************************************************************************/
+
+/******************************************************************************
+*
+
+* Copyright 2009-2013 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.
+*
+******************************************************************************/
+
+/******************************************************************************
+ *
+ *  This file contains the CSIP Client action functions.
+ *
+ ******************************************************************************/
+
+#include <log/log.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <vector>
+#include <string>
+
+#include "bta_csip_int.h"
+#include "bta_csip_api.h"
+#include "bta_gatt_api.h"
+#include "bta_gatt_queue.h"
+#include "btm_api.h"
+#include "btm_ble_api.h"
+#include "btm_int.h"
+#include "osi/include/osi.h"
+#include "osi/include/properties.h"
+#include "bta_dm_api.h"
+#include "bta_dm_adv_audio.h"
+
+/* CSIS Service UUID */
+Uuid CSIS_SERVICE_UUID = Uuid::FromString("1846");
+/* CSIS Characteristic UUID's */
+Uuid CSIS_SERVICE_SIRK_UUID = Uuid::FromString("2B84");
+Uuid CSIS_SERVICE_SIZE_UUID = Uuid::FromString("2B85");
+Uuid CSIS_SERVICE_LOCK_UUID = Uuid::FromString("2B86");
+Uuid CSIS_SERVICE_RANK_UUID = Uuid::FromString("2B87");
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_api_enable
+ *
+ * Description      This function completes tasks to be done on BT ON
+ *
+ * Parameters:      p_cback - callbacks from btif layer
+ *
+ ******************************************************************************/
+void bta_csip_api_enable(tBTA_CSIP_CBACK *p_cback) {
+   APPL_TRACE_DEBUG("%s", __func__);
+
+  bta_csip_cb = tBTA_CSIP_CB();
+  bta_csip_cb.p_cback = p_cback;
+
+  // register with GATT CLient interface
+  bta_csip_gattc_register();
+
+  bta_csip_load_coordinated_sets_from_storage();
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_api_disable
+ *
+ * Description      This function completes tasks to be done on BT OFF
+ *
+ * Parameters:      None
+ *
+ ******************************************************************************/
+void bta_csip_api_disable() {
+  std::vector<tBTA_CSIP_DEV_CB> &dev_cb = bta_csip_cb.dev_cb;
+
+  /* close all active GATT Connections */
+  for (tBTA_CSIP_DEV_CB& p_cb: dev_cb) {
+    if (p_cb.state == BTA_CSIP_CONN_ST) {
+      BTA_GATTC_Close(p_cb.conn_id);
+    }
+  }
+
+  /* Deregister GATT Interface */
+  BTA_GATTC_AppDeregister(bta_csip_cb.gatt_if);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_app_register
+ *
+ * Description      API used to register App/Module for CSIP callbacks.
+ *                  operation
+ *
+ * Parameters:      app_uuid - Application UUID.
+ *                  p_cback - Application callbacks.
+ *                  cb - callback after registration.
+ ******************************************************************************/
+void bta_csip_app_register (const Uuid& app_uuid, tBTA_CSIP_CBACK* p_cback,
+                          BtaCsipAppRegisteredCb cb) {
+  uint8_t i;
+  tBTA_CSIP_STATUS status = BTA_CSIP_FAILURE;
+
+  for (i = 0; i < BTA_CSIP_MAX_SUPPORTED_APPS; i++) {
+    if (!bta_csip_cb.app_rcb[i].in_use) {
+      bta_csip_cb.app_rcb[i].in_use = true;
+      bta_csip_cb.app_rcb[i].app_id = i;
+      bta_csip_cb.app_rcb[i].p_cback = p_cback;
+      status = BTA_CSIP_SUCCESS;
+      break;
+    }
+  }
+
+  if (status == BTA_CSIP_SUCCESS) {
+    LOG(INFO) << "CSIP App Registered Succesfully. App ID: " << +i;
+  } else {
+    LOG(ERROR) << "CSIP App Registration failed. App Limit reached";
+  }
+
+  // Give callback to registering App/Module
+  if (!cb.is_null()) cb.Run(status, i);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_app_unregister
+ *
+ * Description      API used to unregister App/Module for CSIP callbacks.
+ *
+ * Parameters:      app_id: ID of the application to be unregistered.
+ *
+ ******************************************************************************/
+void bta_csip_app_unregister(uint8_t app_id) {
+  if (app_id >= BTA_CSIP_MAX_SUPPORTED_APPS) {
+    LOG(ERROR) << __func__ << " Invalid App ID: " << +app_id;
+    return;
+  }
+
+  bta_csip_cb.app_rcb[app_id].in_use = false;
+  bta_csip_cb.app_rcb[app_id].p_cback = NULL;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_gattc_callback
+ *
+ * Description      This is GATT client callback function used in BTA CSIP.
+ *
+ * Parameters:      event  - received from GATT
+ *                  p_data - data associated with the event
+ *
+ ******************************************************************************/
+static void bta_csip_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
+  tBTA_CSIP_DEV_CB* p_dev_cb;
+
+  APPL_TRACE_DEBUG("bta_csip_gattc_callback event = %d", event);
+
+  if (p_data == NULL) return;
+
+  switch (event) {
+    case BTA_GATTC_OPEN_EVT:
+      p_dev_cb = bta_csip_find_dev_cb_by_bda(p_data->open.remote_bda);
+      if (p_dev_cb) {
+        bta_csip_sm_execute(p_dev_cb, BTA_CSIP_GATT_OPEN_EVT,
+                          (tBTA_CSIP_REQ_DATA*)&p_data->open);
+      }
+      break;
+
+    case BTA_GATTC_CLOSE_EVT:
+      p_dev_cb = bta_csip_find_dev_cb_by_bda(p_data->close.remote_bda);
+      if (p_dev_cb) {
+        APPL_TRACE_DEBUG("BTA_GATTC_CLOSE_EVT state = %d", p_dev_cb->state);
+        bta_csip_sm_execute(p_dev_cb, BTA_CSIP_GATT_CLOSE_EVT,
+                          (tBTA_CSIP_REQ_DATA*)&p_data->close);
+      }
+      break;
+
+    case BTA_GATTC_SEARCH_CMPL_EVT: {
+        tBTA_GATTC_SEARCH_CMPL* p_srch_data = &p_data->search_cmpl;
+        tBTA_CSIP_DISC_SET disc_params = {.conn_id = p_srch_data->conn_id,
+                                          .status = p_srch_data->status
+                                         };
+        p_dev_cb = bta_csip_get_dev_cb_by_cid(p_srch_data->conn_id);
+        if (p_dev_cb) {
+          disc_params.addr = p_dev_cb->addr;
+          p_dev_cb->is_disc_external = true;
+        }
+        bta_csip_gatt_disc_cmpl_act(&disc_params);
+        bta_csip_sm_execute(p_dev_cb, BTA_CSIP_OPEN_CMPL_EVT, NULL);
+      }
+      break;
+
+    case BTA_GATTC_NOTIF_EVT: {
+        bta_csip_handle_notification(&p_data->notify);
+      }
+      break;
+
+    default:
+      break;
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_gattc_register
+ *
+ * Description      API used to register GATT interface for CSIP operations.
+ *
+ * Parameters:      None
+ *
+ ******************************************************************************/
+void bta_csip_gattc_register() {
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  BTA_GATTC_AppRegister(bta_csip_gattc_callback,
+                      base::Bind([](uint8_t client_id, uint8_t status) {
+                        tBTA_CSIP_STATUS csip_status = BTA_CSIP_FAILURE;
+                        if (status == GATT_SUCCESS) {
+                          bta_csip_cb.gatt_if = client_id;
+                          csip_status = BTA_CSIP_SUCCESS;
+                        } else {
+                          bta_csip_cb.gatt_if = BTA_GATTS_INVALID_IF;
+                        }
+
+                        /* BTA_GATTC_AppRegister is done */
+                        if (bta_csip_cb.p_cback) {
+                          LOG(INFO) << "CSIP GATT IF : "
+                                    << +bta_csip_cb.gatt_if;
+                        }
+                      }), true);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_process_set_lock_act
+ *
+ * Description      This function processes lock/unlock request.
+ *
+ * Parameters:      lock_param: params used in LOCK/UNLOCK request.
+ *
+ ******************************************************************************/
+void bta_csip_process_set_lock_act(tBTA_SET_LOCK_PARAMS lock_param) {
+  LOG(INFO) << __func__ << ": App ID = " << +lock_param.app_id
+                        << ", Set ID = " << +lock_param.set_id
+                        << ", Value = " << +lock_param.lock_value;
+
+  tBTA_CSET_CB* cset_cb = bta_csip_get_cset_cb_by_id (lock_param.set_id);
+  if (!cset_cb || !bta_csip_is_valid_lock_request(&lock_param)) {
+    tBTA_LOCK_STATUS_CHANGED res = {.app_id = lock_param.app_id,
+                                    .set_id = lock_param.set_id,
+                                    .status = INVALID_REQUEST_PARAMS};
+    bta_csip_send_lock_req_cmpl_cb(res);
+    return;
+  }
+
+  // Add request in the queue if one is already in progress for this set
+  if (cset_cb->request_in_progress) {
+    cset_cb->lock_req_queue.push(lock_param);
+    LOG(INFO) << __func__ << " pending lock requests in queue for Set:"
+                          << +lock_param.set_id
+                          << " Pending Requests = " << +(int)cset_cb->lock_req_queue.size();
+    return;
+  }
+
+  bta_csip_form_lock_request(lock_param, cset_cb);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_process_set_lock_act
+ *
+ * Description      This function forms request (LOCK/UNLOCK and order of the
+ *                  set members).
+ *
+ * Parameters:      lock_param: params used in LOCK/UNLOCK request.
+ *                  cset_cb: current set control block.
+ *
+ ******************************************************************************/
+void bta_csip_form_lock_request(tBTA_SET_LOCK_PARAMS lock_param,
+                                       tBTA_CSET_CB* cset_cb) {
+  cset_cb->request_in_progress = true;
+
+  std::vector<RawAddress> ordered_members;
+
+  if (lock_param.lock_value == LOCK_VALUE) {
+    ordered_members = bta_csip_arrange_set_members_by_order(cset_cb->set_id,
+                      lock_param.members_addr, true);
+  } else {
+    ordered_members = bta_csip_arrange_set_members_by_order(cset_cb->set_id,
+                      lock_param.members_addr, false);
+  }
+
+  //debug log
+  for (int i = 0; i < (int)ordered_members.size(); i++) {
+    APPL_TRACE_DEBUG("%s: Member %d = %s", __func__, (i+1),
+                     ordered_members[i].ToString().c_str());
+  }
+
+  // update current request in CB
+  cset_cb->cur_lock_req = {lock_param.app_id, lock_param.set_id, lock_param.lock_value,
+                           0, ordered_members};
+
+  // update current response in CB
+  cset_cb->cur_lock_res = {};
+  cset_cb->cur_lock_res.app_id = lock_param.app_id;
+  cset_cb->cur_lock_res.set_id = lock_param.set_id;
+  cset_cb->cur_lock_res.value = UNLOCK_VALUE;
+
+  /* LOCK Request */
+  if (cset_cb->cur_lock_req.value == LOCK_VALUE) {
+    cset_cb->cur_lock_res.status = ALL_LOCKS_ACQUIRED;
+    /* check if lock request for this set was denied earlier */
+    if (bta_csip_validate_req_for_denied_sm(cset_cb)) {
+      bta_csip_get_next_lock_request(cset_cb);
+
+    /* proceed with request otherwise*/
+    } else {
+      bta_csip_send_lock_req_act(cset_cb);
+    }
+  /* UNLOCK Request*/
+  } else {
+    bta_csip_send_unlock_req_act(cset_cb);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_validate_req_for_denied_sm
+ *
+ * Description      This function validates request if received for denied set
+ *                  member
+ *
+ * Parameters:      cset_cb: current set control block.
+ *
+ ******************************************************************************/
+bool bta_csip_validate_req_for_denied_sm (tBTA_CSET_CB* cset_cb) {
+  bool is_denied = false;
+  tBTA_LOCK_REQUEST& lock_req = cset_cb->cur_lock_req;
+
+  for (RawAddress& addr: lock_req.members_addr) {
+    tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(addr);
+    if (!p_cb) {
+      APPL_TRACE_ERROR("%s: Device CB not found for %s", __func__,
+                       addr.ToString().c_str());
+      continue;
+    }
+
+    tBTA_CSIS_SRVC_INFO* srvc = bta_csip_get_csis_instance(p_cb, lock_req.set_id);
+    if (!srvc) {
+      APPL_TRACE_ERROR("%s: CSIS instance not found for %s", __func__,
+                       addr.ToString().c_str());
+      continue;
+    }
+
+    if (!srvc->denied_applist.empty()) {
+      is_denied = true;
+      // add this app_id in the denied_app_list
+      srvc->denied_applist.push_back(lock_req.app_id);
+      cset_cb->cur_lock_res.status = LOCK_DENIED;
+      cset_cb->cur_lock_res.addr.push_back(addr);
+    }
+  }
+
+  if (is_denied) {
+    bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res);
+  }
+
+  return is_denied;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_lock_release_by_denial_cb
+ *
+ * Description      This callback function is called when set member is unlocked
+ *                  after unlock request is sent post lock denial.
+ *
+ * Parameters:      GATT operation callback params.
+ *
+ ******************************************************************************/
+void bta_csip_lock_release_by_denial_cb(uint16_t conn_id, tGATT_STATUS status,
+                                                  uint16_t handle, void* data) {
+  tBTA_CSET_CB* cset_cb = (tBTA_CSET_CB *)data;
+  tBTA_CSIP_DEV_CB* dev_cb = cset_cb->cur_dev_cb;
+  tBTA_CSIS_SRVC_INFO* srvc =
+          bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id);
+
+  LOG(INFO) << __func__ << " Released lock for device: " << dev_cb->addr
+                        << ", Set ID: " << +cset_cb->set_id;
+
+  if (srvc && status == GATT_SUCCESS) {
+    srvc->lock = UNLOCK_VALUE;
+    // remove app id from applist
+    srvc->lock_applist.erase(std::remove(srvc->lock_applist.begin(), srvc->lock_applist.end(),
+        cset_cb->cur_lock_req.app_id), srvc->lock_applist.end());
+  }
+
+  // release next set member with lower rank
+  bta_csip_handle_lock_denial(cset_cb);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_handle_lock_denial
+ *
+ * Description      This function is called when lock has been denied by one of
+ *                  the set members.
+ *
+ * Parameters:      cset_cb: current set control block.
+ *
+ ******************************************************************************/
+void bta_csip_handle_lock_denial(tBTA_CSET_CB* cset_cb) {
+    // start lock release procedure for acquired locks
+    int8_t cur_idx = cset_cb->cur_lock_req.cur_idx - 1;
+    cset_cb->cur_lock_req.cur_idx--;
+
+    if (cur_idx >= 0) {
+      RawAddress bd_addr = cset_cb->cur_lock_req.members_addr[cur_idx];
+      cset_cb->cur_dev_cb = bta_csip_find_dev_cb_by_bda(bd_addr);
+      tBTA_CSIS_SRVC_INFO* srvc =
+          bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id);
+
+      // check if locked by other app
+      if (!cset_cb->cur_dev_cb || !srvc ||
+          bta_csip_is_locked_by_other_apps(srvc, cset_cb->cur_lock_req.app_id)) {
+        LOG(INFO) << "Invalid device or service CB or"
+                  << " other apps have locked this set member(" << bd_addr << "). Skip.";
+        bta_csip_handle_lock_denial(cset_cb);
+        return;
+      }
+
+      // Lock value in vector format (one uint8_t size element with )
+      std::vector<uint8_t> unlock_value(1, UNLOCK_VALUE);
+      BtaGattQueue::WriteCharacteristic(cset_cb->cur_dev_cb->conn_id,
+      srvc->lock_handle, unlock_value, GATT_WRITE, bta_csip_lock_release_by_denial_cb, cset_cb);
+
+    } else {
+      bta_csip_get_next_lock_request(cset_cb);
+    }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_lock_req_cb
+ *
+ * Description      This callback function is called when set member is locked
+ *                  by remote device after LOCK Request
+ *
+ * Parameters:      GATT operation callback params.
+ *
+ ******************************************************************************/
+void bta_csip_lock_req_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
+                          void* data) {
+  LOG(INFO) << __func__ << " status = " << +status;
+  tBTA_CSET_CB* cset_cb = (tBTA_CSET_CB *)data;
+  tBTA_CSIP_DEV_CB* dev_cb = bta_csip_get_dev_cb_by_cid(conn_id);
+  tBTA_CSIS_SRVC_INFO* srvc =
+      bta_csip_get_csis_instance(dev_cb, cset_cb->set_id);
+
+  /* Device control block or corresponding CSIS service instance not found */
+  if (!dev_cb || !srvc) {
+    APPL_TRACE_ERROR("%s: Device CB not found for conn_id = %d", __func__, conn_id);
+    cset_cb->cur_lock_req.cur_idx++;
+    alarm_cancel(cset_cb->unresp_timer);
+    bta_csip_send_lock_req_act(cset_cb);
+    return;
+  }
+
+  /*check if this response is received from unresponsive set member */
+  if (dev_cb->unresponsive) {
+    LOG(INFO) << __func__ << " unresponsive remote: " << dev_cb->addr;
+    bta_csip_handle_unresponsive_sm_res(srvc, status);
+    dev_cb->unresponsive = false;
+    return;
+  }
+  // cancel alarm (used for unresponsive set member)
+  alarm_cancel(cset_cb->unresp_timer);
+
+  if (status == CSIP_LOCK_DENIED) {
+      LOG(INFO) << __func__ << " Locked Denied by " << dev_cb->addr;
+      srvc->lock = UNLOCK_VALUE;
+      cset_cb->cur_lock_res.value = UNLOCK_VALUE;
+      cset_cb->cur_lock_res.status = LOCK_DENIED;
+
+      // add member to the response list for which lock is denied and clear others
+      cset_cb->cur_lock_res.addr.clear();
+      cset_cb->cur_lock_res.addr.push_back(dev_cb->addr);
+
+      // add app_id in the denied applist
+      srvc->denied_applist.push_back(cset_cb->cur_lock_req.app_id);
+      // Give callback to upper layer that lock is denied
+      bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res);
+      // release all the acquired locks till now
+      bta_csip_handle_lock_denial(cset_cb);
+
+  /* PTS: Remote responding with invalid value */
+  } else if (status == CSIP_INVALID_LOCK_VALUE) {
+    LOG(ERROR) << __func__ << " remote " << dev_cb->addr
+                          << " responded with INVALID Value";
+    /* for PTS to ensure set coordinator is working fine */
+    BtaGattQueue::ReadCharacteristic(cset_cb->cur_dev_cb->conn_id,
+        srvc->lock_handle, NULL, NULL);
+
+    /* Stop locking remaining set members and inform requesting app */
+    bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res);
+    /* Process next lock request from pending queue */
+    bta_csip_get_next_lock_request(cset_cb);
+  } else {
+    if (status == GATT_SUCCESS || status == CSIP_LOCK_ALREADY_GRANTED) {
+      LOG(INFO) << __func__ << " successfully locked " << dev_cb->addr;
+      cset_cb->cur_lock_res.addr.push_back(dev_cb->addr);
+      cset_cb->cur_lock_res.value = LOCK_VALUE;
+      srvc->lock = LOCK_VALUE;
+      // add app_id against this device entry
+      srvc->lock_applist.push_back(cset_cb->cur_lock_req.app_id);
+    }
+
+    //proceed with next set member
+    cset_cb->cur_lock_req.cur_idx++;
+    bta_csip_send_lock_req_act(cset_cb);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_send_lock_req_act
+ *
+ * Description      This function is is used to send LOCK request to set member.
+ *                  It validates if it is required to send lock request based on
+ *                  connection state and current lock value.
+ *
+ * Parameters:      cset_cb: current set control block.
+ *
+ ******************************************************************************/
+void bta_csip_send_lock_req_act(tBTA_CSET_CB* cset_cb) {
+  RawAddress bd_addr;
+  uint8_t cur_index = cset_cb->cur_lock_req.cur_idx;
+
+  if (cur_index == (uint8_t)cset_cb->cur_lock_req.members_addr.size()) {
+    LOG(INFO) << __func__ << " lock operation completed for all set members";
+    bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res);
+    bta_csip_get_next_lock_request(cset_cb);
+    return;
+  }
+
+  bd_addr = cset_cb->cur_lock_req.members_addr[cur_index];
+
+  // get device control block and corresponding csis service details
+  cset_cb->cur_dev_cb = bta_csip_find_dev_cb_by_bda(bd_addr);
+  tBTA_CSIS_SRVC_INFO* srvc =
+      bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id);
+
+  // Skip device if it is not in connected state
+  if (!cset_cb->cur_dev_cb || !srvc ||
+      cset_cb->cur_dev_cb->state != BTA_CSIP_CONN_ST) {
+    LOG(INFO) << __func__ << ": Set Member (" << bd_addr.ToString()
+              << ") is not connected. Skip this Set member";
+
+    cset_cb->cur_lock_req.cur_idx++;
+    cset_cb->cur_lock_res.status = SOME_LOCKS_ACQUIRED_REASON_DISC;
+    bta_csip_send_lock_req_act(cset_cb);
+
+  // check if already locked (skip sending write request)
+  } else if (srvc->lock == LOCK_VALUE) {
+    LOG(INFO) << __func__ << ": Set Member (" << cset_cb->cur_dev_cb->addr
+              << ") is already locked. Skip this Set member";
+    cset_cb->cur_lock_res.value = LOCK_VALUE;
+    // add element in the list
+    cset_cb->cur_lock_res.addr.push_back(bd_addr);
+    // add appid in the list if locked by different app
+    if (!bta_csip_is_member_locked_by_app(cset_cb->cur_lock_req.app_id, srvc)) {
+      srvc->lock_applist.push_back(cset_cb->cur_lock_req.app_id);
+    }
+    cset_cb->cur_lock_req.cur_idx++;
+    // process next set member
+    bta_csip_send_lock_req_act(cset_cb);
+
+  // send the lock request
+  } else {
+    // Lock value in vector format (one uint8_t size element with )
+    LOG(INFO) << __func__ << " Sending Lock Request to "<< cset_cb->cur_dev_cb->addr
+                          << " Conn Id: " << +cset_cb->cur_dev_cb->conn_id;
+    std::vector<uint8_t> lock_value = {2};
+    BtaGattQueue::WriteCharacteristic(cset_cb->cur_dev_cb->conn_id,
+        srvc->lock_handle, lock_value, GATT_WRITE, bta_csip_lock_req_cb, cset_cb);
+
+    // Start set member request timeout alarm
+    cset_cb->unresp_timer = alarm_new("csip_unresp_sm_timer");
+    alarm_set_on_mloop(cset_cb->unresp_timer, cset_cb->set_member_tout,
+                       bta_csip_set_member_lock_timeout, cset_cb);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_unlock_req_cb
+ *
+ * Description      This callback function is called when set member is unlocked
+ *                  by remote device after UNLOCK Request
+ *
+ * Parameters:      GATT operation callback params.
+ *
+ ******************************************************************************/
+void bta_csip_unlock_req_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
+                                void* data) {
+  LOG(INFO) << __func__ << " status = " << +status;
+  tBTA_CSET_CB* cset_cb = (tBTA_CSET_CB *)data;
+  tBTA_CSIP_DEV_CB* dev_cb = bta_csip_get_dev_cb_by_cid(conn_id);
+  tBTA_CSIS_SRVC_INFO* srvc =
+      bta_csip_get_csis_instance(dev_cb, cset_cb->cur_lock_req.set_id);
+
+  /* Device control block or corresponding CSIS service instance not found */
+  if (!dev_cb || !srvc) {
+    APPL_TRACE_ERROR("%s: Device CB not found for conn_id = %d", __func__, conn_id);
+    cset_cb->cur_lock_req.cur_idx++;
+    alarm_cancel(cset_cb->unresp_timer);
+    bta_csip_send_unlock_req_act(cset_cb);
+    return;
+  }
+
+  /*check if this response is received from unresponsive set member */
+  if (dev_cb->unresponsive) {
+    LOG(INFO) << __func__ << " unresponsive remote: " << dev_cb->addr;
+    srvc->lock = UNLOCK_VALUE;
+    srvc->unrsp_applist.clear();
+    dev_cb->unresponsive = false;
+    return;
+  }
+
+  // cancel alarm (used for unresponsive set member)
+  alarm_cancel(cset_cb->unresp_timer);
+
+  /* PTS Test Case: read any characteristic */
+  if (status == CSIP_LOCK_RELEASE_NOT_ALLOWED ||
+      status == CSIP_INVALID_LOCK_VALUE) {
+    /* for PTS to ensure set coordinator is working fine */
+    BtaGattQueue::ReadCharacteristic(cset_cb->cur_dev_cb->conn_id,
+        srvc->lock_handle, NULL, NULL);
+
+    /* Stop unlocking remaining set members and inform requesting app */
+    cset_cb->cur_lock_res.status = status;
+    bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res);
+    /* Process next lock request from pending queue */
+    bta_csip_get_next_lock_request(cset_cb);
+  } else {
+    if (status == GATT_SUCCESS) {
+      srvc->lock = UNLOCK_VALUE;
+      // remove app id from applist
+      srvc->lock_applist.erase(std::remove(srvc->lock_applist.begin(),
+          srvc->lock_applist.end(), cset_cb->cur_lock_req.app_id),
+          srvc->lock_applist.end());
+      cset_cb->cur_lock_res.addr.push_back(dev_cb->addr);
+    }
+    //proceed with next set member
+    cset_cb->cur_lock_req.cur_idx++;
+    bta_csip_send_unlock_req_act(cset_cb);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_send_unlock_req_act
+ *
+ * Description      This function is is used to send UNLOCK request to set member.
+ *                  It validates if it is required to send unlock request based on
+ *                  connection state and current lock value.
+ *
+ * Parameters:      cset_cb: current set control block.
+ *
+ ******************************************************************************/
+void bta_csip_send_unlock_req_act(tBTA_CSET_CB* cset_cb) {
+  RawAddress bd_addr;
+  uint8_t cur_index = cset_cb->cur_lock_req.cur_idx;
+
+  if (cur_index == (uint8_t)cset_cb->cur_lock_req.members_addr.size()) {
+    cset_cb->request_in_progress = false;
+    bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res);
+    bta_csip_get_next_lock_request(cset_cb);
+    LOG(INFO) << __func__ << " Request completed for all set members";
+    return;
+  }
+
+  bd_addr = cset_cb->cur_lock_req.members_addr[cur_index];
+
+  LOG(INFO) << __func__ << ": Set Member address: " << bd_addr.ToString();
+
+  // get device control block and corresponding csis service details
+  cset_cb->cur_dev_cb = bta_csip_find_dev_cb_by_bda(bd_addr);
+  tBTA_CSIS_SRVC_INFO* srvc =
+      bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id);
+
+  /* Device control block or corresponding CSIS service instance not found */
+  if (!cset_cb->cur_dev_cb || !srvc) {
+    APPL_TRACE_ERROR("%s: Device CB not found for %s", __func__,
+                       bd_addr.ToString().c_str());
+    cset_cb->cur_lock_req.cur_idx++;
+    bta_csip_send_unlock_req_act(cset_cb);
+    return;
+  }
+
+  /* Set member is not locked by requesting app */
+  if (!bta_csip_is_member_locked_by_app(cset_cb->cur_lock_req.app_id, srvc)) {
+    LOG(INFO) << __func__ << " App "<< +cset_cb->cur_lock_req.app_id
+                        << "has not locked this set member (" << srvc->bd_addr
+                        << "). Skip this set member";
+    cset_cb->cur_lock_req.cur_idx++;
+    bta_csip_send_unlock_req_act(cset_cb);
+    return;
+  }
+
+  // Skip device if it is not in connected state
+  if (cset_cb->cur_dev_cb->state != BTA_CSIP_CONN_ST) {
+    LOG(INFO) << __func__ << ": Set Member (" << bd_addr.ToString()
+              << ") is not connected. Skip this Set member";
+
+    cset_cb->cur_lock_req.cur_idx++;
+    // remove app id from applist
+    srvc->lock_applist.erase(std::remove(srvc->lock_applist.begin(), srvc->lock_applist.end(),
+        cset_cb->cur_lock_req.app_id), srvc->lock_applist.end());
+    bta_csip_send_unlock_req_act(cset_cb);
+
+  // check if already unlocked or locked by multiple apps (skip sending write request)
+  } else if (srvc->lock == UNLOCK_VALUE ||
+             bta_csip_is_locked_by_other_apps(srvc, cset_cb->cur_lock_req.app_id)) {
+    LOG(INFO) << __func__ << ": Set Member (" << bd_addr.ToString()
+              << ") is already unlocked or locked by other app. Skip this Set member";
+    // remove app id from applist
+    srvc->lock_applist.erase(std::remove(srvc->lock_applist.begin(), srvc->lock_applist.end(),
+        cset_cb->cur_lock_req.app_id), srvc->lock_applist.end());
+    cset_cb->cur_lock_req.cur_idx++;
+    cset_cb->cur_lock_res.addr.push_back(cset_cb->cur_dev_cb->addr);
+    // process next set member
+    bta_csip_send_unlock_req_act(cset_cb);
+
+  // send the unlock request
+  } else {
+    // Unlock value in vector format (one uint8_t size element with unlock value)
+    std::vector<uint8_t> lock_value(1, UNLOCK_VALUE);
+    BtaGattQueue::WriteCharacteristic(cset_cb->cur_dev_cb->conn_id,
+        srvc->lock_handle, lock_value, GATT_WRITE, bta_csip_unlock_req_cb, cset_cb);
+
+    // Start set member request timeout alarm
+    cset_cb->unresp_timer = alarm_new("csip_unresp_sm_timer");
+    alarm_set_on_mloop(cset_cb->unresp_timer, cset_cb->set_member_tout,
+                       bta_csip_set_member_lock_timeout, cset_cb);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_set_member_lock_timeout
+ *
+ * Description      This API is called when Set Member has not responded within
+ *                  required set member lock timeout.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_set_member_lock_timeout(void* p_data) {
+  tBTA_CSET_CB* cset_cb = (tBTA_CSET_CB *)p_data;
+  tBTA_CSIP_DEV_CB* dev_cb = cset_cb->cur_dev_cb;
+
+  APPL_TRACE_DEBUG("%s", __func__);
+  // Device not found or disconnected
+  if (!dev_cb || dev_cb->state != BTA_CSIP_CONN_ST) {
+    LOG(ERROR) << __func__ << " device disconnected.";
+    cset_cb->cur_lock_res.status = SOME_LOCKS_ACQUIRED_REASON_DISC;
+    cset_cb->cur_lock_req.cur_idx++;
+    bta_csip_send_lock_req_act(cset_cb);
+    return;
+  }
+
+  tBTA_CSIS_SRVC_INFO* srvc =
+      bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id);
+
+  if (!srvc) {
+    APPL_TRACE_ERROR("%s: CSIS instance not found.", __func__);
+    return;
+  }
+
+  dev_cb->unresponsive = true;
+  // add app_id in unresponsive set members app list
+  srvc->unrsp_applist.push_back(cset_cb->cur_lock_res.app_id);
+
+  if (cset_cb->cur_lock_req.value == LOCK_VALUE) {
+    cset_cb->cur_lock_res.status = SOME_LOCKS_ACQUIRED_REASON_TIMEOUT;
+    APPL_TRACE_DEBUG("%s: Process next device in the lock request", __func__);
+    cset_cb->cur_lock_req.cur_idx++;
+    bta_csip_send_lock_req_act(cset_cb);
+  } else if (cset_cb->cur_lock_req.value == UNLOCK_VALUE) {
+    cset_cb->cur_lock_res.addr.push_back(
+    cset_cb->cur_lock_req.members_addr[cset_cb->cur_lock_req.cur_idx]);
+    cset_cb->cur_lock_req.cur_idx++;
+    bta_csip_send_unlock_req_act(cset_cb);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_le_encrypt_cback
+ *
+ * Description      link encryption complete callback.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_le_encrypt_cback(const RawAddress* bd_addr,
+                             UNUSED_ATTR tGATT_TRANSPORT transport,
+                             UNUSED_ATTR void* p_ref_data, tBTM_STATUS result) {
+  APPL_TRACE_ERROR("%s: status = %d", __func__, result);
+  tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(*bd_addr);
+
+  if (!p_cb) {
+    APPL_TRACE_ERROR("unexpected encryption callback, ignore");
+    return;
+  }
+
+  /* If encryption fails, disconnect the connection */
+  if (result != BTM_SUCCESS) {
+    bta_csip_close_csip_conn(p_cb);
+    return;
+  }
+
+  if (p_cb->state == BTA_CSIP_W4_SEC) {
+    bta_csip_sm_execute(p_cb, BTA_CSIP_ENC_CMPL_EVT, NULL);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_open_act
+ *
+ * Description      API Call to open CSIP Gatt Connection
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_api_open_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) {
+  APPL_TRACE_DEBUG("%s: Open GATT connection for CSIP", __func__);
+
+  tBTA_CSIP_API_CONN* p_conn_req = (tBTA_CSIP_API_CONN *)&p_data->conn_param;
+
+  if (!bta_csip_is_app_reg(p_conn_req->app_id)) {
+    LOG(ERROR) << __func__ << ": Request from Invalid/Unregistered App: "
+                           << +p_conn_req->app_id;
+    if (p_cb && (uint8_t)p_cb->conn_applist.size() == 0) {
+      p_cb->state = BTA_CSIP_IDLE_ST;
+    }
+    // No need to send callback to invalid/unregistered app
+    return;
+  }
+
+  if (btm_sec_is_a_bonded_dev(p_cb->addr) && !bta_csip_is_csis_supported(p_cb)) {
+    APPL_TRACE_DEBUG("%s: Remote (%s) doesnt contain any coordinated set", __func__,
+                     p_cb->addr.ToString().c_str());
+    bta_csip_send_conn_state_changed_cb(p_cb, p_conn_req->app_id,
+        BTA_CSIP_DISCONNECTED, BTA_CSIP_COORDINATED_SET_NOT_SUPPORTED);
+    return;
+  }
+
+  if (!p_cb) {
+    LOG(ERROR) << __func__ << ": Insufficient resources. Max"
+                              " supported Set members have reached ";
+    tBTA_CSIP_DEV_CB invalid_cb = {
+        .addr = p_data->conn_param.bd_addr
+    };
+    bta_csip_send_conn_state_changed_cb(&invalid_cb, p_conn_req->app_id,
+        BTA_CSIP_DISCONNECTED, BTA_CSIP_CONN_ESTABLISHMENT_FAILED);
+    return;
+  }
+
+  // check if connection state is already connected
+  if (p_cb->state == BTA_CSIP_CONN_ST) {
+    if (!bta_csip_is_app_from_applist(p_cb, p_conn_req->app_id)) {
+      bta_csip_add_app_to_applist(p_cb, p_conn_req->app_id);
+    }
+    bta_csip_send_conn_state_changed_cb(p_cb, p_conn_req->app_id,
+        BTA_CSIP_CONNECTED, BTA_CSIP_CONN_ESTABLISHED);
+    return;
+
+  // other app has already started connection procedure
+  } else if (!bta_csip_is_app_from_applist(p_cb, p_conn_req->app_id)
+                && (uint8_t)p_cb->conn_applist.size() > 0
+                && p_cb->state != BTA_CSIP_IDLE_ST) {
+    LOG(INFO) << __func__ << ": Other app is establishing CSIP Connection."
+                          << " Current connection state = " << +p_cb->state;
+    bta_csip_add_app_to_applist(p_cb, p_conn_req->app_id);
+    /* Note: Callback will given to all apps once connection procedure is completed */
+    return;
+  }
+
+  p_cb->addr = p_data->conn_param.bd_addr;
+  bta_csip_add_app_to_applist(p_cb, p_conn_req->app_id);
+
+  BTA_GATTC_Open(bta_csip_cb.gatt_if, p_cb->addr, true, GATT_TRANSPORT_LE,
+                 false);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_api_close_act
+ *
+ * Description      API Call to close CSIP Gatt Connection
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_api_close_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) {
+  if (!p_cb) {
+    LOG(ERROR) << __func__ << " Already Closed";
+    return;
+  }
+
+  tBTA_CSIP_API_CONN* p_req = (tBTA_CSIP_API_CONN *)&p_data->conn_param;
+
+  LOG(INFO) << __func__ << " Disconnect Request from App: " << +p_req->app_id;
+
+  if (!bta_csip_is_app_reg(p_req->app_id)) {
+    LOG(ERROR) << __func__ << ": Request from Invalid/Unregistered App: "
+                           << +p_req->app_id;
+    // No need to send callback to invalid/unregistered app
+    return;
+  } else if (!bta_csip_is_app_from_applist(p_cb, p_req->app_id)) {
+    LOG(ERROR) << __func__ << " App (ID:"<< +p_req->app_id <<") has not connected";
+    bta_csip_send_conn_state_changed_cb(p_cb, p_req->app_id,
+          BTA_CSIP_DISCONNECTED, BTA_CSIP_DISCONNECT_WITHOUT_CONNECT);
+    return;
+  }
+
+  // Check if its last disconnecting app
+  if ((uint8_t)p_cb->conn_applist.size() > 1) {
+    bta_csip_remove_app_from_conn_list(p_cb, p_req->app_id);
+    bta_csip_send_conn_state_changed_cb(p_cb, p_req->app_id,
+          BTA_CSIP_DISCONNECTED, BTA_CSIP_APP_DISCONNECTED);
+    return;
+  }
+
+  bta_csip_close_csip_conn(p_cb);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_gatt_open_act
+ *
+ * Description      Callback function when GATT Connection is created.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_gatt_open_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) {
+  tBTA_GATTC_OPEN* open_param = &p_data->gatt_open_param;
+  LOG(INFO) << __func__ << " Remote = " << open_param->remote_bda
+                        << " Status = " << open_param->status
+                        << " conn_id = " << open_param->conn_id;
+
+  if (open_param->status == GATT_SUCCESS) {
+    p_cb->in_use = true;
+    p_cb->conn_id = open_param->conn_id;
+    BtaGattQueue::Clean(p_cb->conn_id);
+    bta_csip_sm_execute(p_cb, BTA_CSIP_START_ENC_EVT, NULL);
+  } else {
+    /* open failure */
+    bta_csip_sm_execute(p_cb, BTA_CSIP_OPEN_FAIL_EVT, p_data);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_gatt_close_act
+ *
+ * Description      Callback function when GATT Connection is closed.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_gatt_close_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) {
+  tBTA_GATTC_OPEN* open_param = &p_data->gatt_open_param;
+
+  // Give callback to all apps from connection applist
+  bta_csip_send_conn_state_changed_cb(p_cb, BTA_CSIP_DISCONNECTED, open_param->status);
+
+  // Clear applist
+  p_cb->conn_applist.clear();
+
+  for (int i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE; i++) {
+    tBTA_CSIS_SRVC_INFO* srvc = &p_cb->csis_srvc[i];
+    if (srvc->in_use) {
+      srvc->lock = UNLOCK_VALUE;
+    }
+  }
+
+  p_cb->conn_id = 0;
+  p_cb->in_use = false;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_gatt_open_fail_act
+ *
+ * Description      Callback function when GATT Connection fails to be created.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_gatt_open_fail_act (tBTA_CSIP_DEV_CB* p_cb,
+                                  tBTA_CSIP_REQ_DATA* p_data) {
+  LOG(ERROR) << __func__ << " Failed to open GATT Connection";
+
+  tBTA_GATTC_OPEN* open_param = &p_data->gatt_open_param;
+
+  // Give callback to all apps from connection applist waiting for connection
+  bta_csip_send_conn_state_changed_cb(p_cb, BTA_CSIP_DISCONNECTED, open_param->status);
+
+  // Clear applist
+  p_cb->conn_applist.clear();
+  p_cb->in_use = false;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_open_cmpl_act
+ *
+ * Description      Tasks needed to be done when connection is established.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_open_cmpl_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) {
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  if (!p_cb) {
+    LOG(ERROR) << __func__ << " Invalid device contrl block";
+    return;
+  }
+
+  // Give callback to all apps from connection applist waiting for connection
+  bta_csip_send_conn_state_changed_cb(p_cb, BTA_CSIP_CONNECTED,
+                                      BTA_CSIP_CONN_ESTABLISHED);
+
+  /* Register for notification of required CSIS characteristic*/
+  int i = 0;
+  for (i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE; i++) {
+      tBTA_CSIS_SRVC_INFO* srvc = &p_cb->csis_srvc[i];
+    if (srvc->in_use) {
+      bta_csip_write_cccd(p_cb, srvc->lock_handle, srvc->lock_ccd_handle);
+      bta_csip_write_cccd(p_cb, srvc->size_handle, srvc->size_ccd_handle);
+      bta_csip_write_cccd(p_cb, srvc->sirk_handle, srvc->sirk_ccd_handle);
+    }
+  }
+
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_start_sec_act
+ *
+ * Description      Tasks needed to be done to check or establish CSIP required
+ *                  security.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_start_sec_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) {
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  uint8_t sec_flag = 0;
+
+  // Get security flags for the device
+  BTM_GetSecurityFlagsByTransport(p_cb->addr, &sec_flag, BT_TRANSPORT_LE);
+
+  // link is already encrypted, send encryption complete callback to csip
+  if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) {
+    LOG(INFO) << __func__ << " Already Encrypted";
+    bta_csip_sm_execute(p_cb, BTA_CSIP_ENC_CMPL_EVT, NULL);
+  }
+  // device is bonded but link is not encrypted. Start encryption
+  else if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) {
+    sec_flag = BTM_BLE_SEC_ENCRYPT;
+    BTM_SetEncryption(p_cb->addr, BTA_TRANSPORT_LE, bta_csip_le_encrypt_cback,
+                      NULL, sec_flag);
+  }
+  // unbonded device. Set MITM Encryption
+  else if (p_cb->sec_mask != BTA_SEC_NONE) {
+    sec_flag = BTM_BLE_SEC_ENCRYPT_MITM;
+    BTM_SetEncryption(p_cb->addr, BTA_TRANSPORT_LE, bta_csip_le_encrypt_cback,
+                      NULL, sec_flag);
+  }
+  // link is already encrypted
+  else {
+    bta_csip_sm_execute(p_cb, BTA_CSIP_ENC_CMPL_EVT, NULL);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_start_sec_act
+ *
+ * Description      Tasks needed to be done to check or establish CSIP required
+ *                  security.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_sec_cmpl_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) {
+  APPL_TRACE_DEBUG("%s p_cb->csis_srvc[0].in_use = %d, p_cb->csis_srvc[0].sirk_handle = %d",
+      __func__, p_cb->csis_srvc[0].in_use, p_cb->csis_srvc[0].sirk_handle);
+
+  if (!p_cb->csis_srvc[0].in_use || !p_cb->csis_srvc[0].sirk_handle) {
+    /* Service discovery is triggered from this path when csip connection is opened
+     * from 3rd party application */
+    LOG(INFO) << __func__ << "Service discovery is pending";
+    Uuid pri_srvc = Uuid::From16Bit(UUID_SERVCLASS_CSIS);
+    BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc);
+  } else {
+    LOG(INFO) << __func__ << "Service discovery is already completed";
+    bta_csip_sm_execute(p_cb, BTA_CSIP_OPEN_CMPL_EVT, NULL);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_close_csip_conn
+ *
+ * Description      API to close CSIP Connection and remove device from background
+ *                  list.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_close_csip_conn (tBTA_CSIP_DEV_CB* p_cb) {
+  LOG(INFO) << __func__;
+  if (p_cb->conn_id != GATT_INVALID_CONN_ID) {
+    // clear pending GATT Requests
+    BtaGattQueue::Clean(p_cb->conn_id);
+    p_cb->state = BTA_CSIP_DISCONNECTING_ST;
+    // Send Close to GATT Layer
+    if (p_cb->state == BTA_CSIP_CONN_ST || p_cb->conn_id) {
+      BTA_GATTC_Close(p_cb->conn_id);
+    } else {
+      BTA_GATTC_CancelOpen(bta_csip_cb.gatt_if, p_cb->addr, true);
+      tBTA_GATTC_OPEN open = {.status = GATT_SUCCESS};
+      bta_csip_gatt_close_act(p_cb,(tBTA_CSIP_REQ_DATA *)&open);
+    }
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_handle_notification
+ *
+ * Description      This function is called when notification is received on one
+ *                  of the characteristic registered for notification.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_handle_notification(tBTA_GATTC_NOTIFY* ntf) {
+  if (!ntf->is_notify) return;
+  LOG(INFO) << __func__<< " Set Member: " << ntf->bda << ", handle: " << ntf->handle;
+
+  tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(ntf->bda);
+  if (!p_cb) {
+    LOG(ERROR) << __func__ << " No CSIP GATT Connection for this device";
+    return;
+  }
+
+  const gatt::Characteristic* p_char =
+    BTA_GATTC_GetCharacteristic(p_cb->conn_id, ntf->handle);
+  if (p_char == NULL) {
+    APPL_TRACE_ERROR(
+        "%s: notification received for Unknown Characteristic, conn_id: "
+        "0x%04x, handle: 0x%04x",
+        __func__, p_cb->conn_id, ntf->handle);
+    return;
+  }
+
+  if (p_char->uuid == CSIS_SERVICE_LOCK_UUID) {
+    bta_csip_handle_lock_value_notif(p_cb, ntf->handle, ntf->value[0]);
+  } else if (p_char->uuid == CSIS_SERVICE_SIRK_UUID) {
+    //bta_csip_handle_sirk_change();
+  } else if (p_char->uuid == CSIS_SERVICE_SIZE_UUID) {
+    //bta_csip_handle_size_change();
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_handle_lock_value_notif
+ *
+ * Description      This function is called when notification is received for
+ *                  change in lock value on set member.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_handle_lock_value_notif(tBTA_CSIP_DEV_CB* p_cb,
+                                      uint16_t handle, uint8_t value) {
+  tBTA_CSIS_SRVC_INFO* srvc = bta_csip_find_csis_srvc_by_lock_handle(p_cb, handle);
+  if (!srvc) {
+    LOG(ERROR) << __func__ << " CSIS Service instance not found for this handle";
+    return;
+  }
+
+  /* LOCK has been released by Set member (by lock timeout) */
+  if (value == UNLOCK_VALUE && srvc->lock == LOCK_VALUE) {
+    srvc->lock = UNLOCK_VALUE;
+    /* Give lock status changed notification to all apps holding
+     * lock for this set member */
+    LOG(INFO) << __func__ << " Lock released by timeout";
+    for (auto i: srvc->lock_applist) {
+      tBTA_CSIP_RCB* rcb = bta_csip_get_rcb(i);
+      if (rcb && rcb->p_cback) {
+        std::vector<RawAddress> sm(1, p_cb->addr);
+        tBTA_LOCK_STATUS_CHANGED p_data = {i, srvc->set_id, value,
+                                           LOCK_RELEASED_TIMEOUT, sm};
+        (*rcb->p_cback) (BTA_CSIP_LOCK_STATUS_CHANGED_EVT, (tBTA_CSIP_DATA *)&p_data);
+      }
+    }
+    srvc->lock_applist.clear();
+  }
+  /* LOCK held by other set coordinator is released */
+  else if (value == UNLOCK_VALUE && srvc->lock == UNLOCK_VALUE) {
+    // check if lock was denied for any previous request
+    for (auto i: srvc->denied_applist) {
+      tBTA_CSIP_RCB* rcb = bta_csip_get_rcb(i);
+      if (rcb && rcb->p_cback) {
+         tBTA_LOCK_AVAILABLE p_data = {i, srvc->set_id, p_cb->addr};
+        (*rcb->p_cback) (BTA_CSIP_LOCK_AVAILABLE_EVT, (tBTA_CSIP_DATA *)&p_data);
+      }
+    }
+    srvc->denied_applist.clear();
+  }
+  /* Other Set Coordinator acquired the lock */
+  else if (value == LOCK_VALUE && srvc->lock == UNLOCK_VALUE) {
+    // No action is required to be taken
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_csis_disc_complete_ind
+ *
+ * Description      This function informas CSIS service discovery has been
+ *                  completed to DM layer.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_csis_disc_complete_ind (RawAddress& addr) {
+   tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(addr);
+   if (p_cb) {
+     p_cb->total_instance_disc++;
+     LOG(INFO) << __func__ << " discovered = " << +p_cb->total_instance_disc
+                           << " Total = " << +p_cb->csis_instance_count;
+     if (p_cb->total_instance_disc == p_cb->csis_instance_count
+         && !p_cb->is_disc_external) {
+       bta_dm_csis_disc_complete(addr, true);
+       bta_dm_lea_disc_complete(addr);
+     }
+   }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_give_new_set_found_cb
+ *
+ * Description      Give new coordinate set found callback to upper layer.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_give_new_set_found_cb (tBTA_CSIS_SRVC_INFO *srvc) {
+  /* Check if this remote csis instance is included in another service */
+  const std::vector<gatt::Service>* services =
+      BTA_GATTC_GetServices(srvc->conn_id);
+
+  if (services) {
+    for (const gatt::Service& service : *services) {
+      if (service.is_primary) {
+        for (const gatt::IncludedService &included_srvc : service.included_services) {
+          if (included_srvc.uuid == CSIS_SERVICE_UUID
+                  && service.handle == srvc->service_handle) {
+            APPL_TRACE_DEBUG("%s: service Uuid of service including CSIS service : %s",
+                                    __func__, service.uuid.ToString().c_str());
+            srvc->including_srvc_uuid = service.uuid;
+          }
+        }
+      }
+    }
+  }
+
+  // Given New Set found callback to upper layer
+  tBTA_CSIP_NEW_SET_FOUND new_set_params;
+  new_set_params.set_id = srvc->set_id;
+  memcpy(new_set_params.sirk, srvc->sirk, SIRK_SIZE);
+  new_set_params.size = srvc->size;
+  new_set_params.including_srvc_uuid = srvc->including_srvc_uuid;
+  new_set_params.addr = srvc->bd_addr;
+  new_set_params.lock_support = (srvc->lock_handle != 0)? true : false;
+
+  (*bta_csip_cb.p_cback)(BTA_CSIP_NEW_SET_FOUND_EVT, (tBTA_CSIP_DATA *)&new_set_params);
+}
+
+bool bta_csip_decrypt_sirk(tBTA_CSIS_SRVC_INFO *srvc, uint8_t *enc_sirk) {
+  // Get K from LTK or Link Key based on transport
+  Octet16 K = {};
+  uint8_t gatt_if, transport = BT_TRANSPORT_LE;
+  RawAddress bdaddr;
+  GATT_GetConnectionInfor(srvc->conn_id, &gatt_if, bdaddr, &transport);
+
+  char sample_data_prop[6];
+  osi_property_get("vendor.bt.pts.sample_csis_data", sample_data_prop, "false");
+
+  if (!strncmp("true", sample_data_prop, 4)) { // comparing prop with "true"
+    K = {0x67, 0x6e, 0x1b, 0x9b, 0xd4, 0x48, 0x69, 0x6f,
+         0x06, 0x1e, 0xc6, 0x22, 0x3c, 0xe5, 0xce, 0xd9};
+  } else if (transport == BT_TRANSPORT_BR_EDR) {
+    K = BTM_SecGetDeviceLinkKey(srvc->bd_addr);
+  } else if (transport == BT_TRANSPORT_LE) {
+    RawAddress pseudo_addr;
+    pseudo_addr = bta_get_pseudo_addr_with_id_addr(srvc->bd_addr);
+    Octet16 rev_K = BTM_BleGetLTK(pseudo_addr);
+    std::reverse_copy(rev_K.begin(), rev_K.end(), K.begin());
+  }
+
+  if(is_key_empty(K)) {
+    APPL_TRACE_DEBUG("%s Invalid Key received", __func__);
+    srvc->discovery_status = BTA_CSIP_INVALID_KEY;
+    return false;
+  }
+
+  /* compute SALT */
+  Octet16 salt = bta_csip_get_salt();
+
+  // Compute T
+  Octet16 T = bta_csip_compute_T(salt, K);
+
+  // Compute final result k1
+  Octet16 k1 = bta_csip_compute_k1(T);
+
+  // Get decrypted SIRK
+  Octet16 r_k1;
+  std::reverse_copy(k1.begin(), k1.end(), r_k1.begin());
+  bta_csip_get_decrypted_sirk(r_k1, enc_sirk, srvc->sirk);
+  return true;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_sirk_read_cb
+ *
+ * Description      Callback received when remote device Coordinated Sets SIRK
+ *                  is read.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_sirk_read_cb(uint16_t conn_id, tGATT_STATUS status,
+                  uint16_t handle, uint16_t len,
+                  uint8_t* value, void* data) {
+  APPL_TRACE_DEBUG("%s ", __func__);
+
+  if (status != GATT_SUCCESS) {
+    APPL_TRACE_ERROR("%s: SIRK Read failed. conn_id = %d status = %04x",
+                        __func__, conn_id, status);
+    return;
+  }
+
+  tBTA_CSIS_SRVC_INFO *srvc = (tBTA_CSIS_SRVC_INFO *)data;
+
+  uint8_t type = 0xFF;
+  LOG(INFO) << __func__ << " SIRK len = " << +len;
+
+  if (len != (SIRK_SIZE + 1)) {
+    APPL_TRACE_ERROR("%s : Invalid SIRK length", __func__);
+    srvc->discovery_status = BTA_CSIP_INVALID_SIRK_FORMAT;
+    bta_csip_csis_disc_complete_ind(srvc->bd_addr);
+    return;
+  }
+
+  STREAM_TO_UINT8(type, value);
+  APPL_TRACE_DEBUG("%s Type Field with SIRK = %d", __func__, type);
+  if (type != ENCRYPTED_SIRK && type != PLAINTEXT_SIRK) {
+    APPL_TRACE_ERROR("%s : Invalid SIRK Type", __func__);
+    srvc->discovery_status = BTA_CSIP_INVALID_KEY_TYPE;
+    bta_csip_csis_disc_complete_ind(srvc->bd_addr);
+    return;
+  }
+
+  if (type == ENCRYPTED_SIRK) {
+    uint8_t enc_sirk[SIRK_SIZE] = {};
+    STREAM_TO_ARRAY(enc_sirk, value, SIRK_SIZE);
+    if (!bta_csip_decrypt_sirk(srvc, enc_sirk)) {
+      APPL_TRACE_ERROR("%s : Invalid Empty Key", __func__);
+      srvc->discovery_status = BTA_CSIP_INVALID_KEY;
+      bta_csip_csis_disc_complete_ind(srvc->bd_addr);
+      return;
+    }
+  } else {
+    STREAM_TO_ARRAY(srvc->sirk, value, SIRK_SIZE);
+  }
+  // check if this set was found earlier
+  uint8_t set_id = bta_csip_find_set_id_by_sirk (srvc->sirk);
+  tBTA_CSET_CB *cset_cb = NULL;
+
+  /* New Coordinated Set */
+  if (set_id == INVALID_SET_ID) {
+    cset_cb = bta_csip_get_cset_cb();
+    if (!cset_cb) {
+      LOG(ERROR) << __func__ << " Insufficient set control blocks available.";
+      srvc->discovery_status = BTA_CSIP_RSRC_EXHAUSTED;
+      bta_csip_csis_disc_complete_ind(srvc->bd_addr);
+      return;
+    }
+    memcpy(cset_cb->sirk, srvc->sirk, SIRK_SIZE);
+
+    // Create new coordinated set and update in database
+    tBTA_CSIP_CSET cset = {};
+    cset.set_id = cset_cb->set_id;
+    cset.set_members.push_back(srvc->bd_addr);
+    cset.total_discovered++;
+    cset.lock_support = (srvc->lock_handle != 0 ? true : false);
+    LOG(INFO) << __func__ << "New Set. Adding device " << srvc->bd_addr.ToString()
+                          << " Set ID: " << +cset.set_id;
+    bta_csip_cb.csets.push_back(cset);
+
+    // assign set id in respective control blocks
+    srvc->set_id = cset_cb->set_id;
+
+  /* Existing coordinated Set */
+  } else {
+    LOG(INFO) << __func__ << " Device from existing set (set_id: " << +set_id << " )";
+    //bta_csip_csis_disc_complete_ind(srvc->bd_addr);
+    srvc->set_id = set_id;
+    if (!bta_csip_update_set_member(set_id, srvc->bd_addr)) {
+      srvc->discovery_status = BTA_CSIP_ALL_MEMBERS_DISCOVERED;
+      bta_csip_csis_disc_complete_ind(srvc->bd_addr);
+      return;
+    }
+
+    // Give set member found callback
+    tBTA_SET_MEMBER_FOUND set_member_params =
+        { .set_id = set_id,
+          .addr = srvc->bd_addr,
+        };
+
+    bta_csip_cb.p_cback (BTA_CSIP_SET_MEMBER_FOUND_EVT, (tBTA_CSIP_DATA *)&set_member_params);
+    return;
+  }
+
+  /* If size is optional, give callback to upper layer */
+  if (!srvc->size_handle) {
+    bta_csip_give_new_set_found_cb(srvc);
+  }
+
+  if (!srvc->size_handle && !srvc->rank_handle) {
+    bta_csip_preserve_cset(srvc);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_size_read_cb
+ *
+ * Description      Callback received when remote device Coordinated Sets SIZE
+ *                  characteristic is read.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_size_read_cb(uint16_t conn_id, tGATT_STATUS status,
+                  uint16_t handle, uint16_t len,
+                  uint8_t* value, void* data) {
+
+  if (status != GATT_SUCCESS) {
+    APPL_TRACE_ERROR("%s: SIZE Read failed. conn_id = %d status = %04x",
+                        __func__, conn_id, status);
+    return;
+  }
+
+  tBTA_CSIS_SRVC_INFO *srvc = (tBTA_CSIS_SRVC_INFO *)data;
+
+  if (srvc->discovery_status != BTA_CSIP_DISC_SUCCESS) {
+    APPL_TRACE_ERROR("%s: Ignore response (Reason: %d)", __func__, srvc->discovery_status);
+    return;
+  }
+
+  srvc->size = *value;
+  APPL_TRACE_DEBUG("%s size = %d", __func__, srvc->size);
+
+  tBTA_CSIP_CSET* cset = bta_csip_get_or_create_cset(srvc->set_id, true);
+  if (cset) cset->size = srvc->size;
+  // Give callback only when its a first set member
+  uint8_t totalDiscovered = bta_csip_get_coordinated_set(srvc->set_id).set_members.size();
+  if (totalDiscovered == 1) {
+    bta_csip_give_new_set_found_cb(srvc);
+  }
+
+  if (!srvc->rank_handle) {
+    bta_csip_preserve_cset(srvc);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_lock_read_cb
+ *
+ * Description      Callback received when remote device Coordinated Sets LOCK
+ *                  characteristic is read.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_lock_read_cb(uint16_t conn_id, tGATT_STATUS status,
+                  uint16_t handle, uint16_t len,
+                  uint8_t* value, void* data) {
+  if (status != GATT_SUCCESS) {
+    APPL_TRACE_ERROR("%s: LOCK Read failed. conn_id = %d status = %04x",
+                        __func__, conn_id, status);
+    return;
+  }
+
+  APPL_TRACE_DEBUG("%s lock value = %d", __func__, *value);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_rank_read_cb
+ *
+ * Description      Callback received when remote device Coordinated Sets RANK
+ *                  characteristic is read.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_rank_read_cb(uint16_t conn_id, tGATT_STATUS status,
+                  uint16_t handle, uint16_t len,
+                  uint8_t* value, void* data) {
+  if (status != GATT_SUCCESS) {
+    APPL_TRACE_ERROR("%s: Rank Read failed. conn_id = %d status = %04x",
+                        __func__, conn_id, status);
+    return;
+  }
+
+  tBTA_CSIS_SRVC_INFO *srvc = (tBTA_CSIS_SRVC_INFO *)data;
+  if (srvc->discovery_status != BTA_CSIP_DISC_SUCCESS) {
+    APPL_TRACE_ERROR("%s: Ignore response (Reason: %d)", __func__, srvc->discovery_status);
+    return;
+  }
+
+  srvc->rank = *value;
+  APPL_TRACE_DEBUG("%s device: %s Rank = %d set_id: %d", __func__,
+                   srvc->bd_addr.ToString().c_str(), srvc->rank, srvc->set_id);
+
+  // get coordinated set control block from set_id
+  tBTA_CSET_CB *cset_cb = bta_csip_get_cset_cb_by_id(srvc->set_id);
+  if (cset_cb) {
+    cset_cb->ordered_members.insert({srvc->rank, srvc->bd_addr});
+  }
+
+  bta_csip_preserve_cset(srvc);
+  bta_csip_csis_disc_complete_ind(srvc->bd_addr);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_gatt_disc_cmpl_act
+ *
+ * Description      This APIS is used to serach presence of csis service on
+ *                  remote device and initialize CSIS handles in csis service
+ *                  control block.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void bta_csip_gatt_disc_cmpl_act(tBTA_CSIP_DISC_SET *disc_params) {
+  uint16_t conn_id = disc_params->conn_id;
+  uint8_t status = disc_params->status;
+  RawAddress addr = disc_params->addr;
+
+  APPL_TRACE_DEBUG("%s conn_id = %d, status = %d addr: %s", __func__, conn_id,
+                   status, addr.ToString().c_str());
+
+  if (status) return;
+
+  // Fetch remote device gatt services from database
+  const std::vector<gatt::Service>* services =
+      BTA_GATTC_GetServices(conn_id);
+
+  if (!services) {
+    LOG(ERROR) << __func__ << " No Services discovered.";
+    bta_csip_csis_disc_complete_ind(addr);
+    return;
+  }
+
+  tBTA_CSIP_DEV_CB* dev_cb = bta_csip_find_dev_cb_by_bda(addr);
+
+  if (!dev_cb) {
+    dev_cb = bta_csip_create_dev_cb_for_bda(addr);
+  }
+
+  dev_cb->csis_instance_count = 0;
+
+  // Search for CSIS service in the database
+  for (const gatt::Service& service : *services) {
+    if (service.uuid == CSIS_SERVICE_UUID) {
+      dev_cb->csis_instance_count++;
+      // Get service control block from service handle (subsequent connection)
+      tBTA_CSIS_SRVC_INFO *srvc = bta_csip_get_csis_service_by_handle(dev_cb, service.handle);
+      if (!srvc) {
+        // create new service cb (if its a first time connection)
+        srvc = bta_csip_get_csis_service_cb(dev_cb);
+        if (!srvc) {
+          APPL_TRACE_ERROR("%s Resources not available for storing CSIS Service.", __func__);
+          return;
+        }
+      }
+      srvc->bd_addr = addr;
+      srvc->service_handle = service.handle;
+      srvc->conn_id = conn_id;
+      APPL_TRACE_DEBUG("%s: CSIS service found Uuid: %s service_handle = %d", __func__,
+                          service.uuid.ToString().c_str(), srvc->service_handle);
+
+      // Get Characteristic and CCCD handle
+      for (const gatt::Characteristic& charac : service.characteristics) {
+          Uuid uuid1 = charac.uuid;
+          if (uuid1 == CSIS_SERVICE_SIRK_UUID) {
+            srvc->sirk_handle = charac.value_handle;
+            srvc->sirk_ccd_handle = bta_csip_get_cccd_handle(conn_id, charac.value_handle);
+          } else if (uuid1 == CSIS_SERVICE_SIZE_UUID) {
+            srvc->size_handle = charac.value_handle;
+            srvc->size_ccd_handle = bta_csip_get_cccd_handle(conn_id, charac.value_handle);
+          } else if (uuid1 == CSIS_SERVICE_LOCK_UUID) {
+            srvc->lock_handle = charac.value_handle;
+            srvc->lock_ccd_handle = bta_csip_get_cccd_handle(conn_id, charac.value_handle);
+          } else if (uuid1 == CSIS_SERVICE_RANK_UUID) {
+            srvc->rank_handle = charac.value_handle;
+          }
+      }
+
+      /* Skip reading characteristics and Set Discovery procedure if it was done earlier */
+      if (srvc->set_id >= 0 && srvc->set_id < BTA_MAX_SUPPORTED_SETS) {
+        LOG(INFO) << __func__ << " Coordinated set discovery procedure already completed.";
+        continue;
+      }
+
+      if (srvc->sirk_handle) {
+        BtaGattQueue::ReadCharacteristic(
+            conn_id, srvc->sirk_handle, bta_sirk_read_cb, srvc);
+      }
+
+      if (srvc->size_handle) {
+        BtaGattQueue::ReadCharacteristic(
+            conn_id, srvc->size_handle, bta_size_read_cb, srvc);
+      }
+
+      if (srvc->lock_handle) {
+        BtaGattQueue::ReadCharacteristic(
+            conn_id, srvc->lock_handle, bta_lock_read_cb, srvc);
+      }
+
+      if (srvc->rank_handle) {
+        BtaGattQueue::ReadCharacteristic(
+            conn_id, srvc->rank_handle, bta_rank_read_cb, srvc);
+      }
+    }
+
+  }
+}
diff --git a/le_audio/system/bt/bta/csip/bta_csip_api.cc b/le_audio/system/bt/bta/csip/bta_csip_api.cc
new file mode 100644
index 0000000..73a9054
--- /dev/null
+++ b/le_audio/system/bt/bta/csip/bta_csip_api.cc
@@ -0,0 +1,287 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+/******************************************************************************
+ *
+ *  This file contains the CSIP API in the subsystem of BTA.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_bta_csip"
+
+#include <base/bind.h>
+#include <base/bind_helpers.h>
+#include <base/callback.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bta_csip_api.h"
+#include "bta_csip_int.h"
+#include "bta_gatt_queue.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+/*****************************************************************************
+ *  Constants
+ ****************************************************************************/
+static const tBTA_SYS_REG bta_csip_reg = {bta_csip_hdl_event, BTA_CsipDisable};
+
+/*********************************************************************************
+ *
+ * Function         BTA_RegisterCsipApp
+ *
+ * Description      This function is called to register application or module to
+ *                  to register with CSIP for using CSIP functionalities.
+ *
+ * Parameters       p_csip_cb: callback to be received in registering app when
+ *                             required CSIP operation is completed.
+ *                  reg_cb   : callback when app/module is registered with CSIP.
+ *
+ * Returns          None
+ *
+ *********************************************************************************/
+void BTA_RegisterCsipApp(tBTA_CSIP_CBACK* p_csip_cb,
+                              BtaCsipAppRegisteredCb reg_cb) {
+  do_in_bta_thread(FROM_HERE, base::Bind(&bta_csip_app_register, Uuid::GetRandom(),
+                                         p_csip_cb, std::move(reg_cb)));
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_UnregisterCsipApp
+ *
+ * Description      This function is called to unregister application or module.
+ *
+ * Parameters       app_id: id of the app/module that needs to be unregistered.
+ *
+ * Returns          None
+ *
+ *********************************************************************************/
+
+void BTA_UnregisterCsipApp(uint8_t app_id) {
+  do_in_bta_thread(FROM_HERE, base::Bind(&bta_csip_app_unregister, app_id));
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipSetLockValue
+ *
+ * Description      This function is called to request or release lock for the
+ *                  coordinated set.
+ *
+ * Parameters       lock_param: parameters to acquire or release lock.
+ *                                (tBTA_SET_LOCK_PARAMS).
+ *
+ * Returns          None
+ *
+ *********************************************************************************/
+void BTA_CsipSetLockValue(tBTA_SET_LOCK_PARAMS lock_params) {
+  tBTA_CSIP_LOCK_PARAMS* p_buf =
+      (tBTA_CSIP_LOCK_PARAMS*)osi_calloc(sizeof(tBTA_CSIP_LOCK_PARAMS));
+
+  p_buf->hdr.event = BTA_CSIP_SET_LOCK_VALUE_EVT;
+  p_buf->lock_req = lock_params;
+
+  bta_sys_sendmsg(p_buf);
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipGetCoordinatedSet
+ *
+ * Description      This function is called to fetch details of the coordinated set.
+ *
+ * Parameters       set_id: identifier of the coordinated set whose details are
+ *                          required to be fetched.
+ *
+ * Returns          tBTA_CSIP_CSET (containing details of coordinated set).
+ *
+ *********************************************************************************/
+tBTA_CSIP_CSET BTA_CsipGetCoordinatedSet(uint8_t set_id) {
+  APPL_TRACE_DEBUG("%s: set_id = %d", __func__, set_id);
+  return bta_csip_get_coordinated_set(set_id);
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipSetLockValue
+ *
+ * Description      This function is called to request or release lock for the
+ *                  coordinated set.
+ *
+ * Parameters       None.
+ *
+ * Returns          vector<tBTIF_CSIP_CSET>: (all discovered coordinated set)
+ *
+ *********************************************************************************/
+std::vector<tBTA_CSIP_CSET> BTA_CsipGetDiscoveredSets() {
+  return bta_csip_cb.csets;
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipConnect
+ *
+ * Description      This function is called to establish GATT Connection.
+ *
+ * Parameters       bd_addr : Address of the remote device.
+ *
+ * Returns          None.
+ *
+ *********************************************************************************/
+void BTA_CsipConnect (uint8_t app_id, const RawAddress& bd_addr) {
+  tBTA_CSIP_API_CONN* p_buf =
+    (tBTA_CSIP_API_CONN*)osi_calloc(sizeof(tBTA_CSIP_API_CONN));
+  p_buf->hdr.event = BTA_CSIP_API_OPEN_EVT;
+  p_buf->bd_addr = bd_addr;
+  p_buf->app_id = app_id;
+
+  bta_sys_sendmsg(p_buf);
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipConnect
+ *
+ * Description      This function is called to establish GATT Connection.
+ *
+ * Parameters       bd_addr : Address of the remote device.
+ *
+ * Returns          None.
+ *
+ *********************************************************************************/
+void BTA_CsipDisconnect (uint8_t app_id, const RawAddress& bd_addr) {
+  tBTA_CSIP_API_CONN* p_buf =
+    (tBTA_CSIP_API_CONN*)osi_calloc(sizeof(tBTA_CSIP_API_CONN));
+  p_buf->hdr.event = BTA_CSIP_API_CLOSE_EVT;
+  p_buf->bd_addr = bd_addr;
+  p_buf->app_id = app_id;
+
+  bta_sys_sendmsg(p_buf);
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipFindCsisInstance
+ *
+ * Description      This function is called to find presence of CSIS service on
+ *                  remote device.
+ *
+ * Parameters       coon_id : Connection ID of the GATT Connection at DM Layer.
+ *
+ * Returns          None.
+ *
+ *********************************************************************************/
+void BTA_CsipFindCsisInstance(uint16_t conn_id, tGATT_STATUS status,
+                              RawAddress& bd_addr) {
+  APPL_TRACE_DEBUG("%s ", __func__);
+
+  tBTA_CSIP_DISC_SET* p_buf =
+    (tBTA_CSIP_DISC_SET*)osi_calloc(sizeof(tBTA_CSIP_DISC_SET));
+  p_buf->hdr.event = BTA_CSIP_DISC_CMPL_EVT;
+  p_buf->conn_id = conn_id;
+  p_buf->status = status;
+  p_buf->addr = bd_addr;
+
+  bta_sys_sendmsg(p_buf);
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipInit
+ *
+ * Description      This function is invoked to initialize CSIP in BTA layer.
+ *
+ * Parameters       None.
+ *
+ * Returns          None.
+ *
+ *********************************************************************************/
+void BTA_CsipEnable(tBTA_CSIP_CBACK *p_cback) {
+  tBTA_CSIP_ENABLE* p_buf =
+    (tBTA_CSIP_ENABLE*)osi_calloc(sizeof(tBTA_CSIP_ENABLE));
+
+  /* register with BTA system manager */
+  bta_sys_register(BTA_ID_GROUP, &bta_csip_reg);
+
+  p_buf->hdr.event = BTA_CSIP_API_ENABLE_EVT;
+  p_buf->p_cback = p_cback;
+
+  bta_sys_sendmsg(p_buf);
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipDisable
+ *
+ * Description      This function is called for deinitialization.
+ *
+ * Parameters       None.
+ *
+ * Returns          None.
+ *
+ *********************************************************************************/
+void BTA_CsipDisable() {
+  tBTA_CSIP_ENABLE* p_buf =
+    (tBTA_CSIP_ENABLE*)osi_calloc(sizeof(tBTA_CSIP_ENABLE));
+
+  p_buf->hdr.event = BTA_CSIP_API_DISABLE_EVT;
+
+  bta_sys_sendmsg(p_buf);
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipRemoveUnpairedSetMember
+ *
+ * Description      This function is called when a given set member is unpaired.
+ *
+ * Parameters       addr: BD Address of the set member.
+ *
+ * Returns          None.
+ *
+ *********************************************************************************/
+void BTA_CsipRemoveUnpairedSetMember(RawAddress addr) {
+  do_in_bta_thread(FROM_HERE, base::Bind(&bta_csip_remove_set_member, addr));
+}
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipGetDeviceSetId
+ *
+ * Description      This API is used to get set id of the remote device.
+ *
+ * Parameters       addr: BD Address of the set member.
+ *                  uuid: UUID of the service which includes CSIS service.
+ *
+ * Returns          None.
+ *
+ *********************************************************************************/
+uint8_t BTA_CsipGetDeviceSetId(RawAddress addr, bluetooth::Uuid uuid) {
+  for (tBTA_CSIP_CSET cset: bta_csip_cb.csets) {
+    for (RawAddress bd_addr: cset.set_members) {
+      if (bd_addr == addr && (cset.p_srvc_uuid == uuid)) {
+        return cset.set_id;
+      }
+    }
+  }
+
+  return BTA_MAX_SUPPORTED_SETS;
+}
diff --git a/le_audio/system/bt/bta/csip/bta_csip_int.h b/le_audio/system/bt/bta/csip/bta_csip_int.h
new file mode 100644
index 0000000..5000bbf
--- /dev/null
+++ b/le_audio/system/bt/bta/csip/bta_csip_int.h
@@ -0,0 +1,321 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+/******************************************************************************
+ *
+ *  This file contains BTA CSIP Client internal definitions
+ *
+ ******************************************************************************/
+
+#ifndef BTA_CSIP_INT_H
+#define BTA_CSIP_INT_H
+
+#include "bta_csip_api.h"
+#include "bta_gatt_api.h"
+#include "bta_sys.h"
+#include "btm_ble_api_types.h"
+
+/* Max CSIP supported devices (control blocks) */
+#define BTA_CSIP_MAX_DEVICE 32
+
+/* Max CSIP supported coordinated sets */
+#define BTA_MAX_SUPPORTED_SETS 16
+
+/* Maximum number of apps that can be registered with CSIP*/
+#define BTA_CSIP_MAX_SUPPORTED_APPS 16
+
+/* Max Supported coordinated sets per device*/
+#define MAX_SUPPORTED_SETS_PER_DEVICE 5
+
+/* Status of the CSIS Discovery*/
+#define BTA_CSIP_DISC_SUCCESS 0
+#define BTA_CSIP_INVALID_SIRK_FORMAT 1
+#define BTA_CSIP_INVALID_KEY 2
+#define BTA_CSIP_INVALID_KEY_TYPE 3
+#define BTA_CSIP_ALL_MEMBERS_DISCOVERED 4
+#define BTA_CSIP_RSRC_EXHAUSTED 5
+
+using bluetooth::Uuid;
+
+/* state machine events, these events are handled by the state machine */
+enum {
+  BTA_CSIP_API_OPEN_EVT = BTA_SYS_EVT_START(BTA_ID_GROUP),
+  BTA_CSIP_API_CLOSE_EVT,
+  BTA_CSIP_GATT_OPEN_EVT,
+  BTA_CSIP_GATT_CLOSE_EVT,
+  BTA_CSIP_OPEN_FAIL_EVT,
+  BTA_CSIP_OPEN_CMPL_EVT,
+  BTA_CSIP_START_ENC_EVT,
+  BTA_CSIP_ENC_CMPL_EVT,
+  BTA_CSIP_GATT_ENC_CMPL_EVT,
+
+  /* common events: not handled by execute state machine */
+  BTA_CSIP_API_ENABLE_EVT,
+  BTA_CSIP_API_DISABLE_EVT,
+  BTA_CSIP_DISC_CMPL_EVT,
+  BTA_CSIP_SET_LOCK_VALUE_EVT,
+};
+
+/* CSIP device state machine states */
+enum {
+  BTA_CSIP_IDLE_ST,
+  BTA_CSIP_W4_CONN_ST,
+  BTA_CSIP_W4_SEC,
+  BTA_CSIP_CONN_ST,
+  BTA_CSIP_DISCONNECTING_ST,
+};
+
+typedef uint8_t tBTA_CSIP_STATE;
+
+
+/* CSIP Command request parameters in BTA */
+
+/* Find Coordinated set parameters*/
+typedef struct {
+  BT_HDR hdr;
+  uint16_t conn_id;
+  tGATT_STATUS status;
+  RawAddress addr;
+} tBTA_CSIP_DISC_SET;
+
+/* Connection Request parameters */
+typedef struct {
+  BT_HDR hdr;
+  RawAddress bd_addr;
+  uint8_t app_id;
+} tBTA_CSIP_API_CONN;
+
+/* Lock request parameters */
+typedef struct {
+  BT_HDR hdr;
+  tBTA_SET_LOCK_PARAMS lock_req;
+} tBTA_CSIP_LOCK_PARAMS;
+
+
+typedef struct {
+  BT_HDR hdr;
+  tBTA_CSIP_CBACK* p_csip_cb;
+  tBTA_CSIP_CLT_REG_CB* reg_cb;
+} tBTA_CSIP_APP_REG_PARAMS;
+
+typedef struct {
+  BT_HDR hdr;
+  tBTA_CSIP_CBACK *p_cback;
+} tBTA_CSIP_ENABLE;
+
+typedef struct {
+  BT_HDR hdr;
+} tBTA_CSIP_CMD;
+
+typedef union {
+  BT_HDR hdr;
+  tBTA_CSIP_API_CONN conn_param;
+  tBTA_CSIP_LOCK_PARAMS lock_req;
+  tBTA_CSIP_APP_REG_PARAMS reg_param;
+  tBTA_CSIP_ENABLE enable_param;
+  tBTA_GATTC_OPEN gatt_open_param;
+  tBTA_GATTC_CLOSE gatt_close_param;
+  tBTA_CSIP_CMD cmd;
+} tBTA_CSIP_REQ_DATA;
+
+typedef struct {
+  bool in_use;
+  uint8_t set_id = BTA_MAX_SUPPORTED_SETS;
+  uint16_t conn_id;   /* GATT conn_id used for service discovery */
+  RawAddress bd_addr;
+
+  uint16_t service_handle;  /* Handle of this CSIS service */
+  uint16_t sirk_handle;     /* SIRK characteristic value handle */
+  uint16_t size_handle;     /* size characteristic value handle */
+  uint16_t lock_handle;     /* lock characteristic value handle */
+  uint16_t rank_handle;     /* rank characteristic value handle */
+
+  uint16_t sirk_ccd_handle; /* SIRK CCCD handle*/
+  uint16_t size_ccd_handle; /* size CCCD handle */
+  uint16_t lock_ccd_handle; /* lock CCCD handle */
+
+  uint8_t sirk[SIRK_SIZE];  /* Coordinated set SIRK */
+  uint8_t size;             /* size of the coordinated set */
+  uint8_t lock;             /* lock status of the set member */
+  uint8_t rank;             /* rank of the set member*/
+  uint8_t discovery_status = BTA_CSIP_DISC_SUCCESS; /* status of the CSIS discovery*/
+  bluetooth::Uuid including_srvc_uuid; /* uuid of the service which includes CSIS*/
+
+  /* Lock mamangement details*/
+  std::vector<uint8_t> lock_applist;    /* Apps those have locked this set */
+  std::vector<uint8_t> unrsp_applist;   /* Apps to which unresponsive res is sent */
+  std::vector<uint8_t> denied_applist;  /* Apps to which lock was denied */
+} tBTA_CSIS_SRVC_INFO;
+
+typedef struct {
+  bool in_use;
+  RawAddress addr;                  /* Remote device address */
+  uint16_t conn_id;                 /* GATT Connection ID */
+  uint8_t sec_mask = (BTM_SEC_IN_ENCRYPT | BTM_SEC_OUT_ENCRYPT); /* Security Mask for CSIP*/
+  bool security_pending;
+  bool is_disc_external = false;    /* if discovery is started by external App*/
+  uint8_t state;                    /* connection state */
+  uint8_t csis_instance_count;      /* number of CSIS instances on remote device */
+  uint8_t total_instance_disc;      /* total number of instances discovered */
+
+  /* CSIS services found on remote device*/
+  tBTA_CSIS_SRVC_INFO csis_srvc[MAX_SUPPORTED_SETS_PER_DEVICE];
+
+  // list of applications which initiated CSIP connect for this device
+  std::vector<uint8_t> conn_applist; /* List of Apps that sent connection request*/
+  std::string set_info = "";
+  bool unresponsive;                 /* if remote is unresponsive to GATT request */
+} tBTA_CSIP_DEV_CB;
+
+typedef struct {
+  uint8_t app_id;
+  uint8_t set_id;
+  uint8_t value;
+  int8_t cur_idx;
+  std::vector<RawAddress> members_addr;
+} tBTA_LOCK_REQUEST;
+
+typedef struct {
+  bool in_use;
+  uint8_t set_id = BTA_MAX_SUPPORTED_SETS;
+  uint8_t sirk[SIRK_SIZE];
+  uint16_t set_member_tout = 500;
+  bool request_in_progress;
+  tBTA_CSIP_DEV_CB* cur_dev_cb;
+  alarm_t* unresp_timer;
+  tBTA_LOCK_REQUEST cur_lock_req;
+  tBTA_LOCK_STATUS_CHANGED cur_lock_res;
+  std::map<uint8_t, RawAddress> ordered_members;
+  std::queue<tBTA_SET_LOCK_PARAMS> lock_req_queue;
+} tBTA_CSET_CB;
+
+typedef struct {
+  uint8_t app_id;
+  bool in_use;
+  tBTA_CSIP_CBACK* p_cback;
+} tBTA_CSIP_RCB;
+
+typedef struct {
+  tGATT_IF gatt_if;
+  tBTA_CSIP_CBACK* p_cback;                /* callbacks for btif layer */
+  std::vector<tBTA_CSIP_DEV_CB> dev_cb;    /* device control block */
+  tBTA_CSIP_RCB app_rcb[BTA_CSIP_MAX_SUPPORTED_APPS];
+  std::vector<tBTA_CSIP_CSET> csets;
+  tBTA_CSET_CB csets_cb[BTA_MAX_SUPPORTED_SETS];
+} tBTA_CSIP_CB;
+
+/*****************************************************************************
+ *  Global data
+ ****************************************************************************/
+
+/* CSIP control block */
+extern tBTA_CSIP_CB bta_csip_cb;
+
+/*****************************************************************************
+ *  Function prototypes
+ ****************************************************************************/
+void bta_csip_sm_execute(tBTA_CSIP_DEV_CB* p_cb, uint16_t event,
+                         tBTA_CSIP_REQ_DATA* p_data);
+
+
+//action api's
+extern bool bta_csip_hdl_event(BT_HDR* p_msg);
+extern void bta_csip_api_enable(tBTA_CSIP_CBACK *p_cback);
+extern void bta_csip_api_disable();
+extern void bta_csip_app_register (const Uuid& app_uuid, tBTA_CSIP_CBACK* p_cback,
+                                   BtaCsipAppRegisteredCb cb);
+extern void bta_csip_gattc_register();
+extern void bta_csip_app_unregister(uint8_t app_id);
+extern void bta_csip_gatt_disc_cmpl_act(tBTA_CSIP_DISC_SET *disc_params);
+extern void bta_csip_api_open_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data);
+extern void bta_csip_api_close_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data);
+extern void bta_csip_gatt_open_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data);
+extern void bta_csip_gatt_close_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data);
+extern void bta_csip_gatt_open_fail_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data);
+extern void bta_csip_open_cmpl_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data);
+extern void bta_csip_start_sec_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data);
+extern void bta_csip_sec_cmpl_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data);
+extern void bta_csip_close_csip_conn (tBTA_CSIP_DEV_CB* p_cb);
+extern void bta_csip_process_set_lock_act(tBTA_SET_LOCK_PARAMS lock_req);
+extern void bta_csip_send_lock_req_act(tBTA_CSET_CB* cset_cb);
+extern void bta_csip_handle_lock_denial(tBTA_CSET_CB* cset_cb);
+extern bool bta_csip_validate_req_for_denied_sm (tBTA_CSET_CB* cset_cb);
+extern void bta_csip_form_lock_request(tBTA_SET_LOCK_PARAMS lock_param,
+                                            tBTA_CSET_CB* cset_cb);
+extern void bta_csip_send_unlock_req_act(tBTA_CSET_CB* cset_cb);
+extern void bta_csip_set_member_lock_timeout(void* p_data);
+extern void bta_csip_load_coordinated_sets_from_storage();
+
+// bta_csip_utils
+extern tBTA_CSIP_DEV_CB* bta_csip_find_dev_cb_by_bda(const RawAddress& bda);
+extern tBTA_CSIP_DEV_CB* bta_csip_get_dev_cb_by_cid(uint16_t conn_id);
+extern tBTA_CSIP_DEV_CB* bta_csip_create_dev_cb_for_bda(const RawAddress& bda);
+extern tBTA_CSIS_SRVC_INFO* bta_csip_get_csis_service_cb(tBTA_CSIP_DEV_CB* dev_cb);
+extern bool bta_csip_is_csis_supported(tBTA_CSIP_DEV_CB* dev_cb);
+extern tBTA_CSIS_SRVC_INFO* bta_csip_get_csis_service_by_handle(tBTA_CSIP_DEV_CB* dev_cb,
+                                                                uint16_t service_handle);
+extern tBTA_CSIS_SRVC_INFO* bta_csip_find_csis_srvc_by_lock_handle(tBTA_CSIP_DEV_CB* dev_cb,
+                                                                   uint16_t lock_handle);
+extern tBTA_CSIP_CSET* bta_csip_get_or_create_cset (uint8_t set_id, bool existing);
+extern bool bta_csip_validate_set_params(tBTA_SET_LOCK_PARAMS* lock_req);
+extern bool bta_csip_is_valid_lock_request(tBTA_SET_LOCK_PARAMS* lock_req);
+extern std::vector<RawAddress> bta_csip_arrange_set_members_by_order(
+    uint8_t set_id, std::vector<RawAddress>& req_sm, bool ascending);
+extern tBTA_CSIP_CSET bta_csip_get_coordinated_set (uint8_t set_id);
+extern bool bta_csip_update_set_member (uint8_t set_id, RawAddress addr);
+extern tBTA_CSET_CB* bta_csip_get_cset_cb ();
+extern tBTA_CSET_CB* bta_csip_get_cset_cb_by_id (uint8_t set_id);
+extern uint8_t bta_csip_find_set_id_by_sirk (uint8_t* sirk);
+extern tBTA_CSIS_SRVC_INFO* bta_csip_get_csis_instance(tBTA_CSIP_DEV_CB* dev_cb, uint8_t set_id);
+extern bool bta_csip_is_locked_by_other_apps(tBTA_CSIS_SRVC_INFO* srvc, uint8_t app_id);
+extern std::vector<RawAddress> bta_csip_get_set_member_by_order(uint8_t set_id,
+                                                                bool ascending);
+extern bool bta_csip_is_member_locked_by_app (uint8_t app_id, tBTA_CSIS_SRVC_INFO* srvc);
+extern void bta_csip_get_next_lock_request(tBTA_CSET_CB* cset_cb);
+extern uint16_t bta_csip_get_cccd_handle (uint16_t conn_id, uint16_t char_handle);
+extern bool bta_csip_is_app_reg(uint8_t app_id);
+extern tBTA_CSIP_RCB* bta_csip_get_rcb (uint8_t app_id);
+extern void bta_csip_remove_set_member (RawAddress addr);
+extern void bta_csip_handle_notification(tBTA_GATTC_NOTIFY* ntf);
+extern void bta_csip_handle_lock_value_notif(tBTA_CSIP_DEV_CB* p_cb,
+                                             uint16_t handle, uint8_t value);
+extern void bta_csip_add_app_to_applist(tBTA_CSIP_DEV_CB* p_cb, uint8_t app_id);
+extern void bta_csip_handle_unresponsive_sm_res(tBTA_CSIS_SRVC_INFO* srvc,
+                                                tGATT_STATUS status);
+extern void bta_csip_remove_app_from_conn_list(tBTA_CSIP_DEV_CB* p_cb, uint8_t app_id);
+extern bool bta_csip_is_app_from_applist(tBTA_CSIP_DEV_CB* p_cb, uint8_t app_id);
+extern void bta_csip_send_conn_state_changed_cb(tBTA_CSIP_DEV_CB* p_cb,
+                                             uint8_t state, uint8_t status);
+extern void bta_csip_send_conn_state_changed_cb (tBTA_CSIP_DEV_CB* p_cb, uint8_t app_id,
+                                               uint8_t state, uint8_t status);
+extern void bta_csip_send_lock_req_cmpl_cb (tBTA_LOCK_STATUS_CHANGED cset_cb);
+extern void bta_csip_write_cccd (tBTA_CSIP_DEV_CB* p_cb, uint16_t char_handle,
+                                 uint16_t cccd_handle);
+extern void bta_csip_preserve_cset (tBTA_CSIS_SRVC_INFO* srvc);
+extern Octet16 bta_csip_get_salt();
+extern Octet16 bta_csip_compute_T(Octet16 salt, Octet16 K);
+extern Octet16 bta_csip_compute_k1(Octet16 T);
+extern void  bta_csip_get_decrypted_sirk(Octet16 k1, uint8_t *enc_sirk, uint8_t *sirk);
+extern Octet16 bta_csip_get_aes_cmac_result(const Octet16& key, const Octet16& message);
+extern Octet16 bta_csip_get_aes_cmac_result(const Octet16& key, const uint8_t* input,
+                                            uint16_t length);
+extern void hex_string_to_byte_arr(char *str, uint8_t* byte_arr, uint8_t len);
+extern void byte_arr_to_hex_string(uint8_t* byte_arr, char* str, uint8_t len);
+extern bool is_key_empty(Octet16& key);
+#endif
diff --git a/le_audio/system/bt/bta/csip/bta_csip_main.cc b/le_audio/system/bt/bta/csip/bta_csip_main.cc
new file mode 100644
index 0000000..87111f1
--- /dev/null
+++ b/le_audio/system/bt/bta/csip/bta_csip_main.cc
@@ -0,0 +1,296 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 "bt_target.h"
+#include "bta_csip_int.h"
+
+#include <string.h>
+
+#include "bt_common.h"
+
+#define LOG_TAG "bt_bta_csip"
+
+/*****************************************************************************
+ * Static methods
+ ****************************************************************************/
+static const char* bta_csip_evt_code(uint16_t evt_code);
+static const char* bta_csip_state_code(tBTA_CSIP_STATE evt_code);
+
+/*****************************************************************************
+ * Global data
+ ****************************************************************************/
+tBTA_CSIP_CB bta_csip_cb;
+
+enum {
+  BTA_CSIP_OPEN_ACT,
+  BTA_CSIP_CLOSE_ACT,
+  BTA_CSIP_GATT_OPEN_ACT,
+  BTA_CSIP_GATT_CLOSE_ACT,
+  BTA_CSIP_GATT_OPEN_FAIL_ACT,
+  BTA_CSIP_OPEN_CMPL_ACT,
+  BTA_CSIP_START_SEC_ACT,
+  BTA_CSIP_SEC_CMPL_ACT,
+  BTA_CSIP_IGNORE,
+};
+
+/* type for action functions */
+typedef void (*tBTA_CSIP_ACTION)(tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data);
+
+/* action functions */
+const tBTA_CSIP_ACTION bta_csip_action[] = {
+  bta_csip_api_open_act,
+  bta_csip_api_close_act,
+  bta_csip_gatt_open_act,
+  bta_csip_gatt_close_act,
+  bta_csip_gatt_open_fail_act,
+  bta_csip_open_cmpl_act,
+  bta_csip_start_sec_act,
+  bta_csip_sec_cmpl_act,
+};
+
+/* state table information */
+#define BTA_CSIP_ACTION 0     /* position of action */
+#define BTA_CSIP_NEXT_STATE 1 /* position of next state */
+#define BTA_CSIP_NUM_COLS 2   /* number of columns */
+
+/* state table in idle state */
+const uint8_t bta_csip_st_idle[][BTA_CSIP_NUM_COLS] = {
+    /* Event                                 Action              Next state */
+    /* BTA_CSIP_API_OPEN_EVT          */ {BTA_CSIP_OPEN_ACT, BTA_CSIP_W4_CONN_ST},
+    /* BTA_CSIP_API_CLOSE_EVT         */ {BTA_CSIP_IGNORE,   BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_GATT_OPEN_EVT         */ {BTA_CSIP_IGNORE,   BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_GATT_CLOSE_EVT        */ {BTA_CSIP_IGNORE,   BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_GATT_OPEN_FAIL_ACT    */ {BTA_CSIP_IGNORE,   BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_OPEN_CMPL_EVT         */ {BTA_CSIP_OPEN_CMPL_ACT, BTA_CSIP_CONN_ST},
+    /* BTA_CSIP_START_ENC_EVT         */ {BTA_CSIP_IGNORE,   BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_ENC_CMPL_EVT          */ {BTA_CSIP_IGNORE,   BTA_CSIP_IDLE_ST},
+};
+
+/* state table in wait for security state */
+const uint8_t bta_csip_st_w4_conn[][BTA_CSIP_NUM_COLS] = {
+    /* Event                                 Action                       Next state */
+    /* BTA_CSIP_API_OPEN_EVT          */ {BTA_CSIP_OPEN_ACT,           BTA_CSIP_W4_CONN_ST},
+    /* BTA_CSIP_API_CLOSE_EVT         */ {BTA_CSIP_CLOSE_ACT,          BTA_CSIP_W4_CONN_ST},
+    /* BTA_CSIP_GATT_OPEN_EVT         */ {BTA_CSIP_GATT_OPEN_ACT,      BTA_CSIP_W4_CONN_ST},
+    /* BTA_CSIP_GATT_CLOSE_EVT        */ {BTA_CSIP_GATT_CLOSE_ACT,     BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_GATT_OPEN_FAIL_ACT    */ {BTA_CSIP_GATT_OPEN_FAIL_ACT, BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_OPEN_CMPL_EVT         */ {BTA_CSIP_OPEN_CMPL_ACT,      BTA_CSIP_CONN_ST},
+    /* BTA_CSIP_START_ENC_EVT         */ {BTA_CSIP_START_SEC_ACT,      BTA_CSIP_W4_SEC},
+    /* BTA_CSIP_ENC_CMPL_EVT          */ {BTA_CSIP_IGNORE,             BTA_CSIP_W4_CONN_ST},
+};
+
+/* state table in wait for connection state */
+const uint8_t bta_csip_st_w4_sec[][BTA_CSIP_NUM_COLS] = {
+    /* Event                                 Action                       Next state */
+    /* BTA_CSIP_API_OPEN_EVT          */ {BTA_CSIP_OPEN_ACT,           BTA_CSIP_W4_SEC},
+    /* BTA_CSIP_API_CLOSE_EVT         */ {BTA_CSIP_CLOSE_ACT,          BTA_CSIP_W4_SEC},
+    /* BTA_CSIP_GATT_OPEN_EVT         */ {BTA_CSIP_IGNORE,             BTA_CSIP_W4_SEC},
+    /* BTA_CSIP_GATT_CLOSE_EVT        */ {BTA_CSIP_GATT_CLOSE_ACT,     BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_GATT_OPEN_FAIL_ACT    */ {BTA_CSIP_GATT_OPEN_FAIL_ACT, BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_OPEN_CMPL_EVT         */ {BTA_CSIP_OPEN_CMPL_ACT,      BTA_CSIP_CONN_ST},
+    /* BTA_CSIP_START_ENC_EVT         */ {BTA_CSIP_IGNORE,             BTA_CSIP_W4_SEC},
+    /* BTA_CSIP_ENC_CMPL_EVT          */ {BTA_CSIP_SEC_CMPL_ACT,       BTA_CSIP_W4_CONN_ST},
+};
+
+/* state table in connection state */
+const uint8_t bta_csip_st_connected[][BTA_CSIP_NUM_COLS] = {
+    /* Event                                 Action                       Next state */
+    /* BTA_CSIP_API_OPEN_EVT          */ {BTA_CSIP_OPEN_ACT,           BTA_CSIP_CONN_ST},
+    /* BTA_CSIP_API_CLOSE_EVT         */ {BTA_CSIP_CLOSE_ACT,          BTA_CSIP_CONN_ST},
+    /* BTA_CSIP_GATT_OPEN_EVT         */ {BTA_CSIP_IGNORE,             BTA_CSIP_CONN_ST},
+    /* BTA_CSIP_GATT_CLOSE_EVT        */ {BTA_CSIP_GATT_CLOSE_ACT,     BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_GATT_OPEN_FAIL_ACT    */ {BTA_CSIP_GATT_OPEN_FAIL_ACT, BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_OPEN_CMPL_EVT         */ {BTA_CSIP_IGNORE,             BTA_CSIP_CONN_ST},
+    /* BTA_CSIP_START_ENC_EVT         */ {BTA_CSIP_IGNORE,             BTA_CSIP_CONN_ST},
+    /* BTA_CSIP_ENC_CMPL_EVT          */ {BTA_CSIP_IGNORE,             BTA_CSIP_CONN_ST},
+};
+
+/* state table in disconnecting state */
+const uint8_t bta_csip_st_disconnecting[][BTA_CSIP_NUM_COLS] = {
+    /* Event                                 Action                       Next state */
+    /* BTA_CSIP_API_OPEN_EVT          */ {BTA_CSIP_IGNORE,          BTA_CSIP_DISCONNECTING_ST},
+    /* BTA_CSIP_API_CLOSE_EVT         */ {BTA_CSIP_IGNORE,          BTA_CSIP_DISCONNECTING_ST},
+    /* BTA_CSIP_GATT_OPEN_EVT         */ {BTA_CSIP_IGNORE,          BTA_CSIP_DISCONNECTING_ST},
+    /* BTA_CSIP_GATT_CLOSE_EVT        */ {BTA_CSIP_GATT_CLOSE_ACT,  BTA_CSIP_IDLE_ST},
+    /* BTA_CSIP_GATT_OPEN_FAIL_ACT    */ {BTA_CSIP_IGNORE,          BTA_CSIP_DISCONNECTING_ST},
+    /* BTA_CSIP_OPEN_CMPL_EVT         */ {BTA_CSIP_IGNORE,          BTA_CSIP_DISCONNECTING_ST},
+    /* BTA_CSIP_START_ENC_EVT         */ {BTA_CSIP_IGNORE,          BTA_CSIP_DISCONNECTING_ST},
+    /* BTA_CSIP_ENC_CMPL_EVT          */ {BTA_CSIP_IGNORE,          BTA_CSIP_DISCONNECTING_ST},
+};
+
+/* type for state table */
+typedef const uint8_t (*tBTA_CSIP_ST_TBL)[BTA_CSIP_NUM_COLS];
+
+/* state table */
+tBTA_CSIP_ST_TBL bta_csip_st_tbl[] = {bta_csip_st_idle, bta_csip_st_w4_conn,
+                                     bta_csip_st_w4_sec, bta_csip_st_connected,
+                                     bta_csip_st_disconnecting};
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_sm_execute
+ *
+ * Description      API to execute state operation.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void bta_csip_sm_execute(tBTA_CSIP_DEV_CB* p_cb, uint16_t event,
+                              tBTA_CSIP_REQ_DATA* p_data) {
+  tBTA_CSIP_ST_TBL state_table;
+  uint8_t action;
+
+  if (!p_cb) {
+    APPL_TRACE_ERROR("%s: Device not found. Return.", __func__);
+    return;
+  }
+
+  state_table = bta_csip_st_tbl[p_cb->state];
+
+  event &= 0xff;
+
+  p_cb->state = state_table[event][BTA_CSIP_NEXT_STATE];
+  APPL_TRACE_DEBUG("%s: Next State = %d(%s) event = %04x(%s)", __func__,
+                   p_cb->state, bta_csip_state_code(p_cb->state), event,
+                   bta_csip_evt_code(event));
+
+  action = state_table[event][BTA_CSIP_ACTION];
+  APPL_TRACE_DEBUG("%s: action = %d", __func__, action);
+  if (action != BTA_CSIP_IGNORE) {
+    (*bta_csip_action[action])(p_cb, p_data);
+  }
+
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_hdl_event
+ *
+ * Description      CSIP client main event handling function.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+bool bta_csip_hdl_event(BT_HDR* p_msg) {
+  tBTA_CSIP_DEV_CB* dev_cb = NULL;
+
+  APPL_TRACE_DEBUG("%s: Event: %04x", __func__, p_msg->event);
+
+  switch (p_msg->event) {
+    case BTA_CSIP_API_ENABLE_EVT:
+      bta_csip_api_enable(((tBTA_CSIP_ENABLE *)p_msg)->p_cback);
+      break;
+
+    case BTA_CSIP_API_DISABLE_EVT:
+      bta_csip_api_disable();
+      break;
+
+    case BTA_CSIP_DISC_CMPL_EVT:
+      bta_csip_gatt_disc_cmpl_act((tBTA_CSIP_DISC_SET *)p_msg);
+      break;
+
+    case BTA_CSIP_SET_LOCK_VALUE_EVT:
+      bta_csip_process_set_lock_act(((tBTA_CSIP_LOCK_PARAMS*)p_msg)->lock_req);
+      break;
+
+    default:
+      if (p_msg->event ==  BTA_CSIP_API_OPEN_EVT) {
+        RawAddress bd_addr = ((tBTA_CSIP_API_CONN *)p_msg)->bd_addr;
+        dev_cb = bta_csip_find_dev_cb_by_bda(bd_addr);
+        if (!dev_cb) {
+          dev_cb = bta_csip_create_dev_cb_for_bda(bd_addr);
+          APPL_TRACE_DEBUG("%s: Created Device CB for device: %s",
+                              __func__, bd_addr.ToString().c_str());
+        }
+      } else if (p_msg->event ==  BTA_CSIP_API_CLOSE_EVT) {
+        dev_cb = bta_csip_find_dev_cb_by_bda(((tBTA_CSIP_API_CONN *)p_msg)->bd_addr);
+      }
+
+      bta_csip_sm_execute(dev_cb, p_msg->event, (tBTA_CSIP_REQ_DATA*)p_msg);
+  }
+
+  return (true);
+
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_evt_code
+ *
+ * Description      returns event name in string format
+ *
+ * Returns          string representation of event code
+ *
+ ******************************************************************************/
+static const char* bta_csip_evt_code(uint16_t evt_code) {
+  evt_code = (BTA_ID_GROUP << 8) | evt_code;
+  switch (evt_code) {
+    case BTA_CSIP_API_OPEN_EVT:
+      return "BTA_CSIP_API_OPEN_EVT";
+    case BTA_CSIP_API_CLOSE_EVT:
+      return "BTA_CSIP_API_CLOSE_EVT";
+    case BTA_CSIP_GATT_OPEN_EVT:
+      return "BTA_CSIP_GATT_OPEN_EVT";
+    case BTA_CSIP_GATT_CLOSE_EVT:
+      return "BTA_CSIP_GATT_CLOSE_EVT";
+    case BTA_CSIP_OPEN_FAIL_EVT:
+      return "BTA_CSIP_OPEN_FAIL_EVT";
+    case BTA_CSIP_OPEN_CMPL_EVT:
+      return "BTA_CSIP_OPEN_CMPL_EVT";
+    case BTA_CSIP_START_ENC_EVT:
+      return "BTA_CSIP_START_ENC_EVT";
+    case BTA_CSIP_ENC_CMPL_EVT:
+      return "BTA_CSIP_ENC_CMPL_EVT";
+    case BTA_CSIP_API_ENABLE_EVT:
+      return "BTA_CSIP_API_ENABLE_EVT";
+    case BTA_CSIP_API_DISABLE_EVT:
+      return "BTA_CSIP_API_DISABLE_EVT";
+    case BTA_CSIP_SET_LOCK_VALUE_EVT:
+      return "BTA_CSIP_SET_LOCK_VALUE_EVT";
+    default:
+      return "Unknown CSIP event code";
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_state_code
+ *
+ * Description      returns state name in string format
+ *
+ * Returns          string representation of connection state
+ *
+ ******************************************************************************/
+static const char* bta_csip_state_code(tBTA_CSIP_STATE state) {
+  switch (state) {
+    case BTA_CSIP_IDLE_ST:
+      return "BTA_CSIP_IDLE_ST";
+    case BTA_CSIP_W4_CONN_ST:
+      return "BTA_CSIP_W4_CONN_ST";
+    case BTA_CSIP_W4_SEC:
+      return "BTA_CSIP_W4_SEC";
+    case BTA_CSIP_CONN_ST:
+      return "BTA_CSIP_CONN_ST";
+    case BTA_CSIP_DISCONNECTING_ST:
+      return "BTA_CSIP_DISCONNECTING_ST";
+    default:
+      return "Incorrect State";
+  }
+}
+
diff --git a/le_audio/system/bt/bta/csip/bta_csip_utils.cc b/le_audio/system/bt/bta/csip/bta_csip_utils.cc
new file mode 100644
index 0000000..b900c3a
--- /dev/null
+++ b/le_audio/system/bt/bta/csip/bta_csip_utils.cc
@@ -0,0 +1,1318 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+/******************************************************************************
+ *
+ *  This file contains the CSIP Client supporting functions
+ *
+ ******************************************************************************/
+
+#include <log/log.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <vector>
+
+#include "bta_csip_api.h"
+#include "bta_csip_int.h"
+#include "bta_gatt_queue.h"
+
+#include "osi/include/config.h"
+#include "btif/include/btif_config.h"
+#include "stack/crypto_toolbox/crypto_toolbox.h"
+
+/* CSIS Characteristic descriptors handles */
+#define CSIP_CCCD_UUID_VAL 0x2902
+Uuid CSIP_CCCD_UUID = Uuid::From16Bit(CSIP_CCCD_UUID_VAL);
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_validate_set_params
+ *
+ * Description      Validates if set id and its members are valid
+ *
+ * Returns          bool. true - if details are valid otherwise false.
+ *
+ ******************************************************************************/
+bool bta_csip_validate_set_params(tBTA_SET_LOCK_PARAMS* lock_req) {
+  std::vector<tBTA_CSIP_CSET> *csets = &bta_csip_cb.csets;
+  tBTA_CSIP_CSET cset;
+  bool is_valid_set = false;
+
+  std::vector<tBTA_CSIP_CSET>::iterator itr;
+  for (itr = csets->begin(); itr != csets->end(); ++itr) {
+    if (lock_req->set_id == itr->set_id) {
+      cset = *itr;
+      is_valid_set = true;
+      break;
+    }
+  }
+
+  if (!is_valid_set) {
+    LOG(ERROR) << __func__ << ": Invalid Set ID = " << +lock_req->set_id;
+    //TODO: Give Invalid parameters callback
+    return (false);
+  }
+
+  std::vector<RawAddress> req_members = lock_req->members_addr;
+    // TODO: if requested set members size = 0, return true
+  if ((int)lock_req->members_addr.size() == 0) {
+    LOG(INFO) << __func__<< " Lock of All Set Memebers is requested";
+    return (true);
+  }
+
+  std::vector<RawAddress> set_members = cset.set_members;
+  int members_matched = 0;
+  for (int i = 0; i < (int)req_members.size(); i++) {
+    for (int j = 0; j < (int)set_members.size(); j++) {
+      if (req_members[i] == set_members[j]) {
+        members_matched++;
+        break;
+      }
+    }
+  }
+  LOG(INFO) << "set members matched count = " << +members_matched; //debug
+  if (members_matched != (int)req_members.size()) {
+    LOG(ERROR) << __func__ << " Incorrect Set members provided";
+    return (false);
+  }
+
+  return (true);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_is_valid_lock_request
+ *
+ * Description      Validates lock request parameters received
+ *
+ * Returns          bool. true - if details are valid otherwise false.
+ *
+ ******************************************************************************/
+bool bta_csip_is_valid_lock_request(tBTA_SET_LOCK_PARAMS* lock_req) {
+  // validate if correct lock value is provided
+  if (lock_req->lock_value != UNLOCK_VALUE && lock_req->lock_value != LOCK_VALUE) {
+    LOG(ERROR) << __func__ << ": Invalid Lock Value.";
+    return (false);
+  }
+
+  // validate set id
+  if (!bta_csip_validate_set_params(lock_req)) {
+    LOG(INFO) << __func__ << " Invalid params";
+    return (false);
+  }
+
+  return (true);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_get_cset_cb
+ *
+ * Description      Finds coordinated set control block by set_id
+ *
+ * Returns          tBTA_CSET_CB. NULL - if set is not found.
+ *
+ ******************************************************************************/
+tBTA_CSET_CB* bta_csip_get_cset_cb_by_id (uint8_t set_id) {
+  int i;
+  tBTA_CSET_CB* cset_cb = &bta_csip_cb.csets_cb[0];
+
+  for (i = 0; i < BTA_MAX_SUPPORTED_SETS; i++, cset_cb++) {
+    if ((cset_cb->in_use) && (cset_cb->set_id == set_id)) {
+      return (cset_cb);
+    }
+  }
+
+  /* no match found */
+  return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_get_cset_cb
+ *
+ * Description      Creates new coordinated set control block with next available
+ *                  set id.
+ *
+ * Returns          tBTA_CSET_CB. NULL - if no resources are available for set.
+ *
+ ******************************************************************************/
+tBTA_CSET_CB* bta_csip_get_cset_cb () {
+  int i;
+  tBTA_CSET_CB* cset_cb = &bta_csip_cb.csets_cb[0];
+
+  for (i = 0; i < BTA_MAX_SUPPORTED_SETS; i++, cset_cb++) {
+    if (!cset_cb->in_use) {
+      cset_cb->set_id = i;
+      cset_cb->in_use = true;
+      return (cset_cb);
+    }
+  }
+
+  LOG(ERROR) << __func__ << " No resource available for Coordinated set";
+  return (NULL);
+}
+
+/********************************************************************************
+ *
+ * Function         bta_csip_is_app_reg
+ *
+ * Description      Utility function to check if app_id is valid and registered.
+ *
+ * Returns          true - if reistered.
+ *                  false - if invalid app id or its not registered.
+ *
+ *******************************************************************************/
+bool bta_csip_is_app_reg(uint8_t app_id) {
+  if (app_id >= BTA_CSIP_MAX_SUPPORTED_APPS) {
+    return (false);
+  }
+
+  if (bta_csip_cb.app_rcb[app_id].in_use) {
+    return (true);
+  }
+
+  return (false);
+}
+
+/********************************************************************************
+ *
+ * Function         bta_csip_get_rcb
+ *
+ * Description      Utility function to check if app_id is valid and registered.
+ *
+ * Returns          registration control block. NULL if not in use.
+ *
+ *******************************************************************************/
+tBTA_CSIP_RCB* bta_csip_get_rcb (uint8_t app_id) {
+  if (app_id >= BTA_CSIP_MAX_SUPPORTED_APPS) {
+    return (NULL);
+  }
+
+  if (bta_csip_cb.app_rcb[app_id].in_use) {
+    return (&bta_csip_cb.app_rcb[app_id]);
+  }
+
+  return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_get_coordinated_set
+ *
+ * Description      Creates new coordinated set control block
+ *
+ * Returns          tBTA_CSIP_CSET for valid set_id.
+ *                  Empty set with INVALID_SET_ID if not found.
+ *
+ ******************************************************************************/
+tBTA_CSIP_CSET bta_csip_get_coordinated_set (uint8_t set_id) {
+  for (tBTA_CSIP_CSET cset: bta_csip_cb.csets) {
+    if (cset.set_id == set_id) {
+      return cset;
+    }
+  }
+
+  LOG(ERROR) << __func__ << "Coordinated set not found for set_id: " << +set_id;
+  tBTA_CSIP_CSET cset = {.set_id = INVALID_SET_ID,
+                         .size = 0
+                        };
+  return cset;
+ }
+
+/******************************************************************************
+ *
+ * Function         bta_csip_update_set_member
+ *
+ * Description      Updates set member in the given set.
+ *
+ * Returns          bool (true, if added successfully. Otherwise, false.)
+ *
+ ******************************************************************************/
+bool bta_csip_update_set_member (uint8_t set_id, RawAddress addr) {
+  for (tBTA_CSIP_CSET& cset: bta_csip_cb.csets) {
+    if (cset.set_id == set_id) {
+      if (cset.set_members.size() == cset.size) {
+        LOG(ERROR) << __func__ << " All Set members already discovered.";
+        return false;
+      }
+      cset.set_members.push_back(addr);
+      return true;
+    }
+  }
+
+  LOG(ERROR) << __func__ << "Coordinated set not found for set_id: " << +set_id;
+  return false;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_remove_set_member
+ *
+ * Description      Removes set member from given coordinated set after unpairing.
+ *                  If its the last set member in set, coordinated set is deleted.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void bta_csip_remove_set_member (RawAddress addr) {
+  LOG(INFO) << __func__ << " Device = " << addr.ToString();
+  bool is_device_found = false;
+
+  btif_config_remove(addr.ToString().c_str(), "DGroup");
+  tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(addr);
+  if (!p_cb) {
+    APPL_TRACE_DEBUG("%s: Set Member not found", __func__);
+    return;
+  }
+
+  tBTA_CSIS_SRVC_INFO* srvc = &p_cb->csis_srvc[0];
+  for (int i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE && !is_device_found; i++, srvc++) {
+    if (!srvc->in_use) continue;
+    for (tBTA_CSIP_CSET& cset: bta_csip_cb.csets) {
+      if (cset.set_id == srvc->set_id) {
+        //std::remove(cset.set_members.begin(), cset.set_members.end(), addr);
+        cset.set_members.erase(
+          std::remove_if(cset.set_members.begin(), cset.set_members.end(),
+                         [&](RawAddress const & bdaddr) {
+            return bdaddr == addr;
+        }),
+        cset.set_members.end());
+        is_device_found = true;
+        LOG(INFO) << __func__ << " Size = " << +(int)cset.set_members.size();
+        if (cset.set_members.empty()) {
+          tBTA_CSET_CB* cset_cb = bta_csip_get_cset_cb_by_id(cset.set_id);
+          if (cset_cb) {
+            LOG(INFO) << __func__ << " Invalidating set. Last member unpaired.";
+            cset_cb->in_use = false;
+            cset_cb->set_id = INVALID_SET_ID;
+            cset.set_members.clear();
+            bta_csip_cb.csets.erase(
+              std::remove_if(bta_csip_cb.csets.begin(),
+                  bta_csip_cb.csets.end(), [&](tBTA_CSIP_CSET& cs) {
+                return cs.set_id == srvc->set_id;
+            }),
+            bta_csip_cb.csets.end());
+          }
+        }
+        break;
+      }
+    }
+  }
+
+  bta_csip_cb.dev_cb.erase(
+        std::remove_if(bta_csip_cb.dev_cb.begin(), bta_csip_cb.dev_cb.end(),
+                       [&](tBTA_CSIP_DEV_CB const &dev_cb) {
+            return dev_cb.addr == addr;
+        }),
+        bta_csip_cb.dev_cb.end());
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_get_or_create_cset
+ *
+ * Description      API used to create Coordinated Set Control block.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+tBTA_CSIP_CSET* bta_csip_get_or_create_cset (uint8_t set_id, bool existing) {
+  /*std::find_if(bta_csip_cb.csets.begin(), bta_csip_cb.csets.end(),
+               [&set_id](const tBTA_CSIP_CSET& set) {
+                 return set.set_id == set_id;
+               });*/
+
+  for (tBTA_CSIP_CSET& cset: bta_csip_cb.csets) {
+    if (cset.set_id == set_id) {
+      return &cset;
+    }
+  }
+
+  if (existing) return NULL;
+
+  /* Create a new set with invalid set_id*/
+  tBTA_CSIP_CSET cset = {.set_id = INVALID_SET_ID,
+                         .size = 0
+                        };
+  bta_csip_cb.csets.push_back(cset);
+  return &bta_csip_cb.csets.back();
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_find_set_id_by_sirk
+ *
+ * Description      Finds Coordinated set control block by sirk
+ *
+ * Returns          set_id if SIRK is found
+ *                  otherwise, INVALID_SET_ID
+ *
+ ******************************************************************************/
+uint8_t bta_csip_find_set_id_by_sirk (uint8_t* sirk) {
+  int i = 0;
+  tBTA_CSET_CB* csets = &bta_csip_cb.csets_cb[0];
+
+  for (i = 0; i < BTA_MAX_SUPPORTED_SETS; i++, csets++) {
+    if (csets->in_use) {
+      // compare SIRK's
+      if (!memcmp(sirk, csets->sirk, SIRK_SIZE)) {
+        return csets->set_id;
+      }
+    }
+  }
+
+  return INVALID_SET_ID;
+}
+
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_get_cset_cb
+ *
+ * Description      Finds coordinated set control block by set_id
+ *
+ * Returns          tBTA_CSET_CB. NULL - if set is not found.
+ *
+ ******************************************************************************/
+tBTA_CSIS_SRVC_INFO* bta_csip_get_csis_instance(tBTA_CSIP_DEV_CB* dev_cb,
+                                                       uint8_t set_id) {
+  int i = 0;
+
+  if (!dev_cb) return NULL;
+
+  tBTA_CSIS_SRVC_INFO* srvc = &dev_cb->csis_srvc[0];
+
+  for (i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE; i++, srvc++) {
+    srvc = &dev_cb->csis_srvc[i];
+    if ((srvc->in_use) && (srvc->set_id == set_id)) {
+      return (srvc);
+    }
+  }
+
+  /* no match found */
+  return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_get_csis_service_cb
+ *
+ * Description      Creates new coordinated set control block for a given device
+ *
+ * Returns          tBTA_CSET_CB. NULL if no resources available.
+ *
+ ******************************************************************************/
+tBTA_CSIS_SRVC_INFO* bta_csip_get_csis_service_cb(tBTA_CSIP_DEV_CB* dev_cb) {
+  int i = 0;
+  tBTA_CSIS_SRVC_INFO* srvc = &dev_cb->csis_srvc[0];
+
+  for (i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE; i++, srvc++) {
+    if (!srvc->in_use) {
+      srvc->in_use = true;
+      return srvc;
+    }
+  }
+
+  /* no match found */
+  return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_is_csis_supported
+ *
+ * Description      checks if remote device supports coordinated set
+ *
+ * Returns          true if supported, otherwise false.
+ *
+ ******************************************************************************/
+bool bta_csip_is_csis_supported(tBTA_CSIP_DEV_CB* dev_cb) {
+  int i = 0;
+  tBTA_CSIS_SRVC_INFO* srvc = &dev_cb->csis_srvc[0];
+
+  for (i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE; i++, srvc++) {
+    if (srvc->in_use) {
+      return true;
+    }
+  }
+
+  /* no csis instance found */
+  return (false);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_get_csis_service_by_handle
+ *
+ * Description      Gives CSIS Service Control block by service handle.
+ *
+ * Returns          CSIS Service control block. Null if not found.
+ *
+ ******************************************************************************/
+tBTA_CSIS_SRVC_INFO* bta_csip_get_csis_service_by_handle(
+    tBTA_CSIP_DEV_CB* dev_cb, uint16_t service_handle) {
+  int i = 0;
+  tBTA_CSIS_SRVC_INFO* srvc = &dev_cb->csis_srvc[0];
+
+  for (i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE; i++, srvc++) {
+    if (srvc->in_use && srvc->service_handle == service_handle) {
+      return srvc;
+    }
+  }
+
+  /* no match found */
+  return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_find_csis_srvc_by_lock_handle
+ *
+ * Description      Gives CSIS Service Control block by lock handle.
+ *
+ * Returns          CSIS Service control block. Null if not found.
+ *
+ ******************************************************************************/
+tBTA_CSIS_SRVC_INFO* bta_csip_find_csis_srvc_by_lock_handle(
+    tBTA_CSIP_DEV_CB* dev_cb, uint16_t lock_handle) {
+  int i = 0;
+  tBTA_CSIS_SRVC_INFO* srvc = &dev_cb->csis_srvc[0];
+
+  for (i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE; i++, srvc++) {
+    if (srvc->in_use && srvc->lock_handle == lock_handle) {
+      return srvc;
+    }
+  }
+
+  /* no match found */
+  return (NULL);
+
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_is_locked_by_other_apps
+ *
+ * Description      Checks if set is locked by app other than mentioned one.
+ *
+ * Returns          true, if locked by other app otherwise false.
+ *
+ ******************************************************************************/
+bool bta_csip_is_locked_by_other_apps(tBTA_CSIS_SRVC_INFO* srvc, uint8_t app_id) {
+  std::vector<uint8_t> &lock_applist = srvc->lock_applist;
+
+  for (auto& it : lock_applist) {
+    if (it != app_id) {
+      return (true);
+    }
+  }
+
+  return (false);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_form_set_lock_order
+ *
+ * Description      Forms order of set members as per rank.
+ *
+ * Returns          Ordered Set members.
+ *
+ ******************************************************************************/
+std::vector<RawAddress> bta_csip_form_set_lock_order(tBTA_CSET_CB* cset_cb) {
+  std::vector<RawAddress> ordered_members;
+  std::vector<RawAddress> req_members = cset_cb->cur_lock_req.members_addr;
+  std::map<uint8_t, RawAddress> lock_order_map;
+
+  for (int i = 0; i < (int)req_members.size(); i++) {
+    // get device control block and corresponding csis service details
+    tBTA_CSIP_DEV_CB* dev_cb = bta_csip_find_dev_cb_by_bda(req_members[i]);
+    // null checks required
+    tBTA_CSIS_SRVC_INFO* srvc =
+      bta_csip_get_csis_instance(dev_cb, cset_cb->cur_lock_req.set_id);
+    if (srvc) {
+      lock_order_map.insert({srvc->rank, req_members[i]});
+    }
+  }
+
+  for (auto itr: lock_order_map) {
+    ordered_members.push_back(itr.second);
+  }
+
+  return ordered_members;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_arrange_set_members_by_order
+ *
+ * Description      Forms order of set members for LOCK/UNLOCK request.
+ *
+ * Returns          Ordered set members in vector.
+ *
+ ******************************************************************************/
+std::vector<RawAddress> bta_csip_arrange_set_members_by_order(
+    uint8_t set_id, std::vector<RawAddress>& req_sm, bool ascending) {
+  LOG(INFO) << __func__;
+
+  std::vector<RawAddress> ordered_req_sm;
+  std::vector<RawAddress> set_members =
+          bta_csip_get_set_member_by_order(set_id, ascending);
+
+  // Check if all set members are requested
+  if ((uint8_t)req_sm.size() == 0) {
+    LOG(INFO) << __func__ << " original size = " << +set_members.size();
+    return set_members;
+  }
+
+  /* LOCK Request Order*/
+  for (int i = 0; i < (int)set_members.size(); i++) {
+    for (int j = 0; j < (int)req_sm.size(); j++) {
+      if (set_members[i] == req_sm[j]) {
+        ordered_req_sm.push_back(set_members[i]);
+        if (ordered_req_sm.size() == req_sm.size()) {
+          return ordered_req_sm;
+        }
+      }
+    }
+  }
+
+  return {};
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_arrange_set_members_by_order
+ *
+ * Description      Forms order of set members for LOCK/UNLOCK request.
+ *
+ * Returns          Ordered set members in vector.
+ *
+ ******************************************************************************/
+std::vector<RawAddress> bta_csip_get_set_member_by_order(uint8_t set_id,
+                                              bool ascending) {
+  std::vector<RawAddress> ordered_members;
+
+  tBTA_CSET_CB* cset_cb = &bta_csip_cb.csets_cb[set_id];
+  if (!cset_cb->in_use) {
+    LOG(ERROR) << __func__ << " Invalid Set for for Set ID: " << +set_id;
+    return {};
+  }
+
+  if (ascending) {
+    for (auto itr: cset_cb->ordered_members) {
+       ordered_members.push_back(itr.second);
+    }
+  } else {
+    for (auto i = cset_cb->ordered_members.rbegin();
+         i != cset_cb->ordered_members.rend(); ++i) {
+      ordered_members.push_back(i->second);
+    }
+  }
+
+  return ordered_members;
+}
+
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_is_member_locked_by_app
+ *
+ * Description      checks if application (app_id) has locked given set.
+ *
+ * Returns          void.
+ ******************************************************************************/
+bool bta_csip_is_member_locked_by_app (uint8_t app_id, tBTA_CSIS_SRVC_INFO* srvc) {
+  std::vector<uint8_t>& lock_applist = srvc->lock_applist;
+
+  auto it = std::find(lock_applist.begin(), lock_applist.end(), app_id);
+  if (it != lock_applist.end()) {
+    LOG(INFO) << __func__ << " App Id found in app list";
+    return true;
+  }
+
+  LOG(INFO) << __func__ << " App Id not found in app list";
+  return false;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_handle_unresponsive_sm_res
+ *
+ * Description      sends lock response to earlier requesting app.
+ *
+ * Returns          void.
+ ******************************************************************************/
+void bta_csip_handle_unresponsive_sm_res(tBTA_CSIS_SRVC_INFO* srvc,
+                                         tGATT_STATUS status) {
+  LOG(INFO) << __func__ << " Response from unresponsive remote " << srvc->bd_addr.ToString()
+                        << " status: " << +status;
+  std::vector<uint8_t>& unres_applist = srvc->unrsp_applist;
+
+  if (status == GATT_SUCCESS) {
+    srvc->lock = LOCK_VALUE;
+    for (auto& it : unres_applist) {
+      LOG(INFO) << __func__ << " Sending GATT_SUCCESS callback to app: " << +it;
+      std::vector<RawAddress> sm = {srvc->bd_addr};
+      tBTA_LOCK_STATUS_CHANGED res = {.app_id = it, .set_id = srvc->set_id,
+                                      .value = 0x02, .addr = sm};
+      bta_csip_send_lock_req_cmpl_cb(res);
+      // Add app id to the lock applist
+      srvc->lock_applist.push_back(it);
+    }
+  }
+
+  unres_applist.clear();
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_get_next_lock_request
+ *
+ * Description      Schedules next pending lock request for the given set.
+ *
+ * Returns          void.
+ ******************************************************************************/
+void bta_csip_get_next_lock_request(tBTA_CSET_CB* cset_cb) {
+  tBTA_SET_LOCK_PARAMS lock_req_params;
+  std::queue<tBTA_SET_LOCK_PARAMS>& lock_req_queue = cset_cb->lock_req_queue;
+
+  if  (lock_req_queue.empty()) {
+    LOG(INFO) << " No pending Lock Request for Set: " << +cset_cb->set_id;
+    cset_cb->request_in_progress = false;
+    return;
+  }
+
+  lock_req_params = lock_req_queue.front();
+  lock_req_queue.pop();
+
+  bta_csip_form_lock_request(lock_req_params, cset_cb);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_find_dev_cb_by_bda
+ *
+ * Description      Utility function find a device control block by BD address.
+ *
+ * Returns          tBTA_CSIP_DEV_CB - device control block for a given remote.
+ *                  nullptr, if device control block is not present.
+ ******************************************************************************/
+tBTA_CSIP_DEV_CB* bta_csip_find_dev_cb_by_bda(const RawAddress& bda) {
+  /*auto iter = std::find_if(bta_csip_cb.dev_cb.begin(), bta_csip_cb.dev_cb.end(),
+                           [&bda](const tBTA_CSIP_DEV_CB& device) {
+                             return device.addr == bda;
+                           });
+
+  return (iter == bta_csip_cb.dev_cb.end()) ? nullptr : &(*iter);*/
+  for (tBTA_CSIP_DEV_CB& p_cb: bta_csip_cb.dev_cb) {
+    if (p_cb.addr == bda) {
+      return &p_cb;
+    }
+  }
+
+  return NULL;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_get_dev_cb_by_cid
+ *
+ * Description      Utility function find a device control block by gatt conn id.
+ *
+ * Returns          tBTA_CSIP_DEV_CB (device control block for a given remote.)
+ ******************************************************************************/
+tBTA_CSIP_DEV_CB* bta_csip_get_dev_cb_by_cid(uint16_t conn_id) {
+  auto iter = std::find_if(bta_csip_cb.dev_cb.begin(), bta_csip_cb.dev_cb.end(),
+                           [&conn_id](const tBTA_CSIP_DEV_CB& device) {
+                             return device.conn_id == conn_id;
+                           });
+
+  return (iter == bta_csip_cb.dev_cb.end()) ? nullptr : &(*iter);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_csip_create_dev_cb_for_bda
+ *
+ * Description      Utility function find a device control block by BD address.
+ *
+ * Returns          tBTA_CSIP_DEV_CB (device control block for a given remote.
+ ******************************************************************************/
+tBTA_CSIP_DEV_CB* bta_csip_create_dev_cb_for_bda(const RawAddress& bda) {
+  tBTA_CSIP_DEV_CB p_dev_cb = {};
+  p_dev_cb.addr = bda;
+  p_dev_cb.in_use = true;
+
+  bta_csip_cb.dev_cb.push_back(p_dev_cb);
+
+  return &bta_csip_cb.dev_cb.back();
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_get_cccd_handle
+ *
+ * Description      Utility function to fetch cccd handle of a given characteristic.
+ *
+ * Returns          handle of cccd. 0 if not found.
+ ************************************************************************************/
+uint16_t bta_csip_get_cccd_handle(uint16_t conn_id, uint16_t char_handle) {
+    const gatt::Characteristic* p_char =
+        BTA_GATTC_GetCharacteristic(conn_id, char_handle);
+    if (!p_char) {
+      LOG(WARNING) << __func__ << ": Characteristic not found: " << char_handle;
+      return 0;
+    }
+
+    for (const gatt::Descriptor& desc : p_char->descriptors) {
+      if (desc.uuid == CSIP_CCCD_UUID) {
+        LOG(INFO) << __func__ << " desc handle = " << +desc.handle;
+        return desc.handle;
+      }
+    }
+
+    return 0;
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_add_app_to_applist
+ *
+ * Description      Utility function adds app to connection applist of a
+ *                  given device control block.
+ *
+ * Returns          void
+ ************************************************************************************/
+void bta_csip_add_app_to_applist(tBTA_CSIP_DEV_CB* p_cb, uint8_t app_id) {
+  if (p_cb && !bta_csip_is_app_from_applist(p_cb, app_id)) {
+    LOG(INFO) << __func__ << ": adding app(" << +app_id
+                          << ") to connection applist of " << p_cb->addr;
+    p_cb->conn_applist.push_back(app_id);
+  }
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_is_app_from_applist
+ *
+ * Description      Utility function checks if app is from connection applist of a
+ *                  given device control block.
+ *
+ * Returns          true if app has already sent connect request for CSIP.
+ ************************************************************************************/
+bool bta_csip_is_app_from_applist(tBTA_CSIP_DEV_CB* p_cb, uint8_t app_id) {
+  for (auto i: p_cb->conn_applist) {
+    if (i == app_id) {
+      return (true);
+    }
+  }
+
+  return (false);
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_remove_app_from_conn_list
+ *
+ * Description      Utility function to remove application from connection applist of
+ *                  given device control block
+ *
+ * Returns          void
+ ************************************************************************************/
+void bta_csip_remove_app_from_conn_list(tBTA_CSIP_DEV_CB* p_cb, uint8_t app_id) {
+  p_cb->conn_applist.erase(
+      std::remove(p_cb->conn_applist.begin(), p_cb->conn_applist.end(), app_id),
+                  p_cb->conn_applist.end());
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_send_conn_state_changed_cb
+ *
+ * Description      Utility function to send connection state changed to all
+ *                  registered application in connection app list.
+ *
+ * Returns          void
+ ************************************************************************************/
+void bta_csip_send_conn_state_changed_cb(tBTA_CSIP_DEV_CB* p_cb,
+                                         uint8_t state, uint8_t status) {
+  if (!p_cb) {
+    LOG(ERROR) << __func__ << ": Device CB for " << p_cb->addr << " not found";
+    return;
+  }
+
+  // send connection state change to all apps in conn_applist
+  for (auto i: p_cb->conn_applist) {
+    tBTA_CSIP_CONN_STATE_CHANGED conn_cb_params = {
+        .app_id = i,
+        .addr = p_cb->addr,
+        .state = state,
+        .status =status
+    };
+    if (bta_csip_cb.app_rcb[i].p_cback) {
+      (*bta_csip_cb.app_rcb[i].p_cback)
+          (BTA_CSIP_CONN_STATE_CHG_EVT, (tBTA_CSIP_DATA *)&conn_cb_params);
+    }
+  }
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_send_conn_state_changed_cb
+ *
+ * Description      Utility function to send connection state changed to requesting
+ *                  registered application from connection applist.
+ *
+ * Returns          void
+ ************************************************************************************/
+void bta_csip_send_conn_state_changed_cb (tBTA_CSIP_DEV_CB* p_cb, uint8_t app_id,
+                                          uint8_t state, uint8_t status) {
+
+  tBTA_CSIP_CONN_STATE_CHANGED conn_cb_params = {
+      .app_id = app_id,
+      .addr = p_cb->addr,
+      .state = state,
+      .status =status
+  };
+
+  // send connection state change to the requested App
+  if (bta_csip_cb.app_rcb[app_id].p_cback) {
+    (*bta_csip_cb.app_rcb[app_id].p_cback)
+        (BTA_CSIP_CONN_STATE_CHG_EVT, (tBTA_CSIP_DATA *)&conn_cb_params);
+  }
+
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_process_completed_lock_req
+ *
+ * Description      Utility function to send lock state changed to requesting
+ *                  registered application.
+ *
+ * Returns          void
+ ************************************************************************************/
+void bta_csip_send_lock_req_cmpl_cb (tBTA_LOCK_STATUS_CHANGED response) {
+  if (response.app_id >= BTA_CSIP_MAX_SUPPORTED_APPS ||
+          !bta_csip_cb.app_rcb[response.app_id].in_use) {
+    LOG(ERROR) << __func__ << "Invalid or unregistered application: " << +response.app_id;
+    return;
+  }
+
+  if (bta_csip_cb.app_rcb[response.app_id].p_cback) {
+    (*bta_csip_cb.app_rcb[response.app_id].p_cback)
+        (BTA_CSIP_LOCK_STATUS_CHANGED_EVT, (tBTA_CSIP_DATA *)&response);
+  }
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_write_cccd
+ *
+ * Description      API used to write required characteristic descriptor.
+ *
+ * Returns          void
+ ************************************************************************************/
+void bta_csip_write_cccd (tBTA_CSIP_DEV_CB* p_cb, uint16_t char_handle,
+                          uint16_t cccd_handle) {
+      LOG(INFO) << __func__;
+      // Register for LOCK
+      if (BTA_GATTC_RegisterForNotifications(
+              bta_csip_cb.gatt_if, p_cb->addr, char_handle)) {
+        LOG(ERROR) << __func__
+                   << " Notification Registration failed for char handle: " << +char_handle;
+        return;
+      }
+
+      LOG(INFO) << __func__ << " notification registration successful. handle: " << +char_handle;
+      std::vector<uint8_t> value(2);
+      uint8_t* ptr = value.data();
+      UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
+      BtaGattQueue::WriteDescriptor(p_cb->conn_id, cccd_handle, value,
+                                    GATT_WRITE, nullptr, nullptr);
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_load_coordinated_sets_from_storage
+ *
+ * Description      API used to load coordinated sets from storage on BT ON.
+ *
+ * Returns          void
+ ************************************************************************************/
+void bta_csip_load_coordinated_sets_from_storage () {
+  LOG(INFO) << __func__;
+
+  static const char* CONFIG_FILE_PATH = "/data/misc/bluedroid/bt_config.conf";
+  config_t* config = config_new(CONFIG_FILE_PATH);
+  if (!config) {
+    LOG(INFO) << __func__ << " file "<< CONFIG_FILE_PATH << " not found";
+    return;
+  }
+
+  const config_section_node_t* snode = config_section_begin(config);
+  while (snode != config_section_end(config)) {
+
+    const char* name = config_section_name(snode);
+    if (!RawAddress::IsValidAddress(name)) {
+      snode = config_section_next(snode);
+      continue;
+    }
+
+    const char* key = "DGroup";
+    const char* coordinated_sets = config_get_string(config, name, key, "");
+    if (!strcmp(coordinated_sets, "")) {
+      LOG(INFO) << __func__ << " doesnt support cooedinated set.";
+      snode = config_section_next(snode);
+      continue;
+    }
+
+    RawAddress bdaddr;
+    RawAddress::FromString(name, bdaddr);
+    tBTA_CSIP_DEV_CB* dev_cb = bta_csip_find_dev_cb_by_bda(bdaddr);
+    if (!dev_cb) {
+      dev_cb = bta_csip_create_dev_cb_for_bda(bdaddr);
+    }
+
+    char *next = NULL;
+    char *csets = strdup(coordinated_sets);
+    /* Set Level parsing*/
+    char *set_details = strtok_r(csets, " ", &next);
+
+    do {
+      tBTA_CSIP_CSET *cset = NULL;
+      uint8_t set_id = INVALID_SET_ID, size = 0, rank = 0;
+      uint16_t srvc_handle = 0;
+      bluetooth::Uuid uuid;
+      uint8_t sirk[SIRK_SIZE] = {};
+      bool lock_support = false;
+
+      char *part;
+      char *posn;
+      /* separating properties of set*/
+      part = strtok_r(set_details, "~", &posn);
+
+      while (part != NULL)
+      {
+          char *ptr = NULL;
+          /* Decode property type and its value*/
+          char *prop_details = strtok_r(part, ":", &ptr);
+
+          if (prop_details != NULL) {
+            char* prop_val = strtok_r(NULL, ":", &ptr);
+            if (prop_val) {
+              if (!strcmp(prop_details, "SET_ID")) {
+                set_id = (uint8_t)atoi(prop_val);
+                cset = bta_csip_get_or_create_cset(set_id, true);
+                if (!cset) LOG(INFO) << __func__ << " got cset empty";
+                else LOG(INFO) << "valid " << +cset->set_id;
+              } else if (!strcmp(prop_details, "SIZE")) {
+                size = (uint8_t)atoi(prop_val);
+              } else if (!strcmp(prop_details, "SIRK")) {
+                hex_string_to_byte_arr(prop_val, sirk, SIRK_SIZE * 2);
+              } else if (!strcmp(prop_details, "INCLUDING_SRVC")) {
+                uuid = Uuid::FromString(prop_val);
+              } else if (!strcmp(prop_details, "LOCK_SUPPORT")) {
+                if (!strcmp(prop_val, "true")) lock_support = true;
+              } else if (!strcmp(prop_details, "RANK")) {
+                rank = (uint8_t)atoi(prop_val);
+              } else if (!strcmp(prop_details, "SRVC_HANDLE")) {
+                srvc_handle = (uint16_t)atoi(prop_val);
+              }
+            }
+          }
+
+          part = strtok_r(NULL, "~", &posn);
+      }
+
+      if (set_id < BTA_MAX_SUPPORTED_SETS) {
+        if (!cset) {
+          // Create new coordinated set insatnce and device to it
+          cset = bta_csip_get_or_create_cset(set_id, false);
+          cset->set_id = set_id;
+          cset->size = size;
+          cset->p_srvc_uuid = uuid;
+          cset->total_discovered = 1;
+          cset->set_members.push_back(bdaddr);
+
+          // create coordinated set control block
+          tBTA_CSET_CB* cset_cb = &bta_csip_cb.csets_cb[set_id];
+          cset_cb->set_id = set_id;
+          cset_cb->in_use = true;
+          memcpy(cset_cb->sirk, sirk, SIRK_SIZE);
+          if (rank != 0) {
+            cset_cb->ordered_members.insert({rank, bdaddr});
+          }
+
+        } else {
+          //LOG(INFO) << "Existing set = " << +cset->set_id;
+          cset->total_discovered++;
+          cset->set_members.push_back(bdaddr);
+
+          tBTA_CSET_CB* cset_cb = &bta_csip_cb.csets_cb[set_id];
+          if (rank != 0) {
+            cset_cb->ordered_members.insert({rank, bdaddr});
+          }
+        }
+
+        // assign service properties - set_id and bd_addr
+        tBTA_CSIS_SRVC_INFO *srvc = bta_csip_get_csis_service_cb(dev_cb);
+        if (srvc) {
+          srvc->set_id = set_id;
+          srvc->bd_addr = bdaddr;
+          srvc->size = size;
+          srvc->rank = rank;
+          srvc->service_handle = srvc_handle;
+          memcpy(srvc->sirk, sirk, SIRK_SIZE);
+        }
+      }
+    } while ((set_details = strtok_r(NULL, " ", &next)) != NULL);
+
+    snode = config_section_next(snode);
+  }
+
+  /*LOG(INFO) << "------------------DEBUG----------------------------";
+  LOG(INFO) << "printing all loaded coordinated sets";
+  for (int i = 0; i < (int)bta_csip_cb.csets.size(); i++) {
+    tBTA_CSIP_CSET set = bta_csip_cb.csets[i];
+    LOG(INFO) << " Set ID = " << +set.set_id
+              << " Size = " << +set.size
+              << " total discovered = " << +set.total_discovered;
+    for (int j = 0; j < (int)set.set_members.size(); j++) {
+      LOG(INFO) << " Member (" << +(j+1) <<") = " << set.set_members[j].ToString();
+    }
+  }*/
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_preserve_cset
+ *
+ * Description      function used to preserve coordinated set details to storage.
+ *
+ * Returns          void
+ ************************************************************************************/
+void bta_csip_preserve_cset (tBTA_CSIS_SRVC_INFO* srvc) {
+  tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(srvc->bd_addr);
+
+  if (!p_cb) {
+    LOG(ERROR) << " Device cb record not found for " << srvc->bd_addr;
+    return;
+  }
+
+  std::string& set_info = p_cb->set_info;
+  if (set_info.size() > 0) {
+    set_info += " ";
+  }
+
+  set_info += "SET_ID:" + std::to_string(srvc->set_id);
+
+  if (srvc->size_handle) {
+    set_info += "~SIZE:" + std::to_string(srvc->size);
+  }
+
+  char sirk[SIRK_SIZE * 2 + 1] = {0};
+  byte_arr_to_hex_string(srvc->sirk, sirk, SIRK_SIZE);
+  set_info += "~SIRK:" + std::string(sirk);
+
+  if (!srvc->including_srvc_uuid.IsEmpty()) {
+    set_info += "~INCLUDING_SRVC:" + srvc->including_srvc_uuid.ToString();
+  }
+
+  if (srvc->lock_handle) {
+    set_info += "~LOCK_SUPPORT:" + std::string((srvc->lock_handle ? "true" : "false"));
+  }
+
+  if (srvc->rank_handle) {
+    set_info += "~RANK:" + std::to_string(srvc->rank);
+  }
+
+  set_info += "~SRVC_HANDLE:" + std::to_string(srvc->service_handle);
+
+  LOG(INFO) << __func__ << " " << set_info;
+
+  btif_config_set_str(p_cb->addr.ToString().c_str(),
+      "DGroup", set_info.c_str());
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_get_salt
+ *
+ * Description      function s1: Used to compute SALT.
+ *
+ * Returns          Octet16 (cipher - SALT)
+ ************************************************************************************/
+Octet16 bta_csip_get_salt() {
+  Octet16 salt = {};
+  Octet16 zero = {};
+  uint8_t SIRKenc[] = {0x53, 0x49, 0x52, 0x4B, 0x65, 0x6E, 0x63};
+
+  salt = bta_csip_get_aes_cmac_result(zero, SIRKenc, 7);
+
+  return salt;
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_compute_T
+ *
+ * Description      First step of k1 function. Used to compute T from SALT and K.
+ *
+ * Returns          Octet16 (cipher - T)
+ ************************************************************************************/
+Octet16 bta_csip_compute_T(Octet16 salt, Octet16 K) {
+  Octet16 T = {};
+
+  T = bta_csip_get_aes_cmac_result(salt, K);
+
+  return T;
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_get_aes_cmac_result
+ *
+ * Description      Second step of k1 function. Used to compute k1 from T and "csis".
+ *
+ * Returns          Octet16 (cipher - k1)
+ ************************************************************************************/
+Octet16 bta_csip_compute_k1(Octet16 T) {
+  Octet16 k1 = {};
+  uint8_t csis[] = {0x63,0x73,0x69,0x73};
+
+  k1 = bta_csip_get_aes_cmac_result(T, csis, 4);
+
+  return k1;
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_get_aes_cmac_result
+ *
+ * Description      sdf function. Used to compute SIRK from encrypted SIRK and k1.
+ *
+ * Returns          Octet16 (SIRK)
+ ************************************************************************************/
+void  bta_csip_get_decrypted_sirk(Octet16 k1, uint8_t *enc_sirk, uint8_t *sirk) {
+  for (int i = 0; i < 16; i++) {
+    sirk[i] = k1[i] ^ enc_sirk[i];
+  }
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_get_aes_cmac_result
+ *
+ * Description      Used to get aes-cmac result (for 16 byte input and output
+ *                  in LSB->MSB order)
+ *
+ * Returns          Octet16 (cipher)
+ ************************************************************************************/
+Octet16 bta_csip_get_aes_cmac_result(const Octet16& key, const Octet16& message) {
+  Octet16 r_key, r_message, r_result;
+
+  // reverse inputs as required by crypto_toolbox::aes_cmac
+  std::reverse_copy(key.begin(), key.end(), r_key.begin());
+  std::reverse_copy(message.begin(), message.end(), r_message.begin());
+
+  Octet16 result = crypto_toolbox::aes_cmac(r_key, r_message.data(), r_message.size());
+
+  // reverse the result to get LSB->MSB order
+  std::reverse_copy(result.begin(), result.end(), r_result.begin());
+
+  return r_result;
+}
+
+/************************************************************************************
+ *
+ * Function         bta_csip_get_aes_cmac_result
+ *
+ * Description      Used to get aes-cmac result (for variable input and output
+ *                  in LSB->MSB order)
+ *
+ * Returns          Octet16 (cipher)
+ ************************************************************************************/
+Octet16 bta_csip_get_aes_cmac_result(const Octet16& key, const uint8_t* input,
+                                     uint16_t length) {
+  Octet16 r_key, r_result;
+
+  // reverse inputs as required by crypto_toolbox::aes_cmac
+  std::reverse_copy(key.begin(), key.end(), r_key.begin());
+  uint8_t *input_buf = (uint8_t *)osi_malloc(length);
+  for (int i = 0; i < length; i++) {
+    input_buf[i] = input[length - 1 - i];
+  }
+
+  Octet16 result = crypto_toolbox::aes_cmac(r_key, input_buf, length);
+
+  // reverse the result to get LSB->MSB order
+  std::reverse_copy(result.begin(), result.end(), r_result.begin());
+
+  return r_result;
+}
+
+/************************************************************************************
+ *
+ * Function         byte_arr_to_hex_string
+ *
+ * Description      function used to get hex representation in string format for byte[].
+ *
+ * Returns          void
+ ************************************************************************************/
+void byte_arr_to_hex_string(uint8_t* byte_arr, char* str, uint8_t len) {
+  int i;
+  LOG(INFO) << __func__ << " Convert byte array to hex format string";
+
+  for (i = 0; i < len; i++)
+  {
+      snprintf(str + (i * 2), (len * 2 + 1), "%02X", byte_arr[i]);
+  }
+}
+
+/************************************************************************************
+ *
+ * Function         hex_string_to_byte_arr
+ *
+ * Description      function used to get byte array from hex format string.
+ *
+ * Returns          void
+ ************************************************************************************/
+void hex_string_to_byte_arr(char *str, uint8_t* byte_arr, uint8_t len) {
+  for (int length = 0; *str; str += 2, length++)
+     sscanf(str, "%02hhx", &byte_arr[length]);
+}
+
+/************************************************************************************
+ *
+ * Function         is_key_empty
+ *
+ * Description      function used to check if key is 0 intialized.
+ *
+ * Returns          true, if all elements are 0. Otherwise, false.
+ ************************************************************************************/
+bool is_key_empty(Octet16& key) {
+  for (unsigned int i = 0; i < key.size(); i++) {
+    if (key[i] != 0) return false;
+  }
+  return true;
+}
diff --git a/le_audio/system/bt/bta/dm/bta_dm_adv_audio.cc b/le_audio/system/bt/bta/dm/bta_dm_adv_audio.cc
new file mode 100644
index 0000000..4ff1ea4
--- /dev/null
+++ b/le_audio/system/bt/bta/dm/bta_dm_adv_audio.cc
@@ -0,0 +1,1186 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+#define LOG_TAG "bt_bta_dm_adv_audio"
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <string.h>
+
+#include "bt_common.h"
+#include "bt_target.h"
+#include "bt_types.h"
+#include "bta_api.h"
+#include "bta_dm_api.h"
+#include "bta_dm_co.h"
+#include "bta/dm/bta_dm_int.h"
+#include "bta_csip_api.h"
+#include "bta_sys.h"
+#include "btif/include/btif_storage.h"
+#include "btm_api.h"
+#include "btm_int.h"
+#include "btu.h"
+#include "gap_api.h" /* For GAP_BleReadPeerPrefConnParams */
+#include "l2c_api.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+#include "sdp_api.h"
+#include "bta_sdp_api.h"
+#include "stack/gatt/connection_manager.h"
+#include "stack/include/gatt_api.h"
+#include "utl.h"
+#include "device/include/interop_config.h"
+#include "device/include/profile_config.h"
+#include "device/include/interop.h"
+#include "stack/sdp/sdpint.h"
+#include <inttypes.h>
+#include "btif/include/btif_config.h"
+#include "device/include/device_iot_config.h"
+#include <btcommon_interface_defs.h>
+#include <controller.h>
+#include "bta_gatt_queue.h"
+#include "bta_dm_adv_audio.h"
+#include "btif/include/btif_dm_adv_audio.h"
+
+#if (GAP_INCLUDED == TRUE)
+#include "gap_api.h"
+#endif
+
+using bluetooth::Uuid;
+
+#define ADV_AUDIO_VOICE_ROLE_BIT 2
+#define ADV_AUDIO_MEDIA_ROLE_BIT 8
+#define CONN_LESS_MEDIA_SINK_ROLE_BIT 32
+#define CONN_LESS_ASSIST_ROLE_BIT 64
+#define CONN_LESS_DELEGATE_ROLE_BIT 128
+#define PACS_CT_SUPPORT_VALUE 2
+#define PACS_UMR_SUPPORT_VALUE 4
+#define PACS_CONVERSATIONAL_ROLE_VALUE 2
+#define PACS_MEDIA_ROLE_VALUE 4
+
+#define BTA_DM_ADV_AUDIO_GATT_CLOSE_DELAY_TOUT 5000
+
+Uuid UUID_SERVCLASS_WMCP = Uuid::FromString
+                             ("2587db3c-ce70-4fc9-935f-777ab4188fd7");
+
+std::vector<bluetooth::Uuid> uuid_srv_disc_search;
+tBTA_LE_AUDIO_DEV_CB bta_le_audio_dev_cb;
+tBTA_LEA_PAIRING_DB bta_lea_pairing_cb;
+extern void bta_dm_proc_open_evt(tBTA_GATTC_OPEN* p_data);
+bool is_adv_audio_unicast_supported(RawAddress rem_bda, int conn_id);
+
+
+/***************************************************************************
+ *
+ * Function         bta_get_lea_ctrl_cb
+ *
+ * Description      Gets the control block of LE audio device
+ *
+ * Parameters:      tBTA_LE_AUDIO_DEV_INFO*
+ *
+ ****************************************************************************/
+
+tBTA_LE_AUDIO_DEV_INFO* bta_get_lea_ctrl_cb(RawAddress peer_addr) {
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = NULL;
+  p_lea_cb = &bta_le_audio_dev_cb.bta_lea_dev_info[0];
+
+  for (int i = 0; i < MAX_LEA_DEVICES ; i++) {
+      if (p_lea_cb[i].in_use &&
+        (p_lea_cb[i].peer_address == peer_addr)) {
+        APPL_TRACE_DEBUG(" %s Control block Found for addr %s",
+          __func__, peer_addr.ToString().c_str());
+        return &p_lea_cb[i];
+      }
+  }
+  APPL_TRACE_DEBUG(" %s Control block Not Found for addr %s",
+          __func__, peer_addr.ToString().c_str());
+  return NULL;
+}
+
+/* Callback received when remote device Coordinated Sets SIRK is read */
+void bta_gap_gatt_read_cb(uint16_t conn_id, tGATT_STATUS status,
+                  uint16_t handle, uint16_t len,
+                  uint8_t* value, void* data) {
+
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb =
+    bta_get_lea_ctrl_cb(bta_le_audio_dev_cb.gatt_op_addr);
+  uint32_t role = 0;
+  uint8_t *p_val = value;
+
+  STREAM_TO_ARRAY(&role, p_val, len);
+
+  if (p_lea_cb) {
+    APPL_TRACE_DEBUG("%s Addr %s ", __func__,
+      p_lea_cb->peer_address.ToString().c_str());
+    if (status == GATT_SUCCESS) {
+      if (p_lea_cb->t_role_handle == handle) {
+        LOG(INFO) << __func__ << " Role derived by T_ADV_AUDIO "
+          << +role;
+        if (role != 0)  {
+          if (role & ADV_AUDIO_VOICE_ROLE_BIT)
+            p_lea_cb->uuids.push_back(Uuid::From16Bit
+                (UUID_SERVCLASS_T_ADV_AUDIO_VOICE));
+          if (role & ADV_AUDIO_MEDIA_ROLE_BIT)
+            p_lea_cb->uuids.push_back(Uuid::From16Bit
+                (UUID_SERVCLASS_T_ADV_AUDIO_MEDIA_SINK));
+          if (role & CONN_LESS_MEDIA_SINK_ROLE_BIT)
+            p_lea_cb->uuids.push_back(Uuid::From16Bit
+                (UUID_SERVCLASS_T_ADV_AUDIO_CONN_LESS_MEDIA_SINK));
+          if (role & CONN_LESS_ASSIST_ROLE_BIT)
+            p_lea_cb->uuids.push_back(Uuid::From16Bit
+                (UUID_SERVCLASS_T_ADV_AUDIO_ASSIST));
+          if (role & CONN_LESS_DELEGATE_ROLE_BIT)
+            p_lea_cb->uuids.push_back(Uuid::From16Bit
+                (UUID_SERVCLASS_T_ADV_AUDIO_DELEGATE));
+        }
+        p_lea_cb->disc_progress--;
+      } else if(handle == p_lea_cb->pacs_char_handle) {
+        LOG(INFO) << __func__ << " derived by PACS " << +role;
+        if (role == 0) {
+          LOG(INFO) << __func__ << " Invalid Information ";
+        } else {
+          if (is_adv_audio_unicast_supported(bta_le_audio_dev_cb.gatt_op_addr, conn_id)) {
+            LOG(INFO) << __func__ << " ASCS Supported by the remote ";
+            if ((role & PACS_CONVERSATIONAL_ROLE_VALUE) == PACS_CT_SUPPORT_VALUE)
+              p_lea_cb->uuids.push_back(Uuid::From16Bit(UUID_SERVCLASS_PACS_CT_SUPPORT));
+            if ((role & PACS_MEDIA_ROLE_VALUE) == PACS_UMR_SUPPORT_VALUE)
+              p_lea_cb->uuids.push_back(Uuid::From16Bit(UUID_SERVCLASS_PACS_UMR_SUPPORT));
+          }
+          //TODO LEA_DBG Call API which will be provided by BAP
+        }
+        p_lea_cb->disc_progress--;
+      } else {
+        LOG(INFO) << __func__ << " Invalid Handle for LE AUDIO";
+      }
+    } else {
+      p_lea_cb->disc_progress--;
+      LOG(INFO) << __func__ << " GATT READ FAILED" ;
+    }
+
+    if (p_lea_cb->disc_progress <= 0) {
+      bta_dm_lea_disc_complete(p_lea_cb->peer_address);
+    }
+  } else {
+    LOG(INFO) << __func__ << " INVALID CONTROL BLOCK" ;
+  }
+
+}
+
+
+/*******************************************************************************
+ *
+ * Function         bta_get_adv_audio_role
+ *
+ * Description      This API gets role for LE Audio Device after all services
+ *                  discovered
+ *
+ * Parameters:      none
+ *
+ ******************************************************************************/
+void bta_get_adv_audio_role(RawAddress peer_address, uint16_t conn_id,
+                                  tGATT_STATUS status) {
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = bta_get_lea_ctrl_cb(peer_address);
+
+  bta_le_audio_dev_cb.gatt_op_addr = peer_address;
+
+  if (p_lea_cb == NULL) {
+    APPL_TRACE_ERROR(" %s Control block didnt find for peer address %s", __func__,
+        peer_address.ToString().c_str());
+    return;
+  }
+
+  // Fetch remote device gatt services from database
+  const std::vector<gatt::Service>* services = BTA_GATTC_GetServices(conn_id);
+
+  if (services) {
+    APPL_TRACE_DEBUG(" bta_get_adv_audio_role SIZE %d addr %s conn_id %d",
+      (*services).size(),bta_le_audio_dev_cb.gatt_op_addr.ToString().c_str(),
+      conn_id);
+
+    // Search for CSIS service in the database
+    for (const gatt::Service& service : *services) {
+      APPL_TRACE_DEBUG("%s: SERVICES IN REMOTE DEVICE %s ", __func__,
+              service.uuid.ToString().c_str())
+      if (is_le_audio_service(service.uuid)) {
+        size_t len = service.uuid.GetShortestRepresentationSize();
+        uint16_t uuid_val = 0;
+        if (len == Uuid::kNumBytes16) {
+          uuid_val = service.uuid.As16Bit();
+        } else if(len == Uuid::kNumBytes128) {
+          if (service.uuid == UUID_SERVCLASS_WMCP) {
+            APPL_TRACE_DEBUG("%s: WMCP Service UUId found", __func__);
+            std::vector<bluetooth::Uuid>::iterator itr;
+            itr = std::find(p_lea_cb->uuids.begin(), p_lea_cb->uuids.end(),
+                UUID_SERVCLASS_WMCP);
+            if (itr == p_lea_cb->uuids.end()) {
+              p_lea_cb->uuids.push_back(UUID_SERVCLASS_WMCP);
+            }
+          }
+        }
+
+        switch (uuid_val) {
+          case UUID_SERVCLASS_CSIS:
+          {
+            APPL_TRACE_DEBUG("%s:CSIS service found Uuid: %s ", __func__,
+                              service.uuid.ToString().c_str());
+
+            p_lea_cb->is_csip_support = true;
+            bta_dm_csis_disc_complete(bta_dm_search_cb.peer_bdaddr, false);
+            // Get Characteristic and CCCD handle
+            for (const gatt::Characteristic& charac : service.characteristics) {
+              Uuid lock_uuid = charac.uuid;
+              if (lock_uuid.As16Bit() == UUID_SERVCLASS_CSIS_LOCK) {
+                APPL_TRACE_DEBUG("%s: CSIS rank found Uuid: %s ", __func__,
+                    lock_uuid.ToString().c_str());
+                if (p_lea_cb != NULL) {
+                  Uuid csip_lock_uuid = Uuid::FromString("6AD8");
+                  std::vector<bluetooth::Uuid>::iterator itr;
+                  itr = std::find(p_lea_cb->uuids.begin(), p_lea_cb->uuids.end(),
+                    csip_lock_uuid);
+                  if (itr == p_lea_cb->uuids.end()) {
+                    p_lea_cb->uuids.push_back(csip_lock_uuid);
+                  }
+                } else {
+                  APPL_TRACE_DEBUG(" %s No Control Block", __func__);
+                }
+              }
+            }
+          }
+          break;
+          case UUID_SERVCLASS_T_ADV_AUDIO:
+          {
+            if (!p_lea_cb->is_has_found) {
+              APPL_TRACE_DEBUG("%s: T_ADV_AUDIO service found Uuid: %s ", __func__,
+                service.uuid.ToString().c_str());
+              std::vector<bluetooth::Uuid>::iterator itr;
+              itr = std::find(p_lea_cb->uuids.begin(), p_lea_cb->uuids.end(),
+                service.uuid);
+              if (itr == p_lea_cb->uuids.end()) {
+                p_lea_cb->uuids.push_back(service.uuid);
+              }
+              // Get Characteristic and CCCD handle
+              for (const gatt::Characteristic& charac : service.characteristics) {
+                Uuid role_uuid = charac.uuid;
+                if (role_uuid.As16Bit() == UUID_SERVCLASS_T_ADV_AUDIO_ROLE_CHAR) {
+                  APPL_TRACE_DEBUG("%s:T_ADV_AUDIO ROLE CHAR found Uuid: %s ", __func__,
+                      role_uuid.ToString().c_str());
+                  if (p_lea_cb != NULL) {
+                    p_lea_cb->is_t_audio_srvc_found = true;
+                    p_lea_cb->disc_progress++;
+                    p_lea_cb->t_role_handle = charac.value_handle;
+                  } else {
+                    APPL_TRACE_DEBUG(" %s No Control Block", __func__);
+                  }
+                }
+              }
+              if (p_lea_cb->t_role_handle) {
+                APPL_TRACE_DEBUG("%s t_role_handle %d", __func__,
+                  p_lea_cb->t_role_handle);
+                BtaGattQueue::ReadCharacteristic(conn_id, p_lea_cb->t_role_handle,
+                  bta_gap_gatt_read_cb, NULL);
+              }
+            }
+          }
+          break;
+          case UUID_SERVCLASS_HAS:
+            if (!p_lea_cb->is_t_audio_srvc_found) {
+              p_lea_cb->is_has_found = true;
+              APPL_TRACE_DEBUG("%s: HAS service found Uuid: %s ", __func__,
+                service.uuid.ToString().c_str());
+              std::vector<bluetooth::Uuid>::iterator itr;
+              itr = std::find(p_lea_cb->uuids.begin(), p_lea_cb->uuids.end(),
+                service.uuid);
+              if (itr == p_lea_cb->uuids.end()) {
+                p_lea_cb->uuids.push_back(service.uuid);
+              }
+            }
+            FALLTHROUGH_INTENDED; /* FALLTHROUGH */
+          case UUID_SERVCLASS_PACS:
+          {
+            if ((!p_lea_cb->pacs_char_handle) &&
+              ((!p_lea_cb->is_t_audio_srvc_found))) {
+              APPL_TRACE_DEBUG("%s:PACS service found Uuid: %s ", __func__,
+                service.uuid.ToString().c_str());
+
+              std::vector<bluetooth::Uuid>::iterator itr;
+              itr = std::find(p_lea_cb->uuids.begin(), p_lea_cb->uuids.end(),
+                service.uuid);
+              if (itr == p_lea_cb->uuids.end()) {
+                p_lea_cb->uuids.push_back(service.uuid);
+              }
+              // Get Characteristic and CCCD handle
+              for (const gatt::Characteristic& charac : service.characteristics) {
+                Uuid role_uuid = charac.uuid;
+                if (role_uuid.As16Bit() == UUID_SERVCLASS_SOURCE_CONTEXT) {
+                  APPL_TRACE_DEBUG("%s: PACS Source context CHAR found Uuid: %s ",
+                    __func__, role_uuid.ToString().c_str());
+                  if (p_lea_cb != NULL) {
+                    p_lea_cb->disc_progress++;
+                    p_lea_cb->pacs_char_handle = charac.value_handle;
+                  } else {
+                    APPL_TRACE_DEBUG(" %s No Control Block", __func__);
+                  }
+                }
+              }
+              if (p_lea_cb->pacs_char_handle) {
+                BtaGattQueue::ReadCharacteristic(conn_id, p_lea_cb->pacs_char_handle,
+                  bta_gap_gatt_read_cb, NULL);
+              }
+            }
+          }
+          break;
+          default:
+            APPL_TRACE_DEBUG(" Not a LE AUDIO SERVICE-- IGNORE %s ",
+              service.uuid.ToString().c_str());
+        }
+      }
+    }
+  }
+
+  if (p_lea_cb->disc_progress == 0) {
+    bta_dm_lea_disc_complete(peer_address);
+  }
+}
+
+/*****************************************************************************
+ *
+ * Function         bta_dm_csis_disc_complete
+ *
+ * Description      This API updates csis discovery complete status
+ *
+ * Parameters:      none
+ *****************************************************************************/
+void bta_dm_csis_disc_complete(RawAddress p_bd_addr, bool status) {
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = bta_get_lea_ctrl_cb(p_bd_addr);
+  APPL_TRACE_DEBUG("%s %s %d", __func__, p_bd_addr.ToString().c_str(),
+      status);
+
+  if (p_lea_cb) {
+    p_lea_cb->csip_disc_progress = status;
+  } else {
+    RawAddress pseudo_addr = bta_get_pseudo_addr_with_id_addr(p_bd_addr);
+    if (pseudo_addr != RawAddress::kEmpty) {
+      p_lea_cb = bta_get_lea_ctrl_cb(pseudo_addr);
+      if (p_lea_cb) {
+        p_lea_cb->csip_disc_progress = status;
+        APPL_TRACE_DEBUG(" %s Pseudo addr disc_progress resetted", __func__);
+      } else {
+        APPL_TRACE_DEBUG(" %s No Control Block for pseudo addr", __func__);
+      }
+    } else {
+      APPL_TRACE_DEBUG(" %s No Control Block", __func__);
+    }
+  }
+}
+
+/*****************************************************************************
+ *
+ * Function         bta_dm_lea_disc_complete
+ *
+ * Description      This API sends the event to upper layer that LE audio
+ *                  gatt operations are complete.
+ *
+ * Parameters:      none
+ *
+ ****************************************************************************/
+void bta_dm_lea_disc_complete(RawAddress p_bd_addr) {
+  tBTA_DM_SEARCH result;
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = bta_get_lea_ctrl_cb(p_bd_addr);
+  APPL_TRACE_DEBUG("%s %s", __func__, p_bd_addr.ToString().c_str());
+
+  if (p_lea_cb == NULL) {
+    RawAddress pseudo_addr = bta_get_pseudo_addr_with_id_addr(p_bd_addr);
+    p_lea_cb = bta_get_lea_ctrl_cb(pseudo_addr);
+    p_bd_addr = pseudo_addr;
+  }
+
+  if (p_lea_cb) {
+  APPL_TRACE_DEBUG("csip_disc_progress %d", p_lea_cb->csip_disc_progress);
+    if ((p_lea_cb->disc_progress == 0) &&
+        (p_lea_cb->csip_disc_progress)) { //Add CSIS check also
+      result.adv_audio_disc_cmpl.num_uuids = 0;
+      for (uint16_t i = 0; i < p_lea_cb->uuids.size(); i++) {
+        result.adv_audio_disc_cmpl.adv_audio_uuids[i] = p_lea_cb->uuids[i];
+        result.adv_audio_disc_cmpl.num_uuids++;
+      }
+
+      result.adv_audio_disc_cmpl.bd_addr = p_bd_addr;
+      APPL_TRACE_DEBUG("Sending Call back with  no of uuids's"
+        "p_lea_cb->uuids.size() %d", p_lea_cb->uuids.size());
+      bta_dm_search_cb.p_search_cback(BTA_DM_LE_AUDIO_SEARCH_CMPL_EVT, &result);
+    } else {
+      APPL_TRACE_DEBUG("%s Discovery in progress", __func__);
+    }
+  } else {
+    APPL_TRACE_DEBUG(" %s No Control Block", __func__);
+  }
+}
+
+
+/*****************************************************************************
+ *
+ * Function         bta_add_adv_audio_uuid
+ *
+ * Description      This is GATT client callback function used in DM.
+ *
+ * Parameters:
+ *
+ ******************************************************************************/
+void bta_add_adv_audio_uuid(RawAddress peer_address,
+                               tBTA_GATT_ID srvc_uuid) {
+  auto itr = find(uuid_srv_disc_search.begin(),
+                  uuid_srv_disc_search.end(), srvc_uuid.uuid);
+
+  if(itr != uuid_srv_disc_search.end()) {
+    tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = bta_get_lea_ctrl_cb(peer_address);
+    if (p_lea_cb != NULL) {
+      APPL_TRACE_DEBUG(" %s Control Block Found", __func__);
+
+      std::vector<bluetooth::Uuid>::iterator itr;
+      itr = std::find(p_lea_cb->uuids.begin(), p_lea_cb->uuids.end(), srvc_uuid.uuid);
+      if (itr == p_lea_cb->uuids.end()) {
+        p_lea_cb->uuids.push_back(srvc_uuid.uuid);
+      }
+    } else {
+      APPL_TRACE_DEBUG(" %s No Control Block", __func__);
+    }
+  }
+}
+
+
+
+
+/*******************************************************************************
+ *
+ * Function         bta_set_lea_ctrl_cb
+ *
+ * Description      This is GATT client callback function used in DM.
+ *
+ * Parameters:
+ *
+ ******************************************************************************/
+
+tBTA_LE_AUDIO_DEV_INFO* bta_set_lea_ctrl_cb(RawAddress peer_addr) {
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = NULL;
+
+  p_lea_cb = bta_get_lea_ctrl_cb(peer_addr);
+
+  if (p_lea_cb == NULL) {
+    APPL_TRACE_DEBUG("%s Control block create ", __func__);
+
+    for (int i = 0; i < MAX_LEA_DEVICES ; i++) {
+      if (!bta_le_audio_dev_cb.bta_lea_dev_info[i].in_use) {
+        bta_le_audio_dev_cb.bta_lea_dev_info[i].peer_address = peer_addr;
+        bta_le_audio_dev_cb.bta_lea_dev_info[i].in_use = true;
+        bta_le_audio_dev_cb.bta_lea_dev_info[i].csip_disc_progress = true;
+        bta_le_audio_dev_cb.bta_lea_dev_info[i].is_csip_support = false;
+        bta_le_audio_dev_cb.bta_lea_dev_info[i].gatt_disc_progress = true;
+        bta_le_audio_dev_cb.num_lea_devices++;
+        return (&(bta_le_audio_dev_cb.bta_lea_dev_info[i]));
+      }
+    }
+  } else {
+    return p_lea_cb;
+  }
+  return NULL;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_dm_reset_adv_audio_dev_info
+ *
+ * Description      This is GATT client callback function used in DM.
+ *
+ * Parameters:
+ *
+ ******************************************************************************/
+void bta_dm_reset_adv_audio_dev_info(RawAddress p_addr) {
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = bta_get_lea_ctrl_cb(p_addr);
+
+  if (p_lea_cb != NULL) {
+    p_lea_cb->peer_address = RawAddress::kEmpty;
+    p_lea_cb->disc_progress = 0;
+    p_lea_cb->conn_id = 0;
+    p_lea_cb->transport = 0;
+    p_lea_cb->in_use = false;
+    p_lea_cb->t_role_handle = 0;
+    p_lea_cb->is_has_found = false;
+    p_lea_cb->is_t_audio_srvc_found = false;
+    p_lea_cb->pacs_char_handle = 0;
+    p_lea_cb->using_bredr_bonding = 0;
+    p_lea_cb->gatt_disc_progress = false;
+    p_lea_cb->uuids.clear();
+    bta_le_audio_dev_cb.gatt_op_addr = RawAddress::kEmpty;
+    bta_le_audio_dev_cb.pending_peer_addr = RawAddress::kEmpty;
+    bta_le_audio_dev_cb.num_lea_devices--;
+    bta_le_audio_dev_cb.bond_progress = false;
+    APPL_TRACE_DEBUG("bta_dm_reset_adv_audio_dev_info %s  transport %d ",
+      p_lea_cb->peer_address.ToString().c_str(), p_lea_cb->transport);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_dm_set_adv_audio_dev_info
+ *
+ * Description      This is GATT client callback function used in DM.
+ *
+ * Parameters:
+ *
+ ******************************************************************************/
+void bta_dm_set_adv_audio_dev_info(tBTA_GATTC_OPEN* p_data) {
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = bta_set_lea_ctrl_cb(p_data->remote_bda);
+
+  if (p_lea_cb != NULL) {
+    p_lea_cb->peer_address = p_data->remote_bda;
+    p_lea_cb->disc_progress = 0;
+    p_lea_cb->conn_id = p_data->conn_id;
+    p_lea_cb->transport = p_data->transport;//BTM_UseLeLink(p_data->remote_bda);
+    APPL_TRACE_DEBUG("bta_dm_set_adv_audio_dev_info %s  transport %d ",
+      p_lea_cb->peer_address.ToString().c_str(), p_lea_cb->transport);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         is_adv_audio_unicast_supported
+ *
+ * Description      This function checks whether unicast support is there or not on
+ *                  remote side
+ *
+ * Parameters:
+ *
+ ******************************************************************************/
+
+bool is_adv_audio_unicast_supported(RawAddress rem_bda, int conn_id) {
+  const std::vector<gatt::Service>* services = BTA_GATTC_GetServices(conn_id);
+
+  if (services) {
+    for (const gatt::Service& service : *services) {
+      uint16_t uuid_val = service.uuid.As16Bit();
+      if (uuid_val == UUID_SERVCLASS_ASCS)
+        return true;
+    }
+  }
+
+  return false;
+}
+
+/*******************************************************************************
+ *
+ * Function         is_adv_audio_group_supported
+ *
+ * Description      This function checks whether csip support is there or not on
+ *                  remote side
+ *
+ * Parameters:
+ *
+ ******************************************************************************/
+
+bool is_adv_audio_group_supported(RawAddress rem_bda, int conn_id) {
+  const std::vector<gatt::Service>* services = BTA_GATTC_GetServices(conn_id);
+
+  if (services) {
+    for (const gatt::Service& service : *services) {
+      if (is_le_audio_service(service.uuid)) {
+        uint16_t uuid_val = service.uuid.As16Bit();
+        if (uuid_val == UUID_SERVCLASS_CSIS)
+          return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_dm_lea_gattc_callback
+ *
+ * Description      This is GATT client callback function used in DM.
+ *
+ * Parameters:
+ *
+ ******************************************************************************/
+
+void bta_dm_lea_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
+  APPL_TRACE_DEBUG("bta_dm_lea_gattc_callback event = %d", event);
+
+  switch (event) {
+    case BTA_GATTC_OPEN_EVT:
+      if (p_data->status != GATT_SUCCESS) {
+        btif_dm_release_action_uuid(bta_le_audio_dev_cb.pending_peer_addr);
+      } else {
+        if (is_remote_support_adv_audio(bta_le_audio_dev_cb.pending_peer_addr)) {
+          bta_dm_set_adv_audio_dev_info(&p_data->open);
+        }
+        bta_dm_proc_open_evt(&p_data->open);
+      }
+      break;
+
+    case BTA_GATTC_SEARCH_RES_EVT:
+      if (is_remote_support_adv_audio(bta_le_audio_dev_cb.pending_peer_addr)) {
+        bta_add_adv_audio_uuid(bta_le_audio_dev_cb.pending_peer_addr,
+                           p_data->srvc_res.service_uuid);
+      }
+      break;
+
+    case BTA_GATTC_SEARCH_CMPL_EVT:
+      if (is_remote_support_adv_audio(bta_le_audio_dev_cb.pending_peer_addr)) {
+        bta_get_adv_audio_role(bta_le_audio_dev_cb.pending_peer_addr,
+            p_data->search_cmpl.conn_id,
+            p_data->search_cmpl.status);
+        if (is_adv_audio_group_supported(bta_le_audio_dev_cb.pending_peer_addr,
+             p_data->search_cmpl.conn_id)) {
+          RawAddress p_id_addr =
+            bta_get_rem_dev_id_addr(bta_le_audio_dev_cb.pending_peer_addr);
+          if (p_id_addr != RawAddress::kEmpty) {
+            BTA_CsipFindCsisInstance(p_data->search_cmpl.conn_id,
+                p_data->search_cmpl.status,
+                p_id_addr);
+          } else {
+            BTA_CsipFindCsisInstance(p_data->search_cmpl.conn_id,
+                p_data->search_cmpl.status,
+                bta_le_audio_dev_cb.pending_peer_addr);
+          }
+        }
+      }
+      break;
+
+    case BTA_GATTC_CLOSE_EVT:
+      APPL_TRACE_DEBUG("BTA_GATTC_CLOSE_EVT reason = %d, data conn_id %d,"
+          "search conn_id %d", p_data->close.reason, p_data->close.conn_id,
+          bta_dm_search_cb.conn_id);
+
+      if (is_remote_support_adv_audio(bta_le_audio_dev_cb.pending_peer_addr)) {
+        bta_dm_reset_adv_audio_dev_info(bta_le_audio_dev_cb.pending_peer_addr);
+      }
+      break;
+
+    default:
+      break;
+  }
+}
+
+/******************************************************************************
+ *
+ * Function         bta_dm_adv_audio_gatt_conn
+ *
+ * Description      This API opens the gatt conn after finding sdp record
+ *                  during BREDR Discovery
+ *
+ * Parameters:      none
+ *
+ ******************************************************************************/
+void bta_dm_adv_audio_gatt_conn(RawAddress p_bd_addr) {
+  APPL_TRACE_DEBUG("bta_dm_adv_audio_gatt_conn ");
+
+  bta_le_audio_dev_cb.pending_peer_addr = p_bd_addr;
+
+  tBTA_LE_AUDIO_DEV_INFO *tmp_lea_cb = bta_get_lea_ctrl_cb(p_bd_addr);
+  if (tmp_lea_cb && tmp_lea_cb->in_use) {
+    APPL_TRACE_DEBUG("bta_dm_adv_audio_gatt_conn Already exists %d",
+      tmp_lea_cb->conn_id);
+    return;
+  }
+
+  BTA_GATTC_AppRegister(bta_dm_lea_gattc_callback,
+      base::Bind([](uint8_t client_id, uint8_t status) {
+        if (status == GATT_SUCCESS) {
+          tBTA_LE_AUDIO_DEV_INFO *p_lea_cb =
+            bta_set_lea_ctrl_cb(bta_le_audio_dev_cb.pending_peer_addr);
+            if (p_lea_cb) {
+              APPL_TRACE_DEBUG("bta_dm_adv_audio_gatt_conn Client Id: %d",
+                  client_id);
+              p_lea_cb->gatt_if = client_id;
+              p_lea_cb->using_bredr_bonding = true;
+              BTA_GATTC_Open(client_id, bta_le_audio_dev_cb.pending_peer_addr,
+                  true, GATT_TRANSPORT_LE, false);
+            }
+        }
+        }), false);
+
+}
+
+/******************************************************************************
+ *
+ * Function         bta_dm_adv_audio_close
+ *
+ * Description      This API closes the gatt conn with was opened by dm layer
+ *                  for service discovery (or) opened after finding sdp record
+ *                  during BREDR Discovery
+ *
+ * Parameters:      none
+ *
+ ******************************************************************************/
+void bta_dm_adv_audio_close(RawAddress p_bd_addr) {
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = bta_get_lea_ctrl_cb(p_bd_addr);
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  if (p_lea_cb) {
+    APPL_TRACE_DEBUG("%s %d", __func__, p_lea_cb->gatt_if);
+    if (p_lea_cb->using_bredr_bonding) {
+      APPL_TRACE_DEBUG("%s closing LE conn est due to bredr bonding  %d", __func__,
+          p_lea_cb->gatt_if);
+      BTA_GATTC_AppDeregister(p_lea_cb->gatt_if);
+    } else {
+      bta_sys_start_timer(bta_dm_search_cb.gatt_close_timer,
+          BTA_DM_ADV_AUDIO_GATT_CLOSE_DELAY_TOUT,
+          BTA_DM_DISC_CLOSE_TOUT_EVT, 0);
+    }
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_get_lea_ctrl_cb
+ *
+ * Description      This API returns pairing control block of LE AUDIO DEVICE
+ *
+ * Parameters:      tBTA_DEV_PAIRING_CB
+ *
+ ******************************************************************************/
+tBTA_DEV_PAIRING_CB* bta_get_lea_pair_cb(RawAddress peer_addr) {
+  tBTA_DEV_PAIRING_CB *p_lea_pair_cb = NULL;
+  p_lea_pair_cb = &bta_lea_pairing_cb.bta_dev_pair_db[0];
+  APPL_TRACE_DEBUG("%s %s ", __func__, peer_addr.ToString().c_str());
+
+  for (int i = 0; i < MAX_LEA_DEVICES; i++) {
+      if ((p_lea_pair_cb[i].in_use) &&
+        (p_lea_pair_cb[i].p_addr == peer_addr)) {
+        APPL_TRACE_DEBUG("%s Found %s index i %d ", __func__,
+          p_lea_pair_cb[i].p_addr.ToString().c_str(), i);
+        return &p_lea_pair_cb[i];
+      }
+    }
+  return NULL;
+}
+
+
+
+/*******************************************************************************
+ *
+ * Function         bta_set_lea_ctrl_cb
+ *
+ * Description      This is GATT client callback function used in DM.
+ *
+ * Parameters:
+ *
+ ******************************************************************************/
+
+tBTA_DEV_PAIRING_CB* bta_set_lea_pair_cb(RawAddress peer_addr) {
+  tBTA_DEV_PAIRING_CB *p_lea_pair_cb = NULL;
+  APPL_TRACE_DEBUG("bta_set_lea_ctrl_cb %s", peer_addr.ToString().c_str());
+
+  p_lea_pair_cb = bta_get_lea_pair_cb(peer_addr);
+
+  if (p_lea_pair_cb == NULL) {
+    APPL_TRACE_DEBUG("bta_set_lea_ctrl_cb Control block create ");
+
+    for (int i = 0; i < MAX_LEA_DEVICES ; i++) {
+      if (!bta_lea_pairing_cb.bta_dev_pair_db[i].in_use) {
+        bta_lea_pairing_cb.bta_dev_pair_db[i].p_addr = peer_addr;
+        bta_lea_pairing_cb.bta_dev_pair_db[i].in_use = true;
+        bta_lea_pairing_cb.is_pairing_progress = true;
+        bta_lea_pairing_cb.num_devices++;
+        return (&(bta_lea_pairing_cb.bta_dev_pair_db[i]));
+      }
+    }
+  } else {
+    return p_lea_pair_cb;
+  }
+  return NULL;
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_dm_reset_adv_audio_dev_info
+ *
+ * Description      This API resets all the pairing information related to le
+ *                  audio remote device.
+ * Parameters:      none
+ *
+ ******************************************************************************/
+void bta_dm_reset_lea_pairing_info(RawAddress p_addr) {
+
+  APPL_TRACE_DEBUG("%s Addr %s", __func__, p_addr.ToString().c_str());
+
+  auto itr = bta_lea_pairing_cb.dev_addr_map.find(p_addr);
+  if (itr != bta_lea_pairing_cb.dev_addr_map.end()) {
+    bta_lea_pairing_cb.dev_addr_map.erase(p_addr);
+  }
+
+  itr = bta_lea_pairing_cb.dev_rand_addr_map.find(p_addr);
+  if (itr != bta_lea_pairing_cb.dev_rand_addr_map.end()) {
+    bta_lea_pairing_cb.dev_rand_addr_map.erase(p_addr);
+  }
+
+  tBTA_DEV_PAIRING_CB *p_lea_pair_cb = NULL;
+  p_lea_pair_cb = bta_get_lea_pair_cb(p_addr);
+  if (p_lea_pair_cb) {
+    APPL_TRACE_DEBUG("%s RESETTING VALUES", __func__);
+    p_lea_pair_cb->in_use = false;
+    p_lea_pair_cb->is_dumo_device = false;
+    p_lea_pair_cb->is_le_pairing = false;
+    p_lea_pair_cb->dev_type = 0;
+    if (p_lea_pair_cb->p_id_addr != RawAddress::kEmpty) {
+      itr = bta_lea_pairing_cb.dev_addr_map.find(p_lea_pair_cb->p_id_addr);
+      APPL_TRACE_DEBUG("%s RESETTING Addr %s", __func__,
+        p_lea_pair_cb->p_id_addr.ToString().c_str());
+      if (itr != bta_lea_pairing_cb.dev_addr_map.end()) {
+        APPL_TRACE_DEBUG("%s Clearing INSIDE LEA ADDR DB MAP",
+          __func__);
+        bta_lea_pairing_cb.dev_addr_map.erase(p_lea_pair_cb->p_id_addr);
+      }
+      p_lea_pair_cb->p_id_addr = RawAddress::kEmpty;
+      p_lea_pair_cb->transport = 0;
+      p_lea_pair_cb->p_addr = RawAddress::kEmpty;
+    }
+    bta_lea_pairing_cb.is_pairing_progress = false;
+    bta_lea_pairing_cb.num_devices--;
+    bta_lea_pairing_cb.is_sdp_discover = true;
+  } else {
+    APPL_TRACE_DEBUG("%s INVALID CONTROL BLOCK", __func__);
+  }
+}
+
+/*****************************************************************************
+ *
+ * Function        bta_dm_ble_adv_audio_idaddr_map
+ *
+ * Description     storing the identity address information in the device
+ *                 control block. It will used for DUMO devices
+ *
+ * Returns         none
+ *
+ *****************************************************************************/
+void bta_dm_ble_adv_audio_idaddr_map(RawAddress p_bd_addr,
+  RawAddress p_id_addr) {
+  APPL_TRACE_DEBUG("%s p_bd_addr %s id_addr %s ", __func__,
+    p_bd_addr.ToString().c_str(), p_id_addr.ToString().c_str());
+  if (is_remote_support_adv_audio(p_bd_addr)) {
+    bta_lea_pairing_cb.dev_addr_map[p_id_addr] = p_bd_addr;
+    bta_lea_pairing_cb.dev_rand_addr_map[p_bd_addr] = p_id_addr;
+
+    tBTA_DEV_PAIRING_CB *p_lea_pair_cb = NULL;
+    p_lea_pair_cb = bta_get_lea_pair_cb(p_bd_addr);
+    if (p_lea_pair_cb) {
+      if (p_id_addr != p_bd_addr) {
+        APPL_TRACE_DEBUG("%s is_dumo_device %s", __func__,
+          p_id_addr.ToString().c_str());
+        p_lea_pair_cb->p_id_addr = p_id_addr;
+        p_lea_pair_cb->is_dumo_device = true;
+      }
+    }
+  }
+}
+
+bool bta_remote_dev_identity_addr_match(RawAddress p_addr) {
+  APPL_TRACE_DEBUG("%s ", __func__);
+
+  auto itr = bta_lea_pairing_cb.dev_addr_map.find(p_addr);
+
+  if (itr != bta_lea_pairing_cb.dev_addr_map.end()) {
+    APPL_TRACE_DEBUG("%s Identity BD_ADDR %s", __func__,
+      p_addr.ToString().c_str());
+      return true;
+  }
+  return false;
+}
+
+bool bta_is_bredr_primary_transport(RawAddress p_bd_addr) {
+
+  tBTA_DEV_PAIRING_CB *p_lea_pair_cb = NULL;
+
+  p_lea_pair_cb = bta_get_lea_pair_cb(p_bd_addr);
+  APPL_TRACE_DEBUG("%s ", __func__);
+  if (p_lea_pair_cb) {
+    APPL_TRACE_DEBUG("%s Transport %d ", __func__, p_lea_pair_cb->transport);
+    if (p_lea_pair_cb->transport == BT_TRANSPORT_BR_EDR) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool bta_remote_device_is_dumo(RawAddress p_bd_addr) {
+
+  auto itr = bta_lea_pairing_cb.dev_addr_map.find(p_bd_addr);
+  APPL_TRACE_DEBUG("%s Addr %s", __func__, p_bd_addr.ToString().c_str());
+
+  if (itr != bta_lea_pairing_cb.dev_addr_map.end()) {
+    APPL_TRACE_DEBUG("%s DUMO DEVICE Identity BD_ADDR %s", __func__,
+      p_bd_addr.ToString().c_str());
+      return true;
+  }
+
+  auto itr2 = bta_lea_pairing_cb.dev_rand_addr_map.find(p_bd_addr);
+  if (itr2 != bta_lea_pairing_cb.dev_rand_addr_map.end()) {
+    APPL_TRACE_DEBUG("%s Dumo addressed %s %s ", __func__,
+      itr2->first.ToString().c_str(), itr2->second.ToString().c_str());
+    return true;
+  }
+  return false;
+}
+
+RawAddress bta_get_rem_dev_id_addr(RawAddress p_bd_addr) {
+  tBTA_DEV_PAIRING_CB *p_lea_pair_cb = NULL;
+  APPL_TRACE_DEBUG("%s ", __func__);
+
+  p_lea_pair_cb = bta_get_lea_pair_cb(p_bd_addr);
+  if (p_lea_pair_cb) {
+    APPL_TRACE_DEBUG("%s %s", __func__,
+      p_lea_pair_cb->p_id_addr.ToString().c_str());
+    return p_lea_pair_cb->p_id_addr;
+  }
+  return RawAddress::kEmpty;
+}
+
+/*****************************************************************************
+ *
+ * Function        bta_adv_audio_update_bond_db
+ *
+ * Description     Updates pairing control block of the device and the bonding
+ *                 is initiated using LE transport or not.
+ *
+ * Returns         void
+ *
+ *****************************************************************************/
+void bta_adv_audio_update_bond_db(RawAddress p_bd_addr, uint8_t transport) {
+  tBTA_DEV_PAIRING_CB *p_dev_pair_cb = bta_set_lea_pair_cb(p_bd_addr);
+
+  APPL_TRACE_DEBUG("%s", __func__);
+  if (p_dev_pair_cb) {
+    APPL_TRACE_DEBUG("%s Addr %s Transport %d", __func__,
+      p_bd_addr.ToString().c_str(),  transport);
+    p_dev_pair_cb->p_addr = p_bd_addr;
+    p_dev_pair_cb->transport = transport;
+    if (transport == BT_TRANSPORT_LE) {
+      if (is_remote_support_adv_audio(p_dev_pair_cb->p_addr))
+        p_dev_pair_cb->is_le_pairing = true;
+      else
+        p_dev_pair_cb->is_le_pairing = false;
+    } else
+      p_dev_pair_cb->is_le_pairing = false;
+  }
+}
+
+/*****************************************************************************
+ *
+ * Function        is_le_audio_service
+ *
+ * Description     It checks whether the given service is related to the LE
+ *                 Audio service or not.
+ *
+ * Returns         true for LE Audio service which are registered.
+                   false by default
+ *
+ *****************************************************************************/
+bool is_le_audio_service(Uuid uuid) {
+
+  uint16_t uuid_val = 0;
+  bool status = false;
+
+  size_t len = uuid.GetShortestRepresentationSize();
+  if (len == Uuid::kNumBytes16) {
+    uuid_val = uuid.As16Bit();
+    APPL_TRACE_DEBUG("is_le_audio_service : 0x%X  0x%X ", uuid.As16Bit(), uuid_val);
+    //TODO check the service contains any LE AUDIO service or not
+    switch (uuid_val) {
+      case UUID_SERVCLASS_CSIS:
+        FALLTHROUGH_INTENDED; /* FALLTHROUGH */
+      case UUID_SERVCLASS_BASS:
+        FALLTHROUGH_INTENDED; /* FALLTHROUGH */
+      case UUID_SERVCLASS_T_ADV_AUDIO:
+        FALLTHROUGH_INTENDED; /* FALLTHROUGH */
+      case UUID_SERVCLASS_ASCS:
+        FALLTHROUGH_INTENDED; /* FALLTHROUGH */
+      case UUID_SERVCLASS_BAAS:
+        FALLTHROUGH_INTENDED; /* FALLTHROUGH */
+      case UUID_SERVCLASS_PACS:
+        {
+          auto itr = find(uuid_srv_disc_search.begin(),
+              uuid_srv_disc_search.end(), uuid);
+          if (itr != uuid_srv_disc_search.end())
+            status = true;
+        }
+        break;
+      default:
+        APPL_TRACE_DEBUG("%s : Not a LEA service ", __func__);
+    }
+  } else if(len == Uuid::kNumBytes128) {
+    if (uuid == UUID_SERVCLASS_WMCP) {
+      APPL_TRACE_DEBUG("%s: WMCP Service UUId found", __func__);
+      auto itr = find(uuid_srv_disc_search.begin(),
+          uuid_srv_disc_search.end(), uuid);
+      if (itr != uuid_srv_disc_search.end())
+        status = true;
+    }
+  }
+
+  return status;
+}
+
+/*****************************************************************************
+ *
+ * Function        bta_is_adv_audio_valid_bdaddr
+ *
+ * Description     This API is used for DUMO device. If the device contains
+ *                 two address (random and public), it checks for valid
+ *                 address.
+ *
+ * Returns         0 - for random address in dumo device
+ *                 1 - for public address in dumo device
+ *
+ ****************************************************************************/
+int bta_is_adv_audio_valid_bdaddr(RawAddress p_bd_addr) {
+  tBTA_DEV_PAIRING_CB *p_lea_pair_cb = NULL;
+  p_lea_pair_cb = bta_get_lea_pair_cb(p_bd_addr);
+
+  if (p_lea_pair_cb) {
+    APPL_TRACE_DEBUG("%s p_lea_pair_cb %s", __func__,
+      p_lea_pair_cb->p_addr.ToString().c_str());
+    auto itr = bta_lea_pairing_cb.dev_addr_map.find(p_bd_addr);
+    if (itr == bta_lea_pairing_cb.dev_addr_map.end() &&
+        (p_lea_pair_cb->is_dumo_device)) {
+      APPL_TRACE_DEBUG("%s Ignore BD_ADDR because of ID %s", __func__,
+        p_lea_pair_cb->p_id_addr.ToString().c_str());
+        return 0;
+    }
+  }
+  return 1;
+}
+
+/*****************************************************************************
+ *
+ * Function        devclass2uint
+ *
+ * Description     This API is to derive the class of device based of dev_class
+ *
+ * Returns         uint32_t - class of device
+ *
+ ****************************************************************************/
+static uint32_t devclass2uint(DEV_CLASS dev_class) {
+  uint32_t cod = 0;
+
+  if (dev_class != NULL) {
+    /* if COD is 0, irrespective of the device type set it to Unclassified
+     * device */
+    cod = (dev_class[2]) | (dev_class[1] << 8) | (dev_class[0] << 16);
+  }
+  return cod;
+}
+
+/*****************************************************************************
+ *
+ * Function        bta_is_remote_support_lea
+ *
+ * Description     This API is to check the remote device contains LEA service
+ *                 or not. It checks in Inquiry database initially.
+ *                 If the address is Public identity address then it will
+ *                 check in the pairing database of that remote device.
+ *
+ * Returns         true - if remote device inquiry db contains LEA service
+ *
+ ****************************************************************************/
+bool bta_is_remote_support_lea(RawAddress p_addr) {
+  tBTM_INQ_INFO* p_inq_info;
+
+  p_inq_info = BTM_InqDbRead(p_addr);
+  if (p_inq_info != NULL) {
+    uint32_t cod = devclass2uint(p_inq_info->results.dev_class);
+    BTIF_TRACE_DEBUG("%s cod is 0x%06x", __func__, cod);
+    if ((cod & MAJOR_LE_AUDIO_VENDOR_COD)
+          == MAJOR_LE_AUDIO_VENDOR_COD) {
+      return true;
+    }
+  }
+
+  /* check the address is public identity address and its related to random
+   * address which supports to LEA then that Public ID address should return
+   * true.
+   */
+  auto itr = bta_lea_pairing_cb.dev_addr_map.find(p_addr);
+  if (itr != bta_lea_pairing_cb.dev_addr_map.end()) {
+    BTIF_TRACE_DEBUG("%s Idenity address mapping", __func__);
+    return true;
+  }
+
+  return false;
+}
+
+void bta_find_adv_audio_group_instance(uint16_t conn_id, tGATT_STATUS status,
+    RawAddress p_addr) {
+  RawAddress p_id_addr =
+    bta_get_rem_dev_id_addr(p_addr);
+  if (p_id_addr != RawAddress::kEmpty) {
+    BTA_CsipFindCsisInstance(conn_id, status, p_id_addr);
+  } else {
+    BTA_CsipFindCsisInstance(conn_id, status, p_addr);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         is_gatt_srvc_disc_pending
+ *
+ * Description      This function checks whether gatt_srvc_disc is processing
+ *                  or not
+ *
+ * Parameters:
+ *
+ ******************************************************************************/
+bool is_gatt_srvc_disc_pending(RawAddress rem_bda) {
+  tBTA_LE_AUDIO_DEV_INFO *p_lea_cb = bta_get_lea_ctrl_cb(rem_bda);
+
+  APPL_TRACE_DEBUG("%s ", __func__);
+  if (p_lea_cb == NULL) {
+    return false;
+  } else {
+    APPL_TRACE_DEBUG("%s gatt_disc_progress %d ", __func__,
+        p_lea_cb->gatt_disc_progress);
+    return p_lea_cb->gatt_disc_progress;
+  }
+}
+
+/******************************************************************************
+ *
+ * Function         bta_get_pseudo_addr_with_id_addr
+ *
+ * Description      This function returns the mapping id_addr(if present) to
+ *                  pseudo addr
+ *
+ * Parameters:
+ *
+ *****************************************************************************/
+RawAddress bta_get_pseudo_addr_with_id_addr(RawAddress p_addr) {
+  auto itr = bta_lea_pairing_cb.dev_addr_map.find(p_addr);
+
+  APPL_TRACE_DEBUG("%s p_addr %s ", __func__, p_addr.ToString().c_str());
+  if (itr != bta_lea_pairing_cb.dev_addr_map.end()) {
+    APPL_TRACE_DEBUG("%s addr is mapped to %s ", __func__,
+        itr->second.ToString().c_str());
+    if (itr->second != RawAddress::kEmpty) {
+      return itr->second;
+    }
+  }
+  return p_addr;
+}
diff --git a/le_audio/system/bt/bta/include/bta_ascs_client_api.h b/le_audio/system/bt/bta/include/bta_ascs_client_api.h
new file mode 100644
index 0000000..5bcf48e
--- /dev/null
+++ b/le_audio/system/bt/bta/include/bta_ascs_client_api.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+#pragma once
+
+#include <string>
+#include <hardware/bt_ascs_client.h>
+
+namespace bluetooth {
+namespace bap {
+namespace ascs {
+
+class AscsClient {
+  public:
+  virtual ~AscsClient() = default;
+
+  static void Init(bluetooth::bap::ascs::AscsClientCallbacks* callbacks);
+
+  static void CleanUp(uint16_t client_id);
+
+  static AscsClient* Get();
+
+  virtual void Connect(uint16_t client_id, const RawAddress& address,
+                       bool is_direct) = 0;
+
+  virtual void Disconnect(uint16_t client_id, const RawAddress& address) = 0;
+
+  virtual void StartDiscovery(uint16_t client_id,
+                              const RawAddress& address) = 0;
+
+  virtual void GetAseState(uint16_t client_id, const RawAddress& address,
+                           uint8_t ase_id) = 0;
+
+  virtual void CodecConfig(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseCodecConfigOp> codec_configs);
+
+  virtual void QosConfig(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseQosConfigOp> qos_configs);
+
+  virtual void Enable(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseEnableOp> enable_ops);
+
+  virtual void Disable(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseDisableOp> disable_ops);
+
+  virtual void StartReady(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseStartReadyOp> start_ready_ops);
+
+  virtual void StopReady(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseStopReadyOp> stop_ready_ops);
+
+  virtual void Release(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseReleaseOp> release_ops);
+
+  virtual void UpdateStream(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseUpdateMetadataOp> metadata_ops);
+};
+
+}  // namespace ascs
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/include/bta_bap_uclient_api.h b/le_audio/system/bt/bta/include/bta_bap_uclient_api.h
new file mode 100644
index 0000000..33bd53b
--- /dev/null
+++ b/le_audio/system/bt/bta/include/bta_bap_uclient_api.h
@@ -0,0 +1,67 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+
+#pragma once
+
+#include <string>
+#include "connected_iso_api.h"
+#include <hardware/bt_bap_uclient.h>
+#include "bta_ascs_client_api.h"
+#include "bta/bap/uclient_alarm.h"
+
+namespace bluetooth {
+namespace bap {
+namespace ucast {
+
+using bluetooth::bap::pacs::PacsClientCallbacks;
+using bluetooth::bap::ascs::AscsClientCallbacks;
+using bluetooth::bap::cis::CisInterfaceCallbacks;
+using bluetooth::bap::ucast::StreamConnect;
+using bluetooth::bap::ucast::StreamType;
+using bluetooth::bap::alarm::BapAlarmCallbacks;
+
+class UcastClient : public PacsClientCallbacks,
+                    public AscsClientCallbacks,
+                    public CisInterfaceCallbacks,
+                    public BapAlarmCallbacks {
+ public:
+  virtual ~UcastClient() = default;
+
+  static void Initialize(UcastClientCallbacks* callbacks);
+  static void CleanUp();
+  static UcastClient* Get();
+
+  // APIs exposed to upper layer
+  virtual void Connect(std::vector<RawAddress> address, bool is_direct,
+                       std::vector<StreamConnect> streams) = 0;
+  virtual void Disconnect(const RawAddress& address,
+                       std::vector<StreamType> streams) = 0;
+  virtual void Start(const RawAddress& address,
+                     std::vector<StreamType> streams) = 0;
+  virtual void Stop(const RawAddress& address,
+                    std::vector<StreamType> streams) = 0;
+  virtual void Reconfigure(const RawAddress& address,
+                           std::vector<StreamReconfig> streams) = 0;
+  virtual void UpdateStream(const RawAddress& address,
+                           std::vector<StreamUpdate> update_streams) = 0;
+};
+
+}  // namespace ucast
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/include/bta_cc_api.h b/le_audio/system/bt/bta/include/bta_cc_api.h
new file mode 100644
index 0000000..f7dbd69
--- /dev/null
+++ b/le_audio/system/bt/bta/include/bta_cc_api.h
@@ -0,0 +1,472 @@
+/*
+ *Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * Copyright (C) 2003-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.
+ */
+#pragma once
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bta_gatt_api.h"
+#include <hardware/bluetooth_callcontrol_callbacks.h>
+#include <hardware/bluetooth_callcontrol_interface.h>
+
+using bluetooth::Uuid;
+using bluetooth::call_control::CallControllerCallbacks;
+#define MAX_RESPONSE_DATA_LEN 255
+#define MAX_CCS_CONNECTION   5
+#define MAX_BEARER_NAME        255
+#define MAX_UCI_NAME           255
+#define MAX_URI_LENGTH         255
+#define MAX_BEARER_LIST_LEN    255
+#define MAX_FRIENDLY_NAME_LEN      255
+//Atmost there could be only two Indicies (for JOIN)
+//Keeping this as Internal max as SPec don't define any upper limit on this
+#define MAX_NUM_INDICIES 10
+
+#define INBAND_RINGTONE_FEATURE_BIT  0x01
+#define SILENT_MODE_FEATURE_BIT  0x02
+
+typedef enum {
+  CCS_NONE_EVENT = 120,
+  CCS_INIT_EVENT,
+  CCS_CLEANUP_EVENT,
+  CCS_CALL_STATE_UPDATE,
+  CCS_BEARER_NAME_UPDATE,
+  CCS_BEARER_UCI_UPDATE,
+  CCS_BEARER_URI_SCHEMES_SUPPORTED,
+  CCS_UPDATE,
+  CCS_OPT_OPCODES,
+  CCS_BEARER_CURRENT_CALL_LIST_UPDATE,
+  CCS_BEARER_SIGNAL_STRENGTH_UPDATE,
+  CCS_SIGNAL_STRENGTH_REPORT_INTERVAL,
+  CCS_STATUS_FLAGS_UPDATE,
+  CCS_INCOMING_CALL_UPDATE,
+  CCS_INCOMING_TARGET_URI_UPDATE,
+  CCS_TERMINATION_REASON_UPDATE,
+  CCS_BEARER_TECHNOLOGY_UPDATE,
+  CCS_CCID_UPDATE,
+  CCS_ACTIVE_DEVICE_UPDATE,
+  CCS_CALL_CONTROL_RESPONSE,
+  //events to handle in CCS state machine
+  CCS_NOTIFY_ALL,
+  CCS_WRITE_RSP,
+  CCS_READ_RSP,
+  CCS_DESCRIPTOR_WRITE_RSP,
+  CCS_DESCRIPTOR_READ_RSP,
+  CCS_CONNECTION,
+  CCS_DISCONNECTION,
+  CCS_CONNECTION_UPDATE,
+  CCS_CONGESTION_UPDATE,
+  CCS_PHY_UPDATE,
+  CCS_MTU_UPDATE,
+  CCS_SET_ACTIVE_DEVICE,
+  CCS_CONNECTION_CLOSE_EVENT,
+  CCS_BOND_STATE_CHANGE_EVENT,
+}cc_event_t;
+
+typedef enum {
+  CCS_STATUS_SUCCESS = 0x00,
+  CCS_OPCODE_NOT_SUPPORTED,
+  CCS_OPCODE_UNSUCCESSFUL,
+  CCS_INVALID_INDEX,
+  CCS_STATE_MISMATCH,
+  CCS_LACK_OF_RESOURCES,
+  CCS_INVALID_OUTGOING_URI,
+  CCS_CALL_STATE_INACTIVE,
+}cc_error_t;
+
+typedef enum {
+  CCS_DISCONNECTED = 0x00,
+  CCS_CONNECTED,
+  CCS_MAX_DEVICE_STATE
+} call_connect_state_t;
+
+typedef enum {
+  CALL_ACCEPT = 0x00,
+  CALL_TERMINATE,
+  CALL_LOCAL_HOLD,
+  CALL_LOCAL_RETRIEVE,
+  CALL_ORIGINATE,
+  CALL_JOIN,
+ } cc_opcode_t;
+
+ typedef enum {
+   CC_TERM_INVALID_ORIG_URI = 0x00,
+   CC_TERM_FAILED,
+   CC_TERM_END_FROM_REMOTE,
+   CC_TERM_END_FROM_SERVER,
+   CC_TERM_LINE_BUSY,
+   CC_TERM_NW_CONGESTION,
+   CC_TERM_END_FROM_CLIENT,
+   CC_TERM_NO_SERVICE,
+   CC_TERM_NO_ANSWER,
+  } cc_term_reason_t;
+
+ typedef enum {
+   CCS_STATE_INCOMING = 0x00,
+   CCS_STATE_DIALING,
+   CCS_STATE_ALERTING,
+   CCS_STATE_ACTIVE,
+   CCS_STATE_LOCAL_HELD,
+   CCS_STATE_REMOTELY_HELD,
+   CCS_STATE_LOCAL_REMOTE_HELD,
+   CCS_STATE_DISCONNECTED,
+ } cc_state_t;
+
+ //connection state machine
+ bool DeviceStateConnectionHandler(uint32_t event, void* param);
+ bool DeviceStateDisConnectingHandler(uint32_t event, void* param);
+ bool DeviceStateDisconnectedHandler(uint32_t event, void* param);
+
+typedef struct {
+  int server_if;
+  Uuid ccs_service_uuid;
+  Uuid bearer_provider_name_uuid;
+  Uuid call_control_point_uuid;
+  Uuid call_control_point_opcode_supported_uuid;
+  Uuid bearer_uci_uuid;
+  Uuid bearer_technology_uuid;
+  Uuid bearer_uri_schemes_supported_uuid;
+  Uuid bearer_signal_strength_uuid;
+  Uuid bearer_signal_strength_report_interval_uuid;
+  Uuid bearer_list_currentcalls_uuid;
+  Uuid incoming_call_target_beareruri_uuid;
+  Uuid call_status_flags_uuid;
+  Uuid call_state_uuid;
+  Uuid gtbs_ccid_uuid;
+  Uuid call_termination_reason_uuid;
+  Uuid incoming_call_uuid;
+  Uuid call_friendly_name_uuid;
+  //handle for characteristics
+  uint16_t call_state_handle;
+  uint16_t bearer_provider_name_handle;
+  uint16_t call_control_point_opcode_supported_handle;
+  uint16_t call_control_point_handle;
+  uint16_t bearer_uci_handle;
+  uint16_t bearer_technology_handle;
+  uint16_t bearer_uri_schemes_supported_handle;
+  uint16_t bearer_signal_strength_handle;
+  uint16_t bearer_signal_strength_report_interval_handle;
+  uint16_t bearer_list_currentcalls_handle;
+  uint16_t incoming_call_target_beareruri_handle;
+  uint16_t call_status_flags_handle;
+  uint16_t call_termination_reason_handle;
+  uint16_t incoming_call_handle;
+  uint16_t call_friendly_name_handle;
+  uint16_t ccid_handle;
+  uint16_t call_state_desc;
+  uint16_t bearer_provider_name_desc;
+  uint16_t call_control_point_opcode_supported_desc;
+  uint16_t call_control_point_desc;
+  uint16_t bearer_uci_desc;
+  uint16_t bearer_technology_desc;
+  uint16_t bearer_uri_schemes_supported_desc;
+  uint16_t bearer_signal_strength_desc;
+  uint16_t bearer_signal_strength_report_interval_desc;
+  uint16_t bearer_list_currentcalls_desc;
+  uint16_t incoming_call_target_bearerURI_desc;
+  uint16_t call_status_flags_desc;
+  uint16_t call_termination_reason_desc;
+  uint16_t incoming_call_desc;
+  uint16_t call_friendly_name_desc;
+  uint16_t ccid_desc;
+}CcsControlServiceInfo_t;
+
+typedef struct {
+  call_connect_state_t state;
+  uint16_t call_state_notify;
+  uint16_t bearer_provider_name_notify;
+  uint16_t call_control_point_notify;
+  uint16_t call_control_point_opcode_supported_notify;
+  uint16_t bearer_technology_changed_notify;
+  uint16_t bearer_uci_notify;
+  uint16_t bearer_uri_schemes_supported_notify;
+  uint16_t bearer_current_calls_list_notify;
+  uint16_t bearer_signal_strength_notify;
+  uint16_t bearer_signal_strength_report_interval_notify;
+  uint16_t incoming_call_state_notify;
+  uint16_t incoming_call_target_URI_notify;
+  uint16_t status_flags_notify;
+  uint16_t call_termination_reason_notify;
+  uint16_t call_friendly_name_notify;
+  uint8_t signal_strength_report_interval;
+  alarm_t* signal_strength_reporting_timer;
+  bool congested;
+  int conn_id;
+  int trans_id;
+  int timeout;
+  int latency;
+  int interval;
+  int rx_phy;
+  int tx_phy;
+  int mtu;
+
+  RawAddress peer_bda;
+  bool (*DeviceStateHandlerPointer[2])(uint32_t event, void* param);
+}CallControllerDeviceList;
+
+typedef struct {
+  std::vector<RawAddress> address;
+  int set_id;
+}CallActiveDevice;
+
+typedef struct {
+  uint8_t index;
+  uint8_t state;
+  uint8_t flags;
+}tCCS_CALL_STATE;
+
+typedef struct {
+ uint8_t index;
+ uint8_t incoming_target_uri[MAX_URI_LENGTH];
+}tCCS_INCOMING_CALL_URI;
+
+typedef struct {
+ uint8_t index;
+ uint8_t incoming_uri[MAX_URI_LENGTH];
+}tCCS_INCOMING_CALL;
+
+typedef struct {
+  uint8_t operation;
+  uint8_t  index[MAX_NUM_INDICIES];
+  uint8_t supported_flags;
+  char* uri;
+  uint32_t ccid;
+}tCCS_CALL_CONTROL_POINT;
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t index;
+ uint8_t  response_status;
+ RawAddress remote_address;
+}tCCS_CALL_CONTROL_RESPONSE;
+
+typedef struct {
+  uint16_t supported_flags;
+}tCCS_STATUS_FLAGS;
+
+typedef struct {
+  uint16_t supp_opcode;
+}tCCS_SUPP_OPTIONAL_OPCODES;
+
+
+typedef struct {
+    uint8_t index;
+    uint8_t reason;
+}tCCS_TERM_REASON;
+
+typedef struct {
+    uint8_t index;
+    uint8_t name[MAX_FRIENDLY_NAME_LEN];
+}tCCS_FRIENDLY_NAME;
+
+typedef struct {
+  uint32_t ccid;
+}tCCS_CONTENT_CONTROL_ID;
+
+typedef struct {
+  RawAddress addr;
+}tCCS_CONNECTION_CLOSE;
+
+typedef struct {
+  uint8_t list_length;
+  uint8_t call_index;
+  uint8_t call_state;
+  uint8_t call_flags;
+  uint8_t call_uri[MAX_URI_LENGTH];
+ }tCCS_BEARER_LIST_CURRENT_CALLS;
+
+typedef struct {
+  uint8_t name[MAX_BEARER_NAME];
+  uint8_t  uci[MAX_UCI_NAME];
+  uint8_t length;
+  uint8_t  technology_type;
+  uint8_t signal;
+  uint8_t signal_report_interval;
+  int bearer_list_len;
+  uint8_t bearer_schemes_list[MAX_BEARER_LIST_LEN];
+}tCCS_BEARER_PROVIDER_INFO;
+
+typedef struct {
+  bool status;
+}tCCS_BEARER_URI_SCHEMES;
+
+//Union ops
+struct tCCS_CHAR_DESC_WRITE {
+  tCCS_CHAR_DESC_WRITE() {};
+  ~tCCS_CHAR_DESC_WRITE() {};
+  std::vector<uint8_t> value;
+  uint8_t status;
+  uint16_t notification;
+  uint32_t trans_id;
+  uint32_t desc_handle;
+  uint32_t char_handle;
+  bool need_rsp;
+  bool prep_rsp;
+  //is to send notification
+};
+
+struct tCCS_CHAR_DESC_READ {
+  tCCS_CHAR_DESC_READ() {};
+  ~tCCS_CHAR_DESC_READ() {};
+  uint8_t status;
+  uint32_t trans_id;
+  uint32_t desc_handle;
+  uint32_t char_handle;
+};
+
+struct tCCS_CHAR_GATT_READ {
+  tCCS_CHAR_GATT_READ() {};
+  ~tCCS_CHAR_GATT_READ() {};
+  uint8_t status;
+  uint32_t trans_id;
+  uint32_t char_handle;
+};
+
+struct tCCS_CHAR_WRITE {
+  tCCS_CHAR_WRITE() {};
+  ~tCCS_CHAR_WRITE() {};
+  uint8_t status;
+  bool need_rsp;
+  bool prep_rsp;
+  uint16_t offset;
+  uint16_t trans_id;
+  uint32_t char_handle;
+  int len;
+  std::vector<uint8_t> value;
+  uint8_t *data;
+};
+
+struct tCCS_CONNECTION {
+  uint8_t status;
+  CallControllerDeviceList remoteDevice;
+};
+
+struct tCCS_CONN_UPDATE {
+  tCCS_CONN_UPDATE() {};
+  ~tCCS_CONN_UPDATE() {};
+  uint8_t status;
+  CallControllerDeviceList *remoteDevice;
+};
+
+struct tCCS_DISCONNECTION {
+  tCCS_DISCONNECTION() {};
+  ~tCCS_DISCONNECTION() {};
+  uint8_t status;
+  CallControllerDeviceList *remoteDevice;
+};
+
+struct tCCS_CONGESTION {
+  tCCS_CONGESTION() {};
+  ~tCCS_CONGESTION() {};
+  bool congested;
+  CallControllerDeviceList *remoteDevice;
+};
+
+struct tCCS_PHY{
+  tCCS_PHY();
+  ~tCCS_PHY();
+  uint8_t status;
+  uint8_t tx_phy;
+  uint8_t rx_phy;
+  CallControllerDeviceList *remoteDevice;
+};
+
+struct tCCS_MTU {
+  tCCS_MTU() {};
+  ~tCCS_MTU() {};
+  uint8_t status;
+  uint16_t mtu;
+  CallControllerDeviceList *remoteDevice;
+};
+
+struct tCCS_SET_ACTIVE_DEVICE {
+  tCCS_SET_ACTIVE_DEVICE() {};
+  ~tCCS_SET_ACTIVE_DEVICE() {};
+  RawAddress address;
+  uint16_t set_id;
+};
+
+struct tCALL_CONTROL_UPDATE {
+  tCALL_CONTROL_UPDATE() {};
+  ~tCALL_CONTROL_UPDATE() {};
+  std::vector<uint8_t> data;
+};
+
+union CALL_CONTROL_OPERATION{
+  CALL_CONTROL_OPERATION() : CallControllerOp() {
+  };
+  ~CALL_CONTROL_OPERATION() {};
+  tCALL_CONTROL_UPDATE CallControllerOp;
+  tCCS_SET_ACTIVE_DEVICE SetActiveDeviceOp;
+  tCCS_CHAR_DESC_WRITE WriteDescOp;
+  tCCS_CHAR_DESC_READ ReadDescOp;
+  tCCS_CHAR_WRITE WriteOp;
+  tCCS_CHAR_GATT_READ ReadOp;
+  tCCS_CONNECTION ConnectionOp;
+  tCCS_CONN_UPDATE ConnectionUpdateOp;
+  tCCS_DISCONNECTION DisconnectionOp;
+  tCCS_CONGESTION CongestionOp;
+  tCCS_MTU MtuOp;
+  tCCS_PHY PhyOp;
+};
+
+typedef union CALL_CONTROL_OPERATION tCCS_OPERATION;
+
+struct tcc_resp_t {
+  tcc_resp_t()    {};
+  ~tcc_resp_t()    {};
+  uint32_t event = 0;
+  uint16_t handle = 0;
+  uint16_t status = 0;
+  bool force = false;
+  CallControllerDeviceList *remoteDevice = nullptr;
+  tGATTS_RSP rsp_value;
+  tCCS_OPERATION oper;
+ };
+
+class CallController {
+
+  public:
+  virtual ~CallController() = default;
+  static void Initialize(bluetooth::call_control::CallControllerCallbacks* callbacks,
+                     Uuid app_id, int max_ccs_clients, bool inband_ringing_enabled);
+  static void CleanUp();
+  static CallController* Get();
+  static bool IsCcServiceRunnig();
+
+  virtual void CallState(int len, std::vector<uint8_t> value) = 0;
+  virtual void BearerInfoName(uint8_t* bearer_name) = 0;
+  virtual void UpdateBearerTechnology(int tech_type) = 0;
+  virtual void UpdateSupportedBearerList(uint8_t* list) = 0;
+  virtual void UpdateIncomingCallTargetUri(int index, uint8_t* target_uri) = 0;
+  virtual void UpdateIncomingCall(int index, uint8_t* Uri) = 0;
+  virtual void UpdateBearerSignalStrength(int signal) = 0;
+  virtual void UpdateStatusFlags(uint8_t status_flag) = 0;
+  virtual void CallControlOptionalOpSupported(int feature) = 0;
+  virtual void CallControlResponse(uint8_t op, uint8_t index, uint32_t status, const RawAddress& address)= 0;
+  virtual void SetActiveDevice(const RawAddress& address, int setId) = 0;
+  virtual void ContentControlId(uint32_t ccid) = 0;
+  virtual void Disconnect(const RawAddress& bd_add) = 0;
+};
+
+void HandleCcsEvent(uint32_t event, void* param);
+bool CCSHandler(uint32_t event, void* param);
+void CcpCongestionUpdate(tcc_resp_t * p_data);
diff --git a/le_audio/system/bt/bta/include/bta_csip_api.h b/le_audio/system/bt/bta/include/bta_csip_api.h
new file mode 100644
index 0000000..4efb422
--- /dev/null
+++ b/le_audio/system/bt/bta/include/bta_csip_api.h
@@ -0,0 +1,396 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+/******************************************************************************
+ *
+ *  This is the public interface file to provide CSIP API's.
+ *
+ ******************************************************************************/
+
+#ifndef BTA_CSIP_API_H
+#define BTA_CSIP_API_H
+
+//#include "bta_api.h"
+#include <bluetooth/uuid.h>
+#include <raw_address.h>
+
+#include "bta_gatt_api.h" //temp
+
+#include <vector>
+
+#define SIRK_SIZE 16        // SIRK Size
+#define UNLOCK_VALUE 0x01   // UNLOCK Value
+#define LOCK_VALUE   0x02   // LOCK Value
+#define ENCRYPTED_SIRK 0x00     // Encrypted SIRK Type
+#define PLAINTEXT_SIRK 0x01     // Plain Text SIRK
+#define INVALID_SET_ID 0x10 // Invalid set id
+
+/* status for applications for LOCK Status changed callback*/
+enum {
+  LOCK_RELEASED,                      // (LOCK Released successfully)
+  LOCK_RELEASED_TIMEOUT,              // (LOCK Released by timeout)
+  ALL_LOCKS_ACQUIRED,                 // (LOCK Acquired for all requested set members)
+  SOME_LOCKS_ACQUIRED_REASON_TIMEOUT, // (Request timeout for some set members)
+  SOME_LOCKS_ACQUIRED_REASON_DISC,    // (Some of the set members were disconnected)
+  LOCK_DENIED,                        // (Denied by one of the set members)
+  INVALID_REQUEST_PARAMS,             // (Upper layer provided invalid parameters)
+  LOCK_RELEASE_NOT_ALLOWED,           // (Response from remote (PTS))
+  INVALID_VALUE,                      // (Response from remote (PTS))
+};
+
+/* LOCK Request Error Codes from set members */
+#define CSIP_LOCK_DENIED 0x80
+#define CSIP_LOCK_RELEASE_NOT_ALLOWED 0x81
+#define CSIP_INVALID_LOCK_VALUE 0x82
+#define CSIP_LOCK_ALREADY_GRANTED 0x84
+
+/* Events when CSIP operations are completed */
+#define BTA_CSIP_NEW_SET_FOUND_EVT 1
+#define BTA_CSIP_SET_MEMBER_FOUND_EVT 2
+#define BTA_CSIP_CONN_STATE_CHG_EVT 3
+#define BTA_CSIP_LOCK_STATUS_CHANGED_EVT 4
+#define BTA_CSIP_LOCK_AVAILABLE_EVT 5
+#define BTA_CSIP_SET_SIZE_CHANGED 6
+#define BTA_CSIP_SET_SIRK_CHANGED 7
+
+/* CSIP operation completed event*/
+typedef uint8_t tBTA_CSIP_EVT;
+
+enum {
+  BTA_CSIP_SUCCESS,
+  BTA_CSIP_FAILURE,
+};
+
+/* CSIP Operation Status*/
+typedef uint8_t tBTA_CSIP_STATUS;
+
+/* CSIP GATT Connection States (to be notified to upper layer)*/
+/* Mapping to BluetoothProfile Connection States*/
+enum {
+  BTA_CSIP_DISCONNECTED,
+  BTA_CSIP_CONNECTED = 0x02,
+};
+
+ /* CSIP device GATT Connection state */
+typedef uint8_t tBTA_CSIP_CONN_STATE;
+
+enum {
+  BTA_CSIP_CONN_ESTABLISHED = 0x40, // reason values to be decided
+  BTA_CSIP_CONN_ESTABLISHMENT_FAILED,
+  BTA_CSIP_APP_ALREADY_CONNECTED,
+  BTA_CSIP_APP_ALREADY_DISCONNECTED,
+  BTA_CSIP_APP_DISCONNECTED,
+  BTA_CSIP_DISCONNECT_WITHOUT_CONNECT,
+  BTA_CSIP_COORDINATED_SET_NOT_SUPPORTED,
+};
+
+/* CSIP device GATT Connection state */
+typedef uint8_t tBTA_CSIP_CONN_STATUS;
+
+/* Params in callback to requesting app when lock status has been changed */
+typedef struct {
+  uint8_t app_id;
+  uint8_t set_id;
+  uint8_t value = UNLOCK_VALUE;
+  uint8_t status;
+  std::vector<RawAddress> addr;
+} tBTA_LOCK_STATUS_CHANGED;
+
+/* Params in callback to registered app when coordinated set member is discovered */
+typedef struct {
+  uint8_t set_id;
+  bluetooth::Uuid uuid;
+  RawAddress addr;
+} tBTA_SET_MEMBER_FOUND;
+
+/* Params in callback to registered app when discovery for coordinated set is completed */
+/*TODO: not required, to be removed */
+typedef struct {
+  uint8_t set_id;
+  bluetooth::Uuid uuid;
+  std::vector<RawAddress> addr;
+} tBTA_SET_DISC_CMPL;
+
+/* params in callback to app when lock is available on earlier denied member*/
+typedef struct {
+  uint8_t app_id;
+  uint8_t set_id;
+  RawAddress addr;
+} tBTA_LOCK_AVAILABLE;
+
+/* params in callback to app space when new set is found*/
+typedef struct {
+  uint8_t set_id;
+  uint8_t sirk[SIRK_SIZE];
+  uint8_t size;
+  bool lock_support;
+  RawAddress addr;
+  bluetooth::Uuid including_srvc_uuid;
+} tBTA_CSIP_NEW_SET_FOUND;
+
+/* params in callback to app when set size is changed */
+typedef struct {
+  uint8_t set_id;
+  uint8_t size;
+  RawAddress addr;
+} tBTA_CSIP_SET_SIZE_CHANGED;
+
+/* params in callback to app when set sirk is changed */
+typedef struct {
+  uint8_t set_id;
+  uint8_t* sirk;
+  RawAddress addr;
+} tBTA_CSIP_SET_SIRK_CHANGED;
+
+/* params in callback to app when connection state has been changed */
+typedef struct {
+  uint8_t app_id;
+  RawAddress addr;
+  tBTA_CSIP_CONN_STATE state;
+  tBTA_CSIP_CONN_STATUS status;
+} tBTA_CSIP_CONN_STATE_CHANGED;
+
+/* callbacks params on completion of CSIP operations */
+typedef union {
+  tBTA_LOCK_STATUS_CHANGED lock_status_param;
+  tBTA_CSIP_CONN_STATE_CHANGED conn_params;
+  tBTA_SET_MEMBER_FOUND set_member_param;
+  tBTA_SET_DISC_CMPL set_disc_cmpl_param;
+  tBTA_LOCK_AVAILABLE lock_available_param;
+  tBTA_CSIP_NEW_SET_FOUND new_set_params;
+  tBTA_CSIP_CONN_STATE_CHANGED conn_chg_params;
+  tBTA_CSIP_SET_SIZE_CHANGED size_chg_params;
+  tBTA_CSIP_SET_SIRK_CHANGED sirk_chg_params;
+} tBTA_CSIP_DATA;
+
+/* CSIP callbacks to be given to upper layers*/
+
+/* Callback given when one of the operation mentioned in */
+typedef void (tBTA_CSIP_CBACK) (tBTA_CSIP_EVT event, tBTA_CSIP_DATA* p_data);
+
+/* Callback when application is registered with CSIP */
+typedef void (tBTA_CSIP_CLT_REG_CB) (tBTA_CSIP_STATUS status, uint8_t app_id);
+
+/* parameters used for api BTA_CsipSetLockValue() */
+typedef struct {
+  uint8_t app_id;
+  uint8_t set_id;
+  uint8_t lock_value;
+  std::vector<RawAddress> members_addr;
+} tBTA_SET_LOCK_PARAMS;
+
+/* Coordinated set details */
+typedef struct {
+  uint8_t set_id;
+  uint8_t size;
+  uint8_t total_discovered;
+  bool lock_support;
+  std::vector<RawAddress> set_members;
+  bluetooth::Uuid p_srvc_uuid;
+} tBTA_CSIP_CSET;
+
+using BtaCsipAppRegisteredCb =
+    base::Callback<void(uint8_t /* status */, uint8_t /* app_id */)>;
+
+/*********************************************************************************
+ *
+ * Function         BTA_RegisterCsipApp
+ *
+ * Description      This function is called to register application or module to
+ *                  to register with CSIP for using CSIP functionalities.
+ *
+ * Parameters       p_csip_cb: callback to be received in registering app when
+ *                             required CSIP operation is completed.
+ *                  reg_cb   : callback when app/module is registered with CSIP.
+ *
+ * Returns          None
+ *
+ *********************************************************************************/
+extern void BTA_RegisterCsipApp(tBTA_CSIP_CBACK* p_csip_cb,
+                              BtaCsipAppRegisteredCb reg_cb);
+
+/*********************************************************************************
+ *
+ * Function         BTA_UnregisterCsipApp
+ *
+ * Description      This function is called to register application or module to
+ *                  to register with CSIP for using CSIP functionalities.
+ *
+ * Parameters       app_id: Identifier of the app that needs to be unregistered.
+ *
+ * Returns          None
+ *
+ *********************************************************************************/
+extern void BTA_UnregisterCsipApp(uint8_t app_id);
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipSetLockValue
+ *
+ * Description      This function is called to request or release lock for the
+ *                  coordinated set.
+ *
+ * Parameters       lock_param: parameters to acquire or release lock.
+ *                             (tBTA_SET_LOCK_PARAMS).
+ *
+ * Returns          None
+ *
+ *********************************************************************************/
+extern void BTA_CsipSetLockValue(tBTA_SET_LOCK_PARAMS lock_param);
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipGetCoordinatedSet
+ *
+ * Description      This function is called to fetch details of the coordinated set.
+ *
+ * Parameters       set_id: identifier of the coordinated set whose details are
+ *                          required to be fetched.
+ *
+ * Returns          tBTA_CSIP_CSET (containing details of coordinated set).
+ *
+ *********************************************************************************/
+extern tBTA_CSIP_CSET BTA_CsipGetCoordinatedSet(uint8_t set_id);
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipSetLockValue
+ *
+ * Description      This function is called to request or release lock for the
+ *                  coordinated set.
+ *
+ * Parameters       None.
+ *
+ * Returns          vector<tBTIF_CSIP_CSET>: (all discovered coordinated set)
+ *
+ *********************************************************************************/
+extern std::vector<tBTA_CSIP_CSET> BTA_CsipGetDiscoveredSets();
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipConnect
+ *
+ * Description      This function is called to establish GATT Connection.
+ *
+ * Parameters       bd_addr : Address of the remote device.
+ *
+ * Returns          None.
+ *
+ * Note             This shouldnt be used by registered module. CSIP Profile
+ *                  internally manages GATT Connection.
+ *
+ *********************************************************************************/
+extern void BTA_CsipConnect (uint8_t app_id, const RawAddress& bd_addr);
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipConnect
+ *
+ * Description      This function is called to establish GATT Connection.
+ *
+ * Parameters       bd_addr : Address of the remote device.
+ *
+ * Returns          None.
+ *
+ * Note             This shouldnt be used by registered module. CSIP Profile
+ *                  internally manages GATT Connection.
+ *
+ *********************************************************************************/
+extern void BTA_CsipDisconnect (uint8_t app_id, const RawAddress& bd_addr);
+
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipEnable
+ *
+ * Description      This function is invoked to initialize CSIP in BTA layer.
+ *
+ * Parameters       p_cback: callbacks registered with btif_csip module.
+ *
+ * Returns          None.
+ *
+ * Note             This API shouldn't be used by other BT modules.
+ *
+ *********************************************************************************/
+extern void BTA_CsipEnable(tBTA_CSIP_CBACK *p_cback);
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipEnable
+ *
+ * Description      This function is invoked to deinitialize CSIP in BTA layer.
+ *
+ * Parameters       None.
+ *
+ * Returns          None.
+ *
+ * Note             This API shouldn't be used by other BT modules.
+ *
+ *********************************************************************************/
+extern void BTA_CsipDisable();
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipFindCsisInstance
+ *
+ * Description      This function is invoked to find presence of CSIS service on
+ *                  remote device and start coordinated set discovery.
+ *
+ * Parameters       conn_id: GATT Connection ID used for getting remote services
+ *                  status : Status of the discovery procedure
+ *
+ * Returns          None.
+ *
+ * Note             This API shouldn't be used by other BT modules.
+ *
+ *********************************************************************************/
+extern void BTA_CsipFindCsisInstance(uint16_t conn_id, tGATT_STATUS status,
+                                     RawAddress& bd_addr);
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipRemoveUnpairedSetMember
+ *
+ * Description      This function is called when a given set member is unpaired.
+ *
+ * Parameters       addr: BD Address of the set member.
+ *
+ * Returns          None.
+ *
+ * Note             This API shouldn't be used by other BT modules.
+ *
+ *********************************************************************************/
+void BTA_CsipRemoveUnpairedSetMember(RawAddress addr);
+
+/*********************************************************************************
+ *
+ * Function         BTA_CsipGetDeviceSetId
+ *
+ * Description      This API is used to get set id of the remote device.
+ *
+ * Parameters       addr: BD Address of the set member.
+ *                  uuid: UUID of the service which includes CSIS service.
+ *
+ * Returns          None.
+ *
+ *********************************************************************************/
+uint8_t BTA_CsipGetDeviceSetId(RawAddress addr, bluetooth::Uuid uuid);
+
+
+#endif /* BTA_CSIP_API_H */
diff --git a/le_audio/system/bt/bta/include/bta_dm_adv_audio.h b/le_audio/system/bt/bta/include/bta_dm_adv_audio.h
new file mode 100644
index 0000000..d7fe588
--- /dev/null
+++ b/le_audio/system/bt/bta/include/bta_dm_adv_audio.h
@@ -0,0 +1,152 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 BTA_DM_ADV_AUDIO_H
+#define BTA_DM_ADV_AUDIO_H
+
+#include "stack/include/bt_types.h"
+#include "bta/dm/bta_dm_int.h"
+#include <memory>
+#include <map>
+#include "bt_target.h"
+#include "bta_sys.h"
+
+#include "bta_gatt_api.h"
+
+#define UUID_SERVCLASS_CSIS 0x1846 /* Coordinated Set Identification Service */
+#define UUID_SERVCLASS_PACS 0x1850 /* LE AUDIO PACS */
+#define UUID_SERVCLASS_ASCS 0x184E /* LE AUDIO ASCS */
+#define UUID_SERVCLASS_BASS 0x184F /* LE AUDIO BASS */
+#define UUID_SERVCLASS_BAAS 0x1851 /* LE AUDIO BAAS */
+#define UUID_SERVCLASS_BRASS 0x1852 /* LE AUDIO BRASS */
+#define UUID_SERVCLASS_T_ADV_AUDIO 0x1FA0 /* LE AUDIO T_ADV_AUDIO */
+#define UUID_SERVCLASS_CSIS_LOCK 0x2B86 /* LE AUDIO CSIS LOCK */
+#define UUID_SERVCLASS_T_ADV_AUDIO_ROLE_CHAR 0xFE00 /*LE AUDIO CSIS LOCK */
+#define UUID_SERVCLASS_T_ADV_AUDIO_MEDIA_SINK 0x6AD0
+#define UUID_SERVCLASS_T_ADV_AUDIO_VOICE 0x6AD5
+#define UUID_SERVCLASS_T_ADV_AUDIO_CONN_LESS_MEDIA_SINK 0xFFA6
+#define UUID_SERVCLASS_T_ADV_AUDIO_ASSIST 0xFFA7
+#define UUID_SERVCLASS_T_ADV_AUDIO_DELEGATE 0xFFA8
+#define UUID_SERVCLASS_HAS 0x6AD2
+#define UUID_SERVCLASS_SOURCE_CONTEXT 0x2BCE
+#define UUID_SERVCLASS_PACS_CT_SUPPORT 0x6AD4
+#define UUID_SERVCLASS_PACS_UMR_SUPPORT 0x6AD1
+
+#define BTA_DM_LE_AUDIO_SEARCH_CMPL_EVT 7
+
+#define BTA_DM_GROUP_DATA_TYPE 0x2E
+
+typedef struct {
+  RawAddress peer_address;
+  bool in_use =false;
+  bool is_t_audio_srvc_found = false;
+  bool is_has_found = false;
+  std::vector<bluetooth::Uuid> uuids;
+  int transport; //BTM_UseLeLink(remote_bda);
+  int conn_id = 0;
+  int8_t disc_progress = 0;
+  uint8_t gatt_if;
+  bool using_bredr_bonding = false;
+  uint16_t t_role_handle = 0;
+  uint16_t pacs_char_handle = 0;
+  bool csip_disc_progress = true;
+  bool is_csip_support = false;
+  bool gatt_disc_progress = false;
+} tBTA_LE_AUDIO_DEV_INFO;
+
+typedef struct {
+  RawAddress p_addr;
+  RawAddress p_id_addr;
+  bool is_le_pairing = false;
+  bool in_use = false;
+  bool is_dumo_device = false;
+  uint8_t dev_type;
+  uint8_t transport;
+  bool sdp_disc_status = true;
+} tBTA_DEV_PAIRING_CB;
+
+#define MAX_LEA_DEVICES 3
+typedef struct {
+  tBTA_DEV_PAIRING_CB bta_dev_pair_db[MAX_LEA_DEVICES];
+  uint8_t num_devices;
+  bool is_pairing_progress = false;
+  std::map <RawAddress, RawAddress> dev_addr_map;
+  std::map <RawAddress, RawAddress> dev_rand_addr_map;
+  RawAddress pending_address;
+  bool is_sdp_discover = true;
+} tBTA_LEA_PAIRING_DB;
+
+
+typedef struct {
+  tBTA_LE_AUDIO_DEV_INFO bta_lea_dev_info[MAX_LEA_DEVICES];
+  RawAddress pending_peer_addr;
+  RawAddress gatt_op_addr = RawAddress::kEmpty;
+  int num_lea_devices  = 0;
+  bool bond_progress = false;
+} tBTA_LE_AUDIO_DEV_CB;
+
+extern tBTA_LE_AUDIO_DEV_CB bta_le_audio_dev_cb;
+extern bool is_remote_support_adv_audio(const RawAddress remote_bdaddr);
+extern void bta_adv_audio_update_bond_db(RawAddress p_bd_addr, uint8_t transport);
+extern bool is_le_audio_service(bluetooth::Uuid uuid);
+extern int bta_is_adv_audio_valid_bdaddr(RawAddress p_bd_addr);
+extern bool bta_is_le_audio_supported(RawAddress p_bd_addr);
+extern bool bta_remote_device_is_dumo(RawAddress p_bd_addr);
+extern RawAddress bta_get_rem_dev_id_addr(RawAddress p_bd_addr);
+extern tBTA_DEV_PAIRING_CB* bta_get_lea_pair_cb(RawAddress peer_addr);
+extern bool bta_lea_addr_match(RawAddress p_bd_addr);
+extern void bta_dm_reset_lea_pairing_info(RawAddress p_addr);
+extern bool bta_is_bredr_primary_transport(RawAddress p_bd_addr);
+extern bool bta_is_remote_support_lea(RawAddress p_addr);
+extern bool bta_remote_dev_identity_addr_match(RawAddress p_addr);
+extern void bta_dm_lea_disc_complete(RawAddress p_bd_addr);
+extern void bta_dm_csis_disc_complete(RawAddress p_bd_addr, bool status);
+extern tBTA_LE_AUDIO_DEV_INFO* bta_get_lea_ctrl_cb(RawAddress peer_addr);
+extern void bta_gap_gatt_read_cb(uint16_t conn_id, tGATT_STATUS status,
+                  uint16_t handle, uint16_t len,
+                  uint8_t* value, void* data);
+extern void bta_get_adv_audio_role(RawAddress peer_address, uint16_t conn_id,
+                                  tGATT_STATUS status);
+extern void bta_dm_csis_disc_complete(RawAddress p_bd_addr, bool status);
+extern void bta_dm_lea_disc_complete(RawAddress p_bd_addr);
+extern void bta_add_adv_audio_uuid(RawAddress peer_address,
+                               tBTA_GATT_ID srvc_uuid);
+extern tBTA_LE_AUDIO_DEV_INFO* bta_set_lea_ctrl_cb(RawAddress peer_addr);
+extern void bta_dm_reset_adv_audio_dev_info(RawAddress p_addr);
+extern void bta_dm_set_adv_audio_dev_info(tBTA_GATTC_OPEN* p_data);
+extern bool is_adv_audio_group_supported(RawAddress rem_bda, int conn_id);
+extern void bta_dm_lea_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
+extern void bta_dm_adv_audio_gatt_conn(RawAddress p_bd_addr);
+extern void bta_dm_adv_audio_close(RawAddress p_bd_addr);
+extern tBTA_DEV_PAIRING_CB* bta_get_lea_pair_cb(RawAddress peer_addr);
+extern tBTA_DEV_PAIRING_CB* bta_set_lea_pair_cb(RawAddress peer_addr);
+extern void bta_dm_reset_lea_pairing_info(RawAddress p_addr);
+extern void bta_dm_ble_adv_audio_idaddr_map(RawAddress p_bd_addr,
+            RawAddress p_id_addr);
+extern bool bta_remote_dev_identity_addr_match(RawAddress p_addr);
+extern bool bta_lea_is_le_pairing(RawAddress p_bd_addr);
+extern bool bta_remote_device_is_dumo(RawAddress p_bd_addr);
+extern RawAddress bta_get_rem_dev_id_addr(RawAddress p_bd_addr);
+extern void bta_adv_audio_update_bond_db(RawAddress p_bd_addr, uint8_t transport);
+extern int bta_is_adv_audio_valid_bdaddr(RawAddress p_bd_addr);
+extern void bta_find_adv_audio_group_instance(uint16_t conn_id, tGATT_STATUS status,
+    RawAddress p_addr);
+extern bool bta_is_remote_support_lea(RawAddress p_addr);
+extern bool is_gatt_srvc_disc_pending(RawAddress rem_bda);
+extern RawAddress bta_get_pseudo_addr_with_id_addr(RawAddress p_addr);
+#endif /* BTA_DM_ADV_AUDIO_H*/
diff --git a/le_audio/system/bt/bta/include/bta_mcp_api.h b/le_audio/system/bt/bta/include/bta_mcp_api.h
new file mode 100644
index 0000000..de6dfc7
--- /dev/null
+++ b/le_audio/system/bt/bta/include/bta_mcp_api.h
@@ -0,0 +1,489 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 BTA_MCP_API_H
+#define BTA_MCP_API_H
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <hardware/bt_mcp.h>
+#include "bta_gatt_api.h"
+
+using bluetooth::Uuid;
+using bluetooth::mcp_server::McpServerCallbacks;
+
+
+#define MAX_PLAYER_NAME_SIZE    GATT_MAX_ATTR_LEN
+#define MAX_TRACK_TITLE_SIZE    GATT_MAX_ATTR_LEN
+#define MAX_RESPONSE_DATA_LEN   GATT_MAX_ATTR_LEN
+#define MAX_MCP_CONNECTION      5
+
+#define TRACK_POSITION_UNAVAILABLE 0xFFFFFFFF
+#define TRACK_DURATION_UNAVAILABLE 0xFFFFFFFF
+
+/* Media Control Point Opcodes Supported characteristics bit values */
+#define MCP_MEDIA_CONTROL_SUP_PLAY               1<<0
+#define MCP_MEDIA_CONTROL_SUP_PAUSE              1<<1
+#define MCP_MEDIA_CONTROL_SUP_FAST_REWIND        1<<2
+#define MCP_MEDIA_CONTROL_SUP_FAST_FORWARD       1<<3
+#define MCP_MEDIA_CONTROL_SUP_STOP               1<<4
+#define MCP_MEDIA_CONTROL_SUP_MOVE_RELATIVE      1<<5
+#define MCP_MEDIA_CONTROL_SUP_PREVIOUS_SEGMENT   1<<6
+#define MCP_MEDIA_CONTROL_SUP_NEXT_SEGMENT       1<<7
+#define MCP_MEDIA_CONTROL_SUP_FIRST_SEGMENT      1<<8
+#define MCP_MEDIA_CONTROL_SUP_LAST_SEGMENT       1<<9
+#define MCP_MEDIA_CONTROL_SUP_GOTO_SEGMENT       1<<10
+#define MCP_MEDIA_CONTROL_SUP_PREVIOUS_TRACK     1<<11
+#define MCP_MEDIA_CONTROL_SUP_NEXT_TRACK         1<<12
+#define MCP_MEDIA_CONTROL_SUP_FIRST_TRACK        1<<13
+#define MCP_MEDIA_CONTROL_SUP_LAST_TRACK         1<<14
+#define MCP_MEDIA_CONTROL_SUP_GOTO_TRACK         1<<15
+#define MCP_MEDIA_CONTROL_SUP_PREVIOUS_GROUP     1<<16
+#define MCP_MEDIA_CONTROL_SUP_NEXT_GROUP         1<<17
+#define MCP_MEDIA_CONTROL_SUP_FIRST_GROUP        1<<18
+#define MCP_MEDIA_CONTROL_SUP_LAST_GROUP         1<<19
+#define MCP_MEDIA_CONTROL_SUP_GOTO_GROUP         1<<20
+
+//media control point opcodes
+#define MCP_MEDIA_CONTROL_OPCODE_PLAY              0x01
+#define MCP_MEDIA_CONTROL_OPCODE_PAUSE             0x02
+#define MCP_MEDIA_CONTROL_OPCODE_FAST_REWIND       0x03
+#define MCP_MEDIA_CONTROL_OPCODE_FAST_FORWARD      0x04
+#define MCP_MEDIA_CONTROL_OPCODE_STOP              0x05
+#define MCP_MEDIA_CONTROL_OPCODE_PREV_TRACK        0x30
+#define MCP_MEDIA_CONTROL_OPCODE_NEXT_TRACK        0x31
+#define MCP_MEDIA_CONTROL_OPCODE_MOVE_RELATIVE     0x10
+
+/* Playing Order Supported characteristic bit values */
+#define MCP_PLAYING_OREDR_SINGLE_ONCE        1<<0
+#define MCP_PLAYING_OREDR_SINGLE_REPEAT      1<<1
+#define MCP_PLAYING_OREDR_IN_ORDER_ONCE      1<<2
+#define MCP_PLAYING_OREDR_IN_ORDER_REPEAT    1<<3
+#define MCP_PLAYING_OREDR_OLDEST_ONCE        1<<4
+#define MCP_PLAYING_OREDR_OLDEST_REPEAT      1<<5
+#define MCP_PLAYING_OREDR_NEWEST_ONCE        1<<6
+#define MCP_PLAYING_OREDR_NEWEST_REPEAT      1<<7
+#define MCP_PLAYING_OREDR_SHUFFLE_ONCE       1<<8
+#define MCP_PLAYING_OREDR_SHUFFLE_REPEAT     1<<9
+
+#define MCP_DEFAULT_MEDIA_CTRL_SUPP_FEAT  MCP_MEDIA_CONTROL_SUP_PLAY|           \
+                                          MCP_MEDIA_CONTROL_SUP_PAUSE|          \
+                                          MCP_MEDIA_CONTROL_SUP_FAST_REWIND|    \
+                                          MCP_MEDIA_CONTROL_SUP_FAST_FORWARD|   \
+                                          MCP_MEDIA_CONTROL_SUP_STOP|           \
+                                          MCP_MEDIA_CONTROL_SUP_PREVIOUS_TRACK| \
+                                          MCP_MEDIA_CONTROL_SUP_NEXT_TRACK
+
+typedef enum {
+  // TO-DO: Naming in such a way to distinguish BTIF and lower layer events
+  MCP_NONE_EVENT = 70,
+  MCP_INIT_EVENT,
+  MCP_CLEANUP_EVENT,
+  MCP_MEDIA_STATE_UPDATE,
+  MCP_MEDIA_PLAYER_NAME_UPDATE,
+  MCP_MEDIA_SUPPORTED_OPCODE_UPDATE,
+  MCP_MEDIA_CONTROL_POINT_UPDATE,
+  MCP_PLAYING_ORDER_SUPPORTED_UPDATE,
+  MCP_PLAYING_ORDER_UPDATE,
+  MCP_TRACK_CHANGED_UPDATE,
+  MCP_TRACK_POSITION_UPDATE,
+  MCP_TRACK_DURATION_UPDATE,
+  MCP_TRACK_TITLE_UPDATE,
+  MCP_CCID_UPDATE,
+  MCP_ACTIVE_DEVICE_UPDATE,
+  MCP_ACTIVE_PROFILE,
+
+  //local event to handle in mcp state machine,
+  MCP_PLAYING_ORDER_SUPPORTED_READ,
+  MCP_PLAYING_ORDER_READ,
+  MCP_MEDIA_STATE_READ,
+  MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_READ,
+  MCP_MEDIA_PLAYER_NAME_READ,
+  MCP_TRACK_TITLE_READ,
+  MCP_TRACK_POSITION_READ,
+  MCP_TRACK_DURATION_READ,
+  MCP_CCID_READ,
+  MCP_SEEKING_SPEED_READ,
+  MCP_MEDIA_STATE_READ_DESCRIPTOR,
+  MCP_MEDIA_CONTROL_POINT_READ_DESCRIPTOR,
+  MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_DESCRIPTOR_READ,
+  MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_READ,
+  MCP_TRACK_CHANGED_DESCRIPTOR_READ,
+  MCP_TRACK_TITLE_DESCRIPTOR_READ,
+  MCP_TRACK_POSITION_DESCRIPTOR_READ,
+  MCP_TRACK_DURATION_DESCRIPTOR_READ,
+  MCP_PLAYING_ORDER_DESCRIPTOR_READ,
+  MCP_MEDIA_STATE_DESCRIPTOR_WRITE,
+  MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_WRITE,
+  MCP_MEDIA_CONTROL_POINT_DESCRIPTOR_WRITE,
+  MCP_MEDIA_CONTROL_POINT_SUPPORTED_DESCRIPTOR_WRITE,
+  MCP_TRACK_CHANGED_DESCRIPTOR_WRITE,
+  MCP_TRACK_TITLE_DESCRIPTOR_WRITE,
+  MCP_TRACK_POSITION_DESCRIPTOR_WRITE,
+  MCP_TRACK_DURATION_DESCRIPTOR_WRITE,
+  MCP_PLAYING_ORDER_DESCRIPTOR_WRITE,
+  MCP_MEDIA_CONTROL_POINT_WRITE,
+  MCP_PLAYING_ORDER_WRITE,
+  MCP_TRACK_POSITION_WRITE,
+//device state event
+  MCP_NOTIFY_ALL,
+  MCP_WRITE_RSP,
+  MCP_READ_RSP,
+  MCP_DESCRIPTOR_WRITE_RSP,
+  MCP_DESCRIPTOR_READ_RSP,
+  MCP_CONNECTION,
+  MCP_DISCONNECTION,
+  MCP_CONNECTION_UPDATE,
+  MCP_CONGESTION_UPDATE,
+  MCP_PHY_UPDATE,
+  MCP_MTU_UPDATE,
+  MCP_SET_ACTIVE_DEVICE,
+  MCP_CONNECTION_CLOSE_EVENT,
+  MCP_BOND_STATE_CHANGE_EVENT,
+//media write op code event
+  MCP_MEDIA_CONTROL_PLAY_READ_REQ,
+  MCP_MEDIA_CONTROL_PAUSE_REQ,
+  MCP_MEDIA_CONTROL_FAST_FORWARD_REQ,
+  MCP_MEDIA_CONTROL_FAST_REWIND_REQ,
+  MCP_MEDIA_CONTROL_MOVE_RELATIVE_REQ,
+  MCP_MEDIA_CONTROL_STOP_REQ,
+  MCP_MEDIA_CONTROL_NEXT_TRACK_REQ,
+  MCP_MEDIA_CONTROL_PREVIOUS_TRACK_REQ,
+  MCP_PLAYING_OREDR_SHUFFLE_REPEAT_REQ,
+
+}mcp_event_t;
+
+//state handler declaration
+typedef bool (*mcp_handler)(uint32_t event, void* param, uint8_t state);
+typedef enum {
+  //media conrol point success or error code
+  MCP_STATUS_SUCCESS = 1,
+  MCP_OPCODE_NOT_SUPPORTED,
+  MCP_MEDIA_PLAYER_INACTIVE,
+  MCP_COMMAND_CANNOT_COMPLETED,
+
+  BT_STATUS_DEVICE_NOT_CONNECTED,
+  BT_STATUS_HANLDE_NOT_MATCHED,
+}mcp_error_t;
+
+typedef enum {
+  MCP_DISCONNECTED = 0x00,
+  MCP_CONNECTED,
+  MCP_MAX_DEVICE_STATE
+} remote_device_state_t;
+
+typedef enum {
+  MCP_STATE_INACTIVE = 0x00,
+  MCP_STATE_PLAYING,
+  MCP_STATE_PAUSE,
+  MCP_STATE_SEEKING,
+  MCP_MAX_MEDIA_STATE
+} mcp_state_t;
+
+typedef struct {
+  uint8_t media_state;
+  uint16_t media_ctrl_point;
+  uint32_t media_supported_feature;
+  uint8_t player_name[MAX_PLAYER_NAME_SIZE];
+  uint16_t player_name_len;
+  uint8_t track_changed;
+  int32_t duration;
+  int32_t position;
+  uint16_t playing_order_supported;
+  uint8_t playing_order_value;
+  uint8_t title[MAX_TRACK_TITLE_SIZE];
+  uint16_t track_title_len;
+  uint8_t ccid;
+  uint8_t seeking_speed;
+  mcp_handler MediaStateHandlerPointer[MCP_MAX_MEDIA_STATE];
+} MediaPlayerInfo_t;
+
+typedef struct {
+  int server_if;
+  Uuid mcs_service_uuid;
+  Uuid media_state_uuid;
+  Uuid media_player_name_uuid;
+  Uuid media_control_point_uuid;
+  Uuid media_control_point_opcode_supported_uuid;
+  Uuid track_changed_uuid;
+  Uuid track_title_uuid;
+  Uuid track_duration_uuid;
+  Uuid track_position_uuid;
+  Uuid playing_order_supported_uuid;
+  Uuid playing_order_uuid;
+  Uuid ccid_uuid;
+  Uuid seeking_speed_uuid;
+  //handle for characteristics
+  uint16_t media_state_handle;
+  uint16_t media_player_name_handle;
+  uint16_t media_control_point_opcode_supported_handle;
+  uint16_t media_control_point_handle;
+  uint16_t track_changed_handle;
+  uint16_t track_title_handle;
+  uint16_t track_duration_handle;
+  uint16_t track_position_handle;
+  uint16_t playing_order_supported_handle;
+  uint16_t playing_order_handle;
+  uint16_t ccid_handle;
+  uint16_t seeking_speed_handle;
+  uint16_t media_state_desc;
+  uint16_t media_player_name_desc;
+  uint16_t media_control_point_opcode_supported_desc;
+  uint16_t media_control_point_desc;
+  uint16_t track_changed_desc;
+  uint16_t track_title_desc;
+  uint16_t track_duration_desc;
+  uint16_t track_position_desc;
+  uint16_t playing_order_supported_desc;
+  uint16_t playing_order_desc;
+  uint16_t ccid_desc;
+  uint16_t seeking_speed_desc;
+} mcsServerServiceInfo_t;
+
+typedef struct {
+  remote_device_state_t state;
+  uint8_t active_profile;
+  uint16_t media_state_notify;
+  uint16_t media_player_name_notify;
+  uint16_t media_control_point_notify;
+  uint16_t media_control_point_opcode_supported_notify;
+  uint16_t track_changed_notify;
+  uint16_t track_duration_notify;
+  uint16_t track_title_notify;
+  uint16_t track_position_notify;
+  uint16_t playing_order_notify;
+  uint16_t seeking_speed_notify;
+  bool congested;
+  int conn_id;
+  int trans_id;
+  int timeout;
+  int latency;
+  int interval;
+  int rx_phy;
+  int tx_phy;
+  int mtu;
+  RawAddress peer_bda;
+  mcp_handler DeviceStateHandlerPointer[MCP_MAX_DEVICE_STATE];
+}RemoteDevice;
+
+typedef struct {
+  std::vector<RawAddress> address;
+  int set_id;
+}ActiveDevice;
+
+typedef struct {
+  uint8_t status;
+  uint16_t notification;
+  uint32_t trans_id;
+  uint32_t desc_handle;
+  uint32_t char_handle;
+  bool need_rsp;
+  bool prep_rsp;
+  //is to send notification
+  uint8_t *data;
+  uint16_t len;
+}tMCP_DESC_WRITE;
+
+typedef struct {
+  uint8_t status;
+  uint32_t trans_id;
+  uint32_t desc_handle;
+  uint32_t char_handle;
+}tMCP_DESC_READ;
+
+typedef struct {
+  bool is_long;
+  uint8_t status;
+  uint32_t trans_id;
+  uint32_t char_handle;
+}tMCP_READ;
+
+typedef struct {
+  uint8_t status;
+  bool need_rsp;
+  bool prep_rsp;
+  uint16_t offset;
+  uint32_t trans_id;
+  uint16_t char_handle;
+  //is to send notification
+  uint8_t data[GATT_MAX_ATTR_LEN];
+}tMCP_WRITE;
+
+typedef struct {
+  uint8_t status;
+  RemoteDevice remoteDevice;
+}tMCP_CONNECTION;
+
+typedef struct {
+  uint8_t status;
+  int timeout;
+  int latency;
+  int interval;
+}tMCP_CONN_UPDATE;
+
+typedef struct {
+  uint8_t status;
+  RemoteDevice *remoteDevice;
+}tMCP_DISCONNECTION;
+
+typedef struct {
+  bool congested;
+  RemoteDevice *remoteDevice;
+} tMCP_CONGESTION;
+
+typedef struct {
+  uint8_t status;
+  uint8_t tx_phy;
+  uint8_t rx_phy;
+  RemoteDevice *remoteDevice;
+} tMCP_PHY;
+
+typedef struct {
+  uint8_t status;
+  uint16_t mtu;
+  RemoteDevice *remoteDevice;
+} tMCP_MTU;
+
+typedef struct {
+  uint8_t state;
+}tMCP_MEDIA_STATE;
+
+typedef struct {
+  uint32_t req_opcode;
+  uint8_t result;
+}tMCP_MEDIA_CONTROL_POINT;
+
+typedef struct {
+  uint32_t supported;
+}tMCP_MEDIA_OPCODE_SUPPORT;
+
+typedef struct {
+  uint8_t *name;
+  uint16_t len;
+}tMCP_MEDIA_PLAYER_NAME;
+
+typedef struct {
+  bool status;
+}tMCP_TRACK_CHANGED;
+
+typedef struct {
+  int32_t position;
+}tMCP_TRACK_POSTION;
+
+typedef struct {
+  uint32_t duration;
+}tMCP_TRACK_DURATION;
+
+typedef struct {
+  uint8_t *title;
+  uint16_t len;
+}tMCP_TRACK_TITLE;
+
+typedef struct {
+  uint8_t ccid;
+}tMCP_CONTENT_CONTROL_ID;
+
+typedef struct {
+  uint8_t seek_speed;
+}tMCP_SEEKING_SPEED_CONTROL_ID;
+
+typedef struct {
+  RawAddress addr;
+}tMCP_CONNECTION_CLOSE;
+
+typedef struct {
+  RawAddress addr;
+  int state;
+}tMCP_BOND_STATE_CHANGE;
+
+typedef struct {
+  uint32_t order_supported;
+}tMCP_PLAYING_ORDER_SUPPORT;
+
+typedef struct {
+  uint8_t order;
+}tMCP_PLAYING_ORDER;
+
+typedef struct {
+  RawAddress address;
+  uint16_t set_id;
+  uint8_t profile;
+}tMCP_SET_ACTIVE_DEVICE;
+
+typedef struct {
+  uint8_t *data;
+  uint16_t len;
+}tMCP_MEDIA_UPDATE;
+
+union tMCP_MEDIA_OPERATION{
+  tMCP_MEDIA_UPDATE MediaUpdateOp;
+  tMCP_SET_ACTIVE_DEVICE SetActiveDeviceOp;
+  tMCP_DESC_WRITE WriteDescOp;
+  tMCP_DESC_READ ReadDescOp;
+  tMCP_WRITE WriteOp;
+  tMCP_READ ReadOp;
+  tMCP_CONNECTION ConnectionOp;
+  tMCP_CONN_UPDATE ConnectionUpdateOp;
+  tMCP_DISCONNECTION DisconnectionOp;
+  tMCP_CONGESTION CongestionOp;
+  tMCP_MTU MtuOp;
+  tMCP_PHY PhyOp;
+};
+
+typedef union tMCP_MEDIA_OPERATION tMCP_MEDIA_OPERATION;
+
+struct mcp_resp_t {
+  uint32_t event;
+  uint16_t handle;
+  uint16_t status;
+  RemoteDevice *remoteDevice;
+  tGATTS_RSP rsp_value;
+  tMCP_MEDIA_OPERATION oper;
+};
+
+typedef struct mcp_resp_t mcp_resp_t;
+
+class McpServer {
+  public:
+  virtual ~McpServer() = default;
+  static void Initialize(bluetooth::mcp_server::McpServerCallbacks* callbacks, Uuid ap_id);
+  static void CleanUp();
+  static McpServer* Get();
+  static bool isMcpServiceRunnig();
+  virtual void MediaState(uint8_t state) = 0;
+  virtual void MediaPlayerName(uint8_t* player_name) = 0;
+  virtual void MediaControlPointOpcodeSupported(uint32_t feature) = 0;
+  virtual void MediaControlPoint(uint8_t value) = 0;
+  virtual void TrackChanged(bool status) = 0;
+  virtual void TrackDuration(int32_t duration) = 0;
+  virtual void TrackTitle(uint8_t* title) = 0;
+  virtual void TrackPosition(int32_t position) = 0;
+  virtual void PlayingOrderSupported(uint16_t order) = 0;
+  virtual void PlayingOrder(uint8_t value) = 0;
+  virtual void SetActiveDevice(const RawAddress& address, int setId, int profile) = 0;
+  virtual void ContentControlId(uint8_t ccid) = 0;
+  virtual void DisconnectMcp(const RawAddress& address) = 0;
+  virtual void BondStateChange(const RawAddress& address, int state) = 0;
+};
+
+void McpCongestionUpdate(mcp_resp_t *p_data);
+
+#endif // BTA_MCP_API_H
diff --git a/le_audio/system/bt/bta/include/bta_pacs_client_api.h b/le_audio/system/bt/bta/include/bta_pacs_client_api.h
new file mode 100644
index 0000000..1a364d8
--- /dev/null
+++ b/le_audio/system/bt/bta/include/bta_pacs_client_api.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+#pragma once
+
+#include <string>
+#include <hardware/bt_pacs_client.h>
+
+namespace bluetooth {
+namespace bap {
+namespace pacs {
+
+class PacsClient {
+  public:
+    virtual ~PacsClient() = default;
+
+    static void Initialize(bluetooth::bap::pacs::PacsClientCallbacks* callbacks);
+    static void CleanUp(uint16_t client_id);
+    static PacsClient* Get();
+    virtual void Connect(uint16_t client_id, const RawAddress& address,
+                         bool is_direct) = 0;
+    virtual void Disconnect(uint16_t client_id,
+                            const RawAddress& address) = 0;
+    virtual void StartDiscovery(uint16_t client_id,
+                                  const RawAddress& address) = 0;
+    virtual void GetAudioAvailability(uint16_t client_id,
+                                    const RawAddress& address) = 0;
+};
+
+}  // namespace pacs
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/include/bta_vcp_controller_api.h b/le_audio/system/bt/bta/include/bta_vcp_controller_api.h
new file mode 100644
index 0000000..8518262
--- /dev/null
+++ b/le_audio/system/bt/bta/include/bta_vcp_controller_api.h
@@ -0,0 +1,148 @@
+/*
+ *Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <base/callback_forward.h>
+#include <hardware/bt_vcp_controller.h>
+#include <deque>
+#include <future>
+#include <vector>
+
+enum {
+  BTA_VCP_DISCONNECTED = 0x0,
+  BTA_VCP_CONNECTING,
+  BTA_VCP_CONNECTED,
+  BTA_VCP_DISCONNECTING,
+};
+
+enum {
+  VCS_VOLUME_STATE_READ_CMPL_EVT = 0x0,
+  VCS_VOLUME_FLAGS_READ_CMPL_EVT,
+  VCS_VOLUME_STATE_CCC_WRITE_CMPL_EVT,
+  VCS_VOLUME_FLAGS_CCC_WRITE_CMPL_EVT,
+};
+
+enum {
+  VCS_CONTROL_POINT_OP_REL_VOLUME_DOWN = 0x0,
+  VCS_CONTROL_POINT_OP_REL_VOLUME_UP,
+  VCS_CONTROL_POINT_OP_UNMUTE_REL_VOLUME_DOWN,
+  VCS_CONTROL_POINT_OP_UNMUTE_REL_VOLUME_UP,
+  VCS_CONTROL_POINT_OP_SET_ABS_VOL,
+  VCS_CONTROL_POINT_OP_UNMUTE,
+  VCS_CONTROL_POINT_OP_MUTE,
+};
+
+enum {
+  VCS_UNMUTE_STATE = 0x0,
+  VCS_MUTE_STATE,
+};
+
+typedef struct {
+  uint8_t op_id;
+  uint8_t change_counter;
+  uint8_t volume_setting;
+} SetAbsVolumeOp;
+
+typedef struct {
+  uint8_t op_id;
+  uint8_t change_counter;
+} MuteOp;
+
+typedef struct {
+  uint8_t op_id;
+  uint8_t change_counter;
+} UnmuteOp;
+
+struct VolumeState {
+  uint8_t volume_setting;
+  uint8_t mute;
+  uint8_t change_counter;
+
+  VolumeState()
+      : volume_setting(0),
+        mute(0),
+        change_counter(0) {}
+};
+
+struct VolumeControlService {
+  uint16_t volume_state_handle;
+  uint16_t volume_control_point_handle;
+  uint16_t volume_flags_handle;
+  uint16_t volume_state_ccc_handle;
+  uint16_t volume_flags_ccc_handle;
+
+  VolumeState volume_state;
+  uint8_t volume_flags;
+  uint8_t pending_volume_setting;
+  uint8_t pending_mute_setting;
+  uint8_t retry_cmd;
+
+  VolumeControlService()
+      : volume_state_handle(0),
+        volume_control_point_handle(0),
+        volume_flags_handle(0),
+        volume_state_ccc_handle(0),
+        volume_flags_ccc_handle(0),
+        volume_state(),
+        volume_flags(0),
+        pending_volume_setting(0),
+        pending_mute_setting(0),
+        retry_cmd(0) {}
+};
+
+struct RendererDevice {
+  RawAddress address;
+  uint16_t conn_id;
+  uint8_t state;
+  bool bg_conn;
+  bool service_changed_rcvd;
+  VolumeControlService vcs;
+
+  RendererDevice(const RawAddress& address)
+      : address(address),
+        conn_id(0),
+        state(BTA_VCP_DISCONNECTED),
+        bg_conn(false),
+        service_changed_rcvd(false),
+        vcs() {}
+
+  RendererDevice() : RendererDevice(RawAddress::kEmpty) {}
+};
+
+class VcpController {
+   public:
+  virtual ~VcpController() = default;
+
+  static void Initialize(bluetooth::vcp_controller::VcpControllerCallbacks* callbacks);
+  static void CleanUp();
+  static VcpController* Get();
+  static bool IsVcpControllerRunning();
+  static int GetDeviceCount();
+
+  virtual void Connect(const RawAddress& address, bool isDirect) = 0;
+  virtual void Disconnect(const RawAddress& address) = 0;
+  virtual void SetAbsVolume(const RawAddress& address, uint8_t volume) = 0;
+  virtual void Mute(const RawAddress& address) = 0;
+  virtual void Unmute(const RawAddress& address) = 0;
+};
+
diff --git a/le_audio/system/bt/bta/include/connected_iso_api.h b/le_audio/system/bt/bta/include/connected_iso_api.h
new file mode 100644
index 0000000..74e16cc
--- /dev/null
+++ b/le_audio/system/bt/bta/include/connected_iso_api.h
@@ -0,0 +1,111 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "stack/include/bt_types.h"
+#include <hardware/bt_bap_uclient.h>
+
+namespace bluetooth {
+namespace bap {
+namespace cis {
+
+using bluetooth::bap::ucast::CISConfig;
+using bluetooth::bap::ucast::CIGConfig;
+
+constexpr uint8_t  DIR_TO_AIR    = 0x1 << 0;
+constexpr uint8_t  DIR_FROM_AIR  = 0x1 << 1;
+
+typedef uint8_t sdu_interval_t[3];
+
+enum class CisState {
+  INVALID = 0,
+  READY,
+  DESTROYING,
+  ESTABLISHING,
+  ESTABLISHED
+};
+
+enum class CigState {
+  INVALID = 0,
+  IDLE,
+  CREATING,
+  CREATED,
+  REMOVING
+};
+
+enum IsoHciStatus {
+  ISO_HCI_SUCCESS = 0,
+  ISO_HCI_FAILED,
+  ISO_HCI_IN_PROGRESS
+};
+
+class CisInterfaceCallbacks {
+ public:
+  virtual ~CisInterfaceCallbacks() = default;
+
+  /** Callback for connection state change */
+  virtual void OnCigState(uint8_t cig_id, CigState state) = 0;
+
+  virtual void OnCisState(uint8_t cig_id, uint8_t cis_id,
+                          uint8_t direction, CisState state) = 0;
+};
+
+class CisInterface {
+ public:
+  virtual ~CisInterface() = default;
+
+  static void Initialize(CisInterfaceCallbacks* callbacks);
+  static void CleanUp();
+  static CisInterface* Get();
+
+  virtual CigState GetCigState(const uint8_t &cig_id);
+
+  virtual CisState GetCisState(const uint8_t &cig_id, uint8_t cis_id);
+
+  virtual uint8_t GetCisCount(const uint8_t &cig_id) = 0;
+
+  virtual IsoHciStatus CreateCig(RawAddress client_peer_bda,
+                         bool reconfig,
+                         CIGConfig &cig_config,
+                         std::vector<CISConfig> &cis_configs) = 0;
+
+  virtual IsoHciStatus RemoveCig(RawAddress peer_bda,
+                                 uint8_t cig_id) = 0;
+
+  virtual IsoHciStatus CreateCis(uint8_t cig_id, std::vector<uint8_t> cis_ids,
+                                 RawAddress peer_bda) = 0;
+
+  virtual IsoHciStatus DisconnectCis(uint8_t cig_id, uint8_t cis_id,
+                                     uint8_t direction) = 0;
+
+  virtual IsoHciStatus SetupDataPath(uint8_t cig_id, uint8_t cis_id,
+                                     uint8_t direction,
+                                     uint8_t path_id) = 0;
+
+  virtual IsoHciStatus RemoveDataPath(uint8_t cig_id, uint8_t cis_id,
+                              uint8_t direction) = 0;
+};
+
+}  // namespace ucast
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/bta/mcp/bta_mcp_main.cc b/le_audio/system/bt/bta/mcp/bta_mcp_main.cc
new file mode 100644
index 0000000..b920ec2
--- /dev/null
+++ b/le_audio/system/bt/bta/mcp/bta_mcp_main.cc
@@ -0,0 +1,3089 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+
+
+/******************************************************************************
+ *
+ *  This file contains the MCP server main functions and state machine.
+ *
+ ******************************************************************************/
+
+#include "bta_api.h"
+#include "bt_target.h"
+#include "bta_mcp_api.h"
+#include "gatts_ops_queue.h"
+#include "btm_int.h"
+#include "device/include/controller.h"
+#include "osi/include/properties.h"
+#include "bta_sys.h"
+#include "btif_util.h"
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/location.h>
+#include <hardware/bluetooth.h>
+#include <base/strings/string_number_conversions.h>
+#include <vector>
+#include <string.h>
+
+using bluetooth::Uuid;
+using bluetooth::bap::GattsOpsQueue;
+
+class McpServerImpl;
+static McpServerImpl *instance;
+
+//global variables
+mcsServerServiceInfo_t mcsServerServiceInfo;
+MediaPlayerInfo_t mediaPlayerInfo;
+
+void HandleMcsEvent(uint32_t event, void* param);
+
+typedef base::Callback<void(uint8_t status, int server_if,
+                             std::vector<btgatt_db_element_t> service)>
+                            OnMcpServiceAdded;
+
+static void OnMcpServiceAddedCb(uint8_t status, int serverIf,
+                              std::vector<btgatt_db_element_t> service);
+
+/* Media state handlers */
+static bool MediaStateInactiveHandler(uint32_t event, void* param, uint8_t state);
+static bool MediaStatePauseHandler(uint32_t event, void* param, uint8_t state);
+static bool MediaStatePlayingHandler(uint32_t event, void* param, uint8_t state);
+static bool MediaStateSeekingHandler(uint32_t event, void* param, uint8_t state);
+
+/* Connection state machine handlers */
+static bool DeviceStateConnectionHandler(uint32_t event, void* param, uint8_t state);
+static bool DeviceStateDisconnectedHandler(uint32_t event, void* param, uint8_t state);
+
+Uuid MCS_UUID   = Uuid::FromString("1848");
+Uuid GMCS_UUID  = Uuid::FromString("1849");
+
+Uuid DESCRIPTOR_UUID = Uuid::FromString("2902");
+
+Uuid GMCS_MEDIA_STATE_UUID                      = Uuid::FromString("2BA3");
+Uuid GMCS_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED  = Uuid::FromString("2BA5");
+Uuid GMCS_MEDIA_PLAYER_NAME_UUID                = Uuid::FromString("2B93");
+Uuid GMCS_MEDIA_CONTROL_POINT                   = Uuid::FromString("2BA4");
+Uuid GMCS_TRACK_CHANGED                         = Uuid::FromString("2B96");
+Uuid GMCS_TRACK_TITLE                           = Uuid::FromString("2B97");
+Uuid GMCS_TRACK_DURATION                        = Uuid::FromString("2B98");
+Uuid GMCS_TRACK_POSITION                        = Uuid::FromString("2B99");
+Uuid GMCS_PLAYING_ORDER_SUPPORTED               = Uuid::FromString("2BA2");
+Uuid GMCS_PLAYING_ORDER                         = Uuid::FromString("2BA1");
+Uuid GMCS_CONTENT_CONTROLID                     = Uuid::FromString("2BBA");
+Uuid GMCS_SEEKING_SPEED_UUID                    = Uuid::FromString("2B9B");
+
+
+  bool is_pts_running() {
+    char value[PROPERTY_VALUE_MAX] = {'\0'};
+    bool pts_test_enabled = false;
+    osi_property_get("persist.vendor.service.bt.mcs.pts", value, "false");
+    pts_test_enabled = (strcmp(value, "true") == 0);
+    LOG(INFO) << "pts test enabled " << pts_test_enabled;
+    return pts_test_enabled;
+  }
+
+  int playing_order_opcode(int data) {
+    int event = 0;
+    if (data & MCP_PLAYING_OREDR_SHUFFLE_REPEAT) {
+      event = MCP_PLAYING_OREDR_SHUFFLE_REPEAT_REQ;
+    } else
+      LOG(INFO) << "opcode not matched or not supported";
+    return event;
+  }
+
+  bool is_opcode_supported(int data) {
+    LOG(INFO) << __func__ << "data " << data << "media_supported_feature " << mediaPlayerInfo.media_supported_feature;
+    switch (data) {
+      case MCP_MEDIA_CONTROL_OPCODE_PLAY:
+        return (mediaPlayerInfo.media_supported_feature & MCP_MEDIA_CONTROL_SUP_PLAY);
+      case MCP_MEDIA_CONTROL_OPCODE_PAUSE:
+        return (mediaPlayerInfo.media_supported_feature & MCP_MEDIA_CONTROL_SUP_PAUSE);
+      case MCP_MEDIA_CONTROL_OPCODE_FAST_REWIND:
+        return (mediaPlayerInfo.media_supported_feature & MCP_MEDIA_CONTROL_SUP_FAST_REWIND);
+      case MCP_MEDIA_CONTROL_OPCODE_FAST_FORWARD:
+        return (mediaPlayerInfo.media_supported_feature & MCP_MEDIA_CONTROL_SUP_FAST_FORWARD);
+      case MCP_MEDIA_CONTROL_OPCODE_STOP:
+        return (mediaPlayerInfo.media_supported_feature & MCP_MEDIA_CONTROL_SUP_STOP);
+      case MCP_MEDIA_CONTROL_OPCODE_PREV_TRACK:
+        return (mediaPlayerInfo.media_supported_feature & MCP_MEDIA_CONTROL_SUP_PREVIOUS_TRACK);
+      case MCP_MEDIA_CONTROL_OPCODE_NEXT_TRACK:
+        return (mediaPlayerInfo.media_supported_feature & MCP_MEDIA_CONTROL_SUP_NEXT_TRACK);
+    
+      // Fallthrough for all unknown key mappings
+      default:
+        LOG(INFO) << __func__ << "opcode is not supported";
+        return false;
+    }
+  }
+
+const char* get_mcp_event_name(uint32_t event) {
+  switch (event) {
+    CASE_RETURN_STR(MCP_INIT_EVENT)
+    CASE_RETURN_STR(MCP_CLEANUP_EVENT)
+    CASE_RETURN_STR(MCP_MEDIA_STATE_UPDATE)
+    CASE_RETURN_STR(MCP_MEDIA_PLAYER_NAME_UPDATE)
+    CASE_RETURN_STR(MCP_MEDIA_SUPPORTED_OPCODE_UPDATE)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_POINT_UPDATE)
+    CASE_RETURN_STR(MCP_PLAYING_ORDER_SUPPORTED_UPDATE)
+    CASE_RETURN_STR(MCP_PLAYING_ORDER_UPDATE)
+    CASE_RETURN_STR(MCP_TRACK_CHANGED_UPDATE)
+    CASE_RETURN_STR(MCP_TRACK_POSITION_UPDATE)
+    CASE_RETURN_STR(MCP_TRACK_DURATION_UPDATE)
+    CASE_RETURN_STR(MCP_TRACK_TITLE_UPDATE)
+    CASE_RETURN_STR(MCP_CCID_UPDATE)
+    CASE_RETURN_STR(MCP_ACTIVE_DEVICE_UPDATE)
+    CASE_RETURN_STR(MCP_ACTIVE_PROFILE)
+
+    //local event to handle in mcp state machine,
+    CASE_RETURN_STR(MCP_PLAYING_ORDER_SUPPORTED_READ)
+    CASE_RETURN_STR(MCP_PLAYING_ORDER_READ)
+    CASE_RETURN_STR(MCP_MEDIA_STATE_READ)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_READ)
+    CASE_RETURN_STR(MCP_MEDIA_PLAYER_NAME_READ)
+    CASE_RETURN_STR(MCP_TRACK_TITLE_READ)
+    CASE_RETURN_STR(MCP_TRACK_POSITION_READ)
+    CASE_RETURN_STR(MCP_TRACK_DURATION_READ)
+    CASE_RETURN_STR(MCP_CCID_READ)
+    CASE_RETURN_STR(MCP_SEEKING_SPEED_READ)
+    CASE_RETURN_STR(MCP_MEDIA_STATE_READ_DESCRIPTOR)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_POINT_READ_DESCRIPTOR)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_DESCRIPTOR_READ)
+    CASE_RETURN_STR(MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_READ)
+    CASE_RETURN_STR(MCP_TRACK_CHANGED_DESCRIPTOR_READ)
+    CASE_RETURN_STR(MCP_TRACK_TITLE_DESCRIPTOR_READ)
+    CASE_RETURN_STR(MCP_TRACK_POSITION_DESCRIPTOR_READ)
+    CASE_RETURN_STR(MCP_TRACK_DURATION_DESCRIPTOR_READ)
+    CASE_RETURN_STR(MCP_PLAYING_ORDER_DESCRIPTOR_READ)
+    CASE_RETURN_STR(MCP_MEDIA_STATE_DESCRIPTOR_WRITE)
+    CASE_RETURN_STR(MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_WRITE)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_POINT_DESCRIPTOR_WRITE)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_POINT_SUPPORTED_DESCRIPTOR_WRITE)
+    CASE_RETURN_STR(MCP_TRACK_CHANGED_DESCRIPTOR_WRITE)
+    CASE_RETURN_STR(MCP_TRACK_TITLE_DESCRIPTOR_WRITE)
+    CASE_RETURN_STR(MCP_TRACK_POSITION_DESCRIPTOR_WRITE)
+    CASE_RETURN_STR(MCP_TRACK_DURATION_DESCRIPTOR_WRITE)
+    CASE_RETURN_STR(MCP_PLAYING_ORDER_DESCRIPTOR_WRITE)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_POINT_WRITE)
+    CASE_RETURN_STR(MCP_PLAYING_ORDER_WRITE)
+    CASE_RETURN_STR(MCP_TRACK_POSITION_WRITE)
+
+    CASE_RETURN_STR(MCP_NOTIFY_ALL)
+    CASE_RETURN_STR(MCP_WRITE_RSP)
+    CASE_RETURN_STR(MCP_READ_RSP)
+    CASE_RETURN_STR(MCP_DESCRIPTOR_WRITE_RSP)
+    CASE_RETURN_STR(MCP_DESCRIPTOR_READ_RSP)
+    CASE_RETURN_STR(MCP_CONNECTION)
+    CASE_RETURN_STR(MCP_DISCONNECTION)
+    CASE_RETURN_STR(MCP_CONNECTION_UPDATE)
+    CASE_RETURN_STR(MCP_CONGESTION_UPDATE)
+    CASE_RETURN_STR(MCP_PHY_UPDATE)
+    CASE_RETURN_STR(MCP_MTU_UPDATE)
+    CASE_RETURN_STR(MCP_SET_ACTIVE_DEVICE)
+    CASE_RETURN_STR(MCP_CONNECTION_CLOSE_EVENT)
+    CASE_RETURN_STR(MCP_BOND_STATE_CHANGE_EVENT)
+    //media write op code event
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_PLAY_READ_REQ)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_PAUSE_REQ)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_FAST_FORWARD_REQ)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_FAST_REWIND_REQ)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_MOVE_RELATIVE_REQ)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_STOP_REQ)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_NEXT_TRACK_REQ)
+    CASE_RETURN_STR(MCP_MEDIA_CONTROL_PREVIOUS_TRACK_REQ)
+    CASE_RETURN_STR(MCP_PLAYING_OREDR_SHUFFLE_REPEAT_REQ)
+  default:
+    return "Unknown Event";
+  }
+}
+
+const char* get_mcp_media_state_name(uint8_t media_state) {
+  switch (media_state) {
+    CASE_RETURN_STR(MCP_STATE_INACTIVE)
+    CASE_RETURN_STR(MCP_STATE_PLAYING)
+    CASE_RETURN_STR(MCP_STATE_PAUSE)
+    CASE_RETURN_STR(MCP_STATE_SEEKING)
+  default:
+    return "Unknown Media State";
+  }
+}
+
+class RemoteDevices {
+ private:
+   ActiveDevice activeDevice;
+   //int max_connection;
+ public:
+  bool Add(RemoteDevice device) {
+    if (devices.size() == MAX_MCP_CONNECTION) {
+      return false;
+    }
+    if (FindByAddress(device.peer_bda) != nullptr) return false;
+      device.DeviceStateHandlerPointer[MCP_DISCONNECTED] = DeviceStateDisconnectedHandler;
+      device.DeviceStateHandlerPointer[MCP_CONNECTED] = DeviceStateConnectionHandler;
+      devices.push_back(device);
+      return true;
+  }
+
+  void Remove(RawAddress& address) {
+    for (auto it = devices.begin(); it != devices.end();) {
+      if (it->peer_bda != address) {
+        ++it;
+        continue;
+      }
+
+      it = devices.erase(it);
+      return;
+    }
+  }
+
+  RemoteDevice* FindByAddress(const RawAddress& address) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&address](const RemoteDevice& device) {
+                               return device.peer_bda == address;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  RemoteDevice* FindByConnId(uint16_t conn_id) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&conn_id](const RemoteDevice& device) {
+                               return device.conn_id == conn_id;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  size_t size() { return (devices.size()); }
+
+  std::vector<RemoteDevice> GetRemoteDevices() {
+    return devices;
+  }
+  std::vector<RemoteDevice> FindNotifyDevices(uint16_t handle) {
+    std::vector<RemoteDevice> notify_devices;
+    for (size_t it = 0; it != devices.size(); it++){
+      if(mcsServerServiceInfo.media_state_handle == handle &&
+        devices[it].media_state_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(mcsServerServiceInfo.media_player_name_handle == handle &&
+        devices[it].media_player_name_notify) {
+        notify_devices.push_back(devices[it]);
+      }  else if (mcsServerServiceInfo.media_control_point_opcode_supported_handle == handle &&
+        devices[it].media_control_point_opcode_supported_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(mcsServerServiceInfo.media_control_point_handle == handle &&
+        devices[it].media_control_point_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(mcsServerServiceInfo.track_changed_handle == handle &&
+        devices[it].track_changed_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(mcsServerServiceInfo.track_title_handle == handle &&
+        devices[it].track_title_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(mcsServerServiceInfo.track_duration_handle == handle &&
+        devices[it].track_duration_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(mcsServerServiceInfo.track_position_handle == handle &&
+        devices[it].track_position_notify) {
+        notify_devices.push_back(devices[it]);
+      } else if(mcsServerServiceInfo.playing_order_handle == handle &&
+        devices[it].playing_order_notify) {
+        notify_devices.push_back(devices[it]);
+      }
+    }
+    return notify_devices;
+  }
+  void AddSetActiveDevice(tMCP_SET_ACTIVE_DEVICE *device) {
+    if (device->set_id == activeDevice.set_id) {
+      activeDevice.address.push_back(device->address);
+    } else {
+      activeDevice.address.clear();
+      activeDevice.set_id = device->set_id;
+      activeDevice.address.push_back(device->address);
+    }
+  }
+
+  bool FindActiveDevice(RemoteDevice *remoteDevice) {
+    bool flag = false;
+    for (auto& it : activeDevice.address) {
+      if(remoteDevice->peer_bda == it) {
+        flag = true;
+        break;
+      }
+    }
+    return flag;
+  }
+
+  std::vector<RemoteDevice> devices;
+};
+
+
+class McpServerImpl : public McpServer {
+  bluetooth::mcp_server::McpServerCallbacks* callbacks;
+  Uuid app_uuid;
+
+  public:
+     RemoteDevices remoteDevices;
+     virtual ~McpServerImpl() = default;
+
+
+  McpServerImpl(bluetooth::mcp_server::McpServerCallbacks* callback, Uuid uuid)
+        :callbacks(callback),
+     app_uuid(uuid){
+    LOG(INFO) << "McpServerImpl gatts app register";
+    HandleMcsEvent(MCP_INIT_EVENT, &app_uuid);
+
+  }
+
+  void SetActiveDevice(const RawAddress& address, int setId, int profile) {
+    LOG(INFO) << __func__ ;
+    tMCP_SET_ACTIVE_DEVICE SetActiveDeviceOp;
+    SetActiveDeviceOp.set_id = setId;
+    SetActiveDeviceOp.address = address;
+    SetActiveDeviceOp.profile = profile;
+    HandleMcsEvent(MCP_ACTIVE_DEVICE_UPDATE, &SetActiveDeviceOp);
+  }
+
+  void MediaState(uint8_t state) {
+    LOG(INFO) << __func__ << " state: " << unsigned(state);
+    tMCP_MEDIA_STATE MediaStateOp;
+    MediaStateOp.state = state;
+    HandleMcsEvent(MCP_MEDIA_STATE_UPDATE, &MediaStateOp);
+  }
+
+  void MediaPlayerName(uint8_t* player_name) {
+    LOG(INFO) << __func__;
+    tMCP_MEDIA_PLAYER_NAME MediaPlayerNameOp;
+    MediaPlayerNameOp.name = player_name;
+    MediaPlayerNameOp.len = strlen((char *)player_name);
+    if(MediaPlayerNameOp.len != 0)
+      HandleMcsEvent(MCP_MEDIA_PLAYER_NAME_UPDATE, &MediaPlayerNameOp);
+  }
+
+  void MediaControlPointOpcodeSupported(uint32_t feature) {
+    LOG(INFO) << __func__;
+    tMCP_MEDIA_OPCODE_SUPPORT MediaControlPointOpcodeSupportedOp;
+    MediaControlPointOpcodeSupportedOp.supported = feature;
+    HandleMcsEvent(MCP_MEDIA_SUPPORTED_OPCODE_UPDATE, &MediaControlPointOpcodeSupportedOp);
+  }
+
+  void MediaControlPoint(uint8_t value) {
+    LOG(INFO) << __func__;
+    tMCP_MEDIA_CONTROL_POINT MediaControlPoint;
+    MediaControlPoint.req_opcode = value;
+    MediaControlPoint.result = MCP_STATUS_SUCCESS; // success
+    HandleMcsEvent(MCP_MEDIA_CONTROL_POINT_UPDATE, &MediaControlPoint);
+  }
+
+  void TrackChanged(bool status) {
+    LOG(INFO) << __func__;
+    tMCP_TRACK_CHANGED TrackChangedOp;
+    TrackChangedOp.status = status;
+    HandleMcsEvent(MCP_TRACK_CHANGED_UPDATE, &TrackChangedOp);
+  }
+
+  void TrackTitle(uint8_t* track_name) {
+    LOG(INFO) << __func__;
+    tMCP_TRACK_TITLE TrackTitleOp;
+    TrackTitleOp.title = track_name;
+    TrackTitleOp.len = strlen((char *)track_name);
+    if (TrackTitleOp.len != 0)
+      HandleMcsEvent(MCP_TRACK_TITLE_UPDATE, &TrackTitleOp);
+  }
+
+  void TrackDuration(int32_t duration) {
+    LOG(INFO) << __func__;
+    tMCP_TRACK_DURATION TrackDurationOp;
+    TrackDurationOp.duration = duration;
+    HandleMcsEvent(MCP_TRACK_DURATION_UPDATE, &TrackDurationOp);
+  }
+
+  void TrackPosition(int32_t position) {
+    LOG(INFO) << __func__;
+    tMCP_TRACK_POSTION TrackPositionOp;
+    TrackPositionOp.position = position;
+    HandleMcsEvent(MCP_TRACK_POSITION_UPDATE, &TrackPositionOp);
+  }
+
+  void PlayingOrderSupported(uint16_t order) {
+    LOG(INFO) << __func__;
+    tMCP_PLAYING_ORDER_SUPPORT PlayingOrderSupportedOp;
+    PlayingOrderSupportedOp.order_supported = order;
+    HandleMcsEvent(MCP_PLAYING_ORDER_SUPPORTED_UPDATE, &PlayingOrderSupportedOp);
+  }
+
+  void PlayingOrder(uint8_t value) {
+    LOG(INFO) << __func__;
+    tMCP_PLAYING_ORDER PlayingOrderOp;
+    PlayingOrderOp.order = value;
+    HandleMcsEvent(MCP_PLAYING_ORDER_UPDATE, &PlayingOrderOp);
+  }
+
+  void ContentControlId(uint8_t ccid) {
+    LOG(INFO) << __func__;
+    tMCP_CONTENT_CONTROL_ID ContentControlIdOp;
+    ContentControlIdOp.ccid = ccid;
+    HandleMcsEvent(MCP_CCID_UPDATE, &ContentControlIdOp);
+  }
+
+  void DisconnectMcp(const RawAddress& bd_addr) {
+    LOG(INFO) << __func__;
+    tMCP_CONNECTION_CLOSE ConnectClosingOp;
+    ConnectClosingOp.addr = bd_addr;
+    HandleMcsEvent(MCP_CONNECTION_CLOSE_EVENT, &ConnectClosingOp);
+  }
+
+  void BondStateChange(const RawAddress& bd_addr, int state) {
+    LOG(INFO) << __func__;
+    tMCP_BOND_STATE_CHANGE BondStateChangeOP;
+    BondStateChangeOP.addr = bd_addr;
+    BondStateChangeOP.state = state;
+    HandleMcsEvent(MCP_BOND_STATE_CHANGE_EVENT, &BondStateChangeOP);
+  }
+
+  void OnConnectionStateChange(uint8_t state, const RawAddress& address) {
+    LOG(INFO) << __func__ << "   bta";
+    callbacks->OnConnectionStateChange(state, address);
+  }
+
+  void MediaControlPointChangeReq(uint8_t state, const RawAddress& address) {
+    callbacks->MediaControlPointChangeReq(state, address);
+    LOG(INFO) << __func__;
+  }
+
+  void TrackPositionChangeReq(int32_t position) {
+    callbacks->TrackPositionChangeReq(position);
+    LOG(INFO) << __func__;
+  }
+
+  void PlayingOrderChangeReq(uint16_t playingOrder) {
+    callbacks->PlayingOrderChangeReq(playingOrder);
+    LOG(INFO) << __func__;
+  }
+
+};
+
+
+void McpServer::CleanUp() {
+  HandleMcsEvent(MCP_CLEANUP_EVENT, NULL);
+  delete instance;
+  instance = nullptr;
+}
+
+McpServer* McpServer::Get() {
+  CHECK(instance);
+  return instance;
+}
+
+void  McpServer::Initialize(bluetooth::mcp_server::McpServerCallbacks* callbacks, Uuid uuid) {
+  if (instance) {
+  LOG(ERROR) << "Already initialized!";
+  } else {
+     instance = new McpServerImpl(callbacks, uuid);
+  }
+}
+
+bool McpServer::isMcpServiceRunnig() { return instance; }
+
+static std::vector<btgatt_db_element_t> McpAddService(int server_if) {
+
+  std::vector<btgatt_db_element_t> mcs_services;
+  mcs_services.clear();
+  //service
+  btgatt_db_element_t service = {.uuid = GMCS_UUID, .type = BTGATT_DB_PRIMARY_SERVICE, 0};
+  mcs_services.push_back(service);
+  mcsServerServiceInfo.mcs_service_uuid = service.uuid;
+
+  //media state service
+  btgatt_db_element_t mcs_media_state_char = {.uuid = GMCS_MEDIA_STATE_UUID,
+                                              .type = BTGATT_DB_CHARACTERISTIC,
+                                              .properties = GATT_CHAR_PROP_BIT_READ|
+                                                            GATT_CHAR_PROP_BIT_NOTIFY,
+                                              .permissions = GATT_PERM_READ};
+  mcs_services.push_back(mcs_media_state_char);
+  mcsServerServiceInfo.media_state_uuid = mcs_media_state_char.uuid;
+
+  //1st desc
+  btgatt_db_element_t desc1 = {.uuid = DESCRIPTOR_UUID,
+                               .type = BTGATT_DB_DESCRIPTOR,
+                               .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+  mcs_services.push_back(desc1);
+
+  //media supported feature
+  btgatt_db_element_t mcs_media_opcode_supported_char = {.uuid = GMCS_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED,
+                                                         .type = BTGATT_DB_CHARACTERISTIC,
+                                                         .properties = GATT_CHAR_PROP_BIT_READ|
+                                                                       GATT_CHAR_PROP_BIT_NOTIFY,
+                                                         .permissions = GATT_PERM_READ};
+
+  mcs_services.push_back(mcs_media_opcode_supported_char);
+  mcsServerServiceInfo.media_control_point_opcode_supported_uuid =
+    mcs_media_opcode_supported_char.uuid;
+
+  //2nd desc
+  btgatt_db_element_t desc2 = {.uuid = DESCRIPTOR_UUID,
+                               .type = BTGATT_DB_DESCRIPTOR,
+                               .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+  mcs_services.push_back(desc2);
+
+  ////media player name
+  btgatt_db_element_t mcs_media_player_name_char = {.uuid = GMCS_MEDIA_PLAYER_NAME_UUID,
+                                                    .type = BTGATT_DB_CHARACTERISTIC,
+                                                    .properties = GATT_CHAR_PROP_BIT_READ|
+                                                                  GATT_CHAR_PROP_BIT_NOTIFY,
+                                                    .permissions = GATT_PERM_READ};
+  mcs_services.push_back(mcs_media_player_name_char);
+  mcsServerServiceInfo.media_player_name_uuid = mcs_media_player_name_char.uuid;
+
+  //3rd desc
+  btgatt_db_element_t desc3 = {.uuid = DESCRIPTOR_UUID,
+                               .type = BTGATT_DB_DESCRIPTOR,
+                               .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+  mcs_services.push_back(desc3);
+
+  //media control point
+  btgatt_db_element_t mcs_media_control_point_char = {.uuid = GMCS_MEDIA_CONTROL_POINT,
+                                                      .type = BTGATT_DB_CHARACTERISTIC,
+                                                      .properties = GATT_CHAR_PROP_BIT_WRITE_NR|
+                                                                    GATT_CHAR_PROP_BIT_WRITE|
+                                                                    GATT_CHAR_PROP_BIT_NOTIFY,
+                                                      .permissions = GATT_PERM_WRITE};
+
+  mcs_services.push_back(mcs_media_control_point_char);
+
+  mcsServerServiceInfo.media_player_name_uuid = mcs_media_control_point_char.uuid;
+
+  //4th desc
+  btgatt_db_element_t desc4 = {.uuid = DESCRIPTOR_UUID,
+                               .type = BTGATT_DB_DESCRIPTOR,
+                               .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+  mcs_services.push_back(desc4);
+
+  btgatt_db_element_t track_changed_char = {.uuid = GMCS_TRACK_CHANGED,
+                                            .type = BTGATT_DB_CHARACTERISTIC,
+                                            .properties = GATT_CHAR_PROP_BIT_NOTIFY,
+                                            .permissions = GATT_PERM_READ};
+
+  mcs_services.push_back(track_changed_char);
+  mcsServerServiceInfo.track_changed_uuid = track_changed_char.uuid;
+
+  //5th desc
+  btgatt_db_element_t desc5 = {.uuid = DESCRIPTOR_UUID,
+                               .type = BTGATT_DB_DESCRIPTOR,
+                               .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+  mcs_services.push_back(desc5);
+
+  btgatt_db_element_t track_title_char = {.uuid = GMCS_TRACK_TITLE,
+                                          .type = BTGATT_DB_CHARACTERISTIC,
+                                          .properties = GATT_CHAR_PROP_BIT_NOTIFY|
+                                                        GATT_CHAR_PROP_BIT_READ,
+                                          .permissions = GATT_PERM_READ};
+
+  mcs_services.push_back(track_title_char);
+  mcsServerServiceInfo.track_title_uuid = track_title_char.uuid;
+
+  //6th desc
+  btgatt_db_element_t desc6 = {.uuid = DESCRIPTOR_UUID,
+                               .type = BTGATT_DB_DESCRIPTOR,
+                               .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+  mcs_services.push_back(desc6);
+
+
+  btgatt_db_element_t track_duration_char = {.uuid = GMCS_TRACK_DURATION,
+                                             .type = BTGATT_DB_CHARACTERISTIC,
+                                             .properties = GATT_CHAR_PROP_BIT_NOTIFY|
+                                                           GATT_CHAR_PROP_BIT_READ,
+                                             .permissions = GATT_PERM_READ};
+
+  mcs_services.push_back(track_duration_char);
+  mcsServerServiceInfo.track_duration_uuid = track_duration_char.uuid;
+
+  //7th desc
+  btgatt_db_element_t desc7 = {.uuid = DESCRIPTOR_UUID,
+                               .type = BTGATT_DB_DESCRIPTOR,
+                               .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+  mcs_services.push_back(desc7);
+
+  btgatt_db_element_t track_position_char = {.uuid = GMCS_TRACK_POSITION,
+                                             .type = BTGATT_DB_CHARACTERISTIC,
+                                             .properties = GATT_CHAR_PROP_BIT_READ|
+                                                           GATT_CHAR_PROP_BIT_WRITE_NR|
+                                                           GATT_CHAR_PROP_BIT_WRITE|
+                                                           GATT_CHAR_PROP_BIT_NOTIFY,
+                                             .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+
+  mcs_services.push_back(track_position_char);
+  mcsServerServiceInfo.track_position_uuid = track_position_char.uuid;
+
+  //8th desc
+  btgatt_db_element_t desc8 = {.uuid = DESCRIPTOR_UUID,
+                               .type = BTGATT_DB_DESCRIPTOR,
+                               .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+  mcs_services.push_back(desc8);
+
+  btgatt_db_element_t playing_order_supported_char = {.uuid = GMCS_PLAYING_ORDER_SUPPORTED,
+                                                      .type = BTGATT_DB_CHARACTERISTIC,
+                                                      .properties = GATT_CHAR_PROP_BIT_READ,
+                                                      .permissions = GATT_PERM_READ};
+
+  mcs_services.push_back(playing_order_supported_char);
+  mcsServerServiceInfo.playing_order_supported_uuid = playing_order_supported_char.uuid;
+
+  btgatt_db_element_t playing_order_char = {.uuid = GMCS_PLAYING_ORDER,
+                                            .type = BTGATT_DB_CHARACTERISTIC,
+                                             .properties = GATT_CHAR_PROP_BIT_READ|
+                                                           GATT_CHAR_PROP_BIT_WRITE_NR|
+                                                           GATT_CHAR_PROP_BIT_WRITE|
+                                                           GATT_CHAR_PROP_BIT_NOTIFY,
+                                            .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+
+  mcs_services.push_back(playing_order_char);
+  mcsServerServiceInfo.playing_order_uuid = playing_order_char.uuid;
+
+  //10th desc
+  btgatt_db_element_t desc10 = {.uuid = DESCRIPTOR_UUID,
+                                .type = BTGATT_DB_DESCRIPTOR,
+                                .permissions = GATT_PERM_READ|GATT_PERM_WRITE};
+  mcs_services.push_back(desc10);
+
+  btgatt_db_element_t ccid_char = {.uuid = GMCS_CONTENT_CONTROLID,
+                                   .type = BTGATT_DB_CHARACTERISTIC,
+                                   .properties = GATT_CHAR_PROP_BIT_READ,
+                                   .permissions = GATT_PERM_READ};
+
+  mcs_services.push_back(ccid_char);
+  mcsServerServiceInfo.ccid_uuid = ccid_char.uuid;
+
+  btgatt_db_element_t seek_speed_char = {.uuid = GMCS_SEEKING_SPEED_UUID,
+                                         .type = BTGATT_DB_CHARACTERISTIC,
+                                         .properties = GATT_CHAR_PROP_BIT_READ,
+                                         .permissions = GATT_PERM_READ};
+  mcs_services.push_back(seek_speed_char);
+  mcsServerServiceInfo.seeking_speed_uuid = seek_speed_char.uuid;
+
+  return mcs_services;
+}
+
+
+static void OnMcpServiceAddedCb(uint8_t status, int serverIf,
+                                std::vector<btgatt_db_element_t> service) {
+
+  if (service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER) ||
+      service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
+    LOG(INFO) << "%s: Attempt to register restricted service"<< __func__;
+    return;
+  }
+  for(int i = 0; i < (int)service.size(); i++) {
+
+    if (service[i].uuid == GMCS_UUID) {
+      LOG(INFO) << __func__ << " mcs service added attr handle " << service[i].attribute_handle;
+    } else if (service[i].uuid == GMCS_MEDIA_STATE_UUID) {
+      mcsServerServiceInfo.media_state_handle = service[i++].attribute_handle;
+      mcsServerServiceInfo.media_state_desc = service[i].attribute_handle;
+      LOG(INFO) << __func__ << " media_state_handle" <<
+      mcsServerServiceInfo.media_state_handle;
+      LOG(INFO) << __func__ << " media_state_handle desc" <<
+      mcsServerServiceInfo.media_state_desc;
+   } else if(service[i].uuid == GMCS_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED) {
+     mcsServerServiceInfo.media_control_point_opcode_supported_handle =
+         service[i++].attribute_handle;
+     LOG(INFO) << __func__ << " media_control_point_opcode_supported_handle register" <<
+         mcsServerServiceInfo.media_control_point_opcode_supported_handle;
+      mcsServerServiceInfo.media_control_point_opcode_supported_desc =
+         service[i].attribute_handle;
+     LOG(INFO) << __func__ << " media_control_point_opcode_supported_handle desc register" <<
+         mcsServerServiceInfo.media_control_point_opcode_supported_desc;
+    } else if(service[i].uuid == GMCS_MEDIA_PLAYER_NAME_UUID) {
+      mcsServerServiceInfo.media_player_name_handle =
+          service[i++].attribute_handle;
+      LOG(INFO) << __func__ << " media_player_name_handle GMCS_MEDIA_PLAYER_NAME_UUID register"
+          << mcsServerServiceInfo.media_player_name_handle;
+      mcsServerServiceInfo.media_player_name_desc =
+          service[i].attribute_handle;
+      LOG(INFO) << __func__ << " media_player_name_handle GMCS_MEDIA_PLAYER_NAME_UUID desc"
+          << mcsServerServiceInfo.media_player_name_desc;
+    } else if(service[i].uuid == GMCS_MEDIA_CONTROL_POINT) {
+      mcsServerServiceInfo.media_control_point_handle =
+          service[i++].attribute_handle;
+      LOG(INFO) << __func__ << " media_control_point_handle GMCS_MEDIA_CONTROL_POINT register"
+          << mcsServerServiceInfo.media_control_point_handle;
+      mcsServerServiceInfo.media_control_point_desc =
+          service[i].attribute_handle;
+      LOG(INFO) << __func__ << " media_control_point_handle GMCS_MEDIA_CONTROL_POINT desc"
+          << mcsServerServiceInfo.media_control_point_desc;
+    } else if(service[i].uuid == GMCS_TRACK_CHANGED) {
+      mcsServerServiceInfo.track_changed_handle =
+          service[i++].attribute_handle;
+      LOG(INFO) << __func__ << "track_changed_handle GMCS_TRACK_CHANGED register"
+          << mcsServerServiceInfo.track_changed_handle;
+      mcsServerServiceInfo.track_changed_desc =
+          service[i].attribute_handle;
+      LOG(INFO) << __func__ << "track_changed_handle GMCS_TRACK_CHANGED desc"
+          << mcsServerServiceInfo.track_changed_desc;
+    } else if(service[i].uuid == GMCS_TRACK_TITLE) {
+      mcsServerServiceInfo.track_title_handle =
+          service[i++].attribute_handle;
+      LOG(INFO) << __func__ << "track_title_handle GMCS_TRACK_TITLE register"
+          << mcsServerServiceInfo.track_title_handle;
+      mcsServerServiceInfo.track_title_desc =
+          service[i].attribute_handle;
+      LOG(INFO) << __func__ << "track_title_handle GMCS_TRACK_TITLE desc"
+         << mcsServerServiceInfo.track_title_desc;
+    } else if(service[i].uuid == GMCS_TRACK_DURATION) {
+      mcsServerServiceInfo.track_duration_handle =
+          service[i++].attribute_handle;
+
+      LOG(INFO) << __func__ << "track_duration_handle GMCS_TRACK_DURATION register"
+        << mcsServerServiceInfo.track_duration_handle;
+      mcsServerServiceInfo.track_duration_desc =
+        service[i].attribute_handle;
+
+      LOG(INFO) << __func__ << "track_duration_handle GMCS_TRACK_DURATION desc"
+        << mcsServerServiceInfo.track_duration_desc;
+
+    } else if(service[i].uuid == GMCS_TRACK_POSITION) {
+      mcsServerServiceInfo.track_position_handle =
+        service[i++].attribute_handle;
+      LOG(INFO) << __func__ << "track_position_handle GMCS_TRACK_POSITION register"
+        << mcsServerServiceInfo.track_position_handle;
+      mcsServerServiceInfo.track_position_desc =
+        service[i].attribute_handle;
+      LOG(INFO) << __func__ << "track_position_handle GMCS_TRACK_POSITION desc"
+        << mcsServerServiceInfo.track_position_handle;
+
+    } else if(service[i].uuid == GMCS_PLAYING_ORDER_SUPPORTED) {
+      mcsServerServiceInfo.playing_order_supported_handle =
+          service[i].attribute_handle;
+      LOG(INFO) << __func__ << "playing_order_supported_handle GMCS_PLAYING_ORDER_SUPPORTED register"
+          << mcsServerServiceInfo.playing_order_supported_handle;
+    } else if(service[i].uuid == GMCS_PLAYING_ORDER) {
+      mcsServerServiceInfo.playing_order_handle =
+          service[i++].attribute_handle;
+      LOG(INFO) << __func__ << "playing_order_handle GMCS_PLAYING_ORDER register"
+          << mcsServerServiceInfo.playing_order_handle;
+      mcsServerServiceInfo.playing_order_desc =
+          service[i].attribute_handle;
+      LOG(INFO) << __func__ << "playing_order_handle GMCS_PLAYING_ORDER desc"
+          << mcsServerServiceInfo.playing_order_desc;
+    } else if(service[i].uuid == GMCS_CONTENT_CONTROLID) {
+      mcsServerServiceInfo.ccid_handle =
+          service[i].attribute_handle;
+      LOG(INFO) << __func__ << "ccid_handle GMCS_CONTENT_CONTROLID register" <<
+          mcsServerServiceInfo.ccid_handle;
+    } else if(service[i].uuid == GMCS_SEEKING_SPEED_UUID) {
+      mcsServerServiceInfo.seeking_speed_handle =
+          service[i].attribute_handle;
+      LOG(INFO) << __func__ << "ccid_handle GMCS_SEEKING_SPEED_ID register" <<
+          mcsServerServiceInfo.seeking_speed_handle;
+    }
+  } //for
+}
+
+
+void BTMcpCback(tBTA_GATTS_EVT event, tBTA_GATTS* param) {
+  HandleMcsEvent((uint32_t)event, param);
+}
+
+//mcs handle event
+void HandleMcsEvent(uint32_t event, void* param) {
+  LOG(INFO) << __func__ << "   mcs handle event " << get_mcp_event_name(event);
+  tBTA_GATTS* p_data = NULL;
+  uint32_t proc_event;
+  mcp_resp_t *rsp = (mcp_resp_t *)osi_malloc(sizeof(mcp_resp_t));
+  if (rsp == NULL) {
+    LOG(INFO) << __func__ << " mcs handle return rsp not allocated ";
+    return;
+  }
+  uint8_t status = BT_STATUS_SUCCESS;
+  proc_event = MCP_NONE_EVENT;
+  rsp->event = MCP_NONE_EVENT;
+  switch (event) {
+
+    case MCP_INIT_EVENT:
+    {
+       // Uuid app_uuid = (Uuid)*param;
+      Uuid aap_uuid = Uuid::FromString("1849");
+      mediaPlayerInfo.media_state = MCP_STATE_INACTIVE;
+      mediaPlayerInfo.MediaStateHandlerPointer[MCP_STATE_INACTIVE] =
+          MediaStateInactiveHandler;
+      mediaPlayerInfo.MediaStateHandlerPointer[MCP_STATE_PAUSE] =
+          MediaStatePauseHandler;
+      mediaPlayerInfo.MediaStateHandlerPointer[MCP_STATE_PLAYING] =
+          MediaStatePlayingHandler;
+      mediaPlayerInfo.MediaStateHandlerPointer[MCP_STATE_SEEKING] =
+          MediaStateSeekingHandler;
+
+      mediaPlayerInfo.media_supported_feature = MCP_DEFAULT_MEDIA_CTRL_SUPP_FEAT;
+      mediaPlayerInfo.ccid = 0;
+      mediaPlayerInfo.seeking_speed = 0;
+      mediaPlayerInfo.duration = TRACK_POSITION_UNAVAILABLE;
+      mediaPlayerInfo.position = TRACK_DURATION_UNAVAILABLE;
+      mediaPlayerInfo.track_changed = false;
+      mediaPlayerInfo.playing_order_value = 1;
+      mediaPlayerInfo.playing_order_supported = 1;
+      mediaPlayerInfo.player_name_len = 0;
+      mediaPlayerInfo.track_title_len = 0;
+      mediaPlayerInfo.media_ctrl_point = 0;
+      //adding app with random uuid
+      BTA_GATTS_AppRegister(aap_uuid, BTMcpCback, true);
+      break;
+    }
+
+    case MCP_CLEANUP_EVENT:
+    {
+      //initiate disconnection to all connected device
+      //unregister APP
+      BTA_GATTS_AppDeregister(mcsServerServiceInfo.server_if);
+      break;
+    }
+    case BTA_GATTS_REG_EVT:
+    {
+       p_data = (tBTA_GATTS*)param;
+       if (p_data->reg_oper.status == BT_STATUS_SUCCESS) {
+           mcsServerServiceInfo.server_if = p_data->reg_oper.server_if;
+         std::vector<btgatt_db_element_t> service;
+         service = McpAddService(mcsServerServiceInfo.server_if);
+         if (service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER) ||
+                 service[0].uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
+           LOG(INFO) << __func__ << "   service app register uuid is not valid";
+           break;
+         }
+         LOG(INFO) << __func__ << "   service app register";
+         BTA_GATTS_AddService(mcsServerServiceInfo.server_if, service, base::Bind(&OnMcpServiceAddedCb));
+       }
+       break;
+    }
+
+    case BTA_GATTS_DEREG_EVT:
+    {
+      break;
+    }
+
+    case BTA_GATTS_CONF_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      uint16_t conn_id = p_data->req_data.conn_id;
+      uint8_t status = p_data->req_data.status;
+      LOG(INFO) << __func__ << "conn_id :" << conn_id << "status:" << status;
+      if (status == BT_STATUS_SUCCESS) {
+          LOG(INFO) << __func__ << "Notification callback for conn_id :" << conn_id;
+          GattsOpsQueue::NotificationCallback(conn_id);
+      }
+      break;
+    }
+
+    case BTA_GATTS_CONGEST_EVT:
+    {
+      p_data = (tBTA_GATTS*)param;
+      RemoteDevice *remoteDevice;
+      remoteDevice = instance->remoteDevices.FindByConnId(p_data->congest.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << "   connection entry not found conn_id :"
+          << p_data->congest.conn_id;
+        break;
+      }
+     // rsp->ConngestionOp.status = p_data->req_data.status;
+      rsp->remoteDevice = remoteDevice;
+      rsp->oper.CongestionOp.congested = p_data->congest.congested;
+      proc_event = MCP_CONGESTION_UPDATE;
+      rsp->event = MCP_CONGESTION_UPDATE;
+      break;
+    }
+    case BTA_GATTS_MTU_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      RemoteDevice *remoteDevice;
+      remoteDevice = instance->remoteDevices.FindByConnId(p_data->req_data.p_data->mtu);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " connection entry not found conn_id :"
+          << p_data->congest.conn_id;
+        break;
+      }
+      LOG(INFO) << __func__ << " conn_id :"<< p_data->req_data.p_data->mtu;
+      LOG(INFO) <<"mtu " <<p_data->req_data.p_data->mtu;
+      proc_event = MCP_MTU_UPDATE;
+      rsp->event = MCP_MTU_UPDATE;
+      rsp->remoteDevice = remoteDevice;
+      rsp->oper.MtuOp.mtu = p_data->req_data.p_data->mtu;
+      break;
+    }
+    case BTA_GATTS_CONNECT_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      LOG(INFO) << __func__ << "   remote devices connected";
+      //<TBD> need to discuss how to get encryption support
+    /*
+    #if (!defined(BTA_SKIP_BLE_START_ENCRYPTION) || BTA_SKIP_BLE_START_ENCRYPTION == FALSE)
+        btif_gatt_check_encrypted_link(p_data->conn.remote_bda,
+                                     p_data->conn.transport);
+    #endif*/
+      RemoteDevice remoteDevice;
+      memset(&remoteDevice, 0, sizeof(remoteDevice));
+      if(instance->remoteDevices.FindByAddress(p_data->conn.remote_bda)) {
+      LOG(INFO) << __func__ << "  remote devices already there is connected list";
+        status = BT_STATUS_FAIL;
+        return;
+      }
+      remoteDevice.peer_bda = p_data->conn.remote_bda;
+      remoteDevice.conn_id = p_data->conn.conn_id;
+      if(instance->remoteDevices.Add(remoteDevice) == false) {
+        LOG(INFO) << __func__ << "  remote device is not added : max connection reached";
+        //<TBD> need to check disconnection required
+        break;
+      }
+      remoteDevice.state = MCP_DISCONNECTED;
+
+      LOG(INFO) << __func__ << "   remote devices connected conn_id: "<< remoteDevice.conn_id <<
+         "bd_addr " << remoteDevice.peer_bda;
+      rsp->remoteDevice = instance->remoteDevices.FindByAddress(p_data->conn.remote_bda);
+      if ( rsp->remoteDevice == NULL) {
+        LOG(INFO) << __func__ ;
+        break;
+      }
+      proc_event = MCP_CONNECTION;
+      rsp->event = MCP_CONNECTION;
+      break;
+    }
+
+    case BTA_GATTS_CLOSE_EVT:
+    case BTA_GATTS_DISCONNECT_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      LOG(INFO) << __func__ << "   remote devices disconnected conn_id " << p_data->conn.conn_id;
+      RemoteDevice *remoteDevice;
+      remoteDevice = instance->remoteDevices.FindByConnId(p_data->conn.conn_id);
+      if((!remoteDevice) ) {
+        status = BT_STATUS_FAIL;
+        break;
+      }
+
+      rsp->remoteDevice = remoteDevice;
+      proc_event = MCP_DISCONNECTION;
+      rsp->event = MCP_DISCONNECTION;
+      LOG(INFO) << __func__ << "  disconnected conn_id " << p_data->conn.conn_id;
+      break;
+    }
+
+    case BTA_GATTS_STOP_EVT:
+      //<TBD> not required
+      break;
+
+    case BTA_GATTS_DELELTE_EVT:
+      //<TBD> not required
+      break;
+
+    case BTA_GATTS_READ_CHARACTERISTIC_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      std::vector<uint8_t> value;
+      RemoteDevice *remoteDevice =
+          instance->remoteDevices.FindByConnId(p_data->req_data.conn_id);
+      LOG(INFO) << __func__ << " charateristcs read handle " <<
+          p_data->req_data.p_data->read_req.handle <<" trans_id : " <<
+          p_data->req_data.trans_id;
+
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore read operation";
+        status = BT_STATUS_FAIL;
+        break;
+      }
+
+      LOG(INFO) <<"   offset: " << p_data->req_data.p_data->read_req.offset <<
+          " long : " << p_data->req_data.p_data->read_req.is_long;
+
+      rsp->rsp_value.attr_value.auth_req  = 0;
+      rsp->rsp_value.attr_value.handle = p_data->req_data.p_data->read_req.handle;
+      rsp->rsp_value.attr_value.offset = p_data->req_data.p_data->read_req.offset;
+
+      if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.media_state_handle) {
+        proc_event = MCP_MEDIA_STATE_READ;
+        LOG(INFO) << __func__ << " media_state_handle read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+        mcsServerServiceInfo.media_control_point_opcode_supported_handle) {
+        proc_event = MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_READ;
+        LOG(INFO) << __func__ << " media_control_point_opcode_supported_handle read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+        mcsServerServiceInfo.media_player_name_handle) {
+        proc_event = MCP_MEDIA_PLAYER_NAME_READ;
+        LOG(INFO) << __func__ << " media_player_name_handle read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+        mcsServerServiceInfo.track_title_handle) {
+        proc_event = MCP_TRACK_TITLE_READ;
+        LOG(INFO) << __func__ << " track_title_handle read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+        mcsServerServiceInfo.track_position_handle) {
+        proc_event = MCP_TRACK_POSITION_READ;
+        LOG(INFO) << __func__ << " track_position_handle read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+        mcsServerServiceInfo.track_duration_handle) {
+        proc_event = MCP_TRACK_DURATION_READ;
+        LOG(INFO) << __func__ << " track_duration_handle read";
+      } else if(p_data->req_data.p_data->read_req.handle ==
+        mcsServerServiceInfo.ccid_handle) {
+        LOG(INFO) << __func__ << " ccid_handle read";
+        proc_event = MCP_CCID_READ;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+        mcsServerServiceInfo.seeking_speed_handle) {
+        LOG(INFO) << __func__ << " seeking_speed_handle read";
+        proc_event = MCP_SEEKING_SPEED_READ;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+        mcsServerServiceInfo.playing_order_supported_handle) {
+        LOG(INFO) << __func__ << " playing_order_supported_handle read";
+        proc_event = MCP_PLAYING_ORDER_SUPPORTED_READ;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+        mcsServerServiceInfo.playing_order_handle) {
+        LOG(INFO) << __func__ << " playing_order_handle read";
+        proc_event = MCP_PLAYING_ORDER_READ;
+      } else {
+        LOG(INFO) << __func__ << " read request for unknown handle" << p_data->req_data.p_data->read_req.handle;
+        status = BT_STATUS_FAIL;
+        break;
+      }
+      LOG(INFO) << __func__ << " read request handle" << p_data->req_data.p_data->read_req.handle <<
+        "connection id" << p_data->req_data.conn_id;
+      rsp->event = MCP_READ_RSP;
+      rsp->oper.ReadOp.trans_id = p_data->req_data.trans_id;
+      rsp->oper.ReadOp.is_long = p_data->req_data.p_data->read_req.is_long;
+      rsp->oper.ReadOp.status = BT_STATUS_SUCCESS;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case BTA_GATTS_READ_DESCRIPTOR_EVT: {
+      LOG(INFO) << __func__ << "  read descriptor";
+      p_data = (tBTA_GATTS*)param;
+      LOG(INFO) << __func__ << "  charateristcs read desc handle " <<
+          p_data->req_data.p_data->read_req.handle << " offset : "
+          << p_data->req_data.p_data->read_req.offset;
+      RemoteDevice *remoteDevice =
+          instance->remoteDevices.FindByConnId(p_data->req_data.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore write";
+        status = BT_STATUS_FAIL;
+        break;
+      }
+
+      if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.media_state_desc) {
+        LOG(INFO) << __func__ << " media_state_desc read";
+        proc_event = MCP_MEDIA_STATE_READ_DESCRIPTOR;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.media_control_point_desc) {
+        LOG(INFO) << __func__ << " media_control_point_desc read";
+        proc_event = MCP_MEDIA_CONTROL_POINT_READ_DESCRIPTOR;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.media_control_point_opcode_supported_desc) {
+        LOG(INFO) << __func__ << " media_control_point_opcode_supported_desc read";
+        proc_event = MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_DESCRIPTOR_READ;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.media_player_name_desc) {
+        LOG(INFO) << __func__ << " media_player_name_desc read";
+        proc_event = MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_READ;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.track_changed_desc) {
+        LOG(INFO) << __func__ << " track_changed_desc read";
+        proc_event = MCP_TRACK_CHANGED_DESCRIPTOR_READ;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.track_title_desc) {
+        LOG(INFO) << __func__ << " track_title_desc read";
+        proc_event = MCP_TRACK_TITLE_DESCRIPTOR_READ;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.track_position_desc) {
+        LOG(INFO) << __func__ << " track_position_desc read";
+        proc_event = MCP_TRACK_POSITION_DESCRIPTOR_READ;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.track_duration_desc) {
+        LOG(INFO) << __func__ << " track_duration_desc read";
+        proc_event = MCP_TRACK_DURATION_DESCRIPTOR_READ;
+      } else if(p_data->req_data.p_data->read_req.handle ==
+          mcsServerServiceInfo.playing_order_desc) {
+        LOG(INFO) << __func__ << " playing_order_desc read";
+        proc_event = MCP_PLAYING_ORDER_DESCRIPTOR_READ;
+      } else {
+        LOG(INFO) << __func__ << " read request for unknown handle" << p_data->req_data.p_data->read_req.handle;
+        status = BT_STATUS_FAIL;
+        break;
+      }
+      rsp->event = MCP_DESCRIPTOR_READ_RSP;
+      rsp->rsp_value.attr_value.auth_req  = 0;
+      rsp->rsp_value.attr_value.handle = p_data->req_data.p_data->read_req.handle;
+      rsp->rsp_value.attr_value.offset = p_data->req_data.p_data->read_req.offset;
+      //mcp response
+      rsp->oper.ReadDescOp.desc_handle = p_data->req_data.p_data->read_req.handle;
+      rsp->oper.ReadDescOp.trans_id = p_data->req_data.trans_id;
+      rsp->oper.ReadDescOp.status = BT_STATUS_SUCCESS;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case BTA_GATTS_WRITE_CHARACTERISTIC_EVT: {
+      p_data = (tBTA_GATTS*)param;
+        const auto& req = p_data->req_data.p_data->write_req;
+      LOG(INFO) << __func__ << " write characteristics len : " << req.len << " value "<< req.value[0];
+      RemoteDevice *remoteDevice =
+          instance->remoteDevices.FindByConnId(p_data->req_data.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore write";
+        status = BT_STATUS_DEVICE_NOT_CONNECTED;
+        break;
+      }
+
+      rsp->event = MCP_WRITE_RSP;
+      rsp->oper.WriteOp.status = BT_STATUS_SUCCESS;
+      if (req.handle == mcsServerServiceInfo.playing_order_handle) {
+        proc_event = MCP_PLAYING_ORDER_WRITE;
+      } else if (req.handle == mcsServerServiceInfo.media_control_point_handle) {
+        proc_event = MCP_MEDIA_CONTROL_POINT_WRITE;
+      } else if (req.handle == mcsServerServiceInfo.track_position_handle) {
+        proc_event = MCP_TRACK_POSITION_WRITE;
+      } else  {
+        //characteristics handle not matched.
+        //<TBD>
+        rsp->oper.WriteOp.status = BT_STATUS_HANLDE_NOT_MATCHED;
+      }
+      rsp->oper.WriteOp.char_handle = req.handle;
+      rsp->oper.WriteOp.trans_id = p_data->req_data.trans_id;
+      rsp->remoteDevice = remoteDevice;
+      rsp->oper.WriteOp.need_rsp = req.need_rsp;
+      rsp->oper.WriteOp.offset = req.offset; //<TBD> need to check requirement
+      memcpy(rsp->oper.WriteOp.data, req.value, req.len);
+      LOG(INFO) << __func__ << " Local Tx ID " << rsp->oper.WriteOp.trans_id << " Gatt Tx ID: " << p_data->req_data.trans_id;
+      break;
+    }
+
+    case BTA_GATTS_WRITE_DESCRIPTOR_EVT: {
+      p_data = (tBTA_GATTS* )param;
+      uint16_t req_value = 0;
+      const auto& req = p_data->req_data.p_data->write_req;
+      RemoteDevice *remoteDevice =
+           instance->remoteDevices.FindByConnId(p_data->req_data.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore notification";
+        break;
+      }
+      req_value = *(uint16_t* )req.value;
+      //need to initialized with proper error code
+      int status = BT_STATUS_SUCCESS;
+      LOG(INFO) << __func__ << "   write descriptor :" << req.handle <<
+         "is resp: " << req.need_rsp << "is prep: " << req.is_prep << " value " << req_value;
+
+      if(req.handle ==
+          mcsServerServiceInfo.media_state_desc) {
+        LOG(INFO) << __func__ << " media_state_desc descriptor write";
+        remoteDevice->media_state_notify = req_value;
+        proc_event = MCP_MEDIA_STATE_DESCRIPTOR_WRITE;
+      } else if(req.handle ==
+        mcsServerServiceInfo.media_player_name_desc) {
+        remoteDevice->media_player_name_notify = req_value;
+        LOG(INFO) << __func__ << " media_player_name_desc descriptor write";
+        proc_event = MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_WRITE;
+      } else if(req.handle ==
+        mcsServerServiceInfo.media_control_point_desc) {
+            remoteDevice->media_control_point_notify = req_value;
+        LOG(INFO) << __func__ << " media_player_control_point_desc descriptor write";
+        proc_event = MCP_MEDIA_CONTROL_POINT_DESCRIPTOR_WRITE;
+      } else if(req.handle ==
+        mcsServerServiceInfo.media_control_point_opcode_supported_desc) {
+            remoteDevice->media_control_point_opcode_supported_notify = req_value;
+        LOG(INFO) << __func__ << " media_control_point_opcode_supported_desc descriptor write";
+        proc_event = MCP_MEDIA_CONTROL_POINT_SUPPORTED_DESCRIPTOR_WRITE;
+      } else if(req.handle ==
+          mcsServerServiceInfo.track_changed_desc) {
+        remoteDevice->track_changed_notify = req_value;
+        LOG(INFO) << __func__ << " track_changed_desc descriptor write";
+        proc_event = MCP_TRACK_CHANGED_DESCRIPTOR_WRITE;
+      } else if(req.handle ==
+          mcsServerServiceInfo.track_title_desc) {
+        remoteDevice->track_title_notify = req_value;
+        LOG(INFO) << __func__ << " track_title_desc descriptor write";
+        proc_event = MCP_TRACK_TITLE_DESCRIPTOR_WRITE;
+      } else if(req.handle ==
+        mcsServerServiceInfo.track_position_desc) {
+        remoteDevice->track_position_notify   = req_value;
+        LOG(INFO) << __func__ << " track_position_desc descriptor write";
+        proc_event = MCP_TRACK_POSITION_DESCRIPTOR_WRITE;
+      } else if(req.handle ==
+          mcsServerServiceInfo.track_duration_desc) {
+        remoteDevice->track_duration_notify   = req_value;
+        LOG(INFO) << __func__ << " track_duration_desc descriptor write";
+        proc_event = MCP_TRACK_DURATION_DESCRIPTOR_WRITE;
+      } else if(req.handle ==
+          mcsServerServiceInfo.playing_order_desc) {
+        remoteDevice->playing_order_notify = req_value;
+        LOG(INFO) << __func__ << " playing_order_desc descriptor write";
+        proc_event = MCP_PLAYING_ORDER_DESCRIPTOR_WRITE;
+      } else {
+        LOG(INFO) << __func__ << "  descriptor write not matched ";
+        status = 4; //<TBD>need to check error code
+      }
+      rsp->event = MCP_DESCRIPTOR_WRITE_RSP;
+      rsp->oper.WriteDescOp.desc_handle = req.handle;
+      rsp->oper.WriteDescOp.trans_id = p_data->req_data.trans_id;
+      rsp->oper.WriteDescOp.status = status;
+      rsp->oper.WriteDescOp.need_rsp = req.need_rsp;
+      rsp->oper.WriteDescOp.notification = req_value;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case BTA_GATTS_EXEC_WRITE_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      //<TBD> need to check requirement
+      break;
+    }
+
+    case BTA_GATTS_PHY_UPDATE_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      RemoteDevice *remoteDevice =
+        instance->remoteDevices.FindByConnId(p_data->phy_update.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " device not found ignore phy update"
+            << p_data->phy_update.status;
+        status = BT_STATUS_FAIL;
+        break;
+      }
+      proc_event = MCP_PHY_UPDATE;
+      rsp->event = MCP_PHY_UPDATE;
+      rsp->oper.PhyOp.rx_phy = p_data->phy_update.rx_phy;
+      rsp->oper.PhyOp.tx_phy = p_data->phy_update.tx_phy;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case BTA_GATTS_CONN_UPDATE_EVT: {
+      p_data = (tBTA_GATTS*)param;
+      RemoteDevice *remoteDevice =
+        instance->remoteDevices.FindByConnId(p_data->phy_update.conn_id);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " connection update device not found";
+        break;
+      }
+      LOG(INFO) << __func__ << " connection update status" << p_data->phy_update.status;
+      proc_event = MCP_CONNECTION_UPDATE;
+      rsp->event = MCP_CONNECTION_UPDATE;
+      rsp->oper.ConnectionUpdateOp.latency = p_data->conn_update.latency;
+      rsp->oper.ConnectionUpdateOp.timeout = p_data->conn_update.timeout;
+      rsp->oper.ConnectionUpdateOp.interval = p_data->conn_update.interval;
+      rsp->oper.ConnectionUpdateOp.status = p_data->conn_update.status;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case MCP_ACTIVE_DEVICE_UPDATE:
+    {
+      tMCP_SET_ACTIVE_DEVICE *data = (tMCP_SET_ACTIVE_DEVICE *)param;
+      LOG(INFO) << __func__ << " address " << data->address;
+      RemoteDevice *remoteDevice = instance->remoteDevices.FindByAddress(data->address);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " active device update device address not found";
+        break;
+      }
+      instance->remoteDevices.AddSetActiveDevice(data);
+      rsp->remoteDevice = remoteDevice;
+      rsp->oper.SetActiveDeviceOp.profile = data->profile;
+      proc_event = MCP_ACTIVE_DEVICE_UPDATE;
+      rsp->event = MCP_ACTIVE_DEVICE_UPDATE;
+
+
+      break;
+    }
+
+    case MCP_MEDIA_STATE_UPDATE:
+    {
+      tMCP_MEDIA_STATE *data = (tMCP_MEDIA_STATE *) param;
+      if (mediaPlayerInfo.media_state != data->state)
+        mediaPlayerInfo.media_state = data->state;
+      LOG(INFO) << __func__ << " state: " << unsigned(mediaPlayerInfo.media_state);
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle = mcsServerServiceInfo.media_state_handle;
+      rsp->oper.MediaUpdateOp.data = &mediaPlayerInfo.media_state;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.media_state);
+      break;
+    }
+
+    case MCP_MEDIA_PLAYER_NAME_UPDATE:
+    {
+      tMCP_MEDIA_PLAYER_NAME *data = (tMCP_MEDIA_PLAYER_NAME *) param;
+      if (memcmp(mediaPlayerInfo.player_name, data->name, data->len)) {
+        memcpy(mediaPlayerInfo.player_name, data, data->len);
+        mediaPlayerInfo.player_name_len = data->len;
+      }
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle = mcsServerServiceInfo.media_player_name_handle;
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)mediaPlayerInfo.player_name;
+      rsp->oper.MediaUpdateOp.len = mediaPlayerInfo.player_name_len;
+      break;
+    }
+
+    case MCP_MEDIA_SUPPORTED_OPCODE_UPDATE:
+    {
+      tMCP_MEDIA_OPCODE_SUPPORT *data = (tMCP_MEDIA_OPCODE_SUPPORT *)param;
+      mediaPlayerInfo.media_supported_feature = data->supported;
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle = mcsServerServiceInfo.media_control_point_opcode_supported_handle;
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)&mediaPlayerInfo.media_supported_feature;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.media_supported_feature);
+      break;
+    }
+
+    case MCP_MEDIA_CONTROL_POINT_UPDATE:
+    {
+      tMCP_MEDIA_CONTROL_POINT *data = (tMCP_MEDIA_CONTROL_POINT *)param;
+      mediaPlayerInfo.media_ctrl_point = data->req_opcode | (data->result << 8);
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle = mcsServerServiceInfo.media_control_point_handle;
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)&mediaPlayerInfo.media_ctrl_point;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.media_ctrl_point);
+      break;
+    }
+    case MCP_PLAYING_ORDER_SUPPORTED_UPDATE:
+    {
+      tMCP_PLAYING_ORDER_SUPPORT *data = (tMCP_PLAYING_ORDER_SUPPORT *) param;
+      mediaPlayerInfo.playing_order_supported = data->order_supported;
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle =
+          mcsServerServiceInfo.media_control_point_opcode_supported_handle;
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)&mediaPlayerInfo.playing_order_supported;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.playing_order_supported);
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_UPDATE:
+    {
+      tMCP_PLAYING_ORDER *data = (tMCP_PLAYING_ORDER *) param;
+      mediaPlayerInfo.playing_order_value = data->order;
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle = mcsServerServiceInfo.playing_order_handle;
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)&mediaPlayerInfo.playing_order_value;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.playing_order_value);
+      break;
+    }
+
+    case MCP_TRACK_CHANGED_UPDATE:
+    {
+      tMCP_TRACK_CHANGED *data = (tMCP_TRACK_CHANGED *) param;
+      mediaPlayerInfo.track_changed = data->status;
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle = mcsServerServiceInfo.track_changed_handle;
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)&mediaPlayerInfo.track_changed;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.track_changed);
+      break;
+    }
+
+    case MCP_TRACK_POSITION_UPDATE:
+    {
+      tMCP_TRACK_POSTION *data = (tMCP_TRACK_POSTION*) param;
+      if(data->position != mediaPlayerInfo.position) {
+        mediaPlayerInfo.position = data->position;
+      }
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle = mcsServerServiceInfo.track_position_handle;
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)&mediaPlayerInfo.position;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.position);
+      break;
+    }
+
+    case MCP_TRACK_DURATION_UPDATE:
+    {
+      tMCP_TRACK_DURATION* data = (tMCP_TRACK_DURATION *) param;
+      mediaPlayerInfo.duration = data->duration;
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->rsp_value.handle = mcsServerServiceInfo.track_duration_handle;
+      rsp->handle = mcsServerServiceInfo.track_duration_handle;
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)&mediaPlayerInfo.duration;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.duration);
+      break;
+    }
+
+    case MCP_TRACK_TITLE_UPDATE:
+    {
+      tMCP_TRACK_TITLE *data = (tMCP_TRACK_TITLE*) param;
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle = mcsServerServiceInfo.track_title_handle;
+      if (memcmp(mediaPlayerInfo.title, data->title, data->len)) {
+        memcpy(mediaPlayerInfo.title, data->title, data->len);
+        mediaPlayerInfo.track_title_len = data->len;
+      }
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)mediaPlayerInfo.title;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.track_title_len);
+      break;
+    }
+    case MCP_CCID_UPDATE:
+    {
+      tMCP_CONTENT_CONTROL_ID *data = (tMCP_CONTENT_CONTROL_ID *) param;
+      mediaPlayerInfo.ccid = (uint8_t)data->ccid;
+      proc_event = MCP_NOTIFY_ALL;
+      rsp->event = MCP_NOTIFY_ALL;
+      rsp->handle = mcsServerServiceInfo.ccid_handle;
+      rsp->oper.MediaUpdateOp.data = (uint8_t *)&mediaPlayerInfo.ccid;
+      rsp->oper.MediaUpdateOp.len = sizeof(mediaPlayerInfo.ccid);
+      break;
+    }
+
+    case MCP_CONNECTION_CLOSE_EVENT:
+    {
+      tMCP_CONNECTION_CLOSE* p_data = (tMCP_CONNECTION_CLOSE *)param;
+      RemoteDevice* remoteDevice = instance->remoteDevices.FindByAddress(p_data->addr);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " address is not in list";
+        break;
+      }
+      proc_event = MCP_CONNECTION_CLOSE_EVENT;
+      rsp->remoteDevice = remoteDevice;
+      break;
+    }
+
+    case MCP_BOND_STATE_CHANGE_EVENT:
+    {
+      tMCP_BOND_STATE_CHANGE* p_data = (tMCP_BOND_STATE_CHANGE *)param;
+      RemoteDevice* remoteDevice = instance->remoteDevices.FindByAddress(p_data->addr);
+      if(remoteDevice == NULL) {
+        LOG(INFO) << __func__ << " address is not in list";
+        break;
+      }
+      instance->remoteDevices.Remove(p_data->addr);
+      break;
+    }
+    default:
+      LOG(INFO) << __func__ << " event not matched !!";
+      break;
+  }
+
+  if(rsp->event != MCP_NONE_EVENT) {
+    LOG(INFO) << __func__ << " event to media handler " << get_mcp_event_name(rsp->event);
+    mediaPlayerInfo.MediaStateHandlerPointer[mediaPlayerInfo.media_state](proc_event, rsp, mediaPlayerInfo.media_state);
+  }
+  if(rsp) {
+    LOG(INFO) << __func__ << "free rsp data";
+    osi_free(rsp);
+  }
+}
+
+
+bool MediaStateInactiveHandler(uint32_t event, void* param, uint8_t state) {
+  LOG(INFO) << __func__ << " handle event " << get_mcp_event_name(event)
+            << " in state " << get_mcp_media_state_name(state);
+
+  mcp_resp_t *p_data = (mcp_resp_t *)param;
+
+  RemoteDevice *device = p_data->remoteDevice;
+  LOG(INFO) << __func__ << " inactive mcs handle event "<< get_mcp_event_name(p_data->event);
+  switch(event) {
+    case MCP_NOTIFY_ALL: {
+      LOG(INFO) << __func__ << " Notify all handle "<< p_data->handle;
+      std::vector<RemoteDevice>notifyDevices = instance->remoteDevices.FindNotifyDevices(p_data->handle);
+      std::vector<RemoteDevice>::iterator it;
+      if (notifyDevices.size() <= 0) {
+        LOG(INFO) << __func__ << " No device register for notification";
+        break;
+      }
+      for (it = notifyDevices.begin(); it != notifyDevices.end(); it++){
+      LOG(INFO) << __func__ << " Notify all handle device id " << it->conn_id;
+      p_data->remoteDevice = instance->remoteDevices.FindByConnId(it->conn_id);
+        it->DeviceStateHandlerPointer[it->state](p_data->event, p_data, it->state);
+      }
+      break;
+    }
+
+    case MCP_TRACK_POSITION_WRITE:
+    case MCP_PLAYING_ORDER_WRITE: {
+      LOG(INFO) << __func__ << "Ignore other request as player is not active";
+      //need to ignore write rsp because no player is active
+      p_data->rsp_value.attr_value.len = 0;
+      uint8_t data = (uint8_t)(*p_data->oper.WriteOp.data);
+      std::vector<uint8_t> value;
+      value.push_back(data);
+      value.push_back(0x03); // To-Do check ???
+      LOG(INFO) << __func__ << "hndl " << p_data->oper.WriteOp.char_handle;
+      GattsOpsQueue::SendNotification(device->conn_id, p_data->oper.WriteOp.char_handle, value, false);
+      break;
+    }
+
+    case MCP_MEDIA_CONTROL_POINT_WRITE: {
+      LOG(INFO) << __func__ << "Ignore other request as player is not active ctrl pt write";
+      //need to ignore write rsp because no player is active
+      p_data->rsp_value.attr_value.len = 0;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      uint8_t data = (uint8_t)(*p_data->oper.WriteOp.data);
+      uint8_t status = MCP_MEDIA_PLAYER_INACTIVE;
+      std::vector<uint8_t> value;
+      bool opcode_support = is_opcode_supported(data);
+      if (!opcode_support) {
+        status = MCP_OPCODE_NOT_SUPPORTED;
+        LOG(INFO) << __func__ << "Sending OPCode Unsupported indication";
+      } else {
+        LOG(INFO) << __func__ << "Sending INACTIVE indication";
+      }
+      value.push_back(data);
+      value.push_back(status);
+      LOG(INFO) << __func__ << "handle " << p_data->oper.WriteOp.char_handle;
+      GattsOpsQueue::SendNotification(device->conn_id, p_data->oper.WriteOp.char_handle, value, false);
+      break;
+    }
+    case MCP_MEDIA_STATE_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.media_state);
+      *(uint8_t *)p_data->rsp_value.attr_value.value =  (uint8_t)mediaPlayerInfo.media_state;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.media_state);
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_READ: {
+      uint16_t len = mediaPlayerInfo.player_name_len;
+      if (device->mtu != -1 && len >= device->mtu - 3) //to check player len should not greater than mtu
+        len = device->mtu - 3;
+      p_data->rsp_value.attr_value.len = len;
+      memcpy(p_data->rsp_value.attr_value.value, mediaPlayerInfo.player_name,
+          p_data->rsp_value.attr_value.len);
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.media_supported_feature);
+      *(uint32_t*)p_data->rsp_value.attr_value.value =
+          MCP_DEFAULT_MEDIA_CTRL_SUPP_FEAT;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_READ: {
+      uint16_t len = mediaPlayerInfo.track_title_len;
+      if (device->mtu != -1 && len >= device->mtu - 3) //to check title len should not greater than mtu
+        len = device->mtu - 3;
+      p_data->rsp_value.attr_value.len = len;
+      memcpy(p_data->rsp_value.attr_value.value, mediaPlayerInfo.title,
+          p_data->rsp_value.attr_value.len);
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.position);
+      *(int *)p_data->rsp_value.attr_value.value = (int)mediaPlayerInfo.position;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_DURATION_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.duration);
+      *(int *)p_data->rsp_value.attr_value.value = (int)mediaPlayerInfo.duration;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_PLAYING_ORDER_SUPPORTED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.playing_order_supported);
+      *(uint16_t *)p_data->rsp_value.attr_value.value =
+          (uint16_t)mediaPlayerInfo.playing_order_supported;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_PLAYING_ORDER_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.playing_order_value);
+      *(uint16_t *)p_data->rsp_value.attr_value.value = (uint16_t)mediaPlayerInfo.playing_order_value;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_CCID_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.ccid);
+      *(uint32_t *)p_data->rsp_value.attr_value.value = (uint8_t)mediaPlayerInfo.ccid;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_SEEKING_SPEED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.seeking_speed);
+      *(uint8_t *)p_data->rsp_value.attr_value.value = (uint8_t)mediaPlayerInfo.seeking_speed;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_STATE_READ_DESCRIPTOR:{
+      *(uint16_t *)p_data->rsp_value.attr_value.value = (uint16_t)device->media_state_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_state_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_READ_DESCRIPTOR: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = (uint16_t)device->media_control_point_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_control_point_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value =
+         (uint16_t)device->media_control_point_opcode_supported_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_control_point_opcode_supported_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = (uint16_t)device->media_player_name_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_player_name_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_CHANGED_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = (uint16_t)device->track_changed_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_changed_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = (uint16_t)device->track_title_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_title_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = (uint16_t)device->track_position_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_position_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_DURATION_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = (uint16_t)device->track_duration_notify;;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_duration_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_PLAYING_ORDER_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = (uint16_t)device->playing_order_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->playing_order_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_MEDIA_STATE_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_state;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_state);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_state_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.player_name;
+      p_data->oper.WriteDescOp.len = mediaPlayerInfo.player_name_len;
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_player_name_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_ctrl_point;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_ctrl_point);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_control_point_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_SUPPORTED_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_supported_feature;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_supported_feature);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_control_point_opcode_supported_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_CHANGED_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.track_changed;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.track_changed);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_changed_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.title;
+      p_data->oper.WriteDescOp.len = mediaPlayerInfo.track_title_len;
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_title_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_POSITION_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.position;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.position);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_position_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_DURATION_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.duration;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.duration);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_duration_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_DESCRIPTOR_WRITE : {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.playing_order_value;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.playing_order_value);
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_CONGESTION_UPDATE: {
+      McpCongestionUpdate(p_data);
+      break;
+    }
+
+    case MCP_CONNECTION_UPDATE:
+    case MCP_ACTIVE_DEVICE_UPDATE:
+    case MCP_PHY_UPDATE:
+    case MCP_CONNECTION:
+    case MCP_CONNECTION_CLOSE_EVENT:
+    case MCP_DISCONNECTION:
+    case MCP_BOND_STATE_CHANGE_EVENT:
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+
+    default:
+      LOG(INFO) << __func__ << "  event is not in list";
+      break;
+    }
+
+  return BT_STATUS_SUCCESS;
+}
+
+
+
+bool MediaStatePlayingHandler(uint32_t event, void* param, uint8_t state) {
+  LOG(INFO) << __func__ << " handle event " << get_mcp_event_name(event)
+            << " in state " << get_mcp_media_state_name(state);
+  mcp_resp_t *p_data = (mcp_resp_t *)param;
+  RemoteDevice *device = p_data->remoteDevice;
+  switch(event) {
+    case MCP_NOTIFY_ALL: {
+      LOG(INFO) << __func__ << " Notify all handle "<< p_data->handle;
+      std::vector<RemoteDevice>notifyDevices = instance->remoteDevices.FindNotifyDevices(p_data->handle);
+      std::vector<RemoteDevice>::iterator it;
+      if (notifyDevices.size() <= 0) {
+        LOG(INFO) << __func__ << " No device register for notification";
+      }
+      for (it = notifyDevices.begin(); it != notifyDevices.end(); it++){
+        LOG(INFO) << __func__ << " Notify all handle device id " << it->conn_id;
+        p_data->remoteDevice = instance->remoteDevices.FindByConnId(it->conn_id);
+        it->DeviceStateHandlerPointer[it->state](p_data->event, p_data, it->state);
+      }
+      break;
+    }
+    case MCP_PLAYING_ORDER_WRITE: {
+      uint8_t data = (uint8_t)(*p_data->oper.WriteOp.data);
+      if (mediaPlayerInfo.playing_order_supported & data) {
+        instance->PlayingOrderChangeReq(data & mediaPlayerInfo.playing_order_value);
+      } else {
+        LOG(INFO) << __func__ << " ignore playing_order_handle write feature is not supported";
+        break;
+      }
+      p_data->rsp_value.attr_value.len = 0;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_WRITE: {
+      uint8_t data = (uint8_t)(*p_data->oper.WriteOp.data);
+      uint8_t opcode_support = is_opcode_supported(data);
+      LOG(INFO) << __func__ << " data " << data << " Tx ID: " << p_data->oper.WriteOp.trans_id;
+      if (!opcode_support) {
+        uint8_t status = MCP_OPCODE_NOT_SUPPORTED;
+        std::vector<uint8_t> value;
+        value.push_back(data);
+        value.push_back(status);
+        LOG(INFO) << __func__ << "hndl " << p_data->oper.WriteOp.char_handle;
+        LOG(INFO) << __func__ << "opcode not supported " << data;
+        GattsOpsQueue::SendNotification(device->conn_id, p_data->oper.WriteOp.char_handle, value, false);
+      }
+      if (data != MCP_MEDIA_CONTROL_OPCODE_PLAY) {
+        instance->MediaControlPointChangeReq((uint32_t)data, device->peer_bda);
+        LOG(INFO) << __func__ << "media_control_point_handle write ";
+      } else {
+        LOG(INFO) << __func__ << " ignore media_control_point_handle write feature is not supported/already playing";
+      }
+      p_data->rsp_value.attr_value.len = 0;
+      device = p_data->remoteDevice;
+      LOG(INFO) << __func__ << " p_data->event "<< p_data->event;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_WRITE: {
+      uint32_t track_duration = mediaPlayerInfo.duration;
+      uint32_t track_position = mediaPlayerInfo.position;
+      uint32_t data;
+      uint8_t *w_data = p_data->oper.WriteOp.data;
+      STREAM_TO_UINT32(data, w_data);
+      uint32_t position = track_duration;
+      if ((track_position == (uint32_t)data) || (data < 0) ||
+          (track_duration == 0xFFFF) || (track_duration > 0 && track_duration < data)) {
+        LOG(INFO) << __func__ << " ignore track_position_handle write";
+      } else {
+        position = data;
+        instance->TrackPositionChangeReq(data);
+        LOG(INFO) << __func__ << " track_position_handle write";
+      }
+      std::vector<uint8_t> value;
+      value.push_back(position & 0xff);
+      value.push_back((position >> 8)  & 0xff);
+      value.push_back((position >> 16) & 0xff);
+      value.push_back((position >> 24) & 0xff);
+      GattsOpsQueue::SendNotification(device->conn_id, p_data->oper.WriteOp.char_handle, value, false);
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_STATE_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.media_state);
+      *(uint8_t *)p_data->rsp_value.attr_value.value =  *(uint8_t *)&mediaPlayerInfo.media_state;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_READ: {
+      uint16_t len = mediaPlayerInfo.player_name_len;
+      if (device->mtu != -1 && len >= device->mtu - 3) //to check player len should not greater than mtu
+        len = device->mtu - 3;
+      p_data->rsp_value.attr_value.len = len;
+      memcpy(p_data->rsp_value.attr_value.value, mediaPlayerInfo.player_name,
+          p_data->rsp_value.attr_value.len);
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.media_supported_feature);
+      *(uint32_t*)p_data->rsp_value.attr_value.value =
+      *(uint32_t *)&mediaPlayerInfo.media_supported_feature;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_READ: {
+      uint16_t len = mediaPlayerInfo.track_title_len;
+      if (device->mtu != -1 && len >= device->mtu - 3) //to check title len should not greater than mtu
+        len = device->mtu - 3;
+      p_data->rsp_value.attr_value.len = len;
+      memcpy(p_data->rsp_value.attr_value.value, mediaPlayerInfo.title,
+          p_data->rsp_value.attr_value.len);
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.position);
+      *(int *)p_data->rsp_value.attr_value.value = *(int *)&mediaPlayerInfo.position;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_DURATION_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.duration);
+      *(int *)p_data->rsp_value.attr_value.value = *(int *)&mediaPlayerInfo.duration;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_SUPPORTED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.playing_order_supported);
+      *(uint16_t *)p_data->rsp_value.attr_value.value =
+          *(uint16_t *)&mediaPlayerInfo.playing_order_supported;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.playing_order_value);
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&mediaPlayerInfo.playing_order_value;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_CCID_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.ccid);
+      *(uint32_t *)p_data->rsp_value.attr_value.value = *(uint8_t *)&mediaPlayerInfo.ccid;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_SEEKING_SPEED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.seeking_speed);
+      *(uint8_t *)p_data->rsp_value.attr_value.value = (uint8_t)mediaPlayerInfo.seeking_speed;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_MEDIA_STATE_READ_DESCRIPTOR:{
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->media_state_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_state_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_MEDIA_CONTROL_POINT_READ_DESCRIPTOR: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->media_control_point_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_control_point_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value =
+         *(uint16_t *)&device->media_control_point_opcode_supported_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_control_point_opcode_supported_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->media_player_name_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_player_name_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_CHANGED_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_changed_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_changed_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_TITLE_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_title_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_title_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_POSITION_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_position_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_position_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_DURATION_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_duration_notify;;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_duration_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->playing_order_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->playing_order_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      LOG(INFO) << __func__ << " calling device state" << device->state;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_MEDIA_STATE_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_state;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_state);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_state_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.player_name;
+      p_data->oper.WriteDescOp.len = mediaPlayerInfo.player_name_len;
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_player_name_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_ctrl_point;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_ctrl_point);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_control_point_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_SUPPORTED_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_supported_feature;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_supported_feature);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_control_point_opcode_supported_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_CHANGED_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.track_changed;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.track_changed);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_changed_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.title;
+      p_data->oper.WriteDescOp.len = mediaPlayerInfo.track_title_len;
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_title_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_POSITION_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.position;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.position);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_position_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_DURATION_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.duration;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.duration);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_duration_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_DESCRIPTOR_WRITE : {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.playing_order_value;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.playing_order_value);
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_CONGESTION_UPDATE: {
+      McpCongestionUpdate(p_data);
+      break;
+    }
+
+    case MCP_CONNECTION_UPDATE:
+    case MCP_ACTIVE_DEVICE_UPDATE:
+    case MCP_PHY_UPDATE:
+    case MCP_CONNECTION:
+    case MCP_DISCONNECTION:
+    case MCP_CONNECTION_CLOSE_EVENT:
+    case MCP_BOND_STATE_CHANGE_EVENT:
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+
+    default:
+      break;
+  }
+
+  return BT_STATUS_SUCCESS;
+}
+
+bool MediaStateSeekingHandler(uint32_t event, void* param, uint8_t state) {
+  LOG(INFO) << __func__ << " handle event " << get_mcp_event_name(event)
+            << " in state " << get_mcp_media_state_name(state);
+  mcp_resp_t *p_data = (mcp_resp_t *)param;
+  RemoteDevice *device = p_data->remoteDevice;
+  switch(event) {
+    case MCP_NOTIFY_ALL: {
+      LOG(INFO) << __func__ << " Notify all handle "<< p_data->handle;
+      std::vector<RemoteDevice>notifyDevices =
+          instance->remoteDevices.FindNotifyDevices(p_data->handle);
+      if (notifyDevices.size() <= 0) {
+        LOG(INFO) << __func__ << " No device register for notification";
+        break;
+      }
+      std::vector<RemoteDevice>::iterator it;
+      for (it = notifyDevices.begin(); it != notifyDevices.end(); it++){
+        p_data->remoteDevice = instance->remoteDevices.FindByConnId(it->conn_id);
+        it->DeviceStateHandlerPointer[it->state](p_data->event, p_data, it->state);
+        LOG(INFO) << __func__ << " Notify all handle device id " << it->conn_id;
+      }
+
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_WRITE: {
+      uint8_t data = (uint8_t)(*p_data->oper.WriteOp.data);
+      if (mediaPlayerInfo.playing_order_supported & data) {
+        instance->PlayingOrderChangeReq(data & mediaPlayerInfo.playing_order_value);
+      } else {
+        LOG(INFO) << __func__ << " ignore playing_order_handle write feature is not supported";
+        break;
+      }
+      p_data->rsp_value.attr_value.len = 0;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_WRITE: {
+      uint8_t data = (uint8_t)(*p_data->oper.WriteOp.data);
+      uint8_t opcode_support = is_opcode_supported(data);
+      if (!opcode_support) {
+        uint8_t status = MCP_OPCODE_NOT_SUPPORTED;
+        std::vector<uint8_t> value;
+        value.push_back(data);
+        value.push_back(status);
+        LOG(INFO) << __func__ << "hndl " << p_data->oper.WriteOp.char_handle;
+        LOG(INFO) << __func__ << "opcode not supported " << data;
+        GattsOpsQueue::SendNotification(device->conn_id, p_data->oper.WriteOp.char_handle, value, false);
+      }
+
+      if (((data == MCP_MEDIA_CONTROL_OPCODE_PAUSE) ||
+          (data == MCP_MEDIA_CONTROL_OPCODE_PLAY)) &&
+          (instance->remoteDevices.FindActiveDevice(p_data->remoteDevice)) == false) {
+        LOG(INFO) << __func__ << " media control point write received from inactive device";
+        break;
+      }
+      if ((data != MCP_MEDIA_CONTROL_OPCODE_FAST_FORWARD) && (data != MCP_MEDIA_CONTROL_OPCODE_FAST_REWIND)
+          && (data != MCP_MEDIA_CONTROL_OPCODE_MOVE_RELATIVE)) {
+        instance->MediaControlPointChangeReq(data, device->peer_bda);
+        LOG(INFO) << __func__ << "media_control_point_handle write ";
+      }
+      p_data->rsp_value.attr_value.len = 0;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_WRITE: {
+      uint32_t track_duration = mediaPlayerInfo.duration;
+      uint32_t track_position = mediaPlayerInfo.position;
+      uint32_t data;
+      uint8_t *w_data = p_data->oper.WriteOp.data;
+      uint32_t position = track_duration;
+      STREAM_TO_UINT32(data, w_data);
+      if ((track_position == (uint32_t)data) || (data < 0) ||
+          (track_duration == 0xFFFF) || (track_duration > 0 && track_duration < data)) {
+        LOG(INFO) << __func__ << " ignore track_position_handle write";
+      } else {
+        position = data;
+        instance->TrackPositionChangeReq(data);
+        LOG(INFO) << __func__ << " track_position_handle write";
+      }
+      std::vector<uint8_t> value;
+      value.push_back(position & 0xff);
+      value.push_back((position >> 8)  & 0xff);
+      value.push_back((position >> 16) & 0xff);
+      value.push_back((position >> 24) & 0xff);
+      GattsOpsQueue::SendNotification(device->conn_id, p_data->oper.WriteOp.char_handle, value, false);
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_STATE_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.media_state);
+      *(uint8_t *)p_data->rsp_value.attr_value.value =  *(uint8_t *)&mediaPlayerInfo.media_state;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_READ: {
+      uint16_t len = mediaPlayerInfo.player_name_len;
+      if (device->mtu != -1 && len >= device->mtu - 3) //to check player len should not greater than mtu
+        len = device->mtu - 3;
+      p_data->rsp_value.attr_value.len = len;
+      memcpy(p_data->rsp_value.attr_value.value, mediaPlayerInfo.player_name,
+          p_data->rsp_value.attr_value.len);
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.media_supported_feature);
+      *(uint32_t*)p_data->rsp_value.attr_value.value =
+      *(uint32_t *)&mediaPlayerInfo.media_supported_feature;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_READ: {
+      uint16_t len = mediaPlayerInfo.track_title_len;
+      if (device->mtu != -1 && len >= device->mtu - 3) //to check title len should not greater than mtu
+        len = device->mtu - 3;
+      p_data->rsp_value.attr_value.len = len;
+      memcpy(p_data->rsp_value.attr_value.value, mediaPlayerInfo.title,
+          p_data->rsp_value.attr_value.len);
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.position);
+      *(int *)p_data->rsp_value.attr_value.value = *(int *)&mediaPlayerInfo.position;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_DURATION_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.duration);
+      *(int *)p_data->rsp_value.attr_value.value = *(int *)&mediaPlayerInfo.duration;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_PLAYING_ORDER_SUPPORTED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.playing_order_supported);
+      *(uint16_t *)p_data->rsp_value.attr_value.value =
+          *(uint16_t *)&mediaPlayerInfo.playing_order_supported;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_PLAYING_ORDER_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.playing_order_value);
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&mediaPlayerInfo.playing_order_value;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_CCID_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.ccid);
+      *(uint32_t *)p_data->rsp_value.attr_value.value = *(uint8_t *)&mediaPlayerInfo.ccid;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_SEEKING_SPEED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.seeking_speed);
+      *(uint8_t *)p_data->rsp_value.attr_value.value = (uint8_t)mediaPlayerInfo.seeking_speed;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_STATE_READ_DESCRIPTOR:{
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->media_state_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_state_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_READ_DESCRIPTOR: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->media_control_point_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_control_point_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value =
+         *(uint16_t *)&device->media_control_point_opcode_supported_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_control_point_opcode_supported_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->media_player_name_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_player_name_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_CHANGED_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_changed_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_changed_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_title_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_title_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_position_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_position_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_DURATION_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_duration_notify;;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_duration_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_PLAYING_ORDER_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->playing_order_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->playing_order_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_MEDIA_STATE_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_state;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_state);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_state_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.player_name;
+      p_data->oper.WriteDescOp.len = mediaPlayerInfo.player_name_len;
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_player_name_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_ctrl_point;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_ctrl_point);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_control_point_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_SUPPORTED_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_supported_feature;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_supported_feature);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_control_point_opcode_supported_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_CHANGED_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.track_changed;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.track_changed);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_changed_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.title;
+      p_data->oper.WriteDescOp.len = mediaPlayerInfo.track_title_len;
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_title_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_POSITION_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.position;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.position);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_position_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_DURATION_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.duration;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.duration);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_duration_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_DESCRIPTOR_WRITE : {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.playing_order_value;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.playing_order_value);
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_CONGESTION_UPDATE: {
+      LOG(INFO) << __func__ << ": device MCP_CONGESTION_UPDATE update: " << event;
+      McpCongestionUpdate(p_data);
+      break;
+    }
+
+    case MCP_CONNECTION_UPDATE:
+    case MCP_ACTIVE_DEVICE_UPDATE:
+    case MCP_PHY_UPDATE:
+    case MCP_CONNECTION:
+    case MCP_DISCONNECTION:
+    case MCP_CONNECTION_CLOSE_EVENT:
+    case MCP_BOND_STATE_CHANGE_EVENT:
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+
+    default:
+      break;
+  }
+  return BT_STATUS_SUCCESS;
+}
+
+
+bool MediaStatePauseHandler(uint32_t event, void* param, uint8_t state) {
+  LOG(INFO) << __func__ << " handle event " << get_mcp_event_name(event)
+            << " in state " << get_mcp_media_state_name(state);
+
+  mcp_resp_t *p_data = (mcp_resp_t *)param;
+  RemoteDevice *device = p_data->remoteDevice;
+  switch(event) {
+    case MCP_NOTIFY_ALL: {
+      LOG(INFO) << __func__ << " Notify all handle "<< p_data->handle;
+      std::vector<RemoteDevice>notifyDevices = instance->remoteDevices.FindNotifyDevices(p_data->handle);
+      std::vector<RemoteDevice>::iterator it;
+      if (notifyDevices.size() <= 0) {
+        LOG(INFO) << __func__ << " No device register for notification";
+      }
+      for (it = notifyDevices.begin(); it != notifyDevices.end(); it++){
+        p_data->remoteDevice = instance->remoteDevices.FindByConnId(it->conn_id);
+        it->DeviceStateHandlerPointer[it->state](p_data->event, p_data, it->state);
+        LOG(INFO) << __func__ << " Notify all handle device id " << it->conn_id;
+
+      }
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_WRITE: {
+      uint8_t data = (uint8_t)(*p_data->oper.WriteOp.data);
+      if (mediaPlayerInfo.playing_order_supported & data) {
+        instance->PlayingOrderChangeReq(data & mediaPlayerInfo.playing_order_value);
+      } else {
+        LOG(INFO) << __func__ << " ignore playing_order_handle write feature is not supported";
+        break;
+      }
+      p_data->rsp_value.attr_value.len = 0;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_WRITE: {
+      uint8_t data = (uint8_t)(*p_data->oper.WriteOp.data);
+      uint8_t opcode_support = is_opcode_supported(data);
+      if (!opcode_support) {
+        uint8_t status = MCP_OPCODE_NOT_SUPPORTED;
+        std::vector<uint8_t> value;
+        value.push_back(data);
+        value.push_back(status);
+        LOG(INFO) << __func__ << "hndl " << p_data->oper.WriteOp.char_handle;
+        LOG(INFO) << __func__ << "opcode not supported " << data;
+        GattsOpsQueue::SendNotification(device->conn_id, p_data->oper.WriteOp.char_handle, value, false);
+      }
+
+      if ((data != MCP_MEDIA_CONTROL_OPCODE_PLAY) &&
+         (instance->remoteDevices.FindActiveDevice(p_data->remoteDevice) == false)
+         && is_pts_running() == false) {
+        LOG(INFO) << __func__ << " media control point write received from inactive device";
+        break;
+      }
+      if (data != MCP_MEDIA_CONTROL_OPCODE_PAUSE) {
+        //<TBD> MCP_MEDIA_CONTROL_STOP need to check for stop
+        instance->MediaControlPointChangeReq(data, device->peer_bda);
+        LOG(INFO) << __func__ << "media_control_point_handle write ";
+      } else {
+        LOG(INFO) << __func__ << " ignore media_control_point_handle write feature is not supported / already paused";
+      }
+      p_data->rsp_value.attr_value.len = 0;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_WRITE: {
+      uint32_t track_duration = mediaPlayerInfo.duration;
+      uint32_t track_position = mediaPlayerInfo.position;
+      uint32_t data;
+      uint8_t *w_data = p_data->oper.WriteOp.data;
+      STREAM_TO_UINT32(data, w_data);
+      uint32_t position = track_duration;
+      if ((track_position == (uint32_t)data) || (data < 0) ||
+          (track_duration == 0xFFFF) || (track_duration > 0 && track_duration < data)) {
+        LOG(INFO) << __func__ << " ignore track_position_handle write";
+      } else {
+        position = data;
+        instance->TrackPositionChangeReq(data);
+        LOG(INFO) << __func__ << " track_position_handle write";
+      }
+      std::vector<uint8_t> value;
+      value.push_back(position & 0xff);
+      value.push_back((position >> 8)  & 0xff);
+      value.push_back((position >> 16) & 0xff);
+      value.push_back((position >> 24) & 0xff);
+      GattsOpsQueue::SendNotification(device->conn_id, p_data->oper.WriteOp.char_handle, value, false);
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_STATE_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.media_state);
+      *(uint8_t *)p_data->rsp_value.attr_value.value =  *(uint8_t *)&mediaPlayerInfo.media_state;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_READ: {
+      uint16_t len = mediaPlayerInfo.player_name_len;
+      LOG(INFO) << __func__ << " before player name len: " << len;
+      LOG(INFO) << __func__ << " before player name mtu: " << device->mtu;
+      if (device->mtu != -1 && len >= device->mtu - 3) //to check player len should not greater than mtu
+        len = device->mtu - 3;
+      p_data->rsp_value.attr_value.len = len;
+      LOG(INFO) << __func__ << " player name len: " << len;
+      memcpy(p_data->rsp_value.attr_value.value, mediaPlayerInfo.player_name,
+          p_data->rsp_value.attr_value.len);
+      p_data->event = MCP_READ_RSP;
+      LOG(INFO) << __func__ << " calling player name read";
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.media_supported_feature);
+      *(uint32_t*)p_data->rsp_value.attr_value.value =
+      *(uint32_t *)&mediaPlayerInfo.media_supported_feature;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_READ: {
+      uint16_t len = mediaPlayerInfo.track_title_len;
+      if (device->mtu != -1 && len >= device->mtu - 3) //to check title len should not greater than mtu
+        len = device->mtu - 3;
+      p_data->rsp_value.attr_value.len = len;
+      memcpy(p_data->rsp_value.attr_value.value, mediaPlayerInfo.title,
+          p_data->rsp_value.attr_value.len);
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.position);
+      *(int *)p_data->rsp_value.attr_value.value = *(int *)&mediaPlayerInfo.position;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_DURATION_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.duration);
+      *(int *)p_data->rsp_value.attr_value.value = *(int *)&mediaPlayerInfo.duration;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_PLAYING_ORDER_SUPPORTED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.playing_order_supported);
+      *(uint16_t *)p_data->rsp_value.attr_value.value =
+          *(uint16_t*)&mediaPlayerInfo.playing_order_supported;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_PLAYING_ORDER_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.playing_order_value);
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&mediaPlayerInfo.playing_order_value;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_CCID_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.ccid);
+      *(uint32_t *)p_data->rsp_value.attr_value.value = *(uint8_t *)&mediaPlayerInfo.ccid;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_SEEKING_SPEED_READ: {
+      p_data->rsp_value.attr_value.len = sizeof(mediaPlayerInfo.seeking_speed);
+      *(uint8_t *)p_data->rsp_value.attr_value.value = (uint8_t)mediaPlayerInfo.seeking_speed;
+      p_data->event = MCP_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_STATE_READ_DESCRIPTOR:{
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->media_state_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_state_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_READ_DESCRIPTOR: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->media_control_point_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_control_point_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_OPCODE_SUPPORTED_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value =
+         *(uint16_t *)&device->media_control_point_opcode_supported_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_control_point_opcode_supported_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->media_player_name_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->media_player_name_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_CHANGED_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_changed_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_changed_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_title_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_title_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_POSITION_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_position_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_position_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_DURATION_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->track_duration_notify;;
+      p_data->rsp_value.attr_value.len = sizeof(device->track_duration_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_PLAYING_ORDER_DESCRIPTOR_READ: {
+      *(uint16_t *)p_data->rsp_value.attr_value.value = *(uint16_t *)&device->playing_order_notify;
+      p_data->rsp_value.attr_value.len = sizeof(device->playing_order_notify);
+      p_data->event = MCP_DESCRIPTOR_READ_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_MEDIA_STATE_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_state;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_state);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_state_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_PLAYER_NAME_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.player_name;
+      p_data->oper.WriteDescOp.len = mediaPlayerInfo.player_name_len;
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_player_name_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_ctrl_point;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_ctrl_point);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_control_point_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_MEDIA_CONTROL_POINT_SUPPORTED_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.media_supported_feature;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.media_supported_feature);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.media_control_point_opcode_supported_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_CHANGED_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.track_changed;
+      p_data->oper.WriteDescOp.len = sizeof (mediaPlayerInfo.track_changed);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_changed_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+    case MCP_TRACK_TITLE_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.title;
+      p_data->oper.WriteDescOp.len = mediaPlayerInfo.track_title_len;
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_title_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_POSITION_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.position;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.position);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_position_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_TRACK_DURATION_DESCRIPTOR_WRITE: {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.duration;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.duration);
+      p_data->oper.WriteDescOp.char_handle = mcsServerServiceInfo.track_duration_handle;
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_PLAYING_ORDER_DESCRIPTOR_WRITE : {
+      p_data->oper.WriteDescOp.data = (uint8_t *)&mediaPlayerInfo.playing_order_value;
+      p_data->oper.WriteDescOp.len = sizeof(mediaPlayerInfo.playing_order_value);
+      p_data->event = MCP_DESCRIPTOR_WRITE_RSP;
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+    }
+
+    case MCP_CONGESTION_UPDATE: {
+      McpCongestionUpdate(p_data);
+      break;
+    }
+
+    case MCP_CONNECTION_UPDATE:
+    case MCP_ACTIVE_DEVICE_UPDATE:
+    case MCP_PHY_UPDATE:
+    case MCP_CONNECTION:
+    case MCP_DISCONNECTION:
+    case MCP_CONNECTION_CLOSE_EVENT:
+    case MCP_BOND_STATE_CHANGE_EVENT:
+      device = p_data->remoteDevice;
+      device->DeviceStateHandlerPointer[device->state](p_data->event, p_data, device->state);
+      break;
+
+      default:
+      break;
+  }
+  return BT_STATUS_SUCCESS;
+}
+
+bool DeviceStateConnectionHandler(uint32_t event, void* param, uint8_t state) {
+  LOG(INFO) << __func__ << "  device connected handle " << get_mcp_event_name(event);
+  mcp_resp_t *p_data = (mcp_resp_t *) param;
+  switch (event) {
+    case MCP_NOTIFY_ALL: {
+      LOG(INFO) << __func__ << "  device notify all ";
+      if (is_pts_running() || p_data->remoteDevice->active_profile == 0x10) {// need to check profile value
+        std::vector<uint8_t> value;
+        tMCP_MEDIA_UPDATE *notfiyUpdate = &p_data->oper.MediaUpdateOp;
+        value.assign(notfiyUpdate->data, notfiyUpdate->data + notfiyUpdate->len);
+        GattsOpsQueue::SendNotification(p_data->remoteDevice->conn_id,
+                                        p_data->handle, value, false);
+      }
+      break;
+    }
+
+    case MCP_READ_RSP:
+      BTA_GATTS_SendRsp(p_data->remoteDevice->conn_id, p_data->oper.ReadOp.trans_id,
+          BT_STATUS_SUCCESS, &p_data->rsp_value);
+
+      break;
+
+    case MCP_DESCRIPTOR_READ_RSP:
+      BTA_GATTS_SendRsp(p_data->remoteDevice->conn_id, p_data->oper.ReadDescOp.trans_id,
+          BT_STATUS_SUCCESS, &p_data->rsp_value);
+
+      break;
+
+    case MCP_DESCRIPTOR_WRITE_RSP: {
+      LOG(INFO) << __func__ << "  device MCP_DESCRIPTOR_WRITE_RSP update rsp :" << p_data->oper.WriteDescOp.need_rsp;
+      tGATTS_RSP rsp_struct;
+      rsp_struct.attr_value.handle = p_data->rsp_value.attr_value.handle;
+      rsp_struct.attr_value.offset = p_data->rsp_value.attr_value.offset;
+      if (p_data->remoteDevice->congested == false &&
+            p_data->oper.WriteDescOp.need_rsp) {
+      //send rsp to write
+        LOG(INFO) << __func__ << " gatt send rsp status" << p_data->oper.WriteDescOp.status;
+        BTA_GATTS_SendRsp(p_data->remoteDevice->conn_id, p_data->oper.WriteDescOp.trans_id,
+            p_data->oper.WriteDescOp.status, &rsp_struct);
+      }
+      break;
+    }
+
+    case MCP_WRITE_RSP: {
+      LOG(INFO) << __func__ << " device MCP_WRITE_RSP update Tx ID: " << p_data->oper.WriteOp.trans_id;
+      bool need_rsp = p_data->oper.WriteOp.need_rsp;
+      if (need_rsp) {
+      BTA_GATTS_SendRsp(p_data->remoteDevice->conn_id, p_data->oper.WriteOp.trans_id,
+          BT_STATUS_SUCCESS, &p_data->rsp_value);
+      }
+      break;
+    }
+
+    case MCP_CONNECTION_UPDATE: {
+      p_data->remoteDevice->latency = p_data->oper.ConnectionUpdateOp.latency;
+      p_data->remoteDevice->timeout = p_data->oper.ConnectionUpdateOp.timeout;
+      p_data->remoteDevice->interval = p_data->oper.ConnectionUpdateOp.interval;
+      p_data->remoteDevice->active_profile = 0x10;
+      break;
+    }
+
+    case MCP_ACTIVE_DEVICE_UPDATE: {
+      p_data->remoteDevice->active_profile = p_data->oper.SetActiveDeviceOp.profile;
+      break;
+    }
+    case MCP_PHY_UPDATE: {
+      p_data->remoteDevice->rx_phy = p_data->oper.PhyOp.rx_phy;
+      p_data->remoteDevice->tx_phy = p_data->oper.PhyOp.tx_phy;
+      break;
+    }
+
+    case MCP_MTU_UPDATE: {
+      p_data->remoteDevice->mtu = p_data->oper.MtuOp.mtu;
+      break;
+    }
+
+    case MCP_CONGESTION_UPDATE: {
+      McpCongestionUpdate(p_data);
+      break;
+    }
+
+    case MCP_DISCONNECTION: {
+      LOG(INFO) << __func__ << "  device event MCP_DISCONNECTION remove ";
+      instance->remoteDevices.Remove(p_data->remoteDevice->peer_bda);
+      instance->OnConnectionStateChange(MCP_DISCONNECTED, p_data->remoteDevice->peer_bda);
+      break;
+    }
+
+    case MCP_CONNECTION_CLOSE_EVENT: {
+      LOG(INFO) << __func__ << "  device connection closing";
+        // Close active connection
+      if (p_data->remoteDevice->conn_id != 0)
+        BTA_GATTS_Close(p_data->remoteDevice->conn_id);
+      else
+        BTA_GATTS_CancelOpen(mcsServerServiceInfo.server_if, p_data->remoteDevice->peer_bda, true);
+
+      // Cancel pending background connections
+      BTA_GATTS_CancelOpen(mcsServerServiceInfo.server_if, p_data->remoteDevice->peer_bda, false);
+      break;
+    }
+
+    case MCP_BOND_STATE_CHANGE_EVENT:
+      LOG(INFO) << __func__ << "Bond state change : ";
+      instance->remoteDevices.Remove(p_data->remoteDevice->peer_bda);
+      break;
+
+    default:
+      LOG(INFO) << __func__ << "  event not matched";
+      break;
+  }
+
+  return BT_STATUS_SUCCESS;
+}
+
+
+bool DeviceStateDisconnectedHandler(uint32_t event, void* param, uint8_t state) {
+  LOG(INFO) << __func__ << "  device disconnected handle " << get_mcp_event_name(event);
+  mcp_resp_t *p_data = (mcp_resp_t *) param;
+  switch (event) {
+    case MCP_CONNECTION: {
+      p_data->remoteDevice->state = MCP_CONNECTED;
+      p_data->remoteDevice->media_state_notify = 0x00;
+      p_data->remoteDevice->media_player_name_notify = 0x00;
+      p_data->remoteDevice->media_control_point_notify = 0x00;
+      p_data->remoteDevice->track_changed_notify = 0x00;
+      p_data->remoteDevice->track_duration_notify = 0x00;
+      p_data->remoteDevice->track_title_notify = 0x00;
+      p_data->remoteDevice->track_position_notify = 0x00;
+      p_data->remoteDevice->playing_order_notify = 0x00;
+      p_data->remoteDevice->congested = false;
+
+      p_data->remoteDevice->timeout = 0;
+      p_data->remoteDevice->latency = 0;
+      p_data->remoteDevice->interval = 0;
+      p_data->remoteDevice->rx_phy = 0;
+      p_data->remoteDevice->tx_phy = 0;
+      p_data->remoteDevice->mtu = -1;
+      instance->OnConnectionStateChange(MCP_CONNECTED, p_data->remoteDevice->peer_bda);
+      break;
+    }
+
+    case MCP_CONGESTION_UPDATE: {
+      McpCongestionUpdate(p_data);
+      break;
+    }
+
+    case MCP_MTU_UPDATE:
+    case MCP_PHY_UPDATE:
+    case MCP_READ_RSP:
+    case MCP_DESCRIPTOR_READ_RSP:
+    case MCP_WRITE_RSP:
+    case MCP_DESCRIPTOR_WRITE_RSP:
+    case MCP_NOTIFY_ALL:
+    case MCP_DISCONNECTION:
+    case MCP_CONNECTION_CLOSE_EVENT:
+    case MCP_BOND_STATE_CHANGE_EVENT:
+    default:
+      //ignore event
+      LOG(INFO) << __func__ << "  Ignore event " << get_mcp_event_name(event);
+      break;
+  }
+  return BT_STATUS_SUCCESS;
+}
+
+void McpCongestionUpdate(mcp_resp_t *p_data) {
+  p_data->remoteDevice->congested = p_data->oper.CongestionOp.congested;
+  LOG(INFO) << __func__ << ": conn_id: " << p_data->remoteDevice->conn_id
+                        << ", congested: " << p_data->remoteDevice->congested;
+
+  GattsOpsQueue::CongestionCallback(p_data->remoteDevice->conn_id,
+                                    p_data->remoteDevice->congested);
+}
diff --git a/le_audio/system/bt/bta/vcp/bta_vcp_controller.cc b/le_audio/system/bt/bta/vcp/bta_vcp_controller.cc
new file mode 100644
index 0000000..5eb7d6c
--- /dev/null
+++ b/le_audio/system/bt/bta/vcp/bta_vcp_controller.cc
@@ -0,0 +1,1108 @@
+/*
+ *Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#include "bt_target.h"
+#include "bta_vcp_controller_api.h"
+#include "bta_gatt_api.h"
+#include "btm_int.h"
+#include "device/include/controller.h"
+#include "gap_api.h"
+#include "gatt_api.h"
+#include "gattc_ops_queue.h"
+#include "osi/include/properties.h"
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <hardware/bt_vcp_controller.h>
+#include <vector>
+
+using base::Closure;
+using bluetooth::Uuid;
+using bluetooth::bap::GattOpsQueue;
+using bluetooth::vcp_controller::ConnectionState;
+
+// Assigned Numbers for VCS
+Uuid VCS_UUID          = Uuid::FromString("1844");
+Uuid VCS_VOLUME_STATE_UUID      = Uuid::FromString("2B7D");
+Uuid VCS_VOLUME_CONTROL_POINT_UUID   = Uuid::FromString("2B7E");
+Uuid VCS_VOLUME_FLAGS_UUID      = Uuid::FromString("2B7F");
+
+#define VCS_RETRY_SET_ABS_VOL 0x01
+#define VCS_RETRY_SET_MUTE_STATE 0x02
+
+// VCS Application Error Code
+#define VCS_INVALID_CHANGE_COUNTER 0x80
+#define VCS_OPCODE_NOT_SUPPORTED 0x81
+
+void vcp_controller_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
+void vcp_controller_encryption_callback(const RawAddress*, tGATT_TRANSPORT, void*, tBTM_STATUS);
+const char* vcp_controller_gatt_callback_evt_str(uint8_t event);
+const char* vcp_controller_handle_vcs_evt_str(uint8_t event);
+
+class VcpControllerImpl;
+static VcpControllerImpl* instance;
+
+class RendererDevices {
+ private:
+
+ public:
+  void Add(RendererDevice device) {
+    if (FindByAddress(device.address) != nullptr) return;
+    devices.push_back(device);
+  }
+
+  void Remove(const RawAddress& address) {
+    for (auto it = devices.begin(); it != devices.end();) {
+      if (it->address != address) {
+        ++it;
+        continue;
+      }
+
+      it = devices.erase(it);
+      return;
+    }
+  }
+
+  RendererDevice* FindByAddress(const RawAddress& address) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&address](const RendererDevice& device) {
+                               return device.address == address;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  RendererDevice* FindByConnId(uint16_t conn_id) {
+    auto iter = std::find_if(devices.begin(), devices.end(),
+                             [&conn_id](const RendererDevice& device) {
+                               return device.conn_id == conn_id;
+                             });
+
+    return (iter == devices.end()) ? nullptr : &(*iter);
+  }
+
+  size_t size() { return (devices.size()); }
+
+  std::vector<RendererDevice> devices;
+};
+
+class VcpControllerImpl : public VcpController {
+ private:
+  uint8_t gatt_if;
+  bluetooth::vcp_controller::VcpControllerCallbacks* callbacks;
+  RendererDevices rendererDevices;
+
+ public:
+  virtual ~VcpControllerImpl() = default;
+
+  VcpControllerImpl(bluetooth::vcp_controller::VcpControllerCallbacks* callbacks)
+      : gatt_if(0),
+        callbacks(callbacks) {
+    LOG(INFO) << "VcpControllerImpl gattc app register";
+
+    BTA_GATTC_AppRegister(
+        vcp_controller_gattc_callback,
+        base::Bind(
+            [](uint8_t client_id, uint8_t status) {
+              if (status != GATT_SUCCESS) {
+                LOG(ERROR) << "Can't start Vcp profile - no gatt "
+                              "clients left!";
+                return;
+              }
+              instance->gatt_if = client_id;
+            }), true);
+  }
+
+  void Connect(const RawAddress& address, bool isDirect) override {
+    LOG(INFO) << __func__ << " " << address << ", isDirect = " << logbool(isDirect);
+    RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
+
+    if (rendererDevice) {
+      LOG(INFO) << "Device already in connected/connecting state" << address;
+      return;
+    }
+
+    rendererDevices.Add(RendererDevice(address));
+    rendererDevice = rendererDevices.FindByAddress(address);
+    if (!rendererDevice) {
+      LOG(INFO) << "Device address could not be foundL";
+      return;
+    }
+    rendererDevice->state = BTA_VCP_CONNECTING;
+    callbacks->OnConnectionState(ConnectionState::CONNECTING, rendererDevice->address);
+
+    if (!isDirect) {
+      rendererDevice->bg_conn = true;
+    }
+
+    BTA_GATTC_Open(gatt_if, address, isDirect, GATT_TRANSPORT_LE, false);
+  }
+
+  void Disconnect(const RawAddress& address) override {
+    RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
+
+    if (!rendererDevice) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+
+    LOG(INFO) << __func__ << " " << address;
+    rendererDevice->state = BTA_VCP_DISCONNECTING;
+    callbacks->OnConnectionState(ConnectionState::DISCONNECTING, rendererDevice->address);
+    VcpGattClose(rendererDevice);
+  }
+
+  void SetAbsVolume(const RawAddress& address, uint8_t volume) {
+    RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
+
+    if (!rendererDevice) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+
+    if (rendererDevice->conn_id == 0) {
+        LOG(INFO) << __func__ << ": GATT is not connected, skip set absolute volume";
+        return;
+    }
+
+    if (rendererDevice->state != BTA_VCP_CONNECTED) {
+      LOG(INFO)
+          << __func__ << ": VCP is not connected, skip set absolute volume, state = "
+          << loghex(rendererDevice->state);
+      return;
+    }
+    // Send the data packet
+    LOG(INFO) << __func__ << ": Set abs volume. device=" << rendererDevice->address
+              << ", volume=" << loghex(volume);
+
+    rendererDevice->vcs.pending_volume_setting = volume;
+
+    uint8_t p_buf[256];
+    SetAbsVolumeOp set_abs_vol_op;
+    set_abs_vol_op.op_id  = VCS_CONTROL_POINT_OP_SET_ABS_VOL;
+    set_abs_vol_op.change_counter = rendererDevice->vcs.volume_state.change_counter;
+    set_abs_vol_op.volume_setting = volume;
+
+    memcpy(p_buf, &set_abs_vol_op , sizeof(set_abs_vol_op));
+    std::vector<uint8_t> vect_val(p_buf, p_buf + sizeof(set_abs_vol_op));
+
+    GattOpsQueue::WriteCharacteristic(gatt_if,
+        rendererDevice->conn_id, rendererDevice->vcs.volume_control_point_handle, vect_val,
+        GATT_WRITE, VcpControllerImpl::OnSetAbsVolumeStatic, nullptr);
+  }
+
+  void Mute(const RawAddress& address) {
+    RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
+
+    if (!rendererDevice) {
+      LOG(INFO) << "Device not connected to profile" << address;
+      return;
+    }
+
+    if (rendererDevice->conn_id == 0) {
+        LOG(INFO) << __func__ << ": GATT is not connected, skip mute";
+        return;
+    }
+
+    if (rendererDevice->state != BTA_VCP_CONNECTED) {
+      LOG(INFO)
+          << __func__ << ": VCP is not connected, skip mute, state = "
+          << loghex(rendererDevice->state);
+      return;
+    }
+    // Send the data packet
+    LOG(INFO) << __func__ << ": Mute device=" << rendererDevice->address;
+
+    rendererDevice->vcs.pending_mute_setting = VCS_MUTE_STATE;
+
+    uint8_t p_buf[256];
+    MuteOp mute_op;
+    mute_op.op_id  = VCS_CONTROL_POINT_OP_MUTE;
+    mute_op.change_counter = rendererDevice->vcs.volume_state.change_counter;
+
+    memcpy(p_buf, &mute_op , sizeof(mute_op));
+    std::vector<uint8_t> vect_val(p_buf, p_buf + sizeof(mute_op));
+
+    GattOpsQueue::WriteCharacteristic(gatt_if,
+        rendererDevice->conn_id, rendererDevice->vcs.volume_control_point_handle, vect_val,
+        GATT_WRITE, VcpControllerImpl::OnMuteStatic, nullptr);
+  }
+
+  void Unmute(const RawAddress& address) {
+    RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
+
+    if (!rendererDevice) {
+      LOG(WARNING) << "Device not connected to profile" << address;
+      return;
+    }
+
+    if (rendererDevice->conn_id == 0) {
+        LOG(INFO) << __func__ << ": GATT is not connected, skip unmute.";
+        return;
+    }
+
+    if (rendererDevice->state != BTA_VCP_CONNECTED) {
+      LOG(INFO)
+          << __func__ << ": VCP is not connected, skip unmute, state = "
+          << loghex(rendererDevice->state);
+      return;
+    }
+    // Send the data packet
+    LOG(INFO) << __func__ << ": unmute device=" << rendererDevice->address;
+
+    rendererDevice->vcs.pending_mute_setting = VCS_UNMUTE_STATE;
+
+    uint8_t p_buf[256];
+    UnmuteOp unmute_op;
+    unmute_op.op_id  = VCS_CONTROL_POINT_OP_UNMUTE;
+    unmute_op.change_counter = rendererDevice->vcs.volume_state.change_counter;
+
+    memcpy(p_buf, &unmute_op , sizeof(unmute_op));
+    std::vector<uint8_t> vect_val(p_buf, p_buf + sizeof(unmute_op));
+
+    GattOpsQueue::WriteCharacteristic(gatt_if,
+        rendererDevice->conn_id, rendererDevice->vcs.volume_control_point_handle, vect_val,
+        GATT_WRITE, VcpControllerImpl::OnUnmuteStatic, nullptr);
+  }
+
+  void VcpGattClose(RendererDevice* rendererDevice) {
+    LOG(INFO) << __func__ << " " << rendererDevice->address;
+
+    // Removes all registrations for connection.
+    BTA_GATTC_CancelOpen(gatt_if, rendererDevice->address, false);
+    rendererDevice->bg_conn = false;
+
+    if (rendererDevice->conn_id) {
+      GattOpsQueue::Clean(rendererDevice->conn_id);
+      BTA_GATTC_Close(rendererDevice->conn_id);
+    } else {
+       // cancel pending direct connect
+      BTA_GATTC_CancelOpen(gatt_if, rendererDevice->address, true);
+      PostDisconnected(rendererDevice);
+    }
+  }
+
+  void OnGattConnected(tGATT_STATUS status, uint16_t conn_id,
+                       tGATT_IF client_if, RawAddress address,
+                       tBTA_TRANSPORT transport, uint16_t mtu) {
+    RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
+    LOG(INFO) << __func__ <<  ": address=" << address << ", conn_id=" << conn_id;
+
+    if (!rendererDevice) {
+      LOG(WARNING) << "Closing connection to non volume renderer device, address="
+                   << address;
+      BTA_GATTC_Close(conn_id);
+      return;
+    }
+
+    if (status != GATT_SUCCESS) {
+      if (rendererDevice->bg_conn) {
+        // whitelist connection failed, that's ok.
+        LOG(INFO) << "bg conn failed, return immediately";
+        return;
+      }
+
+      LOG(INFO) << "Failed to connect to volume renderer device";
+      rendererDevices.Remove(address);
+      callbacks->OnConnectionState(ConnectionState::DISCONNECTED, address);
+      return;
+    }
+
+    if (rendererDevice->bg_conn) {
+        LOG(INFO) << __func__ <<  ": backgound connection from: address=" << address;
+    }
+
+    rendererDevice->bg_conn = false;
+    rendererDevice->conn_id = conn_id;
+
+    /* verify bond */
+    uint8_t sec_flag = 0;
+    BTM_GetSecurityFlagsByTransport(address, &sec_flag, BT_TRANSPORT_LE);
+
+    LOG(INFO) << __func__ <<  ": sec_flag =" << loghex(sec_flag);
+    if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) {
+      /* if link has been encrypted */
+      OnEncryptionComplete(address, true);
+      return;
+    }
+
+    if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) {
+      /* if bonded and link not encrypted */
+      sec_flag = BTM_BLE_SEC_ENCRYPT;
+      BTM_SetEncryption(address, BTA_TRANSPORT_LE, vcp_controller_encryption_callback, nullptr,
+                        sec_flag);
+      return;
+    }
+
+    /* otherwise let it go through */
+    OnEncryptionComplete(address, true);
+  }
+
+  void OnGattDisconnected(tGATT_STATUS status, uint16_t conn_id,
+                          tGATT_IF client_if, RawAddress remote_bda,
+                          tBTA_GATT_REASON reason) {
+    RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
+
+    if (!rendererDevice) {
+      LOG(WARNING) << "Skipping unknown device disconnect, conn_id="
+              << loghex(conn_id);
+      return;
+    }
+    LOG(INFO) << __func__ << ": conn_id=" << loghex(conn_id)
+            << ", reason=" << loghex(reason) << ", remote_bda=" << remote_bda;
+
+    PostDisconnected(rendererDevice);
+  }
+
+  void PostDisconnected(RendererDevice* rendererDevice) {
+    LOG(INFO) << __func__ << " " << rendererDevice->address;
+    rendererDevice->state = BTA_VCP_DISCONNECTED;
+
+    if(rendererDevice->vcs.volume_state_handle != 0xFFFF) {
+      BTIF_TRACE_WARNING("%s: Deregister notifications", __func__);
+      BTA_GATTC_DeregisterForNotifications(gatt_if,
+                         rendererDevice->address,
+                         rendererDevice->vcs.volume_state_handle);
+    }
+    if(rendererDevice->vcs.volume_flags_handle != 0xFFFF) {
+      BTIF_TRACE_WARNING("%s: Deregister notifications", __func__);
+      BTA_GATTC_DeregisterForNotifications(gatt_if,
+                         rendererDevice->address,
+                         rendererDevice->vcs.volume_flags_handle);
+    }
+
+    if (rendererDevice->conn_id) {
+      GattOpsQueue::Clean(rendererDevice->conn_id);
+      rendererDevice->conn_id = 0;
+    }
+
+    callbacks->OnConnectionState(ConnectionState::DISCONNECTED, rendererDevice->address);
+    rendererDevices.Remove(rendererDevice->address);
+  }
+
+  void OnEncryptionComplete(const RawAddress& address, bool success) {
+    RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
+    LOG(INFO) << __func__ << " " << address;
+
+    if (!rendererDevice) {
+      LOG(WARNING)  << "Skipping unknown device" << address;
+      return;
+    }
+
+    if (!success) {
+      LOG(ERROR) << "encryption failed";
+      BTA_GATTC_Close(rendererDevice->conn_id);
+      return;
+    }
+
+     BTA_GATTC_ServiceSearchRequest(rendererDevice->conn_id, &VCS_UUID);
+  }
+
+  void OnServiceSearchComplete(uint16_t conn_id, tGATT_STATUS status) {
+    RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
+    LOG(INFO) << __func__;
+
+    if (!rendererDevice) {
+      LOG(WARNING)  << "Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    if (status != GATT_SUCCESS) {
+      /* close connection and report service discovery complete with error */
+      LOG(ERROR) << "Service discovery failed";
+      VcpGattClose(rendererDevice);
+      return;
+    }
+
+    const std::vector<gatt::Service>* services = BTA_GATTC_GetServices(conn_id);
+
+    const gatt::Service* service = nullptr;
+    if (services) {
+      for (const gatt::Service& tmp : *services) {
+        if (tmp.uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER)) {
+            /*
+          LOG(INFO) << "Found UUID_SERVCLASS_GATT_SERVER, handle="
+                    << loghex(tmp.handle);
+          const gatt::Service* service_changed_service = &tmp;
+          FindServerChangedCCCHandle(conn_id, service_changed_service);
+          */
+        } else if (tmp.uuid == VCS_UUID) {
+          LOG(INFO) << "Found Volume Control service, handle=" << loghex(tmp.handle);
+          service = &tmp;
+        }
+      }
+    } else {
+      LOG(ERROR) << "no services found for conn_id: " << conn_id;
+      return;
+    }
+
+    if (!service) {
+      LOG(ERROR) << "No VCS found";
+      VcpGattClose(rendererDevice);
+      return;
+    }
+
+    for (const gatt::Characteristic& charac : service->characteristics) {
+      if (charac.uuid == VCS_VOLUME_STATE_UUID) {
+        rendererDevice->vcs.volume_state_handle = charac.value_handle;
+
+        rendererDevice->vcs.volume_state_ccc_handle =
+            FindCccHandle(conn_id, charac.value_handle);
+        if (!rendererDevice->vcs.volume_state_ccc_handle) {
+          LOG(ERROR) << __func__ << ": cannot find volume state CCC descriptor";
+          continue;
+        }
+
+        LOG(INFO) << __func__
+                  << ": vcs volume_state_handle=" << loghex(charac.value_handle)
+                  << ", ccc=" << loghex(rendererDevice->vcs.volume_state_ccc_handle);
+      } else if (charac.uuid == VCS_VOLUME_FLAGS_UUID) {
+        rendererDevice->vcs.volume_flags_handle = charac.value_handle;
+
+        rendererDevice->vcs.volume_flags_ccc_handle =
+            FindCccHandle(conn_id, charac.value_handle);
+        if (!rendererDevice->vcs.volume_flags_ccc_handle) {
+          LOG(ERROR) << __func__ << ": cannot find volume flags CCC descriptor";
+          continue;
+        }
+
+        LOG(INFO) << __func__
+                  << ": vcs volume_flags_handle=" << loghex(charac.value_handle)
+                  << ", ccc=" << loghex(rendererDevice->vcs.volume_flags_ccc_handle);
+      } else if (charac.uuid == VCS_VOLUME_CONTROL_POINT_UUID) {
+        // store volume control point!
+        rendererDevice->vcs.volume_control_point_handle = charac.value_handle;
+      } else {
+        LOG(WARNING) << "Unknown characteristic found:" << charac.uuid;
+      }
+    }
+
+    LOG(WARNING) << "reading vcs volume_state_handle";
+    GattOpsQueue::ReadCharacteristic(gatt_if,
+        conn_id, rendererDevice->vcs.volume_state_handle,
+        VcpControllerImpl::OnVolumeStateReadStatic, nullptr);
+
+  }
+
+  void OnServiceChangeEvent(const RawAddress& address) {
+    RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
+    if (!rendererDevice) {
+      VLOG(2) << "Skipping unknown device" << address;
+      return;
+    }
+    LOG(INFO) << __func__ << ": address=" << address;
+    rendererDevice->service_changed_rcvd = true;
+    GattOpsQueue::Clean(rendererDevice->conn_id);
+  }
+
+  void OnServiceDiscDoneEvent(const RawAddress& address) {
+    RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
+    if (!rendererDevice) {
+      VLOG(2) << "Skipping unknown device" << address;
+      return;
+    }
+    if (rendererDevice->service_changed_rcvd) {
+      BTA_GATTC_ServiceSearchRequest(rendererDevice->conn_id, &VCS_UUID);
+    }
+  }
+
+  void OnVolumeStateRead(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
+                 uint16_t handle, uint16_t len, uint8_t* value, void* data) {
+    RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
+
+    if (!rendererDevice) {
+      LOG(WARNING)  << "Skipping unknown read event, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status)
+                        << ", renderer device state: " << loghex(rendererDevice->state);
+
+    if (status != GATT_SUCCESS) {
+      LOG(ERROR) << "Error reading Volume State for device" << rendererDevice->address;
+    } else {
+      uint8_t* p = value;
+      uint8_t volume_setting;
+      STREAM_TO_UINT8(volume_setting, p);
+      rendererDevice->vcs.volume_state.volume_setting = volume_setting;
+
+      uint8_t mute;
+      STREAM_TO_UINT8(mute, p);
+      rendererDevice->vcs.volume_state.mute = mute;
+
+      uint8_t change_counter;
+      STREAM_TO_UINT8(change_counter, p);
+      rendererDevice->vcs.volume_state.change_counter = change_counter;
+    }
+
+    HandleVCSEvent(rendererDevice, VCS_VOLUME_STATE_READ_CMPL_EVT, status);
+  }
+
+  void OnVolumeFlagsRead(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
+                 uint16_t handle, uint16_t len, uint8_t* value, void* data) {
+    RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
+
+    if (!rendererDevice) {
+      LOG(WARNING)  << "Skipping unknown read event, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
+
+    if (status != GATT_SUCCESS) {
+      LOG(ERROR) << "Error reading Volume Flags for device" << rendererDevice->address;
+    } else {
+      uint8_t* p = value;
+      uint8_t volume_flags;
+      STREAM_TO_UINT8(volume_flags, p);
+      rendererDevice->vcs.volume_flags = volume_flags;
+    }
+
+    HandleVCSEvent(rendererDevice, VCS_VOLUME_FLAGS_READ_CMPL_EVT, status);
+  }
+
+   void OnVolumeStateCCCWrite(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
+                                 uint16_t handle, void* data) {
+     RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
+
+     if (!rendererDevice) {
+       LOG(WARNING)  << "Skipping unknown read event, conn_id=" << loghex(conn_id);
+       return;
+     }
+
+     LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
+
+     HandleVCSEvent(rendererDevice, VCS_VOLUME_STATE_CCC_WRITE_CMPL_EVT, status);
+   }
+
+   void OnVolumeFlagsCCCWrite(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
+                                 uint16_t handle, void* data) {
+     RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
+
+     if (!rendererDevice) {
+       LOG(WARNING)  << "Skipping unknown read event, conn_id=" << loghex(conn_id);
+       return;
+     }
+
+     LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
+
+     HandleVCSEvent(rendererDevice, VCS_VOLUME_FLAGS_CCC_WRITE_CMPL_EVT, status);
+   }
+
+   void OnSetAbsVolume(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
+                                 uint16_t handle, void* data) {
+     RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
+
+     if (!rendererDevice) {
+       LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
+       return;
+     }
+
+     LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
+
+     if (status != GATT_SUCCESS) {
+       // Check for VCS Invalid Change Counter error, it may
+       // conflict with GATT_NO_RESOURCES error.
+       if (status == VCS_INVALID_CHANGE_COUNTER ||
+           status == VCS_OPCODE_NOT_SUPPORTED) {
+         LOG(ERROR) << __func__  << ": Error code: " << status
+                               << " device: " << rendererDevice->address
+                               << " Read Volume State to update change counter";
+
+         rendererDevice->vcs.retry_cmd |= VCS_RETRY_SET_ABS_VOL;
+         GattOpsQueue::ReadCharacteristic(gatt_if,
+             conn_id, rendererDevice->vcs.volume_state_handle,
+             VcpControllerImpl::OnVolumeStateReadStatic, nullptr);
+       } else {
+         LOG(ERROR) <<  __func__ << ": Other errors, not retry";
+       }
+     } else {
+       rendererDevice->vcs.retry_cmd &= ~VCS_RETRY_SET_ABS_VOL;
+       LOG(INFO) << "Set abs volume success " << rendererDevice->address;
+     }
+   }
+
+   void OnMute(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
+                                 uint16_t handle, void* data) {
+     RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
+
+     if (!rendererDevice) {
+       LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
+       return;
+     }
+
+     LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
+
+     if (status != GATT_SUCCESS) {
+       LOG(ERROR) << "Volume State Write failed" << rendererDevice->address
+                            << "Read Volume State";
+
+       rendererDevice->vcs.retry_cmd |= VCS_RETRY_SET_MUTE_STATE;
+       GattOpsQueue::ReadCharacteristic(gatt_if,
+           conn_id, rendererDevice->vcs.volume_state_handle,
+           VcpControllerImpl::OnVolumeStateReadStatic, nullptr);
+     } else {
+       LOG(INFO) << "Mute success" << rendererDevice->address;
+     }
+
+   }
+
+   void OnUnmute(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
+                                 uint16_t handle, void* data) {
+     RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
+
+     if (!rendererDevice) {
+       LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
+       return;
+     }
+
+     LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
+
+     if (status != GATT_SUCCESS) {
+       LOG(ERROR) << "Volume State Write failed" << rendererDevice->address
+                            << "Read Volume State";
+
+       rendererDevice->vcs.retry_cmd |= VCS_RETRY_SET_MUTE_STATE;
+       GattOpsQueue::ReadCharacteristic(gatt_if,
+           conn_id, rendererDevice->vcs.volume_state_handle,
+           VcpControllerImpl::OnVolumeStateReadStatic, nullptr);
+     } else {
+       LOG(INFO) << "Unmute success" << rendererDevice->address;
+     }
+   }
+
+  void RetryVolumeControlOp(RendererDevice* rendererDevice) {
+    LOG(INFO) << __func__ << " " << rendererDevice->address;
+
+    if (rendererDevice->vcs.retry_cmd & VCS_RETRY_SET_ABS_VOL) {
+      rendererDevice->vcs.retry_cmd &= ~VCS_RETRY_SET_ABS_VOL;
+      SetAbsVolume(rendererDevice->address, rendererDevice->vcs.pending_volume_setting);
+    }
+
+    if (rendererDevice->vcs.retry_cmd & VCS_RETRY_SET_MUTE_STATE) {
+      rendererDevice->vcs.retry_cmd &= ~VCS_RETRY_SET_MUTE_STATE;
+      if (rendererDevice->vcs.pending_mute_setting == VCS_MUTE_STATE) {
+        Mute(rendererDevice->address);
+      } else {
+        Unmute(rendererDevice->address);
+      }
+    }
+  }
+
+  static void OnVolumeStateReadStatic(uint16_t client_id, uint16_t conn_id,
+                                             tGATT_STATUS status,
+                                             uint16_t handle, uint16_t len,
+                                             uint8_t* value, void* data) {
+    if (instance)
+      instance->OnVolumeStateRead(client_id, conn_id, status, handle, len, value,
+                                         data);
+  }
+
+  static void OnVolumeFlagsReadStatic(uint16_t client_id, uint16_t conn_id,
+                                             tGATT_STATUS status,
+                                             uint16_t handle, uint16_t len,
+                                             uint8_t* value, void* data) {
+    if (instance)
+      instance->OnVolumeFlagsRead(client_id, conn_id, status, handle, len, value,
+                                         data);
+  }
+
+  static void OnVolumeStateCCCWriteStatic(uint16_t client_id, uint16_t conn_id,
+                                 tGATT_STATUS status, uint16_t handle, void* data) {
+    if (instance)
+      instance->OnVolumeStateCCCWrite(client_id, conn_id, status, handle, data);
+  }
+
+  static void OnVolumeFlagsCCCWriteStatic(uint16_t client_id, uint16_t conn_id,
+                                 tGATT_STATUS status, uint16_t handle, void* data) {
+    if (instance)
+      instance->OnVolumeFlagsCCCWrite(client_id, conn_id, status, handle, data);
+  }
+
+  static void OnSetAbsVolumeStatic(uint16_t client_id, uint16_t conn_id,
+                                 tGATT_STATUS status, uint16_t handle, void* data) {
+    if (instance)
+      instance->OnSetAbsVolume(client_id, conn_id, status, handle, data);
+  }
+
+  static void OnMuteStatic(uint16_t client_id, uint16_t conn_id,
+                                 tGATT_STATUS status, uint16_t handle, void* data) {
+    if (instance)
+      instance->OnMute(client_id, conn_id, status, handle, data);
+  }
+
+  static void OnUnmuteStatic(uint16_t client_id, uint16_t conn_id,
+                                 tGATT_STATUS status, uint16_t handle, void* data) {
+    if (instance)
+      instance->OnUnmute(client_id, conn_id, status, handle, data);
+  }
+
+
+  void OnNotificationEvent(uint16_t conn_id, uint16_t handle, uint16_t len,
+                           uint8_t* value) {
+    RendererDevice* device = rendererDevices.FindByConnId(conn_id);
+
+    if (!device) {
+      LOG(INFO) << __func__
+                << ": Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    if (handle == device->vcs.volume_state_handle) {
+      if ( len != sizeof(device->vcs.volume_state)) {
+        LOG(ERROR) << __func__ << ": Data Length mismatch, len=" << len
+                << ", expecting " << sizeof(device->vcs.volume_state);
+        return;
+      }
+
+      LOG(INFO) << __func__ << " " << device->address << " volume state notification";
+      memcpy(&device->vcs.volume_state, value, len);
+      callbacks->OnVolumeStateChange(device->vcs.volume_state.volume_setting,
+              device->vcs.volume_state.mute, device->address);
+    } else if (handle == device->vcs.volume_flags_handle) {
+      if ( len != sizeof(device->vcs.volume_flags)) {
+        LOG(ERROR) << __func__ << ": Data Length mismatch, len=" << len
+                << ", expecting " << sizeof(device->vcs.volume_flags);
+        return;
+      }
+
+      LOG(INFO) << __func__ << " " << device->address << " volume flags notification";
+      memcpy(&device->vcs.volume_flags, value, len);
+      callbacks->OnVolumeFlagsChange(device->vcs.volume_flags, device->address);
+    } else {
+      LOG(INFO) << __func__ << ": Mismatched handle, "
+                << loghex(device->vcs.volume_state_handle)
+                << " or " << loghex(device->vcs.volume_flags_handle)
+                << "!=" << loghex(handle);
+      return;
+    }
+  }
+
+  void OnCongestionEvent(uint16_t conn_id, bool congested) {
+    RendererDevice* device = rendererDevices.FindByConnId(conn_id);
+    if (!device) {
+      LOG(INFO) << __func__
+                << ": Skipping unknown device, conn_id=" << loghex(conn_id);
+      return;
+    }
+
+    LOG(WARNING) << __func__ << ": conn_id:" << loghex(conn_id)
+                             << ", congested: " << congested;
+    GattOpsQueue::CongestionCallback(conn_id, congested);
+  }
+
+  void HandleVCSEvent(RendererDevice* rendererDevice, uint32_t event, tGATT_STATUS status) {
+    LOG(INFO) << __func__ << " event = " << vcp_controller_handle_vcs_evt_str(event);
+
+    if (status != GATT_SUCCESS) {
+      if (rendererDevice->state == BTA_VCP_CONNECTING) {
+        LOG(ERROR) << __func__ << ": Error status while VCP connecting, Close GATT for device: "
+                              << rendererDevice->address;
+        VcpGattClose(rendererDevice);
+        return;
+      } else if  (rendererDevice->state == BTA_VCP_CONNECTED) {
+        LOG(ERROR) << __func__ << ": Error status while VCP is connected for device: "
+                              << rendererDevice->address;
+        if (rendererDevice->vcs.retry_cmd != 0) {
+          rendererDevice->vcs.retry_cmd = 0;
+        }
+        return;
+      } else {
+        LOG(ERROR) << __func__ << ": Error status in disconnected or disconnecting "
+                              << "Igore handle VCS Event  for device: " << rendererDevice->address;
+        return;
+      }
+    }
+
+    switch (event) {
+      case VCS_VOLUME_STATE_READ_CMPL_EVT: {
+         if (rendererDevice->state == BTA_VCP_CONNECTING) {
+          LOG(WARNING) << "Setup VCP connection, reading vcs volume_flags_handle";
+          GattOpsQueue::ReadCharacteristic(gatt_if,
+              rendererDevice->conn_id, rendererDevice->vcs.volume_flags_handle,
+              VcpControllerImpl::OnVolumeFlagsReadStatic, nullptr);
+          break;
+        } else if (rendererDevice->state == BTA_VCP_CONNECTED) {
+          if (rendererDevice->vcs.retry_cmd != 0) {
+            RetryVolumeControlOp(rendererDevice);
+          }
+        }
+        break;
+      }
+
+      case VCS_VOLUME_FLAGS_READ_CMPL_EVT: {
+        if (rendererDevice->state == BTA_VCP_CONNECTING) {
+          /* Register and enable the Volume State Notification */
+          tGATT_STATUS register_status;
+          register_status = BTA_GATTC_RegisterForNotifications(
+              gatt_if, rendererDevice->address, rendererDevice->vcs.volume_state_handle);
+          if (register_status != GATT_SUCCESS) {
+            LOG(ERROR) << __func__
+                       << ": BTA_GATTC_RegisterForNotifications failed, status="
+                       << loghex(register_status);
+            VcpGattClose(rendererDevice);
+            return;
+          }
+
+          std::vector<uint8_t> value(2);
+          uint8_t* ptr = value.data();
+          UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
+          GattOpsQueue::WriteDescriptor(gatt_if,
+                  rendererDevice->conn_id, rendererDevice->vcs.volume_state_ccc_handle,
+                  std::move(value), GATT_WRITE, VcpControllerImpl::OnVolumeStateCCCWriteStatic,
+                  nullptr);
+        }
+        break;
+      }
+
+      case VCS_VOLUME_STATE_CCC_WRITE_CMPL_EVT: {
+        if (rendererDevice->state == BTA_VCP_CONNECTING) {
+          /* Register and enable the Volume State Notification */
+          tGATT_STATUS register_status;
+          register_status = BTA_GATTC_RegisterForNotifications(
+              gatt_if, rendererDevice->address, rendererDevice->vcs.volume_flags_handle);
+          if (register_status != GATT_SUCCESS) {
+            LOG(ERROR) << __func__
+                   << ": BTA_GATTC_RegisterForNotifications failed, status="
+                   << loghex(register_status);
+            VcpGattClose(rendererDevice);
+            return;
+          }
+
+          std::vector<uint8_t> value(2);
+          uint8_t* ptr = value.data();
+          UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
+          GattOpsQueue::WriteDescriptor(gatt_if,
+                  rendererDevice->conn_id, rendererDevice->vcs.volume_flags_ccc_handle,
+                  std::move(value), GATT_WRITE, VcpControllerImpl::OnVolumeFlagsCCCWriteStatic,
+                  nullptr);
+        }
+        break;
+      }
+
+      case VCS_VOLUME_FLAGS_CCC_WRITE_CMPL_EVT: {
+        if (rendererDevice->state == BTA_VCP_CONNECTING) {
+            LOG(INFO) << __func__ << ": VCP Connection Setup complete";
+            rendererDevice->state = BTA_VCP_CONNECTED;
+            callbacks->OnConnectionState(ConnectionState::CONNECTED, rendererDevice->address);
+            callbacks->OnVolumeFlagsChange(rendererDevice->vcs.volume_flags,
+                    rendererDevice->address);
+            callbacks->OnVolumeStateChange(rendererDevice->vcs.volume_state.volume_setting,
+                    rendererDevice->vcs.volume_state.mute, rendererDevice->address);
+            break;
+        }
+        break;
+      }
+
+      default:
+        LOG(INFO) << __func__ << ": unexpected VCS event";
+        break;
+    }
+  }
+
+    // Find the handle for the client characteristics configuration of a given
+  // characteristics
+  uint16_t FindCccHandle(uint16_t conn_id, uint16_t char_handle) {
+    const gatt::Characteristic* p_char =
+        BTA_GATTC_GetCharacteristic(conn_id, char_handle);
+    LOG(INFO) << __func__ << " " << ", conn_id: " << conn_id << ", char_handle: " << char_handle;
+
+    if (!p_char) {
+      LOG(WARNING) << __func__ << ": No such characteristic: " << char_handle;
+      return 0;
+    }
+
+    for (const gatt::Descriptor& desc : p_char->descriptors) {
+      if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG))
+        return desc.handle;
+    }
+    return 0;
+  }
+
+  void CleanUp() {
+    LOG(INFO) << __func__;
+    BTA_GATTC_AppDeregister(gatt_if);
+    for (RendererDevice& device : rendererDevices.devices) {
+      PostDisconnected(&device);
+    }
+
+    rendererDevices.devices.clear();
+  }
+};
+
+void vcp_controller_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
+  LOG(INFO) << __func__ << " event = " << vcp_controller_gatt_callback_evt_str(event);
+
+  if (p_data == nullptr) return;
+
+  switch (event) {
+    case BTA_GATTC_DEREG_EVT:
+      break;
+
+    case BTA_GATTC_OPEN_EVT: {
+      if (!instance) return;
+      tBTA_GATTC_OPEN& o = p_data->open;
+      instance->OnGattConnected(o.status, o.conn_id, o.client_if, o.remote_bda,
+                                o.transport, o.mtu);
+      break;
+    }
+
+    case BTA_GATTC_CLOSE_EVT: {
+      if (!instance) return;
+      tBTA_GATTC_CLOSE& c = p_data->close;
+      instance->OnGattDisconnected(c.status, c.conn_id, c.client_if,
+                                   c.remote_bda, c.reason);
+    } break;
+
+    case BTA_GATTC_SEARCH_CMPL_EVT:
+      if (!instance) return;
+      instance->OnServiceSearchComplete(p_data->search_cmpl.conn_id,
+                                        p_data->search_cmpl.status);
+      break;
+
+    case BTA_GATTC_NOTIF_EVT:
+      if (!instance) return;
+      if (!p_data->notify.is_notify || p_data->notify.len > GATT_MAX_ATTR_LEN) {
+        LOG(ERROR) << __func__ << ": rejected BTA_GATTC_NOTIF_EVT. is_notify="
+                   << p_data->notify.is_notify
+                   << ", len=" << p_data->notify.len;
+        break;
+      }
+      instance->OnNotificationEvent(p_data->notify.conn_id,
+                                    p_data->notify.handle, p_data->notify.len,
+                                    p_data->notify.value);
+      break;
+
+    case BTA_GATTC_ENC_CMPL_CB_EVT:
+      if (!instance) return;
+      instance->OnEncryptionComplete(p_data->enc_cmpl.remote_bda, true);
+      break;
+
+    case BTA_GATTC_SRVC_CHG_EVT:
+      if (!instance) return;
+      instance->OnServiceChangeEvent(p_data->remote_bda);
+      break;
+
+    case BTA_GATTC_SRVC_DISC_DONE_EVT:
+      if (!instance) return;
+      instance->OnServiceDiscDoneEvent(p_data->remote_bda);
+      break;
+
+    case BTA_GATTC_CONGEST_EVT:
+      if (!instance) return;
+      instance->OnCongestionEvent(p_data->congest.conn_id,
+                                  p_data->congest.congested);
+      break;
+
+    case BTA_GATTC_SEARCH_RES_EVT:
+    case BTA_GATTC_CANCEL_OPEN_EVT:
+    case BTA_GATTC_CONN_UPDATE_EVT:
+
+    default:
+      break;
+  }
+}
+
+void vcp_controller_encryption_callback(const RawAddress* address,
+                            UNUSED_ATTR tGATT_TRANSPORT transport,
+                            UNUSED_ATTR void* data, tBTM_STATUS status) {
+  if (instance) {
+    instance->OnEncryptionComplete(*address,
+                                   status == BTM_SUCCESS ? true : false);
+  }
+}
+
+void VcpController::Initialize(
+            bluetooth::vcp_controller::VcpControllerCallbacks* callbacks) {
+  LOG(INFO) << __func__ ;
+
+  if (instance) {
+    LOG(ERROR) << "Already initialized!";
+  }
+
+  instance = new VcpControllerImpl(callbacks);
+}
+
+bool VcpController::IsVcpControllerRunning() { return instance; }
+
+VcpController* VcpController::Get() {
+  CHECK(instance);
+  return instance;
+};
+
+int VcpController::GetDeviceCount() {
+  if (!instance) {
+    LOG(INFO) << __func__ << ": Not initialized yet";
+    return 0;
+  }
+
+  return (instance->GetDeviceCount());
+}
+
+void VcpController::CleanUp() {
+  VcpControllerImpl* ptr = instance;
+  instance = nullptr;
+
+  ptr->CleanUp();
+
+  delete ptr;
+};
+
+/*******************************************************************************
+ *  Debugging functions
+ ******************************************************************************/
+#define CASE_RETURN_STR(const) \
+  case const:                  \
+    return #const;
+
+const char* vcp_controller_gatt_callback_evt_str(uint8_t event) {
+  switch (event) {
+    CASE_RETURN_STR(BTA_GATTC_DEREG_EVT)
+    CASE_RETURN_STR(BTA_GATTC_OPEN_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CLOSE_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SEARCH_CMPL_EVT)
+    CASE_RETURN_STR(BTA_GATTC_NOTIF_EVT)
+    CASE_RETURN_STR(BTA_GATTC_ENC_CMPL_CB_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SEARCH_RES_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CANCEL_OPEN_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SRVC_CHG_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CONN_UPDATE_EVT)
+    CASE_RETURN_STR(BTA_GATTC_SRVC_DISC_DONE_EVT)
+    CASE_RETURN_STR(BTA_GATTC_CONGEST_EVT)
+    default:
+      return (char*)"Unknown GATT Callback Event";
+  }
+}
+
+const char* vcp_controller_handle_vcs_evt_str(uint8_t event) {
+  switch (event) {
+    CASE_RETURN_STR(VCS_VOLUME_STATE_READ_CMPL_EVT)
+    CASE_RETURN_STR(VCS_VOLUME_FLAGS_READ_CMPL_EVT)
+    CASE_RETURN_STR(VCS_VOLUME_STATE_CCC_WRITE_CMPL_EVT)
+    CASE_RETURN_STR(VCS_VOLUME_FLAGS_CCC_WRITE_CMPL_EVT)
+    default:
+      return (char*)"Unknown handling VCS Event";
+  }
+}
+
diff --git a/le_audio/system/bt/btif/Android.bp b/le_audio/system/bt/btif/Android.bp
new file mode 100644
index 0000000..f60b54e
--- /dev/null
+++ b/le_audio/system/bt/btif/Android.bp
@@ -0,0 +1,98 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+cc_defaults {
+    name: "fluoride_btif_defaults_qti_adva",
+    defaults: ["fluoride_defaults"],
+    include_dirs: [
+        "vendor/qcom/opensource/commonsys/system/bt",
+        "vendor/qcom/opensource/commonsys/system/bt/bta/include",
+        "vendor/qcom/opensource/commonsys/system/bt/bta/ag",
+        "vendor/qcom/opensource/commonsys/system/bt/btcore/include",
+        "vendor/qcom/opensource/commonsys/system/bt/hci/include",
+        "vendor/qcom/opensource/commonsys/system/bt/internal_include",
+        "vendor/qcom/opensource/commonsys/system/bt/stack/include",
+        "vendor/qcom/opensource/commonsys/system/bt/stack/btm",
+        "vendor/qcom/opensource/commonsys/system/bt/udrv/include",
+        "vendor/qcom/opensource/commonsys/system/bt/vnd/include",
+        "vendor/qcom/opensource/commonsys/system/bt/utils/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_ext/system_bt_ext",
+        "vendor/qcom/opensource/commonsys/bluetooth_ext/vhal/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_ext/system_bt_ext/bta/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_ext/system_bt_ext/btif/include",
+        "vendor/qcom/opensource/commonsys-intf/bluetooth/include",
+        "vendor/qcom/opensource/commonsys/system/bt/device/include",
+        "vendor/qcom/opensource/commonsys/system/bt/btif/include",
+        "vendor/qcom/opensource/commonsys/system/bt/bta/include",
+        "vendor/qcom/opensource/commonsys/system/bt/bta/sys",
+        "system/bt/common",
+        "vendor/qcom/opensource/commonsys/bluetooth_lea/system/bt/btif/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_lea/system/bt/bta/include",
+        "vendor/qcom/opensource/commonsys/bluetooth_lea/system/bt",
+        "vendor/qcom/opensource/commonsys/bluetooth_lea/vhal/include",
+        "external/libxml2/include",
+    ],
+    shared_libs: [
+        "libcutils",
+        "vendor.qti.hardware.bluetooth_audio@2.0",
+        "vendor.qti.hardware.bluetooth_audio@2.1",
+        "libcrypto",
+        "libxml2",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    cflags: [
+        "-DBUILDCFG",
+        "-DADV_AUDIO_FEATURE=1",
+    ],
+    required: ["leaudio_configs.xml"],
+}
+
+// BTA static library for target
+// ========================================================
+cc_library_static {
+    name: "libbt-btif_qti_adva",
+    defaults: ["fluoride_btif_defaults_qti_adva"],
+    enabled: false,
+    srcs: [
+        "src/bluetooth_adv_audio.cc",
+        "src/btif_bap_broadcast.cc",
+        "src/btif_csip.cc",
+        "src/btif_acm.cc",
+        "src/btif_pacs_client.cc",
+        "src/btif_ascs_client.cc",
+        "src/btif_bap_config.cc",
+        "src/btif_bap_uclient.cc",
+        "src/btif_bap_codec_utils.cc",
+        "src/btif_vmcp.cc",
+        "src/btif_apm.cc",
+        "src/btif_vcp_controller.cc",
+        "src/btif_dm_adv_audio.cc",
+        "src/btif_acm_source.cc",
+        "src/btif_mcp.cc",
+        "src/btif_cc.cc"
+    ],
+}
+
+// Bluetooth le audio configs xml
+// ========================================================
+prebuilt_etc {
+    name: "leaudio_configs.xml",
+    src: "leaudio_configs.xml",
+    sub_dir: "bluetooth",
+    system_ext_specific: true,
+}
diff --git a/le_audio/system/bt/btif/include/bluetooth_adv_audio.h b/le_audio/system/bt/btif/include/bluetooth_adv_audio.h
new file mode 100644
index 0000000..5fcc410
--- /dev/null
+++ b/le_audio/system/bt/btif/include/bluetooth_adv_audio.h
@@ -0,0 +1,33 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+ *
+ *  Filename:      bluetooth_adv_audio.h
+ *
+ *  Description:   Main API header file for LEA interfacing
+ *
+ ******************************************************************************/
+
+#pragma once
+
+/*******************************************************************************
+ *  TInterface APIs
+ ******************************************************************************/
+
+const void* get_adv_audio_profile_interface(const char* profile_id);
+void init_adv_audio_interfaces();
diff --git a/le_audio/system/bt/btif/include/btif_acm.h b/le_audio/system/bt/btif/include/btif_acm.h
new file mode 100644
index 0000000..c11dcb7
--- /dev/null
+++ b/le_audio/system/bt/btif/include/btif_acm.h
@@ -0,0 +1,250 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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_ACM_H
+#define BTIF_ACM_H
+
+#include <vector>
+
+//#include "bta_acm_api.h"
+#include "btif_common.h"
+#include "bta_bap_uclient_api.h"
+#include "bta_pacs_client_api.h"
+#include "bta_ascs_client_api.h"
+
+typedef uint8_t tBTA_ACM_HNDL;
+typedef uint8_t tBTIF_ACM_STATUS;
+
+#define BTA_ACM_MAX_EVT 26
+#define BTA_ACM_NUM_STRS 6
+#define BTA_ACM_NUM_CIGS 239
+//starting setid from 16 onwards as 16 is inavlid
+#define BTA_ACM_MIN_NUM_SETID 17
+#define BTA_ACM_MAX_NUM_SETID 255
+#define CONTEXT_TYPE_UNKNOWN 0
+#define CONTEXT_TYPE_MUSIC 1
+#define CONTEXT_TYPE_VOICE 2
+#define CONTEXT_TYPE_MUSIC_VOICE 3
+
+#define BTA_ACM_DISCONNECT_EVT 0
+#define BTA_ACM_CONNECT_EVT 1
+#define BTA_ACM_START_EVT 2
+#define BTA_ACM_STOP_EVT 3
+#define BTA_ACM_RECONFIG_EVT 4
+#define BTA_ACM_CONFIG_EVT 5
+#define BTA_ACM_CONN_UPDATE_TIMEOUT_EVT 6
+
+#define BTA_ACM_INITIATOR_SERVICE_ID 0xFF
+#define ACM_UUID 0xFFFF
+#define ACM_TSEP_SNK 1
+
+#define SRC 0
+#define SNK 1
+
+constexpr uint8_t  STREAM_STATE_DISCONNECTED     = 0x00;
+constexpr uint8_t  STREAM_STATE_CONNECTING       = 0x01;
+constexpr uint8_t  STREAM_STATE_CONNECTED        = 0x02;
+constexpr uint8_t  STREAM_STATE_STARTING         = 0x03;
+constexpr uint8_t  STREAM_STATE_STREAMING        = 0x04;
+constexpr uint8_t  STREAM_STATE_STOPPING         = 0x05;
+constexpr uint8_t  STREAM_STATE_DISCONNECTING    = 0x06;
+constexpr uint8_t  STREAM_STATE_RECONFIGURING    = 0x07;
+
+using bluetooth::bap::ucast::StreamStateInfo;
+using bluetooth::bap::ucast::StreamConfigInfo;
+
+using bluetooth::bap::ucast::StreamConnect;
+using bluetooth::bap::ucast::StreamType;
+using bluetooth::bap::ucast::StreamReconfig;
+using bluetooth::bap::ucast::StreamDiscReason;
+using bluetooth::bap::ucast::StreamState;
+using bluetooth::bap::ucast::QosConfig;
+
+using bluetooth::bap::pacs::CodecConfig;
+
+typedef struct {
+  RawAddress bd_addr;
+  int contextType;
+  int profileType;
+}tBTIF_ACM_CONN_DISC;
+
+typedef struct {
+  RawAddress bd_addr;
+}tBTA_ACM_CONN_UPDATE_TIMEOUT_INFO;
+
+typedef struct {
+  RawAddress bd_addr;
+  StreamType stream_type;
+  CodecConfig codec_config;
+  uint32_t audio_location;
+  QosConfig qos_config;
+  std::vector<CodecConfig> codecs_selectable;
+}tBTA_ACM_CONFIG_INFO;
+
+typedef struct {
+  RawAddress bd_addr;
+  StreamType stream_type;
+  StreamState stream_state;
+  StreamDiscReason reason;
+}tBTA_ACM_STATE_INFO;
+
+typedef struct {
+  RawAddress bd_addr;
+  bool is_direct;
+  StreamStateInfo streams_info;
+}tBTIF_ACM_CONNECT;
+
+typedef struct {
+  RawAddress bd_addr;
+  StreamStateInfo streams_info;
+}tBTIF_ACM_DISCONNECT;
+
+typedef struct {
+  RawAddress bd_addr;
+  StreamStateInfo streams_info;
+}tBTIF_ACM_START;
+
+typedef struct {
+  RawAddress bd_addr;
+  StreamStateInfo streams_info;
+}tBTIF_ACM_STOP;
+
+typedef struct {
+  RawAddress bd_addr;
+  StreamReconfig streams_info;
+}tBTIF_ACM_RECONFIG;
+
+typedef union {
+  tBTIF_ACM_CONN_DISC acm_conn_disc;
+  tBTA_ACM_STATE_INFO state_info;
+  tBTA_ACM_CONFIG_INFO config_info;
+  tBTIF_ACM_CONNECT acm_connect;
+  tBTIF_ACM_DISCONNECT acm_disconnect;
+  tBTIF_ACM_START acm_start;
+  tBTIF_ACM_STOP acm_stop;
+  tBTIF_ACM_RECONFIG acm_reconfig;
+}tBTIF_ACM;
+
+typedef enum {
+  /* Reuse BTA_ACM_XXX_EVT - No need to redefine them here */
+  BTIF_ACM_CONNECT_REQ_EVT = BTA_ACM_MAX_EVT,
+  BTIF_ACM_DISCONNECT_REQ_EVT,
+  BTIF_ACM_START_STREAM_REQ_EVT,
+  BTIF_ACM_STOP_STREAM_REQ_EVT,
+  BTIF_ACM_SUSPEND_STREAM_REQ_EVT,
+  BTIF_ACM_RECONFIG_REQ_EVT,
+} btif_acm_sm_event_t;
+
+typedef enum {
+  BTA_CSIP_NEW_SET_FOUND_EVT = 1,
+  BTA_CSIP_SET_MEMBER_FOUND_EVT,
+  BTA_CSIP_CONN_STATE_CHG_EVT,
+  BTA_CSIP_LOCK_STATUS_CHANGED_EVT,
+  BTA_CSIP_LOCK_AVAILABLE_EVT,
+  BTA_CSIP_SET_SIZE_CHANGED,
+  BTA_CSIP_SET_SIRK_CHANGED,
+} btif_csip_sm_event_t;
+
+/**
+ * When the local device is ACM source, get the address of the active peer.
+ */
+RawAddress btif_acm_source_active_peer(void);
+
+/**
+ * When the local device is ACM sink, get the address of the active peer.
+ */
+RawAddress btif_acm_sink_active_peer(void);
+
+/**
+ * Start streaming.
+ */
+void btif_acm_stream_start(void);
+
+/**
+ * Stop streaming.
+ *
+ * @param peer_address the peer address or RawAddress::kEmpty to stop all peers
+ */
+void btif_acm_stream_stop(void);
+
+/**
+ * Suspend streaming.
+ */
+void btif_acm_stream_suspend(void);
+
+/**
+ * Start offload streaming.
+ */
+void btif_acm_stream_start_offload(void);
+
+bool btif_acm_check_if_requested_devices_stopped(void);
+
+/**
+ * Get the Stream Endpoint Type of the Active peer.
+ *
+ * @return the stream endpoint type: either AVDT_TSEP_SRC or AVDT_TSEP_SNK
+ */
+uint8_t btif_acm_get_peer_sep(void);
+
+/**
+
+ * Report ACM 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_acm_report_source_codec_state(
+    const RawAddress& peer_address,
+    const CodecConfig& codec_config,
+    const std::vector<CodecConfig>& codecs_local_capabilities,
+    const std::vector<CodecConfig>&
+        codecs_selectable_capabilities, int contextType);
+
+/**
+ * Initialize / shut down the ACM Initiator service.
+ *
+ * @param enable true to enable the ACM Source service, false to disable it
+ * @return BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+ */
+bt_status_t btif_acm_initiator_execute_service(bool enable);
+
+/**
+ * Dump debug-related information for the BTIF ACM module.
+ *
+ * @param fd the file descriptor to use for writing the ASCII formatted
+ * information
+ */
+void btif_debug_acm_dump(int fd);
+
+bool btif_acm_is_active();
+uint16_t btif_acm_get_sample_rate();
+uint8_t btif_acm_get_ch_mode();
+uint32_t btif_acm_get_bitrate();
+uint32_t btif_acm_get_octets(uint32_t bit_rate);
+uint16_t btif_acm_get_framelength();
+uint8_t btif_acm_get_ch_count();
+uint16_t btif_acm_get_current_active_profile();
+bool btif_acm_is_codec_type_lc3q();
+uint8_t btif_acm_lc3q_ver();
+bool btif_acm_is_call_active(void);
+
+#endif /* BTIF_ACM_H */
diff --git a/le_audio/system/bt/btif/include/btif_acm_source.h b/le_audio/system/bt/btif/include/btif_acm_source.h
new file mode 100644
index 0000000..8203a65
--- /dev/null
+++ b/le_audio/system/bt/btif/include/btif_acm_source.h
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+
+#if AHIM_ENABLED
+void btif_register_cb();
+void btif_acm_process_request(tA2DP_CTRL_CMD cmd);
+void btif_acm_handle_event(uint16_t event, char* p_param);
+bool btif_acm_source_start_session(const RawAddress& peer_address);
+bool btif_acm_source_end_session(const RawAddress& peer_address);
+bool btif_acm_source_restart_session(const RawAddress& old_peer_address,
+                                      const RawAddress& new_peer_address);
+void btif_acm_source_command_ack(tA2DP_CTRL_CMD cmd, tA2DP_CTRL_ACK status);
+void btif_acm_source_on_stopped(tA2DP_CTRL_ACK status);
+void btif_acm_source_on_suspended(tA2DP_CTRL_ACK status);
+bool btif_acm_on_started(tA2DP_CTRL_ACK status);
+bt_status_t btif_acm_source_setup_codec();
+bool btif_acm_update_sink_latency_change(uint16_t sink_latency);
+
+#endif
diff --git a/le_audio/system/bt/btif/include/btif_bap_broadcast.h b/le_audio/system/bt/btif/include/btif_bap_broadcast.h
new file mode 100644
index 0000000..f2bf96d
--- /dev/null
+++ b/le_audio/system/bt/btif/include/btif_bap_broadcast.h
@@ -0,0 +1,92 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+ *
+ *  Filename:      btif_bap_broadcast.h
+ *
+ *  Description:   Main API header file for all BTIF BAP Broadcast functions
+ *                 accessed from internal stack.
+ *
+ ******************************************************************************/
+
+#ifndef BTIF_BAP_BROADCAST_H
+#define BTIF_BAP_BROADCAST_H
+
+#include "bta_av_api.h"
+#include "btif_common.h"
+#include "btif_sm.h"
+
+
+/*******************************************************************************
+ *  Type definitions for callback functions
+ ******************************************************************************/
+
+typedef enum {
+  /* Reuse BTA_AV_XXX_EVT - No need to redefine them here */
+  BTIF_BAP_BROADCAST_ENABLE_EVT,
+  BTIF_BAP_BROADCAST_DISABLE_EVT,
+  BTIF_BAP_BROADCAST_START_STREAM_REQ_EVT,
+  BTIF_BAP_BROADCAST_STOP_STREAM_REQ_EVT,
+  BTIF_BAP_BROADCAST_SUSPEND_STREAM_REQ_EVT,
+  BTIF_BAP_BROADCAST_SOURCE_CONFIG_REQ_EVT,
+  BTIF_BAP_BROADCAST_CLEANUP_REQ_EVT,
+  BTIF_BAP_BROADCAST_SET_ACTIVE_REQ_EVT,
+  BTIF_BAP_BROADCAST_REMOVE_ACTIVE_REQ_EVT,
+  BTIF_BAP_BROADCAST_SETUP_ISO_DATAPATH_EVT,
+  BTIF_BAP_BROADCAST_REMOVE_ISO_DATAPATH_EVT,
+  BTIF_BAP_BROADCAST_GENERATE_ENC_KEY_EVT,
+  BTIF_BAP_BROADCAST_BISES_SETUP_EVT,
+  BTIF_BAP_BROADCAST_BISES_REMOVE_EVT,
+  BTIF_BAP_BROADCAST_BIG_SETUP_EVT,
+  BTIF_BAP_BROADCAST_BIG_REMOVED_EVT,
+  BTIF_BAP_BROADCAST_SETUP_NEXT_BIS_EVENT,
+  BTIF_BAP_BROADCAST_PROCESS_HIDL_REQ_EVT,
+} btif_bap_broadcast_sm_event_t;
+
+enum {
+  BTBAP_CODEC_CHANNEL_MODE_JOINT_STEREO = 0x01 << 2,
+  BTBAP_CODEC_CHANNEL_MODE_DUAL_MONO = 0x1 << 3
+};
+/*******************************************************************************
+ *  BTIF AV API
+ ******************************************************************************/
+bool btif_bap_broadcast_is_active();
+
+uint16_t btif_bap_broadcast_get_sample_rate();
+uint8_t btif_bap_broadcast_get_ch_mode();
+uint16_t btif_bap_broadcast_get_framelength();
+uint32_t btif_bap_broadcast_get_mtu(uint32_t bitrate);
+uint32_t btif_bap_broadcast_get_bitrate();
+uint8_t btif_bap_broadcast_get_ch_count();
+bool btif_bap_broadcast_is_simulcast_enabled();
+/*******************************************************************************
+ *
+ * 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_bap_ba_dispatch_sm_event(btif_bap_broadcast_sm_event_t event, void *p_data, int len);
+
+
+#endif /* BTIF_BAP_BROADCAST_H */
+
diff --git a/le_audio/system/bt/btif/include/btif_bap_codec_utils.h b/le_audio/system/bt/btif/include/btif_bap_codec_utils.h
new file mode 100644
index 0000000..e557268
--- /dev/null
+++ b/le_audio/system/bt/btif/include/btif_bap_codec_utils.h
@@ -0,0 +1,90 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <hardware/bt_pacs_client.h>
+#include "bt_types.h"
+
+using bluetooth::bap::pacs::CodecConfig;
+
+bool UpdateCapaSupFrameDurations(CodecConfig *config , uint8_t sup_frame);
+
+bool UpdateCapaMaxSupLc3Frames(CodecConfig *config,
+                                uint8_t max_sup_lc3_frames);
+
+
+bool UpdateCapaPreferredContexts(CodecConfig *config, uint16_t contexts);
+
+
+bool UpdateCapaSupOctsPerFrame(CodecConfig *config,
+                                          uint32_t octs_per_frame);
+
+bool UpdateCapaVendorMetaDataLc3QPref(CodecConfig *config, bool lc3q_pref);
+
+bool UpdateCapaVendorMetaDataLc3QVer(CodecConfig *config, uint8_t lc3q_ver);
+
+uint8_t GetCapaSupFrameDurations(CodecConfig *config);
+
+uint8_t GetCapaMaxSupLc3Frames(CodecConfig *config);
+
+uint16_t GetCapaPreferredContexts(CodecConfig *config);
+
+uint32_t GetCapaSupOctsPerFrame(CodecConfig *config);
+
+bool GetCapaVendorMetaDataLc3QPref(CodecConfig *config);
+
+uint8_t GetCapaVendorMetaDataLc3QVer(CodecConfig *config);
+
+// configurations
+bool UpdateFrameDuration(CodecConfig *config , uint8_t frame_dur);
+
+bool UpdateLc3BlocksPerSdu(CodecConfig *config,
+                                uint8_t lc3_blocks_per_sdu) ;
+
+bool UpdateOctsPerFrame(CodecConfig *config , uint16_t octs_per_frame);
+
+bool UpdateLc3QPreference(CodecConfig *config , bool lc3q_pref);
+
+bool UpdateVendorMetaDataLc3QPref(CodecConfig *config, bool lc3q_pref);
+
+bool UpdateVendorMetaDataLc3QVer(CodecConfig *config, uint8_t lc3q_ver);
+
+bool UpdatePreferredAudioContext(CodecConfig *config ,
+                                    uint16_t pref_audio_context);
+
+uint8_t GetFrameDuration(CodecConfig *config);
+
+uint8_t GetLc3BlocksPerSdu(CodecConfig *config);
+
+uint16_t GetOctsPerFrame(CodecConfig *config);
+
+uint8_t GetLc3QPreference(CodecConfig *config);
+
+uint8_t GetVendorMetaDataLc3QPref(CodecConfig *config);
+
+uint8_t GetVendorMetaDataLc3QVer(CodecConfig *config);
+
+uint16_t GetPreferredAudioContext(CodecConfig *config);
+
+bool IsCodecConfigEqual(CodecConfig *src_config, CodecConfig *dst_config);
+
+
diff --git a/le_audio/system/bt/btif/include/btif_bap_config.h b/le_audio/system/bt/btif/include/btif_bap_config.h
new file mode 100644
index 0000000..a83b8e9
--- /dev/null
+++ b/le_audio/system/bt/btif/include/btif_bap_config.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/******************************************************************************
+ *
+ *  Copyright (C) 2014 Google, Inc.
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <hardware/bt_pacs_client.h>
+#include "bt_types.h"
+
+using bluetooth::bap::pacs::CodecConfig;
+using bluetooth::bap::pacs::CodecDirection;
+using bluetooth::bap::pacs::CodecIndex;
+
+typedef enum {
+  REC_TYPE_CAPABILITY = 0x01,
+  REC_TYPE_CONFIGURATION
+} btif_bap_record_type_t;
+
+const char BTIF_BAP_CONFIG_MODULE[] = "btif_bap_config_module";
+
+typedef struct btif_bap_config_section_iter_t btif_bap_config_section_iter_t;
+
+bool btif_bap_add_record(const RawAddress& bd_addr,
+                         btif_bap_record_type_t rec_type,
+                         uint16_t context_type,
+                         CodecDirection direction,
+                         CodecConfig *record);
+
+bool btif_bap_remove_record(const RawAddress& bd_addr,
+                            btif_bap_record_type_t rec_type,
+                            uint16_t context_type,
+                            CodecDirection direction,
+                            CodecConfig *record);
+
+bool btif_bap_remove_record_by_context(const RawAddress& bd_addr,
+                                       btif_bap_record_type_t rec_type,
+                                       uint16_t context_type,
+                                       CodecDirection direction);
+
+bool btif_bap_remove_all_records(const RawAddress& bd_addr);
+
+bool btif_bap_get_records(const RawAddress& bd_addr,
+                          btif_bap_record_type_t rec_type,
+                          uint16_t context_type,
+                          CodecDirection direction,
+                          std::vector<CodecConfig> *pac_records);
+
+bool btif_bap_add_audio_loc(const RawAddress& bd_addr,
+                            CodecDirection direction, uint32_t audio_loc);
+
+bool btif_bap_rem_audio_loc(const RawAddress& bd_addr,
+                            CodecDirection direction);
+
+bool btif_bap_add_supp_contexts(const RawAddress& bd_addr,
+                                  uint32_t supp_contexts);
+
+bool btif_bap_get_supp_contexts(const RawAddress& bd_addr,
+                                 uint32_t *supp_contexts);
+
+bool btif_bap_rem_supp_contexts(const RawAddress& bd_addr);
+
+bool btif_bap_config_clear(void);
diff --git a/le_audio/system/bt/btif/include/btif_dm_adv_audio.h b/le_audio/system/bt/btif/include/btif_dm_adv_audio.h
new file mode 100644
index 0000000..d288633
--- /dev/null
+++ b/le_audio/system/bt/btif/include/btif_dm_adv_audio.h
@@ -0,0 +1,33 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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_DM_ADV_AUDIO_H
+#define BTIF_DM_ADV_AUDIO_H
+
+extern std::unordered_map<RawAddress, uint32_t> adv_audio_device_db;
+extern tBTA_TRANSPORT btif_dm_get_adv_audio_transport(const RawAddress& bd_addr);
+extern void btif_dm_lea_search_services_evt(uint16_t event, char* p_param);
+extern void btif_register_uuid_srvc_disc(bluetooth::Uuid uuid);
+extern bool check_adv_audio_cod(uint32_t cod);
+extern bool is_remote_support_adv_audio(const RawAddress p_addr);
+extern void bte_dm_adv_audio_search_services_evt(tBTA_DM_SEARCH_EVT event,
+    tBTA_DM_SEARCH* p_data);
+extern void btif_dm_release_action_uuid(RawAddress bd_addr);
+
+#endif
+
diff --git a/le_audio/system/bt/btif/include/btif_vmcp.h b/le_audio/system/bt/btif/include/btif_vmcp.h
new file mode 100644
index 0000000..7eb72bf
--- /dev/null
+++ b/le_audio/system/bt/btif/include/btif_vmcp.h
@@ -0,0 +1,147 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 <vector>
+#include "raw_address.h"
+#include "hardware/bt_pacs_client.h"
+
+using bluetooth::bap::pacs::CodecSampleRate;
+using bluetooth::bap::pacs::CodecIndex;
+using bluetooth::bap::pacs::CodecFrameDuration;
+using bluetooth::bap::pacs::CodecConfig;
+
+#define BAP        0x01
+#define GCP        0x02
+#define WMCP       0x04
+#define VMCP       0x08
+#define BAP_CALL   0x10
+#define GCP_TX_RX  0x20
+
+#define EB_CONFIG           1
+#define STEREO_HS_CONFIG_1  2
+#define STEREO_HS_CONFIG_2  3
+
+#define VOICE_CONTEXT     1
+#define MEDIA_CONTEXT     2
+#define MEDIA_LL_CONTEXT  3
+#define MEDIA_HR_CONTEXT  4
+
+#define SAMPLE_RATE_8000   8000
+#define SAMPLE_RATE_16000 16000
+#define SAMPLE_RATE_24000 24000
+#define SAMPLE_RATE_32000 32000
+#define SAMPLE_RATE_44100 44100
+#define SAMPLE_RATE_48000 48000
+
+
+#define FRM_DURATION_7_5_MS   7.5
+#define FRM_DURATION_10_MS     10
+
+#define OCT_PER_CODEC_FRM_26     26
+#define OCT_PER_CODEC_FRM_30     30
+#define OCT_PER_CODEC_FRM_60     60
+#define OCT_PER_CODEC_FRM_75     75
+#define OCT_PER_CODEC_FRM_80     80
+#define OCT_PER_CODEC_FRM_90     90
+#define OCT_PER_CODEC_FRM_98     98
+#define OCT_PER_CODEC_FRM_100   100
+#define OCT_PER_CODEC_FRM_117   117
+#define OCT_PER_CODEC_FRM_120   120
+#define OCT_PER_CODEC_FRM_130   130
+#define OCT_PER_CODEC_FRM_150   150
+#define OCT_PER_CODEC_FRM_155   155
+
+#define UNFRAMED 0
+#define FRAMED   1
+
+#define RETRANS_NO_2     2
+#define RETRANS_NO_5     5
+#define RETRANS_NO_7     7
+#define RETRANS_NO_11   11
+#define RETRANS_NO_13   13
+#define RETRANS_NO_23   23
+#define RETRANS_NO_29   29
+#define RETRANS_NO_35   35
+#define RETRANS_NO_41   41
+#define RETRANS_NO_47   47
+#define RETRANS_NO_53   53
+
+#define MAX_TRANS_LAT_MS_8      8
+#define MAX_TRANS_LAT_MS_10    10
+#define MAX_TRANS_LAT_MS_15    15
+#define MAX_TRANS_LAT_MS_20    20
+#define MAX_TRANS_LAT_MS_24    24
+#define MAX_TRANS_LAT_MS_25    25
+#define MAX_TRANS_LAT_MS_31    31
+#define MAX_TRANS_LAT_MS_33    33
+#define MAX_TRANS_LAT_MS_45    45
+#define MAX_TRANS_LAT_MS_54    54
+#define MAX_TRANS_LAT_MS_60    60
+#define MAX_TRANS_LAT_MS_71    71
+#define MAX_TRANS_LAT_MS_95    95
+
+#define PRES_DELAY_20_MS    20
+#define PRES_DELAY_25_MS    25
+#define PRES_DELAY_40_MS    40
+
+#define LEAUDIO_CONFIG_PATH "/system_ext/etc/bluetooth/leaudio_configs.xml"
+using namespace std;
+
+// for storing codec config as it is read from xml
+struct codec_config
+{
+    uint32_t freq_in_hz;
+    float frame_dur_msecs;
+    uint8_t oct_per_codec_frm;
+    uint8_t mandatory;
+};
+
+// for storing QoS settings as it is read from xml
+struct qos_config
+{
+    uint32_t freq_in_hz;
+    uint32_t sdu_int_micro_secs;
+    uint8_t framing;
+    uint8_t max_sdu_size;
+    uint8_t retrans_num;
+    uint8_t max_trans_lat;
+    uint32_t presentation_delay;
+    uint8_t mandatory;
+};
+
+
+// QoS configuration in the structure needed by ACM
+struct QoSConfig
+{
+    CodecSampleRate sample_rate;
+    uint32_t sdu_int_micro_secs;
+    uint8_t framing;
+    uint8_t max_sdu_size;
+    uint8_t retrans_num;
+    uint8_t max_trans_lat;
+    uint32_t presentation_delay;
+    uint8_t mandatory;
+};
+void btif_vmcp_init();
+
+vector<CodecConfig> get_all_codec_configs(uint8_t profile, uint8_t context);
+vector<CodecConfig> get_preferred_codec_configs(uint8_t profile, uint8_t context);
+
+vector<QoSConfig> get_all_qos_params(uint8_t profile, uint8_t context);
+vector<QoSConfig> get_qos_params_for_codec(uint8_t profile, uint8_t context, CodecSampleRate freq, uint8_t frame_dur, uint8_t octets);
diff --git a/le_audio/system/bt/btif/leaudio_configs.xml b/le_audio/system/bt/btif/leaudio_configs.xml
new file mode 100644
index 0000000..0971d6f
--- /dev/null
+++ b/le_audio/system/bt/btif/leaudio_configs.xml
@@ -0,0 +1,727 @@
+<!--
+
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+-->
+<LEAudioConfig>
+<VMCPRoles> </VMCPRoles>
+
+<VMCP>
+	<CodecCapabilitiesForVoice>
+		<CodecSettings>
+			<SamplingFrequencyInHz>8000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>7500</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>26</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>8000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>10000</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>30</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>7500</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>60</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>10000</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>80</OctetsPerCodecFrame>
+			<Mandatory>1</Mandatory>
+		</CodecSettings>
+	</CodecCapabilitiesForVoice>
+
+	<CodecCapabilitiesForMedia>
+		<CodecSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>7500</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>75</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>10000</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>100</OctetsPerCodecFrame>
+			<Mandatory>1</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>7500</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>90</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>10000</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>120</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>7500</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>117</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>10000</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>155</OctetsPerCodecFrame>
+			<Mandatory>1</Mandatory>
+		</CodecSettings>
+	</CodecCapabilitiesForMedia>
+
+	<QosSettingsForLowLatencyVoice>
+		<QoSSettings>
+			<SamplingFrequencyInHz>8000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>26</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>8</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>8000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>30</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>10</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>60</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>8</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>1</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>80</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>10</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>1</Mandatory>
+		</QoSSettings>
+	</QosSettingsForLowLatencyVoice>
+	<QosSettingsForLowLatencyMedia>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>75</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>15</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>100</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>20</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>1</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>90</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>15</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>120</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>20</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>117</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>15</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>155</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>20</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+	</QosSettingsForLowLatencyMedia>
+	<QosSettingsForHighReliabilityMedia>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>75</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>100</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>60</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>1</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>90</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>120</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>60</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>117</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>155</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>60</MaxTransportLatency>
+			<PresentationDelay>20000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+	</QosSettingsForHighReliabilityMedia>
+</VMCP>
+
+<BAP>
+	<CodecCapabilitiesForVoice>
+		<CodecSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>10000</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>80</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+	</CodecCapabilitiesForVoice>
+	<CodecCapabilitiesForMedia>
+		<CodecSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>10000</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>100</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+	</CodecCapabilitiesForMedia>
+	<QosSettingsForLowLatencyVoice>
+		<QoSSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>80</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>10</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+	</QosSettingsForLowLatencyVoice>
+	<QosSettingsForLowLatencyMedia>
+	    <QoSSettings>
+			<SamplingFrequencyInHz>8000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>26</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>8</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>8000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>30</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>10</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>30</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>8</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>40</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>10</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>24000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>45</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>8</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>24000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>60</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>10</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>75</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>15</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>100</MaxSDUSize>
+			<RetransmissionNumber>15</RetransmissionNumber>
+			<MaxTransportLatency>70</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>90</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>15</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>120</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>20</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>60</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>8</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>80</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>10</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>117</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>15</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>155</MaxSDUSize>
+			<RetransmissionNumber>13</RetransmissionNumber>
+			<MaxTransportLatency>100</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+	</QosSettingsForLowLatencyMedia>
+	<QosSettingsForHighReliabilityMedia>
+		<QoSSettings>
+			<SamplingFrequencyInHz>8000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>26</MaxSDUSize>
+			<RetransmissionNumber>41</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>8000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>30</MaxSDUSize>
+			<RetransmissionNumber>53</RetransmissionNumber>
+			<MaxTransportLatency>60</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>30</MaxSDUSize>
+			<RetransmissionNumber>41</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>40</MaxSDUSize>
+			<RetransmissionNumber>47</RetransmissionNumber>
+			<MaxTransportLatency>60</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>1</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>24000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>45</MaxSDUSize>
+			<RetransmissionNumber>35</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>24000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>60</MaxSDUSize>
+			<RetransmissionNumber>41</RetransmissionNumber>
+			<MaxTransportLatency>60</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>60</MaxSDUSize>
+			<RetransmissionNumber>29</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>80</MaxSDUSize>
+			<RetransmissionNumber>35</RetransmissionNumber>
+			<MaxTransportLatency>60</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>75</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>100</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>60</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>90</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>120</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>60</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>117</MaxSDUSize>
+			<RetransmissionNumber>23</RetransmissionNumber>
+			<MaxTransportLatency>45</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>155</MaxSDUSize>
+			<RetransmissionNumber>13</RetransmissionNumber>
+			<MaxTransportLatency>100</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+	</QosSettingsForHighReliabilityMedia>
+</BAP>
+
+<GCP>
+	<CodecCapabilitiesForVoice>
+		<CodecSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>7500</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>60</OctetsPerCodecFrame>
+			<Mandatory>1</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>7500</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>60</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+		<CodecSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>7500</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>30</OctetsPerCodecFrame>
+			<Mandatory>1</Mandatory>
+		</CodecSettings>
+	</CodecCapabilitiesForVoice>
+
+	<CodecCapabilitiesForMedia>
+		<CodecSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>7500</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>75</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+	</CodecCapabilitiesForMedia>
+
+	<QosSettingsForLowLatencyVoice>
+		<QoSSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>15000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>60</MaxSDUSize>
+			<RetransmissionNumber>2</RetransmissionNumber>
+			<MaxTransportLatency>8</MaxTransportLatency>
+			<PresentationDelay>25000</PresentationDelay>
+			<Mandatory>1</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>15000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>60</MaxSDUSize>
+			<RetransmissionNumber>7</RetransmissionNumber>
+			<MaxTransportLatency>25</MaxTransportLatency>
+			<PresentationDelay>25000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>30</MaxSDUSize>
+			<RetransmissionNumber>11</RetransmissionNumber>
+			<MaxTransportLatency>33</MaxTransportLatency>
+			<PresentationDelay>25000</PresentationDelay>
+			<Mandatory>1</Mandatory>
+		</QoSSettings>
+	</QosSettingsForLowLatencyVoice>
+
+	<QosSettingsForLowLatencyMedia>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>75</MaxSDUSize>
+			<RetransmissionNumber>5</RetransmissionNumber>
+			<MaxTransportLatency>12</MaxTransportLatency>
+			<PresentationDelay>25000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+	</QosSettingsForLowLatencyMedia>
+</GCP>
+
+<WMCP>
+	<CodecCapabilitiesForMedia>
+		<CodecSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<FrameDurationInMicroSecs>10000</FrameDurationInMicroSecs>
+			<OctetsPerCodecFrame>100</OctetsPerCodecFrame>
+			<Mandatory>0</Mandatory>
+		</CodecSettings>
+	</CodecCapabilitiesForMedia>
+    <QosSettingsForHighReliabilityMedia>
+		<QoSSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>40</MaxSDUSize>
+			<RetransmissionNumber>13</RetransmissionNumber>
+			<MaxTransportLatency>95</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>16000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>30</MaxSDUSize>
+			<RetransmissionNumber>13</RetransmissionNumber>
+			<MaxTransportLatency>75</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>80</MaxSDUSize>
+			<RetransmissionNumber>13</RetransmissionNumber>
+			<MaxTransportLatency>95</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>32000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>60</MaxSDUSize>
+			<RetransmissionNumber>13</RetransmissionNumber>
+			<MaxTransportLatency>75</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>10000</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>100</MaxSDUSize>
+			<RetransmissionNumber>11</RetransmissionNumber>
+			<MaxTransportLatency>40</MaxTransportLatency>
+			<PresentationDelay>25000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+		<QoSSettings>
+			<SamplingFrequencyInHz>48000</SamplingFrequencyInHz>
+			<SDUIntervalInMicroSecs>7500</SDUIntervalInMicroSecs>
+			<Framing>0</Framing>
+			<MaxSDUSize>75</MaxSDUSize>
+			<RetransmissionNumber>13</RetransmissionNumber>
+			<MaxTransportLatency>75</MaxTransportLatency>
+			<PresentationDelay>40000</PresentationDelay>
+			<Mandatory>0</Mandatory>
+		</QoSSettings>
+	</QosSettingsForHighReliabilityMedia>
+</WMCP>
+
+</LEAudioConfig>
+
diff --git a/le_audio/system/bt/btif/src/bluetooth_adv_audio.cc b/le_audio/system/bt/btif/src/bluetooth_adv_audio.cc
new file mode 100644
index 0000000..ad1b1ac
--- /dev/null
+++ b/le_audio/system/bt/btif/src/bluetooth_adv_audio.cc
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/******************************************************************************
+ *
+ *  Copyright (C) 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:      bluetooth_adv_audio.cc
+ *
+ *  Description:   Bluetooth LEA HAL implementation
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_btif_adv_audio"
+
+#include <base/logging.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_csip.h>
+#include <hardware/bt_apm.h>
+#include <hardware/bt_acm.h>
+#include <hardware/bt_pacs_client.h>
+#include <hardware/bt_ascs_client.h>
+#include <hardware/bt_bap_uclient.h>
+#include <hardware/bt_vcp_controller.h>
+#include <hardware/bt_mcp.h>
+#include <hardware/bluetooth_callcontrol_interface.h>
+#include "osi/include/log.h"
+#include "btif_bap_config.h"
+#include "bta_csip_api.h"
+#include "stack_interface.h"
+#include "btcore/include/module.h"
+#include "btcore/include/osi_module.h"
+#include <hardware/bt_bap_ba.h>
+
+/*******************************************************************************
+ *  Externs
+ ******************************************************************************/
+
+/* list all extended interfaces here */
+using bluetooth::bap::pacs::PacsClientInterface;
+using bluetooth::bap::ascs::AscsClientInterface;
+using bluetooth::bap::ucast::UcastClientInterface;
+using bluetooth::vcp_controller::VcpControllerInterface;
+using bluetooth::mcp_server::McpServerInterface;
+using bluetooth::call_control::CallControllerInterface;
+extern PacsClientInterface *btif_pacs_client_get_interface();
+extern AscsClientInterface *btif_ascs_client_get_interface();
+extern UcastClientInterface *btif_bap_uclient_get_interface();
+extern bt_apm_interface_t *btif_apm_get_interface();
+extern btacm_initiator_interface_t* btif_acm_initiator_get_interface();
+extern btbap_broadcast_interface_t * btif_bap_broadcast_get_interface();
+/* Coordinated set identification profile - client */
+extern btcsip_interface_t* btif_csip_get_interface();
+/*Vcp Controller*/
+extern VcpControllerInterface* btif_vcp_get_controller_interface();
+/*Mcp server*/
+extern McpServerInterface* btif_mcp_server_get_interface();
+extern CallControllerInterface* btif_cc_server_get_interface();
+
+/*******************************************************************************
+ *  Functions
+ ******************************************************************************/
+
+static bool is_profile(const char* p1, const char* p2) {
+  CHECK(p1);
+  CHECK(p2);
+  return strlen(p1) == strlen(p2) && strncmp(p1, p2, strlen(p2)) == 0;
+}
+
+/*****************************************************************************
+ *
+ *   BLUETOOTH LEA HAL INTERFACE FUNCTIONS
+ *
+ ****************************************************************************/
+
+StackCallbacks *stack_callbacks;
+
+const void* get_adv_audio_profile_interface(const char* profile_id) {
+  LOG_INFO(LOG_TAG, "%s: id = %s", __func__, profile_id);
+
+  if (is_profile(profile_id, BT_PROFILE_PACS_CLIENT_ID)) {
+    return btif_pacs_client_get_interface();
+  }
+
+  if (is_profile(profile_id, BT_APM_MODULE_ID)) {
+    return btif_apm_get_interface();
+  }
+
+  if (is_profile(profile_id, BT_PROFILE_ACM_ID)) {
+    return btif_acm_initiator_get_interface();
+  }
+
+  if (is_profile(profile_id, BT_PROFILE_BAP_BROADCAST_ID))
+    return btif_bap_broadcast_get_interface();
+
+  if (is_profile(profile_id, BT_PROFILE_CSIP_CLIENT_ID)) {
+    return btif_csip_get_interface();
+  }
+
+  if (is_profile(profile_id, BT_PROFILE_VOLUME_CONTROL_ID)) {
+    return btif_vcp_get_controller_interface();
+  }
+
+  if (is_profile(profile_id, BT_PROFILE_MCP_ID)) {
+    return btif_mcp_server_get_interface();
+  }
+
+  if (is_profile(profile_id, BT_PROFILE_CC_ID)) {
+     return btif_cc_server_get_interface();
+  }
+
+  if (is_profile(profile_id, BT_PROFILE_ASCS_CLIENT_ID)) {
+    return btif_ascs_client_get_interface();
+  }
+
+  if (is_profile(profile_id, BT_PROFILE_BAP_UCLIENT_ID)) {
+    return bluetooth::bap::ucast::btif_bap_uclient_get_interface();
+  }
+  return NULL;
+}
+
+class StackCallbacksImpl : public StackCallbacks {
+  public:
+    ~StackCallbacksImpl() = default;
+    void OnDevUnPaired(const RawAddress& address) override {
+      BTA_CsipRemoveUnpairedSetMember(address);
+      btif_bap_remove_all_records(address);
+    }
+
+    void OnConfigCleared(void)  override {
+      btif_bap_config_clear();
+    }
+
+    void OnStackState(StackState state) {
+      switch(state) {
+        case StackState::INITIALIZING:
+          module_init(get_module(BTIF_BAP_CONFIG_MODULE));
+          break;
+        case StackState::TURNING_ON:
+          module_start_up(get_module(BTIF_BAP_CONFIG_MODULE));
+          break;
+        case StackState::TURNING_OFF:
+          module_shut_down(get_module(BTIF_BAP_CONFIG_MODULE));
+          break;
+        case StackState::CLEAND_UP:
+          module_clean_up(get_module(BTIF_BAP_CONFIG_MODULE));
+          break;
+        default:
+          break;
+      }
+    }
+};
+
+void init_adv_audio_interfaces() {
+  stack_callbacks = new StackCallbacksImpl;
+  StackInterface::Initialize(stack_callbacks);
+}
diff --git a/le_audio/system/bt/btif/src/btif_acm.cc b/le_audio/system/bt/btif/src/btif_acm.cc
new file mode 100644
index 0000000..0bc0124
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_acm.cc
@@ -0,0 +1,6672 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+#define LOG_TAG "btif_acm"
+#include "btif_acm.h"
+#include <base/bind.h>
+#include <base/bind_helpers.h>
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <map>
+#include <future>
+#include "bta_closure_api.h"
+#include "btif_storage.h"
+#include <hardware/bluetooth.h>
+#include <hardware/bt_acm.h>
+#include "audio_hal_interface/a2dp_encoding.h"
+#include "bt_common.h"
+#include "bt_utils.h"
+#include "bta/include/bta_api.h"
+#include "btif/include/btif_a2dp_source.h"
+#include "btif_common.h"
+#include <base/callback.h>
+#include "audio_a2dp_hw/include/audio_a2dp_hw.h"
+#include "btif_av_co.h"
+#include "btif_util.h"
+#include "btu.h"
+#include "common/state_machine.h"
+#include "osi/include/allocator.h"
+#include "osi/include/osi.h"
+#include "osi/include/properties.h"
+#include "btif/include/btif_bap_config.h"
+#include "bta_bap_uclient_api.h"
+#include "btif_bap_codec_utils.h"
+#include "bta/include/bta_csip_api.h"
+#include <base/threading/thread.h>
+#include "osi/include/thread.h"
+#include <pthread.h>
+#include "bta_api.h"
+#include <hardware/bt_pacs_client.h>
+#include <hardware/bt_bap_uclient.h>
+#include "btif/include/btif_vmcp.h"
+#include "btif/include/btif_acm_source.h"
+#include "l2c_api.h"
+#include "bt_types.h"
+#include "btm_int.h"
+#include <inttypes.h>
+
+/*****************************************************************************
+ *  Constants & Macros
+ *****************************************************************************/
+#define LE_AUDIO_MASK                      0x00000300
+#define LE_AUDIO_NOT_AVAILABLE             0x00000100
+#define LE_AUDIO_AVAILABLE_NOT_LICENSED    0x00000200  //LC3
+#define LE_AUDIO_AVAILABLE_LICENSED        0x00000300  //LC3Q
+#define LE_AUDIO_CS_3_1ST_BYTE_INDEX       0x00
+#define LE_AUDIO_CS_3_2ND_BYTE_INDEX       0x01
+#define LE_AUDIO_CS_3_3RD_BYTE_INDEX       0x02
+#define LE_AUDIO_CS_3_4TH_BYTE_INDEX       0x03
+#define LE_AUDIO_CS_3_5TH_BYTE_INDEX       0x04
+#define LE_AUDIO_CS_3_7TH_BYTE_INDEX       0x06
+#define LE_AUDIO_CS_3_8TH_BYTE_INDEX       0x07
+
+static RawAddress active_bda = {};
+static constexpr int kDefaultMaxConnectedAudioDevices = 5;
+CodecConfig current_active_config;
+static CodecConfig current_media_config;
+static CodecConfig current_voice_config;
+static CodecConfig current_recording_config;
+uint16_t current_active_profile_type = 0;
+uint16_t current_active_context_type;
+
+using bluetooth::bap::ucast::UcastClientInterface;
+using bluetooth::bap::ucast::UcastClientCallbacks;
+using bluetooth::bap::ucast::UcastClient;
+using bluetooth::bap::ucast::StreamState;
+using bluetooth::bap::ucast::StreamConnect;
+using bluetooth::bap::ucast::StreamType;
+
+using bluetooth::bap::pacs::CodecIndex;
+using bluetooth::bap::pacs::CodecPriority;
+using bluetooth::bap::pacs::CodecSampleRate;
+using bluetooth::bap::pacs::CodecBPS;
+using bluetooth::bap::pacs::CodecChannelMode;
+using bluetooth::bap::pacs::CodecFrameDuration;
+using bluetooth::bap::ucast::CodecQosConfig;
+using bluetooth::bap::ucast::StreamStateInfo;
+using bluetooth::bap::ucast::StreamConfigInfo;
+using bluetooth::bap::ucast::StreamReconfig;
+using bluetooth::bap::ucast::CISConfig;
+using bluetooth::bap::pacs::CodecDirection;
+using bluetooth::bap::ucast::CONTENT_TYPE_MEDIA;
+using bluetooth::bap::ucast::CONTENT_TYPE_CONVERSATIONAL;
+using bluetooth::bap::ucast::CONTENT_TYPE_LIVE;
+using bluetooth::bap::ucast::CONTENT_TYPE_UNSPECIFIED;
+using bluetooth::bap::ucast::CONTENT_TYPE_INSTRUCTIONAL;
+using bluetooth::bap::ucast::CONTENT_TYPE_NOTIFICATIONS;
+using bluetooth::bap::ucast::CONTENT_TYPE_ALERT;
+using bluetooth::bap::ucast::CONTENT_TYPE_MAN_MACHINE;
+using bluetooth::bap::ucast::CONTENT_TYPE_EMERGENCY;
+using bluetooth::bap::ucast::CONTENT_TYPE_RINGTONE;
+using bluetooth::bap::ucast::CONTENT_TYPE_SOUND_EFFECTS;
+using bluetooth::bap::ucast::CONTENT_TYPE_GAME;
+
+using bluetooth::bap::ucast::ASE_DIRECTION_SRC;
+using bluetooth::bap::ucast::ASE_DIRECTION_SINK;
+using bluetooth::bap::ucast::ASCSConfig;
+using bluetooth::bap::ucast::LE_2M_PHY;
+using bluetooth::bap::ucast::LE_QHS_PHY;
+
+using base::Bind;
+using base::Unretained;
+using base::IgnoreResult;
+using bluetooth::Uuid;
+extern void do_in_bta_thread(const base::Location& from_here,
+                             const base::Closure& task);
+
+bool reconfig_acm_initiator(const RawAddress& peer_address, int profileType);
+
+static void btif_acm_initiator_dispatch_sm_event(const RawAddress& peer_address,
+                                                  btif_acm_sm_event_t event);
+void btif_acm_update_lc3q_params(int64_t* cs3, tBTIF_ACM* p_acm_data);
+
+uint16_t btif_acm_bap_to_acm_context(uint16_t bap_context);
+
+std::mutex acm_session_wait_mutex_;
+std::condition_variable acm_session_wait_cv;
+bool acm_session_wait;
+
+
+/*****************************************************************************
+ *  Local type definitions
+ *****************************************************************************/
+
+class BtifCsipEvent {
+ public:
+  BtifCsipEvent(uint32_t event, const void* p_data, size_t data_length);
+  BtifCsipEvent(const BtifCsipEvent& other);
+  BtifCsipEvent() = delete;
+  ~BtifCsipEvent();
+  BtifCsipEvent& operator=(const BtifCsipEvent& 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 BtifAcmEvent {
+ public:
+  BtifAcmEvent(uint32_t event, const void* p_data, size_t data_length);
+  BtifAcmEvent(const BtifAcmEvent& other);
+  BtifAcmEvent() = delete;
+  ~BtifAcmEvent();
+  BtifAcmEvent& operator=(const BtifAcmEvent& 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 BtifAcmPeer;
+
+class BtifAcmStateMachine : public bluetooth::common::StateMachine {
+ public:
+  enum {
+    kStateIdle,            // ACM state disconnected
+    kStateOpening,         // ACM state connecting
+    kStateOpened,          // ACM state connected
+    kStateStarted,         // ACM state streaming
+    kStateReconfiguring,   // ACM state reconfiguring
+    kStateClosing,         // ACM state disconnecting
+  };
+
+  class StateIdle : public State {
+   public:
+    StateIdle(BtifAcmStateMachine& sm)
+        : State(sm, kStateIdle), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAcmPeer& peer_;
+  };
+
+  class StateOpening : public State {
+   public:
+    StateOpening(BtifAcmStateMachine& sm)
+        : State(sm, kStateOpening), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAcmPeer& peer_;
+  };
+
+  class StateOpened : public State {
+   public:
+    StateOpened(BtifAcmStateMachine& sm)
+        : State(sm, kStateOpened), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAcmPeer& peer_;
+  };
+
+  class StateStarted : public State {
+   public:
+    StateStarted(BtifAcmStateMachine& sm)
+        : State(sm, kStateStarted), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAcmPeer& peer_;
+  };
+
+  class StateReconfiguring : public State {
+   public:
+    StateReconfiguring(BtifAcmStateMachine& sm)
+        : State(sm, kStateReconfiguring), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAcmPeer& peer_;
+  };
+
+  class StateClosing : public State {
+   public:
+    StateClosing(BtifAcmStateMachine& sm)
+        : State(sm, kStateClosing), peer_(sm.Peer()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifAcmPeer& peer_;
+  };
+
+  BtifAcmStateMachine(BtifAcmPeer& btif_acm_peer) : peer_(btif_acm_peer) {
+    state_idle_ = new StateIdle(*this);
+    state_opening_ = new StateOpening(*this);
+    state_opened_ = new StateOpened(*this);
+    state_started_ = new StateStarted(*this);
+    state_reconfiguring_ = new StateReconfiguring(*this);
+    state_closing_ = new StateClosing(*this);
+
+    AddState(state_idle_);
+    AddState(state_opening_);
+    AddState(state_opened_);
+    AddState(state_started_);
+    AddState(state_reconfiguring_);
+    AddState(state_closing_);
+    SetInitialState(state_idle_);
+  }
+
+  BtifAcmPeer& Peer() { return peer_; }
+
+ private:
+  BtifAcmPeer& peer_;
+  StateIdle* state_idle_;
+  StateOpening* state_opening_;
+  StateOpened* state_opened_;
+  StateStarted* state_started_;
+  StateReconfiguring* state_reconfiguring_;
+  StateClosing* state_closing_;
+};
+
+class BtifAcmPeer {
+ public:
+  enum {
+    kFlagPendingLocalSuspend       = 0x01,
+    kFlagPendingReconfigure        = 0x02,
+    kFlagPendingStart              = 0x04,
+    kFlagPendingStop               = 0x08,
+    kFLagPendingStartAfterReconfig = 0x10,
+  };
+
+  enum {
+    kFlagAggresiveMode             = 0x01,
+    kFlagRelaxedMode               = 0x02,
+  };
+
+  static constexpr uint64_t  kTimeoutLockReleaseMs = 5 * 1000;
+
+  BtifAcmPeer(const RawAddress& peer_address, uint8_t peer_sep,
+              uint8_t set_id, uint8_t cig_id, uint8_t cis_id);
+  ~BtifAcmPeer();
+
+  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;
+
+  bool IsPeerActiveForMusic() const {
+    return (SetId() == MusicActiveSetId());
+  }
+  bool IsPeerActiveForVoice() const {
+    return (SetId() == VoiceActiveSetId());
+  }
+
+  bool IsAcceptor() const { return (peer_sep_ == ACM_TSEP_SNK); }
+
+  const RawAddress& MusicActivePeerAddress() const;
+  const RawAddress& VoiceActivePeerAddress() const;
+  uint8_t MusicActiveSetId() const;
+  uint8_t VoiceActiveSetId() const;
+
+  const RawAddress& PeerAddress() const { return peer_address_; }
+
+  void SetContextType(uint16_t contextType) { context_type_ = context_type_ | contextType; }
+  uint16_t GetContextType() { return context_type_; }
+  void ResetContextType(uint16_t contextType) { context_type_ &= ~contextType; }
+
+
+  void SetProfileType(uint16_t profileType) { profile_type_ = profile_type_ | profileType; }
+  uint16_t GetProfileType() {return profile_type_;}
+  void ResetProfileType(uint16_t profileType) { profile_type_ &= ~profileType; }
+
+
+  void SetRcfgProfileType(uint16_t profileType) { rcfg_profile_type_ = profileType; }
+  uint16_t GetRcfgProfileType() {return rcfg_profile_type_;}
+
+  void SetPrefContextType(uint16_t preferredContext) {preferred_context_ = preferredContext;};
+  uint16_t GetPrefContextType() {return preferred_context_;}
+
+  void SetStreamContextType(uint16_t contextType) { stream_context_type_ = contextType; }
+  uint16_t GetStreamContextType() { return stream_context_type_; }
+
+  void SetPeerVoiceRxState(StreamState state) {voice_rx_state = state;}
+  StreamState GetPeerVoiceRxState() {return voice_rx_state;}
+
+  void SetPeerVoiceTxState(StreamState state) {voice_tx_state = state;}
+  StreamState GetPeerVoiceTxState() {return voice_tx_state;}
+
+  void SetPeerMusicTxState(StreamState state) {music_tx_state = state;}
+  StreamState GetPeerMusicTxState() {return music_tx_state;}
+
+  void SetPeerMusicRxState(StreamState state) {music_rx_state = state;}
+  StreamState GetPeerMusicRxState() {return music_rx_state;}
+
+  void SetPeerLatency(uint16_t peerLatency) { peer_latency_ = peerLatency; }
+  uint16_t GetPeerLatency() {return peer_latency_;}
+
+  void SetIsStereoHsType(bool stereoHsType) { is_stereohs_type_= stereoHsType; }
+  bool IsStereoHsType() {return is_stereohs_type_;}
+
+  void set_peer_media_codec_config(CodecConfig &codec_config) {
+      peer_media_codec_config = codec_config;
+  }
+  CodecConfig get_peer_media_codec_config() {return peer_media_codec_config;}
+
+  void set_peer_media_qos_config(QosConfig &qos_config) {peer_media_qos_config = qos_config;}
+  QosConfig get_peer_media_qos_config() {return peer_media_qos_config;}
+
+  void set_peer_media_codec_qos_config(CodecQosConfig &codec_qos_config) {
+      peer_media_codec_qos_config = codec_qos_config;}
+  CodecQosConfig get_peer_media_codec_qos_config() {return peer_media_codec_qos_config;}
+
+  void set_peer_voice_rx_codec_config(CodecConfig &codec_config) {
+      peer_voice_rx_codec_config = codec_config;
+  }
+  CodecConfig get_peer_voice_rx_codec_config() {return peer_voice_rx_codec_config;}
+
+  void set_peer_voice_rx_qos_config(QosConfig &qos_config) {peer_voice_rx_qos_config = qos_config;}
+  QosConfig get_peer_voice_rx_qos_config() {return peer_voice_rx_qos_config;}
+
+  void set_peer_voice_rx_codec_qos_config(CodecQosConfig &codec_qos_config) {
+      peer_voice_rx_codec_qos_config = codec_qos_config;}
+  CodecQosConfig get_peer_voice_rx_codec_qos_config() {return peer_voice_rx_codec_qos_config;}
+
+  void set_peer_voice_tx_codec_config(CodecConfig &codec_config) {
+      peer_voice_tx_codec_config = codec_config;
+  }
+  CodecConfig get_peer_voice_tx_codec_config() {return peer_voice_tx_codec_config;}
+
+  void set_peer_voice_tx_qos_config(QosConfig &qos_config) {peer_voice_tx_qos_config = qos_config;}
+  QosConfig get_peer_voice_tx_qos_config() {return peer_voice_tx_qos_config;}
+
+  void set_peer_voice_tx_codec_qos_config(CodecQosConfig &codec_qos_config) {
+      peer_voice_tx_codec_qos_config = codec_qos_config;}
+  CodecQosConfig get_peer_voice_tx_codec_qos_config() {return peer_voice_tx_codec_qos_config;}
+
+  uint8_t SetId() const { return set_id_; }
+  uint8_t CigId() const { return cig_id_; }
+  uint8_t CisId() const { return cis_id_; }
+
+  BtifAcmStateMachine& StateMachine() { return state_machine_; }
+  const BtifAcmStateMachine& StateMachine() const { return state_machine_; }
+
+  bool IsConnected() const;
+  bool IsStreaming() const;
+
+  bool CheckConnUpdateMode(uint8_t mode) const {
+    return (conn_mode_ == mode);
+  }
+
+  void SetConnUpdateMode(uint8_t mode) {
+    if(conn_mode_ == mode) return;
+    if(mode == kFlagAggresiveMode) {
+      BTIF_TRACE_DEBUG("%s: push aggressive intervals", __func__);
+      L2CA_UpdateBleConnParams(peer_address_, 16, 32, 0, 1000);
+    } else if(mode == kFlagRelaxedMode) {
+      BTIF_TRACE_DEBUG("%s: push relaxed intervals", __func__);
+      L2CA_UpdateBleConnParams(peer_address_, 40, 56, 0, 1000);
+    }
+    conn_mode_ = mode;
+  }
+
+  void ClearConnUpdateMode() { conn_mode_ = 0; }
+
+  bool CheckFlags(uint8_t flags_mask) const {
+    return ((flags_ & flags_mask) != 0);
+  }
+
+  /**
+   * Set only the flags as specified by the flags mask.
+   *
+   * @param flags_mask the flags to set
+   */
+  void SetFlags(uint8_t flags_mask) { flags_ |= flags_mask; }
+
+  /**
+   * Clear only the flags as specified by the flags mask.
+   *
+   * @param flags_mask the flags to clear
+   */
+  void ClearFlags(uint8_t flags_mask) { flags_ &= ~flags_mask; }
+
+  /**
+   * Clear all the flags.
+   */
+  void ClearAllFlags() { flags_ = 0; }
+
+  /**
+   * Get string for the flags set.
+   */
+  std::string FlagsToString() const;
+
+ private:
+  const RawAddress peer_address_;
+  const uint8_t peer_sep_;// SEP type of peer device
+  uint8_t set_id_, cig_id_, cis_id_;
+  BtifAcmStateMachine state_machine_;
+  uint8_t flags_;
+  uint8_t conn_mode_;
+  bool is_stereohs_type_ = false;
+  StreamState voice_rx_state, voice_tx_state, music_tx_state, music_rx_state;
+  uint16_t peer_latency_;
+  uint16_t context_type_ = 0;
+  uint16_t profile_type_ = 0;
+  uint16_t rcfg_profile_type_ = 0;
+  uint16_t preferred_context_ = 0;
+  uint16_t stream_context_type_ = 0;
+  CodecConfig peer_media_codec_config, peer_voice_rx_codec_config, peer_voice_tx_codec_config;
+  QosConfig peer_media_qos_config, peer_voice_rx_qos_config, peer_voice_tx_qos_config;
+  CodecQosConfig peer_media_codec_qos_config, peer_voice_rx_codec_qos_config, peer_voice_tx_codec_qos_config;
+};
+
+static void btif_acm_check_and_cancel_lock_release_timer(uint8_t setId);
+bool btif_acm_request_csip_unlock(uint8_t setId);
+void btif_acm_process_request(tA2DP_CTRL_CMD cmd);
+
+void btif_acm_source_on_stopped();
+void btif_acm_source_on_suspended();
+void btif_acm_on_idle(void);
+bool btif_acm_check_if_requested_devices_stopped();
+
+void btif_acm_source_cleanup(void);
+
+bt_status_t btif_acm_source_setup_codec();
+uint16_t btif_acm_get_active_device_latency();
+
+class BtifAcmInitiator {
+ public:
+  static constexpr uint8_t kCigIdMin = 0;
+  static constexpr uint8_t kCigIdMax = BTA_ACM_NUM_CIGS;
+  static constexpr uint8_t kPeerMinSetId = BTA_ACM_MIN_NUM_SETID;
+  static constexpr uint8_t kPeerMaxSetId = BTA_ACM_MAX_NUM_SETID;
+
+  enum {
+    kFlagStatusUnknown = 0x0,
+    kFlagStatusUnlocked = 0x1,
+    kFlagStatusPendingLock = 0x2,
+    kFlagStatusSubsetLocked = 0x4,
+    kFlagStatusLocked = 0x8,
+    kFlagStatusPendingUnlock = 0x10,
+  };
+
+  // acm group procedure timer
+  static constexpr uint64_t kTimeoutAcmGroupProcedureMs = 10 * 1000;
+  static constexpr uint64_t kTimeoutConnIntervalMs = 5 * 1000;
+
+  BtifAcmInitiator()
+      : callbacks_(nullptr),
+        enabled_(false),
+        max_connected_peers_(kDefaultMaxConnectedAudioDevices),
+        music_active_setid_(INVALID_SET_ID),
+        voice_active_setid_(INVALID_SET_ID),
+        csip_app_id_(0),
+        is_csip_reg_(false),
+        lock_flags_(0),
+        music_set_lock_release_timer_(nullptr),
+        voice_set_lock_release_timer_(nullptr),
+        acm_group_procedure_timer_(nullptr),
+        acm_conn_interval_timer_(nullptr){}
+  ~BtifAcmInitiator();
+
+  bt_status_t Init(
+      btacm_initiator_callbacks_t* callbacks, int max_connected_audio_devices,
+      const std::vector<CodecConfig>& codec_priorities);
+  void Cleanup();
+  bool IsSetIdle(uint8_t setId) const;
+
+  btacm_initiator_callbacks_t* Callbacks() { return callbacks_; }
+  bool Enabled() const { return enabled_; }
+
+  BtifAcmPeer* FindPeer(const RawAddress& peer_address);
+  uint8_t FindPeerSetId(const RawAddress& peer_address);
+  uint8_t FindPeerBySetId(uint8_t set_id);
+  uint8_t FindPeerCigId(uint8_t set_id);
+  uint8_t FindPeerByCigId(uint8_t cig_id);
+  uint8_t FindPeerByCisId(uint8_t cig_id, uint8_t cis_id);
+  BtifAcmPeer* FindOrCreatePeer(const RawAddress& peer_address);
+  BtifAcmPeer* FindMusicActivePeer();
+
+  /**
+   * 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;
+  bool IsAcmIdle() const;
+
+  bool IsOtherSetPeersIdle(const RawAddress& peer_address, uint8_t setId) const;
+
+  alarm_t* MusicSetLockReleaseTimer() { return music_set_lock_release_timer_; }
+  alarm_t* VoiceSetLockReleaseTimer() { return voice_set_lock_release_timer_; }
+  alarm_t* AcmGroupProcedureTimer() { return acm_group_procedure_timer_; }
+  alarm_t* AcmConnIntervalTimer() { return acm_conn_interval_timer_; }
+
+  /**
+   * Delete a peer.
+   *
+   * @param peer_address of the peer to be deleted
+   * @return true on success, false on failure
+   */
+  bool DeletePeer(const RawAddress& peer_address);
+
+  /**
+   * Delete all peers that are in Idle state and can be deleted.
+   */
+  void DeleteIdlePeers();
+
+  /**
+   * Get the Music active peer.
+   *
+   * @return the music active peer
+   */
+  const RawAddress& MusicActivePeer() const { return music_active_peer_; }
+
+  /**
+   * Get the Voice active peer.
+   *
+   * @return the voice active peer
+   */
+  const RawAddress& VoiceActivePeer() const { return voice_active_peer_; }
+
+  uint8_t MusicActiveCSetId() const { return music_active_setid_; }
+  uint8_t VoiceActiveCSetId() const { return voice_active_setid_; }
+
+  void SetCsipAppId(uint8_t csip_app_id) { csip_app_id_ = csip_app_id; }
+  uint8_t GetCsipAppId() const { return csip_app_id_; }
+
+  void SetCsipRegistration(bool is_csip_reg) { is_csip_reg_ = is_csip_reg; }
+  bool IsCsipRegistered() const { return is_csip_reg_;}
+
+  void SetMusicActiveGroupStarted(bool flag) { is_music_active_set_started_ = flag; }
+  bool IsMusicActiveGroupStarted () { return is_music_active_set_started_; }
+
+  bool IsConnUpdateEnabled() const {
+    return (is_conn_update_enabled_ == true);
+  }
+
+  void SetOrUpdateGroupLockStatus(uint8_t set_id, int lock_status) {
+    std::map<uint8_t, int>::iterator p = set_lock_status_.find(set_id);
+    if (p == set_lock_status_.end()) {
+      set_lock_status_.insert(std::make_pair(set_id, lock_status));
+    } else {
+      set_lock_status_.erase(set_id);
+      set_lock_status_.insert(std::make_pair(set_id, lock_status));
+    }
+  }
+
+  int GetGroupLockStatus(uint8_t set_id) {
+    auto it = set_lock_status_.find(set_id);
+    if (it != set_lock_status_.end()) return it->second;
+    return kFlagStatusUnknown;
+  }
+
+  bool CheckLockFlags(uint8_t bitlockflags_mask) const {
+    return ((lock_flags_ & bitlockflags_mask) != 0);
+  }
+
+    /**
+     * Set only the flags as specified by the bitlockflags_mask.
+     *
+     * @param bitlockflags_mask the lock flags to set
+     */
+  void SetLockFlags(uint8_t bitlockflags_mask) { lock_flags_ |= bitlockflags_mask;}
+
+    /**
+     * Clear only the flags as specified by the bitlockflags_mask.
+     *
+     * @param bitlockflags_mask the lock flags to clear
+     */
+  void ClearLockFlags(uint8_t bitlockflags_mask) { lock_flags_ &= ~bitlockflags_mask;}
+
+    /**
+     * Clear all lock flags.
+     */
+  void ClearAllLockFlags() { lock_flags_ = 0;}
+
+    /**
+     * Get a string for lock flags.
+     */
+  std::string LockFlagsToString() const;
+
+  bool SetAcmActivePeer(const RawAddress& peer_address, uint16_t contextType, uint16_t profileType,
+                        std::promise<void> peer_ready_promise) {
+    LOG(INFO) << __PRETTY_FUNCTION__ << ": peer: " << peer_address
+           << " music_active_peer_: " << music_active_peer_ << " voice_active_peer_: " << voice_active_peer_;
+    uint16_t sink_latency;
+    active_bda = peer_address;// for stereo LEA active_bda = peer_address
+    BtifAcmPeer* peer = FindPeer(peer_address);
+    BTIF_TRACE_DEBUG("%s address byte BDA:%02x", __func__,active_bda.address[5]);
+    if (contextType == CONTEXT_TYPE_MUSIC) {
+      if (music_active_peer_ == active_bda) {
+        //Same active device, profileType may have changed.
+        if ((peer != nullptr) && (current_active_profile_type != 0) && (current_active_profile_type != profileType)) {
+          BTIF_TRACE_DEBUG("%s current_active_profile_type %d, profileType %d peer->GetProfileType() %d",
+                  __func__, current_active_profile_type, profileType, peer->GetProfileType());
+          if ((peer->GetProfileType() & profileType) == 0) {
+            std::unique_lock<std::mutex> guard(acm_session_wait_mutex_);
+            acm_session_wait = false;
+            if (reconfig_acm_initiator(peer_address, profileType)) {
+              acm_session_wait_cv.wait_for(guard, std::chrono::milliseconds(3000), []{return acm_session_wait;});
+              BTIF_TRACE_EVENT("%s: done with signal",__func__);
+            }
+          } else {
+            current_active_profile_type = profileType;
+            if (current_active_profile_type != WMCP)
+              current_active_config = current_media_config;
+            else
+              current_active_config = current_recording_config;
+            if (!btif_acm_source_restart_session(music_active_peer_, active_bda)) {
+              // cannot set promise but need to be handled within restart_session
+              return false;
+            }
+            if (current_active_profile_type == WMCP) {
+              sink_latency = btif_acm_get_active_device_latency();
+              BTIF_TRACE_EVENT("%s: sink_latency = %dms", __func__, sink_latency);
+              if ((sink_latency > 0) && !btif_acm_update_sink_latency_change(sink_latency * 10)) {
+                BTIF_TRACE_ERROR("%s: unable to update latency", __func__);
+              }
+            }
+          }
+          peer_ready_promise.set_value();
+          return true;
+        } else {
+          peer_ready_promise.set_value();
+          return true;
+        }
+      }
+
+      if (active_bda.IsEmpty()) {
+        BTIF_TRACE_EVENT("%s: set address is empty, shutdown the Acm initiator",
+                         __func__);
+        btif_acm_check_and_cancel_lock_release_timer(music_active_setid_);
+        if ((GetGroupLockStatus(music_active_setid_) == BtifAcmInitiator::kFlagStatusLocked) ||
+            (GetGroupLockStatus(music_active_setid_) == BtifAcmInitiator::kFlagStatusSubsetLocked)) {
+          if (!btif_acm_request_csip_unlock(music_active_setid_)) {
+            BTIF_TRACE_ERROR("%s: error unlocking", __func__);
+          }
+        }
+        btif_acm_source_end_session(music_active_peer_);
+        music_active_peer_ = active_bda;
+        current_active_profile_type = 0;
+        memset(&current_active_config, 0, sizeof(current_active_config));
+        peer_ready_promise.set_value();
+        return true;
+      }
+
+      btif_acm_check_and_cancel_lock_release_timer(music_active_setid_);
+      if ((GetGroupLockStatus(music_active_setid_) == BtifAcmInitiator::kFlagStatusLocked) ||
+          (GetGroupLockStatus(music_active_setid_) == BtifAcmInitiator::kFlagStatusSubsetLocked)) {
+        if (!btif_acm_request_csip_unlock(music_active_setid_)) {
+          BTIF_TRACE_ERROR("%s: error unlocking", __func__);
+        }
+      }
+
+      /*check if previous active device is streaming, then STOP it first*/
+      if (!music_active_peer_.IsEmpty()) {
+        int setid = music_active_setid_;
+        if (setid < INVALID_SET_ID) {
+          tBTA_CSIP_CSET cset_info;
+          memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+          cset_info = BTA_CsipGetCoordinatedSet(setid);
+          if (cset_info.size != 0) {
+            std::vector<RawAddress>::iterator itr;
+            BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+            if ((cset_info.set_members).size() > 0) {
+              for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+                BtifAcmPeer* grp_peer = FindPeer(*itr);
+                if (grp_peer != nullptr && grp_peer->IsStreaming()) {
+                  BTIF_TRACE_DEBUG("%s: peer is streaming %s ", __func__, grp_peer->PeerAddress().ToString().c_str());
+                  btif_acm_initiator_dispatch_sm_event(*itr, BTIF_ACM_STOP_STREAM_REQ_EVT);
+                }
+              }
+            }
+          }
+        } else {
+          BTIF_TRACE_DEBUG("%s: music_active_peer_ is twm device ", __func__);
+          BtifAcmPeer* twm_peer = FindPeer(music_active_peer_);
+          if (twm_peer != nullptr && twm_peer->IsStreaming()) {
+            BTIF_TRACE_DEBUG("%s: music_active_peer_ %s is streaming, send stop ", __func__, twm_peer->PeerAddress().ToString().c_str());
+            btif_acm_initiator_dispatch_sm_event(music_active_peer_, BTIF_ACM_STOP_STREAM_REQ_EVT);
+          }
+        }
+      }
+
+      if ((peer != nullptr) && ((peer->GetProfileType() & profileType) == 0)) {
+        BTIF_TRACE_DEBUG("%s peer.GetProfileType() %d, profileType %d", __func__, peer->GetProfileType(), profileType);
+        std::unique_lock<std::mutex> guard(acm_session_wait_mutex_);
+        acm_session_wait = false;
+        if (reconfig_acm_initiator(peer_address, profileType)) {
+          acm_session_wait_cv.wait_for(guard, std::chrono::milliseconds(3000), []{return acm_session_wait;});
+          BTIF_TRACE_EVENT("%s: done with signal",__func__);
+        }
+      } else {
+        current_active_profile_type = profileType;
+        if (current_active_profile_type != WMCP)
+          current_active_config = current_media_config;
+        else
+          current_active_config = current_recording_config;
+        if (!btif_acm_source_restart_session(music_active_peer_, active_bda)) {
+          // cannot set promise but need to be handled within restart_session
+          return false;
+        }
+      }
+      music_active_peer_ = active_bda;
+      if (active_bda.address[0] == 0x9E && active_bda.address[1] == 0x8B && active_bda.address[2] == 0x00) {
+        BTIF_TRACE_DEBUG("%s: get set ID from group BD address ", __func__);
+        music_active_setid_ = active_bda.address[5];
+      } else {
+        BTIF_TRACE_DEBUG("%s: get set ID from peer data ", __func__);
+        if (peer != nullptr)
+          music_active_setid_ = peer->SetId();
+      }
+
+      if (current_active_profile_type == WMCP) {
+        sink_latency = btif_acm_get_active_device_latency();
+        BTIF_TRACE_EVENT("%s: sink_latency = %dms", __func__, sink_latency);
+        if ((sink_latency > 0) && !btif_acm_update_sink_latency_change(sink_latency * 10)) {
+          BTIF_TRACE_ERROR("%s: unable to update latency", __func__);
+        }
+      }
+      peer_ready_promise.set_value();
+      return true;
+    } else if (contextType == CONTEXT_TYPE_VOICE) {
+      if (voice_active_peer_ == active_bda) {
+        peer_ready_promise.set_value();
+        return true;
+      }
+      if (active_bda.IsEmpty()) {
+        BTIF_TRACE_EVENT("%s: peer address is empty, shutdown the acm initiator",
+                         __func__);
+        voice_active_peer_ = active_bda;
+        peer_ready_promise.set_value();
+        return true;
+      }
+
+      /*check if previous active device is streaming, then STOP it first*/
+      if (!voice_active_peer_.IsEmpty()) {
+        int setid = voice_active_setid_;
+        if (setid < INVALID_SET_ID) {
+          tBTA_CSIP_CSET cset_info;
+          memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+          cset_info = BTA_CsipGetCoordinatedSet(setid);
+          if (cset_info.size != 0) {
+            std::vector<RawAddress>::iterator itr;
+            BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+            if ((cset_info.set_members).size() > 0) {
+              for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+                BtifAcmPeer* grp_peer = FindPeer(*itr);
+                if (grp_peer != nullptr && grp_peer->IsStreaming()) {
+                  BTIF_TRACE_DEBUG("%s: voice peer is streaming %s ", __func__, grp_peer->PeerAddress().ToString().c_str());
+                  btif_acm_initiator_dispatch_sm_event(*itr, BTIF_ACM_STOP_STREAM_REQ_EVT);
+                }
+              }
+            }
+          }
+        } else {
+          BTIF_TRACE_DEBUG("%s: voice_active_peer_ is twm device ", __func__);
+          BtifAcmPeer* twm_peer = FindPeer(voice_active_peer_);
+          if (twm_peer != nullptr && twm_peer->IsStreaming()) {
+            BTIF_TRACE_DEBUG("%s: voice_active_peer_ %s is streaming, send stop ", __func__, twm_peer->PeerAddress().ToString().c_str());
+            btif_acm_initiator_dispatch_sm_event(voice_active_peer_, BTIF_ACM_STOP_STREAM_REQ_EVT);
+          }
+        }
+      }
+
+      voice_active_peer_ = active_bda;
+      if (active_bda.address[0] == 0x9E && active_bda.address[1] == 0x8B && active_bda.address[2] == 0x00) {
+        BTIF_TRACE_DEBUG("%s: get set ID from group BD address ", __func__);
+        voice_active_setid_ = active_bda.address[5];
+      } else {
+        BTIF_TRACE_DEBUG("%s: get set ID from peer data ", __func__);
+        if (peer != nullptr)
+          voice_active_setid_ = peer->SetId();
+      }
+      peer_ready_promise.set_value();
+      return true;
+    } else {
+      peer_ready_promise.set_value();
+      return true;
+    }
+  }
+
+  void btif_acm_initiator_encoder_user_config_update_req(
+      const RawAddress& peer_addr,
+      const std::vector<CodecConfig>& codec_user_preferences,
+      std::promise<void> peer_ready_promise);
+
+
+  void UpdateCodecConfig(
+      const RawAddress& peer_address,
+      const std::vector<CodecConfig>& codec_preferences,
+      int contextType,
+      int profileType,
+      std::promise<void> peer_ready_promise) {
+    // Restart the session if the codec for the active peer is updated
+    if (!peer_address.IsEmpty() && music_active_peer_ == peer_address) {
+      btif_acm_source_end_session(music_active_peer_);
+    }
+
+    btif_acm_initiator_encoder_user_config_update_req(
+        peer_address, codec_preferences, std::move(peer_ready_promise));
+  }
+
+  const std::map<RawAddress, BtifAcmPeer*>& Peers() const { return peers_; }
+ // const std::map<uint8_t, BtifAcmPeer*>& SetPeers() const { return set_peers_; }
+
+  std::vector<RawAddress> locked_devices;
+ private:
+  void CleanupAllPeers();
+
+  btacm_initiator_callbacks_t* callbacks_;
+  bool enabled_;
+  int max_connected_peers_;
+
+  RawAddress music_active_peer_;
+  RawAddress voice_active_peer_;
+  uint8_t music_active_setid_;
+  uint8_t voice_active_setid_;
+  uint8_t music_active_set_locked_dev_count_;
+  uint8_t voice_active_set_locked_dev_count_;
+  bool is_music_active_set_started_;
+  bool is_voice_active_set_started_;
+  bool is_conn_update_enabled_;
+
+  uint8_t csip_app_id_;
+  bool is_csip_reg_;
+  uint8_t lock_flags_;
+
+  alarm_t* music_set_lock_release_timer_;
+  alarm_t* voice_set_lock_release_timer_;
+  alarm_t* acm_group_procedure_timer_;
+  alarm_t* acm_conn_interval_timer_;
+
+
+  std::map<RawAddress, BtifAcmPeer*> peers_;
+  std::map<RawAddress, uint8_t> addr_setid_pair;
+  std::map<uint8_t, uint8_t> set_cig_pair;//setid and cig id pair
+  std::map<RawAddress, std::map<uint8_t, uint8_t> > cig_cis_pair;//cig id and cis id pair
+  std::map<uint8_t, int> set_lock_status_;
+};
+
+
+/*****************************************************************************
+ *  Static variables
+ *****************************************************************************/
+static BtifAcmInitiator btif_acm_initiator;
+std::vector<CodecConfig> unicast_codecs_capabilities;
+static CodecConfig acm_local_capability =
+                           {CodecIndex::CODEC_INDEX_SOURCE_LC3,
+                            CodecPriority::CODEC_PRIORITY_DEFAULT,
+                            CodecSampleRate::CODEC_SAMPLE_RATE_48000,
+                            CodecBPS::CODEC_BITS_PER_SAMPLE_24,
+                            CodecChannelMode::CODEC_CHANNEL_MODE_STEREO, 0, 0, 0, 0};
+static CodecConfig default_config;
+static bool mandatory_codec_selected = false;
+static bt_status_t disconnect_acm_initiator(const RawAddress& peer_address,
+                                            uint16_t contextType);
+
+static bt_status_t start_stream_acm_initiator(const RawAddress& peer_address,
+                                              uint16_t contextType);
+static bt_status_t stop_stream_acm_initiator(const RawAddress& peer_address,
+                                             uint16_t contextType);
+
+static void btif_acm_handle_csip_status_locked(std::vector<RawAddress> addr, uint8_t setId);
+
+static void btif_acm_handle_evt(uint16_t event, char* p_param);
+static void btif_report_connection_state(const RawAddress& peer_address,
+                                         btacm_connection_state_t state, uint16_t contextType);
+static void btif_report_audio_state(const RawAddress& peer_address,
+                                    btacm_audio_state_t state, uint16_t contextType);
+
+static void btif_acm_check_and_start_lock_release_timer(uint8_t setId);
+
+static void btif_acm_initiator_lock_release_timer_timeout(void* data);
+
+static void btif_acm_check_and_start_group_procedure_timer(uint8_t setId);
+static void btif_acm_check_and_start_conn_Interval_timer(BtifAcmPeer* peer);
+static void btif_acm_initiator_conn_Interval_timer_timeout(void *data);
+static void btif_acm_check_and_cancel_conn_Interval_timer();
+
+
+static void btif_acm_check_and_cancel_group_procedure_timer(uint8_t setId);
+static void btif_acm_initiator_group_procedure_timer_timeout(void *data);
+static void SelectCodecQosConfig(const RawAddress& bd_addr, int profile_type,
+                                 int context_type, int direction, int config_type);
+bool compare_codec_config_(CodecConfig &first, CodecConfig &second);
+void print_codec_parameters(CodecConfig config);
+void print_qos_parameters(QosConfig qos_config);
+void select_best_codec_config(const RawAddress& bd_addr, uint16_t context_type,
+                              uint8_t profile_type, CodecConfig *codec_config, int dir, int config_type);
+static UcastClientInterface* sUcastClientInterface = nullptr;
+
+/*****************************************************************************
+ * Local helper functions
+ *****************************************************************************/
+
+const char* dump_acm_sm_event_name(btif_acm_sm_event_t event) {
+  switch ((int)event) {
+    CASE_RETURN_STR(BTA_ACM_DISCONNECT_EVT)
+    CASE_RETURN_STR(BTA_ACM_CONNECT_EVT)
+    CASE_RETURN_STR(BTA_ACM_START_EVT)
+    CASE_RETURN_STR(BTA_ACM_STOP_EVT)
+    CASE_RETURN_STR(BTA_ACM_RECONFIG_EVT)
+    CASE_RETURN_STR(BTA_ACM_CONFIG_EVT)
+    CASE_RETURN_STR(BTIF_ACM_CONNECT_REQ_EVT)
+    CASE_RETURN_STR(BTIF_ACM_DISCONNECT_REQ_EVT)
+    CASE_RETURN_STR(BTIF_ACM_START_STREAM_REQ_EVT)
+    CASE_RETURN_STR(BTIF_ACM_STOP_STREAM_REQ_EVT)
+    CASE_RETURN_STR(BTIF_ACM_SUSPEND_STREAM_REQ_EVT)
+    CASE_RETURN_STR(BTIF_ACM_RECONFIG_REQ_EVT)
+    CASE_RETURN_STR(BTA_ACM_CONN_UPDATE_TIMEOUT_EVT)
+    default:
+      return "UNKNOWN_EVENT";
+  }
+}
+
+const char* dump_csip_event_name(btif_csip_sm_event_t event) {
+  switch ((int)event) {
+    CASE_RETURN_STR(BTA_CSIP_NEW_SET_FOUND_EVT)
+    CASE_RETURN_STR(BTA_CSIP_SET_MEMBER_FOUND_EVT)
+    CASE_RETURN_STR(BTA_CSIP_CONN_STATE_CHG_EVT)
+    CASE_RETURN_STR(BTA_CSIP_LOCK_STATUS_CHANGED_EVT)
+    CASE_RETURN_STR(BTA_CSIP_LOCK_AVAILABLE_EVT)
+    CASE_RETURN_STR(BTA_CSIP_SET_SIZE_CHANGED)
+    CASE_RETURN_STR(BTA_CSIP_SET_SIRK_CHANGED)
+    default:
+      return "UNKNOWN_EVENT";
+  }
+}
+
+void btif_acm_signal_session_ready() {
+  std::unique_lock<std::mutex> guard(acm_session_wait_mutex_);
+  if(!acm_session_wait) {
+    acm_session_wait = true;
+    acm_session_wait_cv.notify_all();
+  } else {
+    BTIF_TRACE_WARNING("%s: already signalled ",__func__);
+  }
+}
+
+void fetch_media_tx_codec_qos_config(const RawAddress& bd_addr, int profile_type, StreamConnect *conn_media) {
+    BTIF_TRACE_DEBUG("%s: Peer %s , profile_type: %d", __func__, bd_addr.ToString().c_str(), profile_type);
+    CodecQosConfig conf;
+    BtifAcmPeer* peer = btif_acm_initiator.FindPeer(bd_addr);
+    if (peer == nullptr) {
+      BTIF_TRACE_WARNING("%s: peer is NULL", __func__);
+      return;
+    }
+    if (peer->IsStereoHsType()) {
+      //Stereo HS config 1
+      SelectCodecQosConfig(peer->PeerAddress(), profile_type, MEDIA_CONTEXT, SNK, STEREO_HS_CONFIG_1);
+      conf = peer->get_peer_media_codec_qos_config();
+      print_codec_parameters(conf.codec_config);
+      print_qos_parameters(conf.qos_config);
+      conn_media->codec_qos_config_pair.push_back(conf);
+    } else {
+      //EB config
+      SelectCodecQosConfig(peer->PeerAddress(), profile_type, MEDIA_CONTEXT, SNK, EB_CONFIG);
+      conf = peer->get_peer_media_codec_qos_config();
+      print_codec_parameters(conf.codec_config);
+      print_qos_parameters(conf.qos_config);
+      conn_media->codec_qos_config_pair.push_back(conf);
+    }
+    conn_media->stream_type.type = CONTENT_TYPE_MEDIA;
+    conn_media->stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    conn_media->stream_type.direction = ASE_DIRECTION_SINK;
+}
+
+void fetch_media_rx_codec_qos_config(const RawAddress& bd_addr, int profile_type, StreamConnect *conn_media) {
+    BTIF_TRACE_DEBUG("%s: Peer %s , profile_type: %d", __func__, bd_addr.ToString().c_str(), profile_type);
+    CodecQosConfig conf;
+    BtifAcmPeer* peer = btif_acm_initiator.FindPeer(bd_addr);
+    if (peer == nullptr) {
+      BTIF_TRACE_WARNING("%s: peer is NULL", __func__);
+      return;
+    }
+    if (peer->IsStereoHsType()) {
+      //Stereo HS config 1
+      SelectCodecQosConfig(peer->PeerAddress(), WMCP, MEDIA_CONTEXT, SRC, STEREO_HS_CONFIG_1);
+      conf = peer->get_peer_media_codec_qos_config();
+      print_codec_parameters(conf.codec_config);
+      print_qos_parameters(conf.qos_config);
+      conn_media->codec_qos_config_pair.push_back(conf);
+    } else {
+      //EB config
+      SelectCodecQosConfig(peer->PeerAddress(), WMCP, MEDIA_CONTEXT, SRC, EB_CONFIG);
+      conf = peer->get_peer_media_codec_qos_config();
+      print_codec_parameters(conf.codec_config);
+      print_qos_parameters(conf.qos_config);
+      conn_media->codec_qos_config_pair.push_back(conf);
+    }
+    conn_media->stream_type.type = CONTENT_TYPE_MEDIA;
+    conn_media->stream_type.audio_context = CONTENT_TYPE_LIVE; //Live audio context
+    conn_media->stream_type.direction = ASE_DIRECTION_SRC;
+}
+
+void fetch_voice_rx_codec_qos_config(const RawAddress& bd_addr, int profile_type, StreamConnect *conn_voice) {
+    BTIF_TRACE_DEBUG("%s: Peer %s , profile_type: %d", __func__, bd_addr.ToString().c_str(), profile_type);
+    CodecQosConfig conf;
+    BtifAcmPeer* peer = btif_acm_initiator.FindPeer(bd_addr);
+    if (peer == nullptr) {
+      BTIF_TRACE_WARNING("%s: peer is NULL", __func__);
+      return;
+    }
+    if (peer->IsStereoHsType()) {
+      //Stereo HS config 1
+      SelectCodecQosConfig(peer->PeerAddress(), BAP, VOICE_CONTEXT, SRC, STEREO_HS_CONFIG_1);
+      conf = peer->get_peer_voice_rx_codec_qos_config();
+      print_codec_parameters(conf.codec_config);
+      print_qos_parameters(conf.qos_config);
+      conn_voice->codec_qos_config_pair.push_back(conf);
+    } else {
+      // EB config
+      SelectCodecQosConfig(peer->PeerAddress(), BAP, VOICE_CONTEXT, SRC, EB_CONFIG);
+      conf = peer->get_peer_voice_rx_codec_qos_config();
+      print_codec_parameters(conf.codec_config);
+      print_qos_parameters(conf.qos_config);
+      conn_voice->codec_qos_config_pair.push_back(conf);
+    }
+    conn_voice->stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    conn_voice->stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    conn_voice->stream_type.direction = ASE_DIRECTION_SRC;
+}
+
+void fetch_voice_tx_codec_qos_config(const RawAddress& bd_addr, int profile_type, StreamConnect *conn_voice) {
+    BTIF_TRACE_DEBUG("%s: Peer %s , profile_type: %d", __func__, bd_addr.ToString().c_str(), profile_type);
+    CodecQosConfig conf;
+    BtifAcmPeer* peer = btif_acm_initiator.FindPeer(bd_addr);
+    if (peer == nullptr) {
+      BTIF_TRACE_WARNING("%s: peer is NULL", __func__);
+      return;
+    }
+    if (peer->IsStereoHsType()) {
+      //Stereo HS config 1
+      SelectCodecQosConfig(peer->PeerAddress(), BAP, VOICE_CONTEXT, SNK, STEREO_HS_CONFIG_1);
+      conf = peer->get_peer_voice_tx_codec_qos_config();
+      print_codec_parameters(conf.codec_config);
+      print_qos_parameters(conf.qos_config);
+      conn_voice->codec_qos_config_pair.push_back(conf);
+    } else {
+      // EB config
+      SelectCodecQosConfig(peer->PeerAddress(), BAP, VOICE_CONTEXT, SNK, EB_CONFIG);
+      conf = peer->get_peer_voice_tx_codec_qos_config();
+      print_codec_parameters(conf.codec_config);
+      print_qos_parameters(conf.qos_config);
+      conn_voice->codec_qos_config_pair.push_back(conf);
+    }
+    conn_voice->stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    conn_voice->stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    conn_voice->stream_type.direction = ASE_DIRECTION_SINK;
+}
+
+BtifAcmEvent::BtifAcmEvent(uint32_t event, const void* p_data, size_t data_length)
+    : event_(event), data_(nullptr), data_length_(0) {
+  DeepCopy(event, p_data, data_length);
+}
+
+BtifAcmEvent::BtifAcmEvent(const BtifAcmEvent& other)
+    : event_(0), data_(nullptr), data_length_(0) {
+  *this = other;
+}
+
+BtifAcmEvent& BtifAcmEvent::operator=(const BtifAcmEvent& other) {
+  DeepFree();
+  DeepCopy(other.Event(), other.Data(), other.DataLength());
+  return *this;
+}
+
+BtifAcmEvent::~BtifAcmEvent() { DeepFree(); }
+
+std::string BtifAcmEvent::ToString() const {
+  return BtifAcmEvent::EventName(event_);
+}
+
+std::string BtifAcmEvent::EventName(uint32_t event) {
+  std::string name = dump_acm_sm_event_name((btif_acm_sm_event_t)event);
+  std::stringstream ss_value;
+  ss_value << "(0x" << std::hex << event << ")";
+  return name + ss_value.str();
+}
+
+void BtifAcmEvent::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 {
+    data_ = osi_malloc(data_length_);
+    memcpy(data_, p_data, data_length);
+  }
+}
+
+void BtifAcmEvent::DeepFree() {
+  osi_free_and_reset((void**)&data_);
+  data_length_ = 0;
+}
+
+BtifCsipEvent::BtifCsipEvent(uint32_t event, const void* p_data, size_t data_length)
+    : event_(event), data_(nullptr), data_length_(0) {
+  DeepCopy(event, p_data, data_length);
+}
+
+BtifCsipEvent::BtifCsipEvent(const BtifCsipEvent& other)
+    : event_(0), data_(nullptr), data_length_(0) {
+  *this = other;
+}
+
+BtifCsipEvent& BtifCsipEvent::operator=(const BtifCsipEvent& other) {
+  DeepFree();
+  DeepCopy(other.Event(), other.Data(), other.DataLength());
+  return *this;
+}
+
+BtifCsipEvent::~BtifCsipEvent() { DeepFree(); }
+
+std::string BtifCsipEvent::ToString() const {
+  return BtifCsipEvent::EventName(event_);
+}
+
+std::string BtifCsipEvent::EventName(uint32_t event) {
+  std::string name = dump_csip_event_name((btif_csip_sm_event_t)event);
+  std::stringstream ss_value;
+  ss_value << "(0x" << std::hex << event << ")";
+  return name + ss_value.str();
+}
+
+void BtifCsipEvent::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 {
+    data_ = osi_malloc(data_length_);
+    memcpy(data_, p_data, data_length);
+  }
+}
+
+void BtifCsipEvent::DeepFree() {
+  osi_free_and_reset((void**)&data_);
+  data_length_ = 0;
+}
+
+BtifAcmPeer::BtifAcmPeer(const RawAddress& peer_address, uint8_t peer_sep,
+                         uint8_t set_id, uint8_t cig_id, uint8_t cis_id)
+    : peer_address_(peer_address),
+      peer_sep_(peer_sep),
+      set_id_(set_id),
+      cig_id_(cig_id),
+      cis_id_(cis_id),
+      state_machine_(*this),
+      flags_(0) {}
+
+BtifAcmPeer::~BtifAcmPeer() { /*alarm_free(av_open_on_rc_timer_);*/ }
+
+std::string BtifAcmPeer::FlagsToString() const {
+  std::string result;
+
+  if (flags_ & BtifAcmPeer::kFlagPendingLocalSuspend) {
+    if (!result.empty()) result += "|";
+    result += "LOCAL_SUSPEND_PENDING";
+  }
+  if (flags_ & BtifAcmPeer::kFlagPendingReconfigure) {
+    if (!result.empty()) result += "|";
+    result += "PENDING_RECONFIGURE";
+  }
+  if (flags_ & BtifAcmPeer::kFlagPendingStart) {
+    if (!result.empty()) result += "|";
+    result += "PENDING_START";
+  }
+  if (flags_ & BtifAcmPeer::kFlagPendingStop) {
+    if (!result.empty()) result += "|";
+    result += "PENDING_STOP";
+  }
+  if (flags_ & BtifAcmPeer::kFLagPendingStartAfterReconfig) {
+    if (!result.empty()) result += "|";
+    result += "PENDING_START_AFTER_RECONFIG";
+  }
+  if (result.empty()) result = "None";
+
+  return base::StringPrintf("0x%x(%s)", flags_, result.c_str());
+}
+
+bt_status_t BtifAcmPeer::Init() {
+  state_machine_.Start();
+  return BT_STATUS_SUCCESS;
+}
+
+void BtifAcmPeer::Cleanup() {
+  state_machine_.Quit();
+}
+
+bool BtifAcmPeer::CanBeDeleted() const {
+  return (
+      (state_machine_.StateId() == BtifAcmStateMachine::kStateIdle) &&
+      (state_machine_.PreviousStateId() != BtifAcmStateMachine::kStateInvalid));
+}
+
+const RawAddress& BtifAcmPeer::MusicActivePeerAddress() const {
+  return btif_acm_initiator.MusicActivePeer();
+}
+const RawAddress& BtifAcmPeer::VoiceActivePeerAddress() const {
+  return btif_acm_initiator.VoiceActivePeer();
+}
+uint8_t BtifAcmPeer::MusicActiveSetId() const {
+  return btif_acm_initiator.MusicActiveCSetId();
+}
+uint8_t BtifAcmPeer::VoiceActiveSetId() const {
+  return btif_acm_initiator.VoiceActiveCSetId();
+}
+
+bool BtifAcmPeer::IsConnected() const {
+  int state = state_machine_.StateId();
+  return ((state == BtifAcmStateMachine::kStateOpened) ||
+          (state == BtifAcmStateMachine::kStateStarted));
+}
+
+bool BtifAcmPeer::IsStreaming() const {
+  int state = state_machine_.StateId();
+  return (state == BtifAcmStateMachine::kStateStarted);
+}
+
+BtifAcmInitiator::~BtifAcmInitiator() {
+  CleanupAllPeers();
+}
+
+void init_local_capabilities() {
+  unicast_codecs_capabilities.push_back(acm_local_capability);
+}
+
+void BtifAcmInitiator::Cleanup() {
+  LOG_INFO(LOG_TAG, "%s", __PRETTY_FUNCTION__);
+  if (!enabled_) return;
+  std::promise<void> peer_ready_promise;
+  btif_disable_service(BTA_ACM_INITIATOR_SERVICE_ID); // ACM deregistration required?
+  CleanupAllPeers();
+  alarm_free(music_set_lock_release_timer_);
+  music_set_lock_release_timer_ = nullptr;
+  alarm_free(music_set_lock_release_timer_);
+  music_set_lock_release_timer_ = nullptr;
+  alarm_free(acm_group_procedure_timer_);
+  acm_group_procedure_timer_ = nullptr;
+  alarm_free(acm_conn_interval_timer_);
+  acm_conn_interval_timer_ = nullptr;
+  callbacks_ = nullptr;
+  enabled_ = false;
+  if (sUcastClientInterface != nullptr) {
+    sUcastClientInterface->Cleanup();
+    sUcastClientInterface = nullptr;
+  }
+}
+
+BtifAcmPeer* BtifAcmInitiator::FindPeer(const RawAddress& peer_address) {
+  auto it = peers_.find(peer_address);
+  if (it != peers_.end()) return it->second;
+  return nullptr;
+}
+
+uint8_t BtifAcmInitiator:: FindPeerSetId(const RawAddress& peer_address) {
+    auto it = addr_setid_pair.find(peer_address);
+    if (it != addr_setid_pair.end()) return it->second;
+    return 0xff;
+}
+
+uint8_t BtifAcmInitiator:: FindPeerBySetId(uint8_t setid) {
+  for (auto it : addr_setid_pair) {
+    if (it.second == setid) {
+      return setid;
+    }
+  }
+  return 0xff;
+}
+
+uint8_t BtifAcmInitiator:: FindPeerCigId(uint8_t setid) {
+    auto it = set_cig_pair.find(setid);
+    if (it != set_cig_pair.end()) return it->second;
+    return 0xff;
+}
+
+uint8_t BtifAcmInitiator:: FindPeerByCigId(uint8_t cigid) {
+  for (auto it : set_cig_pair) {
+    if (it.second == cigid) {
+      return cigid;
+    }
+  }
+  return 0xff;
+}
+
+uint8_t BtifAcmInitiator:: FindPeerByCisId(uint8_t cigid, uint8_t cisid) {
+  for (auto itr = cig_cis_pair.begin(); itr != cig_cis_pair.end(); itr++) {
+    for (auto ptr = itr->second.begin(); ptr != itr->second.end(); ptr++) {
+      if (ptr->first == cigid) {
+        if (ptr->second == cisid) {
+          return cisid;
+        }
+      }
+    }
+  }
+  return 0xff;
+}
+
+BtifAcmPeer* BtifAcmInitiator::FindOrCreatePeer(const RawAddress& peer_address) {
+  BTIF_TRACE_DEBUG("%s: peer_address=%s ", __PRETTY_FUNCTION__,
+                   peer_address.ToString().c_str());
+
+  BtifAcmPeer* peer = FindPeer(peer_address);
+  if (peer != nullptr) return peer;
+
+  uint8_t SetId, CigId, CisId;
+  //get the set id from CSIP.
+  //TODO: need UUID ?
+  Uuid uuid = Uuid::kEmpty;
+  LOG_INFO(LOG_TAG, "%s ACM UUID = %s", __func__, uuid.ToString().c_str());
+  SetId = BTA_CsipGetDeviceSetId(peer_address, uuid);
+  BTIF_TRACE_EVENT("%s: set id from csip : %d", __func__, SetId);
+  if (SetId == INVALID_SET_ID) {
+    SetId = FindPeerSetId(peer_address);
+    // Find next available SET ID to use
+    if (SetId == 0xff) {
+      for (SetId = kPeerMinSetId; SetId < kPeerMaxSetId; SetId++) {
+        if (FindPeerBySetId(SetId) == 0xff) break;
+      }
+    }
+  }
+  if (SetId == kPeerMaxSetId) {
+    BTIF_TRACE_ERROR(
+        "%s: Cannot create peer for peer_address=%s : "
+        "cannot allocate unique SET ID",
+        __PRETTY_FUNCTION__, peer_address.ToString().c_str());
+    return nullptr;
+  }
+  addr_setid_pair.insert(std::make_pair(peer_address, SetId));
+
+  //Find next available CIG ID to use
+  CigId = FindPeerCigId(SetId);
+  if (CigId == 0xff) {
+    for (CigId = kCigIdMin; CigId < kCigIdMax; ) {
+      if (FindPeerByCigId(CigId) == 0xff) break;
+      CigId += 4;
+    }
+  }
+  if (CigId == kCigIdMax) {
+    BTIF_TRACE_ERROR(
+        "%s: cannot allocate unique CIG ID to = %s ",
+        __func__, peer_address.ToString().c_str());
+    return nullptr;
+  }
+  set_cig_pair.insert(std::make_pair(SetId, CigId));
+
+  //Find next available CIS ID to use
+  for (CisId = kCigIdMin; CisId < kCigIdMax; CisId++) {
+    if (FindPeerByCisId(CigId, CisId) == 0xff) break;
+  }
+  if (CisId == kCigIdMax) {
+    BTIF_TRACE_ERROR(
+        "%s: cannot allocate unique CIS ID to = %s ",
+        __func__, peer_address.ToString().c_str());
+    return nullptr;
+  }
+  cig_cis_pair.insert(std::make_pair(peer_address, map<uint8_t, uint8_t>()));
+  cig_cis_pair[peer_address].insert(std::make_pair(CigId, CisId));
+
+  LOG_INFO(LOG_TAG,
+           "%s: Create peer: peer_address=%s, set_id=%d, cig_id=%d, cis_id=%d",
+           __PRETTY_FUNCTION__, peer_address.ToString().c_str(), SetId, CigId, CisId);
+  peer = new BtifAcmPeer(peer_address, ACM_TSEP_SNK, SetId, CigId, CisId);
+  peer->SetPeerVoiceTxState(StreamState::DISCONNECTED);
+  peer->SetPeerVoiceRxState(StreamState::DISCONNECTED);
+  peer->SetPeerMusicTxState(StreamState::DISCONNECTED);
+  peer->SetPeerMusicRxState(StreamState::DISCONNECTED);
+  if (SetId >= kPeerMinSetId && SetId < kPeerMaxSetId) {
+    LOG_INFO(LOG_TAG,
+             "%s: Created peer is TWM device",__PRETTY_FUNCTION__);
+    peer->SetIsStereoHsType(true);
+  }
+  peers_.insert(std::make_pair(peer_address, peer));
+  peer->Init();
+  return peer;
+}
+
+BtifAcmPeer* BtifAcmInitiator::FindMusicActivePeer() {
+  for (auto it : peers_) {
+    BtifAcmPeer* peer = it.second;
+    if (peer->IsPeerActiveForMusic()) {
+      return peer;
+    }
+  }
+  return nullptr;
+}
+
+bool BtifAcmInitiator::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 BtifAcmPeer* peer = it.second;
+    switch (peer->StateMachine().StateId()) {
+      case BtifAcmStateMachine::kStateOpening:
+      case BtifAcmStateMachine::kStateOpened:
+      case BtifAcmStateMachine::kStateStarted:
+      case BtifAcmStateMachine::kStateReconfiguring:
+        if (peer->PeerAddress() == peer_address) {
+          return true;  // Already connected or accounted for
+        }
+        connected++;
+        break;
+      default:
+        break;
+    }
+  }
+  return (connected < max_connected_peers_);
+}
+
+bool BtifAcmInitiator::IsAcmIdle() const {
+  int connected = 0;
+
+  // Count peers that are in the process of connecting or already connected
+  for (auto it : peers_) {
+    const BtifAcmPeer* peer = it.second;
+    switch (peer->StateMachine().StateId()) {
+      case BtifAcmStateMachine::kStateOpening:
+      case BtifAcmStateMachine::kStateOpened:
+      case BtifAcmStateMachine::kStateStarted:
+      case BtifAcmStateMachine::kStateReconfiguring:
+      case BtifAcmStateMachine::kStateClosing:
+        connected++;
+        break;
+      default:
+        break;
+    }
+  }
+  return (connected == 0);
+}
+
+bool BtifAcmInitiator::IsSetIdle(uint8_t setId) const {
+  int connected = 0;
+  tBTA_CSIP_CSET cset_info = BTA_CsipGetCoordinatedSet(setId);
+  std::vector<RawAddress>::iterator itr;
+  if ((cset_info.set_members).size() > 0) {
+    for (itr = (cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+      BtifAcmPeer* peer = btif_acm_initiator.FindPeer(*itr);
+      switch (peer->StateMachine().StateId()) {
+        case BtifAcmStateMachine::kStateOpening:
+        case BtifAcmStateMachine::kStateOpened:
+        case BtifAcmStateMachine::kStateStarted:
+        case BtifAcmStateMachine::kStateReconfiguring:
+        case BtifAcmStateMachine::kStateClosing:
+          connected++;
+          break;
+        default:
+          break;
+      }
+    }
+  }
+  return (connected == 0);
+}
+
+bool BtifAcmInitiator::IsOtherSetPeersIdle(const RawAddress& peer_address, uint8_t setId) const {
+  int connected = 0;
+  tBTA_CSIP_CSET cset_info = BTA_CsipGetCoordinatedSet(setId);
+  std::vector<RawAddress>::iterator itr;
+  if ((cset_info.set_members).size() > 0) {
+    for (itr = (cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+      if (*itr  == peer_address) continue;
+      BtifAcmPeer* peer = btif_acm_initiator.FindPeer(*itr);
+      if (peer == nullptr) continue;
+      switch (peer->StateMachine().StateId()) {
+        case BtifAcmStateMachine::kStateOpening:
+        case BtifAcmStateMachine::kStateOpened:
+        case BtifAcmStateMachine::kStateStarted:
+        case BtifAcmStateMachine::kStateReconfiguring:
+        case BtifAcmStateMachine::kStateClosing:
+          connected++;
+          break;
+        default:
+          break;
+      }
+    }
+  }
+  return (connected == 0);
+}
+
+bool BtifAcmInitiator::DeletePeer(const RawAddress& peer_address) {
+  auto it = peers_.find(peer_address);
+  if (it == peers_.end()) return false;
+  BtifAcmPeer* peer = it->second;
+  for (auto itr = addr_setid_pair.begin(); itr != addr_setid_pair.end(); ++itr) {
+    if (itr->second == peer->SetId()) {
+      addr_setid_pair.erase(itr);
+      break;
+    }
+  }
+  for (auto itr = set_cig_pair.begin(); itr != set_cig_pair.end(); ++itr) {
+    if (itr->second == peer->SetId()) {
+      set_cig_pair.erase(itr);
+      break;
+    }
+  }
+  bool found = false;
+  for (auto itr = cig_cis_pair.begin(); itr != cig_cis_pair.end(); itr++) {
+    for (auto ptr = itr->second.begin(); ptr != itr->second.end(); ptr++) {
+      if (ptr->first == peer->CigId()) {
+        if (ptr->second == peer->CisId()) {
+          cig_cis_pair.erase(itr);
+          found = true;
+          break;
+        }
+      }
+    }
+    if (found)
+      break;
+  }
+  peer->Cleanup();
+  peers_.erase(it);
+  delete peer;
+  return true;
+}
+
+void BtifAcmInitiator::DeleteIdlePeers() {
+  for (auto it = peers_.begin(); it != peers_.end();) {
+    BtifAcmPeer* peer = it->second;
+    auto prev_it = it++;
+    if (!peer->CanBeDeleted()) continue;
+    LOG_INFO(LOG_TAG, "%s: Deleting idle peer: %s ", __func__,
+             peer->PeerAddress().ToString().c_str());
+    for (auto itr = addr_setid_pair.begin(); itr != addr_setid_pair.end(); ++itr) {
+      if (itr->second == peer->SetId()) {
+        addr_setid_pair.erase(itr);
+        break;
+      }
+    }
+    for (auto itr = set_cig_pair.begin(); itr != set_cig_pair.end(); ++itr) {
+      if (itr->second == peer->SetId()) {
+        set_cig_pair.erase(itr);
+        break;
+      }
+    }
+    bool found = false;
+    for (auto itr = cig_cis_pair.begin(); itr != cig_cis_pair.end(); itr++) {
+      for (auto ptr = itr->second.begin(); ptr != itr->second.end(); ptr++) {
+        if (ptr->first == peer->CigId()) {
+          if (ptr->second == peer->CisId()) {
+            cig_cis_pair.erase(itr);
+            found = true;
+            break;
+          }
+        }
+      }
+      if (found)
+        break;
+    }
+    peer->Cleanup();
+    peers_.erase(prev_it);
+    delete peer;
+  }
+}
+
+void BtifAcmInitiator::CleanupAllPeers() {
+  while (!peers_.empty()) {
+    auto it = peers_.begin();
+    BtifAcmPeer* peer = it->second;
+    for (auto itr = addr_setid_pair.begin(); itr != addr_setid_pair.end(); ++itr) {
+      if (itr->second == peer->SetId()) {
+        addr_setid_pair.erase(itr);
+        break;
+      }
+    }
+    for (auto itr = set_cig_pair.begin(); itr != set_cig_pair.end(); ++itr) {
+      if (itr->second == peer->SetId()) {
+        set_cig_pair.erase(itr);
+        break;
+      }
+    }
+    bool found = false;
+    for (auto itr = cig_cis_pair.begin(); itr != cig_cis_pair.end(); itr++) {
+      for (auto ptr = itr->second.begin(); ptr != itr->second.end(); ptr++) {
+        if (ptr->first == peer->CigId()) {
+          if (ptr->second == peer->CisId()) {
+            cig_cis_pair.erase(itr);
+            found = true;
+            break;
+          }
+        }
+      }
+      if (found)
+        break;
+    }
+    peer->Cleanup();
+    peers_.erase(it);
+    delete peer;
+  }
+}
+
+class UcastClientCallbacksImpl : public UcastClientCallbacks {
+ public:
+  ~UcastClientCallbacksImpl() = default;
+  void OnStreamState(const RawAddress& address,
+                     std::vector<StreamStateInfo> streams_state_info) override {
+    LOG(INFO) << __func__;
+    BtifAcmPeer* peer = btif_acm_initiator.FindPeer(address);
+    if (peer == nullptr) {
+      BTIF_TRACE_DEBUG("%s: Peer is NULL", __PRETTY_FUNCTION__);
+    }
+    for (auto it = streams_state_info.begin(); it != streams_state_info.end(); ++it) {
+      LOG(WARNING) << __func__ << ": address: " << address;
+      LOG(WARNING) << __func__ << ": stream type:    "
+                   << GetStreamType(it->stream_type.type);
+      LOG(WARNING) << __func__ << ": stream context: "
+                   << GetStreamType(it->stream_type.audio_context);
+      LOG(WARNING) << __func__ << ": stream dir:     "
+                   << GetStreamDirection(it->stream_type.direction);
+      LOG(WARNING) << __func__ << ": stream state:   "
+                   << GetStreamState(static_cast<int> (it->stream_state));
+      switch (it->stream_state) {
+        case StreamState::DISCONNECTED:
+        case StreamState::DISCONNECTING: {
+          tBTA_ACM_STATE_INFO data = {.bd_addr = address, .stream_type = it->stream_type,
+                                      .stream_state = it->stream_state, .reason = it->reason};
+          btif_acm_handle_evt(BTA_ACM_DISCONNECT_EVT, (char*)&data);
+        } break;
+
+        case StreamState::CONNECTING:
+        case StreamState::CONNECTED: {
+          tBTA_ACM_STATE_INFO data = {.bd_addr = address, .stream_type = it->stream_type,
+                                      .stream_state = it->stream_state, .reason = it->reason};
+          btif_acm_handle_evt(BTA_ACM_CONNECT_EVT, (char*)&data);
+        } break;
+
+        case StreamState::STARTING:
+        case StreamState::STREAMING: {
+          tBTA_ACM_STATE_INFO data = {.bd_addr = address, .stream_type = it->stream_type,
+                                      .stream_state = it->stream_state, .reason = it->reason};
+          btif_acm_handle_evt(BTA_ACM_START_EVT, (char*)&data);
+        } break;
+
+        case StreamState::STOPPING: {
+          tBTA_ACM_STATE_INFO data = {.bd_addr = address, .stream_type = it->stream_type,
+                                      .stream_state = it->stream_state, .reason = it->reason};
+          btif_acm_handle_evt(BTA_ACM_STOP_EVT, (char*)&data);
+        } break;
+
+        case StreamState::RECONFIGURING: {
+          tBTA_ACM_STATE_INFO data = {.bd_addr = address, .stream_type = it->stream_type,
+                                      .stream_state = it->stream_state, .reason = it->reason};
+          btif_acm_handle_evt(BTA_ACM_RECONFIG_EVT, (char*)&data);
+        } break;
+        default:
+          break;
+      }
+    }
+  }
+
+  void OnStreamConfig(const RawAddress& address,
+                      std::vector<StreamConfigInfo> streams_config_info) override {
+    LOG(INFO) << __func__;
+    BtifAcmPeer* peer = btif_acm_initiator.FindPeer(address);
+    if (peer == nullptr) {
+      BTIF_TRACE_DEBUG("%s: Peer is NULL", __PRETTY_FUNCTION__);
+    }
+    for (auto it = streams_config_info.begin(); it != streams_config_info.end(); ++it) {
+      tBTA_ACM_CONFIG_INFO data = {.bd_addr = address, .stream_type = it->stream_type,
+                                   .codec_config = it->codec_config, .audio_location = it->audio_location,
+                                   .qos_config = it->qos_config, .codecs_selectable = it->codecs_selectable};
+      btif_acm_handle_evt(BTA_ACM_CONFIG_EVT, (char*)&data);
+    }
+  }
+
+  void OnStreamAvailable(const RawAddress& bd_addr, uint16_t src_audio_contexts,
+                                      uint16_t sink_audio_contexts) override {
+     LOG(INFO) << __func__;
+     //Need to use during START of src and sink audio context
+     BTIF_TRACE_DEBUG("%s: Peer %s, src_audio_context: 0x%x, sink_audio_contexts: 0x%x",
+                      __func__,
+                      bd_addr.ToString().c_str(), src_audio_contexts, sink_audio_contexts);
+  }
+
+  const char* GetStreamType(uint16_t stream_type) {
+    switch (stream_type) {
+      CASE_RETURN_STR(CONTENT_TYPE_UNSPECIFIED)
+      CASE_RETURN_STR(CONTENT_TYPE_CONVERSATIONAL)
+      CASE_RETURN_STR(CONTENT_TYPE_MEDIA)
+      CASE_RETURN_STR(CONTENT_TYPE_INSTRUCTIONAL)
+      CASE_RETURN_STR(CONTENT_TYPE_NOTIFICATIONS)
+      CASE_RETURN_STR(CONTENT_TYPE_ALERT)
+      CASE_RETURN_STR(CONTENT_TYPE_MAN_MACHINE)
+      CASE_RETURN_STR(CONTENT_TYPE_EMERGENCY)
+      CASE_RETURN_STR(CONTENT_TYPE_RINGTONE)
+      CASE_RETURN_STR(CONTENT_TYPE_SOUND_EFFECTS)
+      CASE_RETURN_STR(CONTENT_TYPE_LIVE)
+      CASE_RETURN_STR(CONTENT_TYPE_GAME)
+      default:
+       return "Unknown StreamType";
+    }
+  }
+
+  const char* GetStreamDirection(uint8_t event) {
+    switch (event) {
+      CASE_RETURN_STR(ASE_DIRECTION_SINK)
+      CASE_RETURN_STR(ASE_DIRECTION_SRC)
+      default:
+       return "Unknown StreamDirection";
+    }
+  }
+
+  const char* GetStreamState(uint8_t event) {
+    switch (event) {
+      CASE_RETURN_STR(STREAM_STATE_DISCONNECTED)
+      CASE_RETURN_STR(STREAM_STATE_CONNECTING)
+      CASE_RETURN_STR(STREAM_STATE_CONNECTED)
+      CASE_RETURN_STR(STREAM_STATE_STARTING)
+      CASE_RETURN_STR(STREAM_STATE_STREAMING)
+      CASE_RETURN_STR(STREAM_STATE_STOPPING)
+      CASE_RETURN_STR(STREAM_STATE_DISCONNECTING)
+      CASE_RETURN_STR(STREAM_STATE_RECONFIGURING)
+      default:
+       return "Unknown StreamState";
+    }
+  }
+};
+
+static UcastClientCallbacksImpl sUcastClientCallbacks;
+
+bt_status_t BtifAcmInitiator::Init(
+    btacm_initiator_callbacks_t* callbacks, int max_connected_acceptors,
+    const std::vector<CodecConfig>& codec_priorities) {
+  LOG_INFO(LOG_TAG, "%s: max_connected_acceptors=%d", __PRETTY_FUNCTION__,
+           max_connected_acceptors);
+  if (enabled_) return BT_STATUS_SUCCESS;
+  CleanupAllPeers();
+  max_connected_peers_ = max_connected_acceptors;
+  alarm_free(music_set_lock_release_timer_);
+  alarm_free(voice_set_lock_release_timer_);
+  alarm_free(acm_group_procedure_timer_);
+  alarm_free(acm_conn_interval_timer_);
+  music_set_lock_release_timer_ = alarm_new("btif_acm_initiator.music_set_lock_release_timer");
+  voice_set_lock_release_timer_ = alarm_new("btif_acm_initiator.voice_set_lock_release_timer");
+  acm_group_procedure_timer_ = alarm_new("btif_acm_initiator.acm_group_procedure_timer");
+  acm_conn_interval_timer_ = alarm_new("btif_acm_initiator.acm_conn_interval_timer");
+
+  callbacks_ = callbacks;
+  //init local capabilties
+  init_local_capabilities();
+
+  // register ACM with AHIM
+  btif_register_cb();
+
+  btif_vmcp_init();
+  bt_status_t status1 = btif_acm_initiator_execute_service(true);
+  if (status1 == BT_STATUS_SUCCESS) {
+    BTIF_TRACE_EVENT("%s: status success", __func__);
+  }
+  if (sUcastClientInterface != nullptr) {
+    LOG_INFO(LOG_TAG, "%s Cleaning up BAP client Interface before initializing...",
+             __PRETTY_FUNCTION__);
+    sUcastClientInterface->Cleanup();
+    sUcastClientInterface = nullptr;
+  }
+  sUcastClientInterface = bluetooth::bap::ucast::btif_bap_uclient_get_interface();
+
+
+  if (sUcastClientInterface == nullptr) {
+    LOG_ERROR(LOG_TAG, "%s Failed to get BAP Interface", __PRETTY_FUNCTION__);
+    return BT_STATUS_FAIL;
+  }
+  char value[PROPERTY_VALUE_MAX];
+  if(property_get("persist.vendor.service.bt.bap.conn_update", value, "false")
+                     && !strcmp(value, "true")) {
+    is_conn_update_enabled_ = true;
+  } else {
+    is_conn_update_enabled_ = false;
+  }
+  sUcastClientInterface->Init(&sUcastClientCallbacks);
+  enabled_ = true;
+  return BT_STATUS_SUCCESS;
+}
+
+void BtifAcmStateMachine::StateIdle::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+  if(btif_acm_initiator.IsConnUpdateEnabled()) {
+    if ((peer_.StateMachine().PreviousStateId() == BtifAcmStateMachine::kStateOpened) ||
+       (peer_.StateMachine().PreviousStateId() == BtifAcmStateMachine::kStateStarted))
+    {
+      if (alarm_is_scheduled(btif_acm_initiator.AcmConnIntervalTimer())) {
+        btif_acm_check_and_cancel_conn_Interval_timer();
+        peer_.SetConnUpdateMode(BtifAcmPeer::kFlagRelaxedMode);
+      } else {
+          LOG_ERROR(LOG_TAG, "%s Already in relaxed intervals", __PRETTY_FUNCTION__);
+      }
+    } else if (peer_.StateMachine().PreviousStateId() != BtifAcmStateMachine::kStateInvalid) {
+      if (alarm_is_scheduled(btif_acm_initiator.AcmConnIntervalTimer())) {
+        btif_acm_check_and_cancel_conn_Interval_timer();
+      }
+      peer_.SetConnUpdateMode(BtifAcmPeer::kFlagRelaxedMode);
+    }
+  }
+  peer_.ClearConnUpdateMode();
+  peer_.ClearAllFlags();
+  peer_.SetProfileType(0);
+  peer_.SetRcfgProfileType(0);
+  memset(&current_media_config, 0, sizeof(current_media_config));
+
+  // Delete peers that are re-entering the Idle state
+  if (peer_.IsAcceptor()) {
+    do_in_bta_thread(FROM_HERE, base::Bind(&BtifAcmInitiator::DeleteIdlePeers,
+                                            base::Unretained(&btif_acm_initiator)));
+  }
+}
+
+void BtifAcmStateMachine::StateIdle::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+}
+
+bool BtifAcmStateMachine::StateIdle::ProcessEvent(uint32_t event, void* p_data) {
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s music_active_peer=%s voice_active_peer=%s",
+                   __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+                   BtifAcmEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str(),
+                   logbool(peer_.IsPeerActiveForMusic()).c_str(),
+                   logbool(peer_.IsPeerActiveForVoice()).c_str());
+
+  switch (event) {
+    case BTIF_ACM_STOP_STREAM_REQ_EVT:
+    case BTIF_ACM_SUSPEND_STREAM_REQ_EVT:
+      break;
+#if 0
+    case BTIF_ACM_DISCONNECT_REQ_EVT: {
+      tBTIF_ACM_CONN_DISC* p_bta_data = (tBTIF_ACM_CONN_DISC*)p_data;
+      std::vector<StreamType> disconnect_streams;
+      if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC) {
+        StreamType type_1;
+        if (peer_.GetProfileType() & (BAP|GCP)) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_MEDIA,
+                    .direction = ASE_DIRECTION_SINK
+                   };
+          disconnect_streams.push_back(type_1);
+        }
+        if (peer_.GetProfileType() & WMCP) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_LIVE,
+                    .direction = ASE_DIRECTION_SRC
+                   };
+          disconnect_streams.push_back(type_1);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC_VOICE) {
+        StreamType type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SRC
+                            };
+        StreamType type_3 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SINK
+                            };
+        disconnect_streams.push_back(type_2);
+        disconnect_streams.push_back(type_3);
+        StreamType type_1;
+        if (peer_.GetProfileType() & (BAP|GCP)) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_MEDIA,
+                    .direction = ASE_DIRECTION_SINK
+                    };
+          disconnect_streams.push_back(type_1);
+        }
+        if (peer_.GetProfileType() & WMCP) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_LIVE,
+                    .direction = ASE_DIRECTION_SRC
+                    };
+          disconnect_streams.push_back(type_1);
+        }
+      }
+      LOG(WARNING) << __func__ << " size of disconnect_streams " << disconnect_streams.size();
+      if (!sUcastClientInterface) break;
+      sUcastClientInterface->Disconnect(peer_.PeerAddress(), disconnect_streams);
+
+      // Re-enter Idle so the peer can be deleted
+      peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+    }
+    break;
+#endif
+
+    case BTIF_ACM_CONNECT_REQ_EVT: {
+      tBTIF_ACM_CONN_DISC* p_bta_data = (tBTIF_ACM_CONN_DISC*)p_data;
+      bool can_connect = true;
+      // Check whether connection is allowed
+      if (peer_.IsAcceptor()) {
+        //There is no char in current spec. Should we check VMCP role here?
+        // shall we assume VMCP role would have been checked in apps and no need to check here?
+        can_connect = btif_acm_initiator.AllowedToConnect(peer_.PeerAddress());
+        if (!can_connect) disconnect_acm_initiator(peer_.PeerAddress(), p_bta_data->contextType);
+      }
+      if (!can_connect) {
+        BTIF_TRACE_ERROR(
+            "%s: Cannot connect to peer %s: too many connected "
+            "peers",
+            __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+        break;
+      }
+      std::vector<StreamConnect> streams;
+      if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC) {
+        StreamConnect conn_media;
+        if (peer_.GetProfileType() & (BAP|GCP)) {
+          //keeping media tx as BAP/GCP config
+          memset(&conn_media, 0, sizeof(conn_media));
+          fetch_media_tx_codec_qos_config(peer_.PeerAddress(), peer_.GetProfileType() & (BAP|GCP), &conn_media);
+          streams.push_back(conn_media);
+#if 0
+          if (false) {//enable when GCP support is available
+            SelectCodecQosConfig(peer_.PeerAddress(), (peer_.GetProfileType() & ~WMCP), VOICE_CONTEXT, SRC, EB_CONFIG);
+            StreamConnect conn_voice;
+            CodecQosConfig config;
+            conn_voice.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+            conn_voice.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+            config = peer_.get_peer_voice_rx_codec_qos_config();
+            print_codec_parameters(config.codec_config);
+            print_qos_parameters(config.qos_config);
+            conn_voice.stream_type.direction = ASE_DIRECTION_SRC;
+            conn_voice.codec_qos_config_pair.push_back(config);
+            streams.push_back(conn_voice);
+          }
+#endif
+        }
+        if (peer_.GetProfileType() & WMCP) {
+          //keeping media rx as WMCP config
+          memset(&conn_media, 0, sizeof(conn_media));
+          fetch_media_rx_codec_qos_config(peer_.PeerAddress(), WMCP, &conn_media);
+          streams.push_back(conn_media);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC_VOICE) {
+        StreamConnect conn_media, conn_voice;
+        //keeping voice tx as BAP config
+        memset(&conn_voice, 0, sizeof(conn_voice));
+        fetch_voice_tx_codec_qos_config(peer_.PeerAddress(), BAP, &conn_voice);
+        streams.push_back(conn_voice);
+        //keeping voice rx as BAP config
+        memset(&conn_voice, 0, sizeof(conn_voice));
+        fetch_voice_rx_codec_qos_config(peer_.PeerAddress(), BAP, &conn_voice);
+        streams.push_back(conn_voice);
+        if (peer_.GetProfileType() & (BAP|GCP)) {
+          //keeping media tx as BAP/GCP config
+          memset(&conn_media, 0, sizeof(conn_media));
+          fetch_media_tx_codec_qos_config(peer_.PeerAddress(), peer_.GetProfileType() & (BAP|GCP), &conn_media);
+          streams.push_back(conn_media);
+        }
+        if (peer_.GetProfileType() & WMCP) {
+          //keeping media rx as WMCP config
+          memset(&conn_media, 0, sizeof(conn_media));
+          fetch_media_rx_codec_qos_config(peer_.PeerAddress(), WMCP, &conn_media);
+          streams.push_back(conn_media);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_VOICE) {
+        StreamConnect conn_voice;
+        //keeping voice tx as BAP config
+        memset(&conn_voice, 0, sizeof(conn_voice));
+        fetch_voice_tx_codec_qos_config(peer_.PeerAddress(), BAP, &conn_voice);
+        streams.push_back(conn_voice);
+        //keeping voice rx as BAP config
+        memset(&conn_voice, 0, sizeof(conn_voice));
+        fetch_voice_rx_codec_qos_config(peer_.PeerAddress(), BAP, &conn_voice);
+        streams.push_back(conn_voice);
+      }
+      LOG(WARNING) << __func__ << " size of streams " << streams.size();
+      if (!sUcastClientInterface) break;
+      // intiate background connection
+      std::vector<RawAddress> address;
+      address.push_back(peer_.PeerAddress());
+      sUcastClientInterface->Connect(address, false, streams);
+      peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpening);
+    } break;
+#if 0
+    case BTA_ACM_DISCONNECT_EVT: {
+      tBTIF_ACM* p_acm = (tBTIF_ACM*)p_data;
+      int context_type = p_acm->state_info.stream_type.type;
+      if (p_acm->state_info.stream_state == StreamState::DISCONNECTED) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            BTIF_TRACE_DEBUG("%s: received Media Rx disconnected state from BAP, set state & ignore", __func__);
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          }
+        }
+      } else if (p_acm->state_info.stream_state == StreamState::DISCONNECTING) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          BTIF_TRACE_DEBUG("%s: received Media disconnecting state from BAP, ignore", __func__);
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC)
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+        }
+      }
+    } break;
+#endif
+
+    case BTA_ACM_CONN_UPDATE_TIMEOUT_EVT:
+      peer_.SetConnUpdateMode(BtifAcmPeer::kFlagRelaxedMode);
+      break;
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAcmEvent::EventName(event).c_str());
+      return false;
+  }
+
+  return true;
+}
+
+void BtifAcmStateMachine::StateOpening::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+
+  if(btif_acm_initiator.IsConnUpdateEnabled()) {
+    //Cancel the timer if start streamng comes before
+    // 5 seconds while moving the interval to relaxed mode.
+    if (alarm_is_scheduled(btif_acm_initiator.AcmConnIntervalTimer())) {
+       btif_acm_check_and_cancel_conn_Interval_timer();
+    }
+    else {
+       peer_.SetConnUpdateMode(BtifAcmPeer::kFlagAggresiveMode);
+    }
+  }
+
+}
+
+void BtifAcmStateMachine::StateOpening::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+}
+
+bool BtifAcmStateMachine::StateOpening::ProcessEvent(uint32_t event, void* p_data) {
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s music_active_peer=%s voice_active_peer=%s",
+                   __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+                   BtifAcmEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str(),
+                   logbool(peer_.IsPeerActiveForMusic()).c_str(),
+                   logbool(peer_.IsPeerActiveForVoice()).c_str());
+
+  switch (event) {
+    case BTIF_ACM_STOP_STREAM_REQ_EVT:
+    case BTIF_ACM_SUSPEND_STREAM_REQ_EVT:
+      break;  // Ignore
+
+    case BTA_ACM_CONNECT_EVT: {
+      tBTIF_ACM* p_bta_data = (tBTIF_ACM*)p_data;
+      btacm_connection_state_t state;
+      uint8_t status = (uint8_t)p_bta_data->state_info.stream_state;
+      uint16_t contextType = p_bta_data->state_info.stream_type.type;
+
+      LOG_INFO(
+          LOG_TAG, "%s: Peer %s : event=%s flags=%s status=%d contextType=%d",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAcmEvent::EventName(event).c_str(), peer_.FlagsToString().c_str(),
+          status, contextType);
+      if (contextType == CONTENT_TYPE_MEDIA) {
+        if (p_bta_data->state_info.stream_state == StreamState::CONNECTED) {
+          state = BTACM_CONNECTION_STATE_CONNECTED;
+          // Report the connection state to the application
+          btif_report_connection_state(peer_.PeerAddress(), state, CONTEXT_TYPE_MUSIC);
+          if (p_bta_data->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerMusicTxState(p_bta_data->state_info.stream_state);
+            BTIF_TRACE_DEBUG("%s: received connected state from BAP for mediaTx, move in opened state", __func__);
+          } else if (p_bta_data->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerMusicRxState(p_bta_data->state_info.stream_state);
+            BTIF_TRACE_DEBUG("%s: received connected state from BAP for mediaRx, move in opened state", __func__);
+          }
+          // Change state to OPENED
+          peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+        } else if (p_bta_data->state_info.stream_state == StreamState::CONNECTING) {
+          BTIF_TRACE_DEBUG("%s: received connecting state from BAP for MEDIA Tx or Rx, ignore", __func__);
+          if (p_bta_data->state_info.stream_type.direction == ASE_DIRECTION_SINK)
+            peer_.SetPeerMusicTxState(p_bta_data->state_info.stream_state);
+          else if (p_bta_data->state_info.stream_type.direction == ASE_DIRECTION_SRC)
+            peer_.SetPeerMusicRxState(p_bta_data->state_info.stream_state);
+        }
+      } else if (contextType == CONTENT_TYPE_CONVERSATIONAL) {
+        if (p_bta_data->state_info.stream_state == StreamState::CONNECTED) {
+          state = BTACM_CONNECTION_STATE_CONNECTED;
+          if (p_bta_data->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_bta_data->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::CONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), state, CONTEXT_TYPE_VOICE);
+              BTIF_TRACE_DEBUG("%s: received connected state from BAP for voice Tx, move in opened state", __func__);
+              peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+            }
+          } else if (p_bta_data->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_bta_data->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::CONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), state, CONTEXT_TYPE_VOICE);
+              BTIF_TRACE_DEBUG("%s: received connected state from BAP for voice Rx, move in opened state", __func__);
+              peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+            }
+          }
+        } else if (p_bta_data->state_info.stream_state == StreamState::CONNECTING) {
+          BTIF_TRACE_DEBUG("%s: received connecting state from BAP for CONVERSATIONAL Tx or Rx, ignore", __func__);
+          if (p_bta_data->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_bta_data->state_info.stream_state);
+          } else if (p_bta_data->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_bta_data->state_info.stream_state);
+          }
+        }
+      }
+    } break;
+
+    case BTA_ACM_DISCONNECT_EVT: {
+      tBTIF_ACM* p_acm = (tBTIF_ACM*)p_data;
+      int context_type = p_acm->state_info.stream_type.type;
+      if (p_acm->state_info.stream_state == StreamState::DISCONNECTED) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          }
+          if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+              peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            btif_report_connection_state(peer_.PeerAddress(),
+                BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_MUSIC);
+          }
+          if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when Voice Tx+Rx & Media Rx/Tx was disconnected, move in idle state", __func__);
+            peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+          } else {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when either Voice Tx or Rx or Media Rx/Tx is connecting, remain in opening state", __func__);
+          }
+        } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx, music Tx+Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else if (peer_.GetPeerMusicTxState() == StreamState::CONNECTING ||
+                         peer_.GetPeerMusicRxState() == StreamState::CONNECTING) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx is disconnected but either music Tx or Rx still connecting,"
+                                 " remain in opening state", __func__);
+              }
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Tx, music Tx+Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else if (peer_.GetPeerMusicTxState() == StreamState::CONNECTING ||
+                         peer_.GetPeerMusicRxState() == StreamState::CONNECTING) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Tx is disconnected but music Tx or Rx still connecting,"
+                                 " remain in opening state", __func__);
+              }
+            }
+          }
+        }
+      } else if (p_acm->state_info.stream_state == StreamState::DISCONNECTING) {
+          if (context_type == CONTENT_TYPE_MEDIA) {
+            BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for MEDIA Tx or Rx, ignore", __func__);
+            if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+              peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+            } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+              peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+            }
+            btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_MUSIC);
+          } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+            BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for CONVERSATIONAL Tx or Rx, ignore", __func__);
+            if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+              peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+              peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            }
+            btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_VOICE);
+          }
+      }
+    }
+    break;
+
+    case BTIF_ACM_DISCONNECT_REQ_EVT:{
+      tBTIF_ACM_CONN_DISC* p_bta_data = (tBTIF_ACM_CONN_DISC*)p_data;
+      std::vector<StreamType> disconnect_streams;
+      if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC) {
+        StreamType type_1;
+        if (p_bta_data->profileType & (BAP|GCP)) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_MEDIA,
+                    .direction = ASE_DIRECTION_SINK
+                   };
+          disconnect_streams.push_back(type_1);
+        }
+        if (p_bta_data->profileType & WMCP) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_LIVE,
+                    .direction = ASE_DIRECTION_SRC
+                   };
+          disconnect_streams.push_back(type_1);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC_VOICE) {
+        StreamType type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SRC
+                            };
+        StreamType type_3 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SINK
+                            };
+        disconnect_streams.push_back(type_3);
+        disconnect_streams.push_back(type_2);
+        StreamType type_1;
+        if (p_bta_data->profileType & (BAP|GCP)) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_MEDIA,
+                    .direction = ASE_DIRECTION_SINK
+                    };
+          disconnect_streams.push_back(type_1);
+        }
+        if (p_bta_data->profileType & WMCP) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_LIVE,
+                    .direction = ASE_DIRECTION_SRC
+                    };
+          disconnect_streams.push_back(type_1);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_VOICE) {
+        StreamType type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SRC
+                            };
+        StreamType type_3 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SINK
+                            };
+        disconnect_streams.push_back(type_3);
+        disconnect_streams.push_back(type_2);
+      }
+      LOG(WARNING) << __func__ << " size of disconnect_streams " << disconnect_streams.size();
+      if (!sUcastClientInterface) break;
+      sUcastClientInterface->Disconnect(peer_.PeerAddress(), disconnect_streams);
+
+      if ((p_bta_data->contextType == CONTEXT_TYPE_MUSIC) && ((peer_.GetPeerVoiceRxState() == StreamState::CONNECTING) ||
+          (peer_.GetPeerVoiceTxState() == StreamState::CONNECTING))) {
+        LOG(WARNING) << __func__ << " voice connecting remain in opening ";
+        btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_MUSIC);
+      } else if ((p_bta_data->contextType == CONTEXT_TYPE_VOICE) && (peer_.GetPeerMusicTxState() == StreamState::CONNECTING ||
+          (peer_.GetPeerMusicRxState() == StreamState::CONNECTING))) {
+        LOG(WARNING) << __func__ << " Music connecting remain in opening ";
+        btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+      } else {
+        LOG(WARNING) << __func__ << " Move in idle state ";
+        btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_MUSIC_VOICE);
+        peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+      }
+    }
+    break;
+
+    case BTIF_ACM_CONNECT_REQ_EVT: {
+      BTIF_TRACE_WARNING(
+          "%s: Peer %s : event=%s : device is already connecting, "
+          "ignore Connect request",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAcmEvent::EventName(event).c_str());
+    } break;
+
+    case BTA_ACM_CONFIG_EVT: {
+       tBTIF_ACM* p_acm_data = (tBTIF_ACM*)p_data;
+       uint16_t contextType = p_acm_data->state_info.stream_type.type;
+       uint16_t peer_latency_ms = 0;
+       uint32_t presen_delay = 0;
+       bool is_update_require = false;
+       if (contextType == CONTENT_TYPE_MEDIA) {
+         if (p_acm_data->state_info.stream_type.audio_context == CONTENT_TYPE_MEDIA) {
+           BTIF_TRACE_DEBUG("%s: compare with current media config", __PRETTY_FUNCTION__);
+           is_update_require = compare_codec_config_(current_media_config, p_acm_data->config_info.codec_config);
+         } else if (p_acm_data->state_info.stream_type.audio_context == CONTENT_TYPE_LIVE) {
+           BTIF_TRACE_DEBUG("%s: cache current_recording_config", __PRETTY_FUNCTION__);
+           current_recording_config = p_acm_data->config_info.codec_config;
+         }
+         if (mandatory_codec_selected) {
+           BTIF_TRACE_DEBUG("%s: Mandatory codec selected, do not store config", __PRETTY_FUNCTION__);
+         } else {
+           BTIF_TRACE_DEBUG("%s: store configuration", __PRETTY_FUNCTION__);
+         }
+         //Cache the peer latency in WMCP case
+         if (p_acm_data->state_info.stream_type.audio_context == CONTENT_TYPE_LIVE) {
+           BTIF_TRACE_DEBUG("%s: presentation delay[0] = %x", __func__,
+                            p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[0]);
+           BTIF_TRACE_DEBUG("%s: presentation delay[1] = %x", __func__,
+                            p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[1]);
+           BTIF_TRACE_DEBUG("%s: presentation delay[2] = %x", __func__,
+                            p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[2]);
+           presen_delay = static_cast<uint32_t>(p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[0]) |
+                          static_cast<uint32_t>(p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[1] << 8) |
+                          static_cast<uint32_t>(p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[2] << 16);
+           BTIF_TRACE_DEBUG("%s: presen_delay = %dus", __func__, presen_delay);
+           peer_latency_ms = presen_delay/1000;
+           BTIF_TRACE_DEBUG("%s: s_to_m latency = %dms", __func__,
+                           p_acm_data->config_info.qos_config.cig_config.max_tport_latency_s_to_m);
+           peer_latency_ms += p_acm_data->config_info.qos_config.cig_config.max_tport_latency_s_to_m;
+           peer_.SetPeerLatency(peer_latency_ms);
+           BTIF_TRACE_DEBUG("%s: cached peer Latency = %dms", __func__, peer_.GetPeerLatency());
+         }
+         if (is_update_require) {
+           current_media_config = p_acm_data->config_info.codec_config;
+           BTIF_TRACE_DEBUG("%s: current_media_config.codec_specific_3: %"
+                                 PRIi64, __func__, current_media_config.codec_specific_3);
+           btif_acm_update_lc3q_params(&current_media_config.codec_specific_3, p_acm_data);
+           btif_acm_report_source_codec_state(peer_.PeerAddress(), current_media_config,
+                                              unicast_codecs_capabilities,
+                                              unicast_codecs_capabilities, CONTEXT_TYPE_MUSIC);
+         }
+       } else if (contextType == CONTENT_TYPE_CONVERSATIONAL &&
+                  p_acm_data->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+         BTIF_TRACE_DEBUG("%s: cache current_voice_config", __PRETTY_FUNCTION__);
+         current_voice_config = p_acm_data->config_info.codec_config;
+         BTIF_TRACE_DEBUG("%s: current_voice_config.codec_specific_3: %"
+                               PRIi64, __func__, current_voice_config.codec_specific_3);
+         btif_acm_update_lc3q_params(&current_voice_config.codec_specific_3, p_acm_data);
+         btif_acm_report_source_codec_state(peer_.PeerAddress(), current_voice_config,
+                                            unicast_codecs_capabilities,
+                                            unicast_codecs_capabilities, CONTEXT_TYPE_VOICE);
+       }
+      //Handle BAP START if reconfig comes in mid of streaming
+      //peer_.SetStreamReconfigInfo(p_acm->acm_reconfig);
+      //TODO: local capabilities
+      //CodecConfig record = p_bta_data->acm_reconfig.codec_config;
+      //saving codec config as negotiated parameter as true
+      //btif_pacs_add_record(peer_.PeerAddress(), true, CodecDirection::CODEC_DIR_SRC, &record);
+
+    } break;
+
+    case BTA_ACM_CONN_UPDATE_TIMEOUT_EVT:
+      peer_.SetConnUpdateMode(BtifAcmPeer::kFlagRelaxedMode);
+      break;
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAcmEvent::EventName(event).c_str());
+      return false;
+  }
+  return true;
+}
+
+bool btif_peer_device_is_streaming(uint8_t Id) {
+    bool is_streaming = false;
+    tBTA_CSIP_CSET cset_info;
+    memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+    cset_info = BTA_CsipGetCoordinatedSet(Id);
+    if (cset_info.size == 0) {
+      BTIF_TRACE_ERROR("%s: CSET info size is zero, return", __func__);
+      return false;
+    }
+    std::vector<RawAddress>::iterator itr;
+    BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+    if ((cset_info.set_members).size() > 0) {
+      for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+        BtifAcmPeer* peer = btif_acm_initiator.FindPeer(*itr);
+        if (peer != nullptr && (peer->IsStreaming() || peer->CheckFlags(BtifAcmPeer::kFlagPendingStart)) &&
+            !peer->CheckFlags(BtifAcmPeer::kFlagPendingLocalSuspend)) {
+          BTIF_TRACE_DEBUG("%s: fellow device is streaming %s ", __func__, peer->PeerAddress().ToString().c_str());
+          is_streaming = true;
+          break;
+        }
+      }
+    }
+    return is_streaming;
+}
+
+bool btif_peer_device_is_reconfiguring(uint8_t Id) {
+    bool is_reconfigured = false;
+    if (Id < INVALID_SET_ID) {
+      tBTA_CSIP_CSET cset_info;
+      memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+      cset_info = BTA_CsipGetCoordinatedSet(Id);
+      if (cset_info.size == 0) {
+        BTIF_TRACE_ERROR("%s: CSET info size is zero, return", __func__);
+        return false;
+      }
+      std::vector<RawAddress>::iterator itr;
+      BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+      if ((cset_info.set_members).size() > 0) {
+        for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+          BtifAcmPeer* peer = btif_acm_initiator.FindPeer(*itr);
+          if (peer != nullptr && peer->CheckFlags(BtifAcmPeer::kFlagPendingReconfigure)) {
+            BTIF_TRACE_DEBUG("%s: peer is reconfiguring %s ", __func__, peer->PeerAddress().ToString().c_str());
+            is_reconfigured = true;
+            break;
+          }
+        }
+      }
+    } else {
+      is_reconfigured = true;
+      BTIF_TRACE_ERROR("%s: peer is TWM device, return is_reconfigured %d", __func__, is_reconfigured);
+    }
+    return is_reconfigured;
+}
+
+void BtifAcmStateMachine::StateOpened::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s, Peer SetId = %d, MusicActiveSetId = %d, ContextType = %d", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str(), peer_.SetId(),
+                   btif_acm_initiator.MusicActiveCSetId(), peer_.GetContextType());
+
+  //Starting the timer for 5 seconds before moving to relaxed state as
+  //stop event or start streaming event moght immediately come
+  //which requires aggresive interval
+  if(btif_acm_initiator.IsConnUpdateEnabled()) {
+    btif_acm_check_and_start_conn_Interval_timer(&peer_);
+  }
+  peer_.ClearFlags(BtifAcmPeer::kFlagPendingLocalSuspend |
+                   BtifAcmPeer::kFlagPendingStart |
+                   BtifAcmPeer::kFlagPendingStop);
+
+  BTIF_TRACE_DEBUG("%s: kFlagPendingReconfigure %d and kFLagPendingStartAfterReconfig %d",  __PRETTY_FUNCTION__,
+          peer_.CheckFlags(BtifAcmPeer::kFlagPendingReconfigure),
+          peer_.CheckFlags(BtifAcmPeer::kFLagPendingStartAfterReconfig));
+
+  if (peer_.CheckFlags(BtifAcmPeer::kFlagPendingReconfigure)) {
+    peer_.ClearFlags(BtifAcmPeer::kFlagPendingReconfigure);
+    if ((peer_.GetRcfgProfileType() != BAP_CALL) &&
+            (current_active_profile_type != peer_.GetRcfgProfileType())) {
+      current_active_profile_type = peer_.GetRcfgProfileType();
+      if (current_active_profile_type != WMCP)
+        current_active_config = current_media_config;
+      else
+        current_active_config = current_recording_config;
+
+      if (btif_peer_device_is_reconfiguring(peer_.SetId()))
+        btif_acm_source_restart_session(active_bda, active_bda);
+
+      if (current_active_profile_type == WMCP) {
+        uint16_t sink_latency = btif_acm_get_active_device_latency();
+        BTIF_TRACE_EVENT("%s: sink_latency = %dms", __func__, sink_latency);
+        if ((sink_latency > 0) && !btif_acm_update_sink_latency_change(sink_latency * 10)) {
+        BTIF_TRACE_ERROR("%s: unable to update latency", __func__);
+        }
+      }
+      if (current_active_profile_type == BAP) {
+        peer_.ResetProfileType(GCP);
+        peer_.SetProfileType(BAP);
+      } else if (current_active_profile_type == GCP) {
+        peer_.ResetProfileType(BAP);
+        peer_.SetProfileType(GCP);
+      }
+      BTIF_TRACE_DEBUG("%s: cummulative_profile_type %d", __func__, peer_.GetProfileType());
+      BTIF_TRACE_DEBUG("%s: Reconfig + restart session completed for media, signal session ready", __func__);
+      btif_acm_signal_session_ready();
+    } else if (current_active_profile_type == peer_.GetRcfgProfileType()) {
+      BTIF_TRACE_DEBUG("%s: Reconfig to remote is completed for media, restart session wasn't needed", __func__);
+    } else {
+      BTIF_TRACE_DEBUG("%s: Reconfig completed for BAP_CALL", __func__);
+    }
+  }
+  //Start the lock release timer here.
+  //check if peer device is in started state
+  if (btif_peer_device_is_streaming(peer_.SetId()) ||
+      peer_.CheckFlags(BtifAcmPeer::kFLagPendingStartAfterReconfig)) {
+    StreamType type_1, type_2;
+    std::vector<StreamType> start_streams;
+    if (peer_.GetRcfgProfileType() != BAP_CALL) {
+      if ((current_active_profile_type == BAP || current_active_profile_type == GCP) &&
+              (peer_.GetPeerMusicTxState() == StreamState::CONNECTED)) {
+        type_1 = {.type = CONTENT_TYPE_MEDIA,
+                  .audio_context = CONTENT_TYPE_MEDIA,
+                  .direction = ASE_DIRECTION_SINK
+                 };
+        start_streams.push_back(type_1);
+      } else if ((current_active_profile_type == WMCP) &&
+              (peer_.GetPeerMusicRxState() == StreamState::CONNECTED)) {
+        type_1 = {.type = CONTENT_TYPE_MEDIA,
+                  .audio_context = CONTENT_TYPE_LIVE,
+                  .direction = ASE_DIRECTION_SRC
+                 };
+        start_streams.push_back(type_1);
+      }
+    } else {
+      if ((peer_.GetPeerVoiceTxState() == StreamState::CONNECTED) &&
+              (peer_.GetPeerVoiceRxState() == StreamState::CONNECTED)) {
+        type_1 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                  .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                  .direction = ASE_DIRECTION_SINK
+                 };
+        type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                  .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                  .direction = ASE_DIRECTION_SRC
+                 };
+        start_streams.push_back(type_1);
+        start_streams.push_back(type_2);
+      }
+    }
+
+    if(btif_acm_initiator.IsConnUpdateEnabled()) {
+      //Cancel the timer if start streamng comes before
+      // 5 seconds while moving the interval to relaxed mode.
+      if (alarm_is_scheduled(btif_acm_initiator.AcmConnIntervalTimer())) {
+        btif_acm_check_and_cancel_conn_Interval_timer();
+      } else {
+        peer_.SetConnUpdateMode(BtifAcmPeer::kFlagAggresiveMode);
+      }
+    }
+    sUcastClientInterface->Start(peer_.PeerAddress(), start_streams);
+    peer_.SetFlags(BtifAcmPeer::kFlagPendingStart);
+    peer_.ClearFlags(BtifAcmPeer::kFLagPendingStartAfterReconfig);
+  }
+  peer_.SetRcfgProfileType(0);
+
+  if (peer_.StateMachine().PreviousStateId() == BtifAcmStateMachine::kStateStarted) {
+    BTIF_TRACE_DEBUG("%s: Entering Opened from Started State", __PRETTY_FUNCTION__);
+    if ((btif_acm_initiator.GetGroupLockStatus(peer_.SetId()) !=
+         BtifAcmInitiator::kFlagStatusUnknown) &&
+         alarm_is_scheduled(btif_acm_initiator.AcmGroupProcedureTimer())) {
+      BTIF_TRACE_DEBUG("%s: All locked and stop/suspend requested device have stopped, ack mm audio", __func__);
+      btif_acm_check_and_cancel_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+      tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+      if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+         pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+        btif_acm_source_on_suspended(A2DP_CTRL_ACK_SUCCESS);
+      } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+        btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+      } else {
+        BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+      }
+      btif_acm_check_and_start_lock_release_timer(btif_acm_initiator.MusicActiveCSetId());
+    }
+  }
+
+  if (peer_.StateMachine().PreviousStateId() == BtifAcmStateMachine::kStateStarted) {
+    if ((btif_acm_initiator.MusicActiveCSetId() > 0) &&
+        (btif_acm_initiator.GetGroupLockStatus(btif_acm_initiator.MusicActiveCSetId()) == BtifAcmInitiator::kFlagStatusLocked)) {
+      BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+      btif_acm_check_and_start_lock_release_timer(btif_acm_initiator.MusicActiveCSetId());
+    }
+  }
+}
+
+void BtifAcmStateMachine::StateOpened::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+
+  peer_.ClearFlags(BtifAcmPeer::kFlagPendingStart);
+}
+
+bool BtifAcmStateMachine::StateOpened::ProcessEvent(uint32_t event,
+                                                   void* p_data) {
+  tBTIF_ACM* p_acm = (tBTIF_ACM*)p_data;
+
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s music_active_peer=%s voice_active_peer=%s",
+                   __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+                   BtifAcmEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str(),
+                   logbool(peer_.IsPeerActiveForMusic()).c_str(),
+                   logbool(peer_.IsPeerActiveForVoice()).c_str());
+
+  switch (event) {
+    case BTIF_ACM_CONNECT_REQ_EVT: {
+      tBTIF_ACM_CONN_DISC* p_bta_data = (tBTIF_ACM_CONN_DISC*)p_data;
+      bool can_connect = true;
+      // Check whether connection is allowed
+      if (peer_.IsAcceptor()) {
+        //There is no char in current spec. Should we check VMCP role here?
+        // shall we assume VMCP role would have been checked in apps and no need to check here?
+        can_connect = btif_acm_initiator.AllowedToConnect(peer_.PeerAddress());
+        if (!can_connect) disconnect_acm_initiator(peer_.PeerAddress(), p_bta_data->contextType);
+      }
+      if (!can_connect) {
+        BTIF_TRACE_ERROR(
+            "%s: Cannot connect to peer %s: too many connected "
+            "peers",
+            __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+        break;
+      }
+      std::vector<StreamConnect> streams;
+      if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC) {
+        StreamConnect conn_media;
+        if (peer_.GetProfileType() & (BAP|GCP)) {
+          //keeping media tx as BAP/GCP config
+          memset(&conn_media, 0, sizeof(conn_media));
+          fetch_media_tx_codec_qos_config(peer_.PeerAddress(), peer_.GetProfileType() & (BAP|GCP), &conn_media);
+          streams.push_back(conn_media);
+#if 0
+          if (false) {//enable when GCP support is available
+            SelectCodecQosConfig(peer_.PeerAddress(), (peer_.GetProfileType() & ~WMCP), VOICE_CONTEXT, SRC, EB_CONFIG);
+            StreamConnect conn_voice;
+            CodecQosConfig config;
+            conn_voice.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+            conn_voice.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+            config = peer_.get_peer_voice_rx_codec_qos_config();
+            print_codec_parameters(config.codec_config);
+            print_qos_parameters(config.qos_config);
+            conn_voice.stream_type.direction = ASE_DIRECTION_SRC;
+            conn_voice.codec_qos_config_pair.push_back(config);
+            streams.push_back(conn_voice);
+          }
+#endif
+        }
+        if (peer_.GetProfileType() & WMCP) {
+          //keeping media rx as WMCP config
+          memset(&conn_media, 0, sizeof(conn_media));
+          fetch_media_rx_codec_qos_config(peer_.PeerAddress(), WMCP, &conn_media);
+          streams.push_back(conn_media);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC_VOICE) {
+	    StreamConnect conn_media, conn_voice;
+        //keeping voice tx as BAP config
+        memset(&conn_voice, 0, sizeof(conn_voice));
+        fetch_voice_tx_codec_qos_config(peer_.PeerAddress(), BAP, &conn_voice);
+        streams.push_back(conn_voice);
+        //keeping voice rx as BAP config
+        memset(&conn_voice, 0, sizeof(conn_voice));
+        fetch_voice_rx_codec_qos_config(peer_.PeerAddress(), BAP, &conn_voice);
+        streams.push_back(conn_voice);
+        if (peer_.GetProfileType() & (BAP|GCP)) {
+          //keeping media tx as BAP/GCP config
+          memset(&conn_media, 0, sizeof(conn_media));
+          fetch_media_tx_codec_qos_config(peer_.PeerAddress(), peer_.GetProfileType() & (BAP|GCP), &conn_media);
+          streams.push_back(conn_media);
+        }
+        if (peer_.GetProfileType() & WMCP) {
+          //keeping media rx as WMCP config
+          memset(&conn_media, 0, sizeof(conn_media));
+          fetch_media_rx_codec_qos_config(peer_.PeerAddress(), WMCP, &conn_media);
+          streams.push_back(conn_media);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_VOICE) {
+        StreamConnect conn_voice;
+        //keeping voice tx as BAP config
+        memset(&conn_voice, 0, sizeof(conn_voice));
+        fetch_voice_tx_codec_qos_config(peer_.PeerAddress(), BAP, &conn_voice);
+        streams.push_back(conn_voice);
+        //keeping voice rx as BAP config
+        memset(&conn_voice, 0, sizeof(conn_voice));
+        fetch_voice_rx_codec_qos_config(peer_.PeerAddress(), BAP, &conn_voice);
+        streams.push_back(conn_voice);
+      }
+      LOG(WARNING) << __func__ << " size of streams " << streams.size();
+      if (!sUcastClientInterface) break;
+      std::vector<RawAddress> address;
+      address.push_back(peer_.PeerAddress());
+      sUcastClientInterface->Connect(address, false, streams);
+      //peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpening);
+    } break;
+
+    case BTIF_ACM_STOP_STREAM_REQ_EVT:
+    case BTIF_ACM_SUSPEND_STREAM_REQ_EVT: {
+      BTIF_TRACE_DEBUG("%s: Already in OPENED state, ACK success", __PRETTY_FUNCTION__);
+      tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+      if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+         pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+        btif_acm_source_on_suspended(A2DP_CTRL_ACK_SUCCESS);
+      } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+        btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+      } else {
+        BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+      }
+    } break;
+
+    case BTIF_ACM_START_STREAM_REQ_EVT: {
+      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+              peer_.PeerAddress().ToString().c_str(),
+              BtifAcmEvent::EventName(event).c_str(),
+              peer_.FlagsToString().c_str());
+      if (peer_.CheckFlags(BtifAcmPeer::kFlagPendingStart)) {
+        BTIF_TRACE_DEBUG("%s: Ignore Start req", __PRETTY_FUNCTION__);
+        break;
+      }
+#if 0
+      //Can be either music or voice, prior to coming here,
+      //this must have been evaluated for locking logic + grp logic
+      StreamType type_1;
+      std::vector<StreamType> start_streams;
+      if (current_active_profile_type != WMCP) {
+        if (peer_.GetStreamContextType() == CONTEXT_TYPE_MUSIC) {
+          StreamType type_1 = {.type = CONTENT_TYPE_MEDIA,
+                               .audio_context = CONTENT_TYPE_MEDIA,
+                               .direction = ASE_DIRECTION_SINK
+                              };
+          start_streams.push_back(type_1);
+        } else if (peer_.GetStreamContextType() == CONTEXT_TYPE_VOICE) {
+          StreamType type_1 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                               .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                               .direction = ASE_DIRECTION_SINK
+                              };
+          StreamType type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                               .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                               .direction = ASE_DIRECTION_SRC
+                              };
+          start_streams.push_back(type_1);
+          start_streams.push_back(type_2);
+          LOG_INFO(LOG_TAG, "%s: sending start for voice###", __PRETTY_FUNCTION__);
+        }
+      } else {
+        type_1 = {.type = CONTENT_TYPE_MEDIA,
+                  .audio_context = CONTENT_TYPE_LIVE,
+                  .direction = ASE_DIRECTION_SRC
+                 };
+        start_streams.push_back(type_1);
+      }
+
+      if(btif_acm_initiator.IsConnUpdateEnabled()) {
+        //Cancel the timer if start streamng comes before
+        // 5 seconds while moving the interval to relaxed mode.
+        if (alarm_is_scheduled(btif_acm_initiator.AcmConnIntervalTimer())) {
+          btif_acm_check_and_cancel_conn_Interval_timer();
+        } else {
+          peer_.SetConnUpdateMode(BtifAcmPeer::kFlagAggresiveMode);
+        }
+      }
+      if (!sUcastClientInterface) break;
+        sUcastClientInterface->Start(peer_.PeerAddress(), start_streams);
+#endif
+#if 1
+      if (peer_.GetStreamContextType() == CONTEXT_TYPE_MUSIC) {
+        reconfig_acm_initiator(peer_.PeerAddress(), current_active_profile_type);
+      } else if (peer_.GetStreamContextType() == CONTEXT_TYPE_VOICE) {
+        reconfig_acm_initiator(peer_.PeerAddress(), BAP_CALL);
+      }
+      peer_.SetFlags(BtifAcmPeer::kFLagPendingStartAfterReconfig);
+#endif
+    }
+    break;
+
+    case BTA_ACM_START_EVT: {
+      tBTIF_ACM_STATUS status = (uint8_t)p_acm->state_info.stream_state;
+      //int contextType = p_acm->state_info.stream_type.type;
+      LOG_INFO(LOG_TAG,
+               "%s: Peer %s : event=%s status=%d ",
+               __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+               BtifAcmEvent::EventName(event).c_str(),status);
+
+      if (p_acm->state_info.stream_state == StreamState::STARTING) {
+        //Check what to do in this case
+        BTIF_TRACE_DEBUG("%s: BAP returned as starting, ignore", __PRETTY_FUNCTION__);
+        break;
+      } else if (p_acm->state_info.stream_state == StreamState::STREAMING){
+        peer_.ClearFlags(BtifAcmPeer::kFlagPendingStart);
+        peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateStarted);
+      }
+    } break;
+
+    case BTIF_ACM_DISCONNECT_REQ_EVT:{
+      tBTIF_ACM_CONN_DISC* p_bta_data = (tBTIF_ACM_CONN_DISC*)p_data;
+      std::vector<StreamType> disconnect_streams;
+      if (peer_.CheckFlags(BtifAcmPeer::kFlagPendingStart)) {
+        peer_.ClearFlags(BtifAcmPeer::kFlagPendingStart);
+        tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+        if (pending_cmd == A2DP_CTRL_CMD_START) {
+          btif_acm_on_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+        }
+      }
+      if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC) {
+        StreamType type_1;
+        if (p_bta_data->profileType & (BAP|GCP)) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_MEDIA,
+                    .direction = ASE_DIRECTION_SINK
+                   };
+          disconnect_streams.push_back(type_1);
+        }
+        if (p_bta_data->profileType & WMCP) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_LIVE,
+                    .direction = ASE_DIRECTION_SRC
+                   };
+          disconnect_streams.push_back(type_1);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC_VOICE) {
+        StreamType type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SRC
+                            };
+        StreamType type_3 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SINK
+                            };
+        disconnect_streams.push_back(type_3);
+        disconnect_streams.push_back(type_2);
+        StreamType type_1;
+        if (p_bta_data->profileType & (BAP|GCP)) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_MEDIA,
+                    .direction = ASE_DIRECTION_SINK
+                    };
+          disconnect_streams.push_back(type_1);
+        }
+        if (p_bta_data->profileType & WMCP) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_LIVE,
+                    .direction = ASE_DIRECTION_SRC
+                    };
+          disconnect_streams.push_back(type_1);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_VOICE) {
+        StreamType type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SRC
+                            };
+        StreamType type_3 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SINK
+                            };
+        disconnect_streams.push_back(type_3);
+        disconnect_streams.push_back(type_2);
+      }
+      LOG(WARNING) << __func__ << " size of disconnect_streams " << disconnect_streams.size();
+      if (!sUcastClientInterface) break;
+      sUcastClientInterface->Disconnect(peer_.PeerAddress(), disconnect_streams);
+
+      if ((p_bta_data->contextType == CONTEXT_TYPE_MUSIC) && ((peer_.GetPeerVoiceRxState() == StreamState::CONNECTED) ||
+          (peer_.GetPeerVoiceTxState() == StreamState::CONNECTED))) {
+        LOG(WARNING) << __func__ << " voice connected remain in opened ";
+      } else if ((p_bta_data->contextType == CONTEXT_TYPE_VOICE) && ((peer_.GetPeerMusicTxState() == StreamState::CONNECTED) ||
+          (peer_.GetPeerMusicRxState() == StreamState::CONNECTED))) {
+        LOG(WARNING) << __func__ << " Music connected remain in opened ";
+      } else {
+        LOG(WARNING) << __func__ << " Move in closing state ";
+        peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+      }
+    } break;
+
+    case BTA_ACM_STOP_EVT: { //Sumit: what is this case?
+      int contextType = p_acm->acm_connect.streams_info.stream_type.type;
+      btif_report_audio_state(peer_.PeerAddress(), BTACM_AUDIO_STATE_STOPPED, peer_.GetStreamContextType());
+      if (contextType == CONTENT_TYPE_MEDIA)
+        peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+    } break;
+
+    case BTA_ACM_CONNECT_EVT: {// above evnt can come and handle for voice/media case
+      tBTIF_ACM_STATUS status = (uint8_t)p_acm->state_info.stream_state;
+      int contextType = p_acm->state_info.stream_type.type;
+
+      LOG_INFO(
+          LOG_TAG, "%s: Peer %s : event=%s status=%d",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAcmEvent::EventName(event).c_str(),status);
+      if (peer_.CheckFlags(BtifAcmPeer::kFlagPendingLocalSuspend)) {
+        peer_.ClearFlags(BtifAcmPeer::kFlagPendingLocalSuspend);
+        BTIF_TRACE_DEBUG("%s: peer device is suspended, send MM any pending ACK", __func__);
+        tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+        if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+         pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+         btif_acm_source_on_suspended(A2DP_CTRL_ACK_SUCCESS);
+        } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+          btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+        } else {
+         BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+        }
+        break;
+      }
+      if (p_acm->state_info.stream_state == StreamState::CONNECTED) {
+        if (contextType == CONTENT_TYPE_MEDIA) {
+          if ((btif_acm_initiator.MusicActivePeer() == peer_.PeerAddress()) &&
+                  peer_.CheckFlags(BtifAcmPeer::kFlagPendingReconfigure)) { //recheck
+            LOG(INFO) << __PRETTY_FUNCTION__ << " : Peer " << peer_.PeerAddress()
+                  << " : Reconfig done - calling startSession() to audio HAL";
+            std::promise<void> peer_ready_promise;
+            std::future<void> peer_ready_future = peer_ready_promise.get_future();
+            //TODO: cannot use peer addr here, must need group address.
+            btif_acm_source_start_session(peer_.PeerAddress());
+            //Perform group operation here
+          } else if (((peer_.GetPeerVoiceRxState() == StreamState::CONNECTED) ||
+                     (peer_.GetPeerVoiceTxState() == StreamState::CONNECTED) ||
+                     (peer_.GetPeerMusicRxState() == StreamState::CONNECTED)) &&
+                     (peer_.GetPeerMusicTxState() == StreamState::CONNECTING) &&
+                     (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK)) {
+            BTIF_TRACE_DEBUG("%s: music Tx connected when either Voice Tx/Rx or Music Rx was connected,"
+                             "remain in opened state", __func__);
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+            BTIF_TRACE_DEBUG("%s: received connected state from BAP for Music TX, update state", __func__);
+            btif_report_connection_state(peer_.PeerAddress(),
+                                         BTACM_CONNECTION_STATE_CONNECTED, CONTEXT_TYPE_MUSIC);
+          } else if ((peer_.GetPeerMusicRxState() == StreamState::CONNECTING) &&
+              (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC)) {
+            BTIF_TRACE_DEBUG("%s: received connected state from BAP for Music RX(recording), update state", __func__);
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+            btif_report_connection_state(peer_.PeerAddress(),
+                                         BTACM_CONNECTION_STATE_CONNECTED, CONTEXT_TYPE_MUSIC);
+          }
+#if 0
+          if (peer_.CheckFlags(BtifAcmPeer::kFlagPendingStart)) {
+            LOG(INFO) << __PRETTY_FUNCTION__ << " : Peer " << peer_.PeerAddress()
+                      << " : Reconfig done - calling BTA_AvStart()";
+            StreamType type_1;
+            if (current_active_profile_type != WMCP) {
+              type_1 = {.type = CONTENT_TYPE_MEDIA,
+                        .audio_context = CONTENT_TYPE_MEDIA,
+                        .direction = ASE_DIRECTION_SINK
+                       };
+            } else {
+              type_1 = {.type = CONTENT_TYPE_MEDIA,
+                        .audio_context = CONTENT_TYPE_LIVE,
+                        .direction = ASE_DIRECTION_SRC
+                       };
+            }
+            std::vector<StreamType> start_streams;
+            start_streams.push_back(type_1);
+            if (!sUcastClientInterface) break;
+            sUcastClientInterface->Start(peer_.PeerAddress(), start_streams);
+          }
+#endif
+        } else if (contextType == CONTENT_TYPE_CONVERSATIONAL) {
+          BTIF_TRACE_DEBUG("%s: voice context connected, remain in opened state"
+                  " peer_.GetPeerVoiceTxState() %d peer_.GetPeerVoiceRxState() %d",
+                  __func__, peer_.GetPeerVoiceTxState(), peer_.GetPeerVoiceRxState());
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK &&
+                  (peer_.GetPeerVoiceTxState() != StreamState::CONNECTED)) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::CONNECTED) {
+              BTIF_TRACE_DEBUG("%s: received connected state from BAP for voice TX, update state", __func__);
+              btif_report_connection_state(peer_.PeerAddress(),
+                                           BTACM_CONNECTION_STATE_CONNECTED, CONTEXT_TYPE_VOICE);
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC &&
+                  (peer_.GetPeerVoiceRxState() != StreamState::CONNECTED)) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::CONNECTED) {
+              BTIF_TRACE_DEBUG("%s: received connected state from BAP for voice RX, update state", __func__);
+              btif_report_connection_state(peer_.PeerAddress(),
+                                           BTACM_CONNECTION_STATE_CONNECTED, CONTEXT_TYPE_VOICE);
+            }
+          }
+        }
+      } else if (p_acm->state_info.stream_state == StreamState::CONNECTING){
+          if (contextType == CONTENT_TYPE_MEDIA) {
+            BTIF_TRACE_DEBUG("%s: received connecting state from BAP for MEDIA Tx or Rx, ignore", __func__);
+            if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK)
+              peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+            else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC)
+              peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          } else if (contextType == CONTENT_TYPE_CONVERSATIONAL) {
+            BTIF_TRACE_DEBUG("%s: received connecting state from BAP for CONVERSATIONAL Tx or Rx, ignore", __func__);
+            if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+              peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+              peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            }
+          }
+      }
+    } break;
+
+    case BTA_ACM_DISCONNECT_EVT: {
+      int context_type = p_acm->state_info.stream_type.type;
+      if (p_acm->state_info.stream_state == StreamState::DISCONNECTED) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          }
+          if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+              peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            btif_report_connection_state(peer_.PeerAddress(),
+                BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_MUSIC);
+          }
+          if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when Voice Tx+Rx & Media Rx/Tx was disconnected, move in idle state", __func__);
+            peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+          } else {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when either Voice Tx or Rx or Media Rx/Tx is connected, remain in opened state", __func__);
+          }
+        } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx, Music Tx & Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx is disconnected but music Tx or Rx still not disconnected,"
+                                 " remain in opened state", __func__);
+              }
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Tx, Music Tx & Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Rx is disconnected but music Tx or Rx still not disconnected,"
+                                 " remain in opened state", __func__);
+              }
+            }
+          }
+        }
+      } else if (p_acm->state_info.stream_state == StreamState::DISCONNECTING) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          }
+          btif_report_connection_state(peer_.PeerAddress(),
+                                  BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_MUSIC);
+          if ((peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTING) &&
+               (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTING) &&
+               (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING) &&
+               (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING)) {
+              BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnecting state from BAP"
+                               " when Voice Tx+Rx and Media Rx/Tx disconnected/ing, move in closing state", __func__);
+              peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+          } else {
+              BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnecting state from BAP"
+                               " when either Voice Tx or Rx or Media Rx/Tx is connected, remain in opened state", __func__);
+          }
+        } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTING ||
+                peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_VOICE);
+              if (((peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING)) &&
+                  ((peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING))) {
+                BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Tx,"
+                                 " voice Rx, music Tx+Rx are disconnected/ing move in closing state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Tx,"
+                                 " voice Rx is disconncted/ing but music Tx or Rx still not disconnected/ing,"
+                                 " remain in opened state", __func__);
+              }
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTING ||
+                peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_VOICE);
+              if (((peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING)) &&
+                  ((peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING))) {
+                BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Rx,"
+                                 " voice Tx, music Tx+Rx are disconnected/ing move in closing state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Tx is disconncted/ing but music Tx or Rx still not disconnected/ing,"
+                                 " remain in opened state", __func__);
+              }
+            }
+          }
+        }
+      }
+    }
+    break;
+
+    case BTIF_ACM_RECONFIG_REQ_EVT: {
+        std::vector<StreamReconfig> reconf_streams;
+        StreamReconfig reconf_info;
+        CodecQosConfig cfg;
+        if (p_acm->acm_reconfig.streams_info.stream_type.type != CONTENT_TYPE_CONVERSATIONAL) {
+          reconf_info.stream_type.type = p_acm->acm_reconfig.streams_info.stream_type.type;
+          reconf_info.stream_type.audio_context =
+                           p_acm->acm_reconfig.streams_info.stream_type.audio_context;
+          reconf_info.stream_type.direction = p_acm->acm_reconfig.streams_info.stream_type.direction;
+          reconf_info.reconf_type = p_acm->acm_reconfig.streams_info.reconf_type;
+          cfg = peer_.get_peer_media_codec_qos_config();
+          reconf_info.codec_qos_config_pair.push_back(cfg);
+          reconf_streams.push_back(reconf_info);
+        } else {
+          reconf_info.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+          reconf_info.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+          reconf_info.stream_type.direction = ASE_DIRECTION_SRC;
+          reconf_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+          if (peer_.IsStereoHsType()) {
+            SelectCodecQosConfig(peer_.PeerAddress(), BAP, VOICE_CONTEXT, SRC, STEREO_HS_CONFIG_1);
+          } else {
+            SelectCodecQosConfig(peer_.PeerAddress(), BAP, VOICE_CONTEXT, SRC, EB_CONFIG);
+          }
+          cfg = peer_.get_peer_voice_rx_codec_qos_config();
+          print_codec_parameters(cfg.codec_config);
+          print_qos_parameters(cfg.qos_config);
+          reconf_info.codec_qos_config_pair.push_back(cfg);
+          reconf_streams.push_back(reconf_info);
+
+          reconf_info.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+          reconf_info.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+          reconf_info.stream_type.direction = ASE_DIRECTION_SINK;
+          reconf_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+          if (peer_.IsStereoHsType()) {
+            SelectCodecQosConfig(peer_.PeerAddress(), BAP, VOICE_CONTEXT, SRC, STEREO_HS_CONFIG_1);
+          } else {
+            SelectCodecQosConfig(peer_.PeerAddress(), BAP, VOICE_CONTEXT, SRC, EB_CONFIG);
+          }
+          cfg = peer_.get_peer_voice_tx_codec_qos_config();
+          print_codec_parameters(cfg.codec_config);
+          print_qos_parameters(cfg.qos_config);
+          reconf_info.codec_qos_config_pair.push_back(cfg);
+          reconf_streams.push_back(reconf_info);
+
+          peer_.SetPeerVoiceRxState(StreamState::RECONFIGURING);
+          peer_.SetPeerVoiceTxState(StreamState::RECONFIGURING);
+        }
+        if (!sUcastClientInterface) break;
+          sUcastClientInterface->Reconfigure(peer_.PeerAddress(), reconf_streams);
+        peer_.SetFlags(BtifAcmPeer::kFlagPendingReconfigure);
+        peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateReconfiguring);
+    }
+    break;
+    case BTA_ACM_CONFIG_EVT: {
+       tBTIF_ACM* p_acm_data = (tBTIF_ACM*)p_data;
+       uint16_t contextType = p_acm_data->state_info.stream_type.type;
+       uint16_t peer_latency_ms = 0;
+       uint32_t presen_delay = 0;
+       bool is_update_require = false;
+       if (contextType == CONTENT_TYPE_MEDIA) {
+         if (p_acm_data->state_info.stream_type.audio_context == CONTENT_TYPE_MEDIA) {
+           BTIF_TRACE_DEBUG("%s: compare with current media config", __PRETTY_FUNCTION__);
+           is_update_require = compare_codec_config_(current_media_config, p_acm_data->config_info.codec_config);
+         } else if (p_acm_data->state_info.stream_type.audio_context == CONTENT_TYPE_LIVE) {
+           BTIF_TRACE_DEBUG("%s: cache current_recording_config", __PRETTY_FUNCTION__);
+           current_recording_config = p_acm_data->config_info.codec_config;
+         }
+         if (mandatory_codec_selected) {
+           BTIF_TRACE_DEBUG("%s: Mandatory codec selected, do not store config", __PRETTY_FUNCTION__);
+         } else {
+           BTIF_TRACE_DEBUG("%s: store configuration", __PRETTY_FUNCTION__);
+         }
+         //Cache the peer latency in WMCP case
+         if (p_acm_data->state_info.stream_type.audio_context == CONTENT_TYPE_LIVE) {
+           BTIF_TRACE_DEBUG("%s: presentation delay[0] = %x", __func__,
+                            p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[0]);
+           BTIF_TRACE_DEBUG("%s: presentation delay[1] = %x", __func__,
+                            p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[1]);
+           BTIF_TRACE_DEBUG("%s: presentation delay[2] = %x", __func__,
+                            p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[2]);
+           presen_delay = static_cast<uint32_t>(p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[0]) |
+                          static_cast<uint32_t>(p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[1] << 8) |
+                          static_cast<uint32_t>(p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[2] << 16);
+           BTIF_TRACE_DEBUG("%s: presen_delay = %dus", __func__, presen_delay);
+           peer_latency_ms = presen_delay/1000;
+           BTIF_TRACE_DEBUG("%s: s_to_m latency = %dms", __func__,
+                           p_acm_data->config_info.qos_config.cig_config.max_tport_latency_s_to_m);
+           peer_latency_ms += p_acm_data->config_info.qos_config.cig_config.max_tport_latency_s_to_m;
+           peer_.SetPeerLatency(peer_latency_ms);
+           BTIF_TRACE_DEBUG("%s: cached peer Latency = %dms", __func__, peer_.GetPeerLatency());
+         }
+         if (is_update_require) {
+           current_media_config = p_acm_data->config_info.codec_config;
+           BTIF_TRACE_DEBUG("%s: current_media_config.codec_specific_3: %"
+                                 PRIi64, __func__, current_media_config.codec_specific_3);
+           btif_acm_update_lc3q_params(&current_media_config.codec_specific_3, p_acm_data);
+           btif_acm_report_source_codec_state(peer_.PeerAddress(), current_media_config,
+                                              unicast_codecs_capabilities,
+                                              unicast_codecs_capabilities, CONTEXT_TYPE_MUSIC);
+         }
+       } else if (contextType == CONTENT_TYPE_CONVERSATIONAL &&
+                  p_acm_data->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+         BTIF_TRACE_DEBUG("%s: cache current_voice_config", __PRETTY_FUNCTION__);
+         current_voice_config = p_acm_data->config_info.codec_config;
+         BTIF_TRACE_DEBUG("%s: current_voice_config.codec_specific_3: %"
+                               PRIi64, __func__, current_voice_config.codec_specific_3);
+         btif_acm_update_lc3q_params(&current_voice_config.codec_specific_3, p_acm_data);
+         btif_acm_report_source_codec_state(peer_.PeerAddress(), current_voice_config,
+                                            unicast_codecs_capabilities,
+                                            unicast_codecs_capabilities, CONTEXT_TYPE_VOICE);
+       }
+      //Handle BAP START if reconfig comes in mid of streaming
+      //peer_.SetStreamReconfigInfo(p_acm->acm_reconfig);
+      //TODO: local capabilities
+      //CodecConfig record = p_bta_data->acm_reconfig.codec_config;
+      //saving codec config as negotiated parameter as true
+      //btif_pacs_add_record(peer_.PeerAddress(), true, CodecDirection::CODEC_DIR_SRC, &record);
+
+    } break;
+
+    case BTA_ACM_CONN_UPDATE_TIMEOUT_EVT:
+      peer_.SetConnUpdateMode(BtifAcmPeer::kFlagRelaxedMode);
+      break;
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAcmEvent::EventName(event).c_str());
+      return false;
+  }
+  return true;
+}
+
+bool btif_acm_check_if_requested_devices_started() {
+  std::vector<RawAddress>::iterator itr;
+  if ((btif_acm_initiator.locked_devices).size() > 0) {
+    for (itr = (btif_acm_initiator.locked_devices).begin(); itr != (btif_acm_initiator.locked_devices).end(); itr++) {
+      BTIF_TRACE_DEBUG("%s: address =%s", __func__, *itr->ToString().c_str());
+      BtifAcmPeer* peer = btif_acm_initiator.FindPeer(*itr);
+      if ((peer == nullptr) || (peer != nullptr && !peer->IsStreaming())) {
+        break;
+      }
+    }
+    if (itr == (btif_acm_initiator.locked_devices).end()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool btif_acm_check_if_requested_devices_stopped() {
+  std::vector<RawAddress>::iterator itr;
+  if ((btif_acm_initiator.locked_devices).size() > 0) {
+    for (itr = (btif_acm_initiator.locked_devices).begin(); itr != (btif_acm_initiator.locked_devices).end(); itr++) {
+      BTIF_TRACE_DEBUG("%s: address =%s", __func__, *itr->ToString().c_str());
+      BtifAcmPeer* peer = btif_acm_initiator.FindPeer(*itr);
+      if ((peer == nullptr) || (peer != nullptr /*&& !peer->IsSuspended()*/)) {
+        break;
+      }
+    }
+    if (itr == (btif_acm_initiator.locked_devices).end()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void BtifAcmStateMachine::StateStarted::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s, Peer SetId = %d, MusicActiveSetId = %d, ContextType = %d", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str(),
+                   peer_.SetId(), btif_acm_initiator.MusicActiveCSetId(), peer_.GetContextType());
+
+  if(btif_acm_initiator.IsConnUpdateEnabled()) {
+    //Starting the timer for 5 seconds before moving to relaxed state as
+    //stop event or start streaming event moght immediately come
+    //which requires aggresive interval
+    btif_acm_check_and_start_conn_Interval_timer(&peer_);
+  }
+
+  // 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(), BTACM_AUDIO_STATE_STARTED, peer_.GetStreamContextType());
+  if (alarm_is_scheduled(btif_acm_initiator.AcmGroupProcedureTimer())) {
+    btif_acm_check_and_cancel_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+    tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+    if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+       pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+      btif_acm_source_on_suspended(A2DP_CTRL_ACK_SUCCESS);
+    } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+      btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+    } else {
+      BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+    }
+  } else {
+    BTIF_TRACE_DEBUG("%s:no group procedure timer running ACK pending cmd", __func__);
+    tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+    if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+       pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+      btif_acm_source_on_suspended(A2DP_CTRL_ACK_SUCCESS);
+    } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+      btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+    } else {
+      BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+    }
+  }
+#if 0
+  if ((btif_acm_initiator.GetGroupLockStatus(peer_.SetId()) != BtifAcmInitiator::kFlagStatusUnknown) &&
+       alarm_is_scheduled(btif_acm_initiator.AcmGroupProcedureTimer())) {
+    BTIF_TRACE_DEBUG("%s: All locked and start requested device have started, ack mm audio", __func__);
+    //in this case, we need to change channel mode to stereo
+    btif_acm_check_and_cancel_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+    tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+    if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+       pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+      btif_acm_source_on_suspended(A2DP_CTRL_ACK_SUCCESS);
+    } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+      btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+    } else {
+      BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+    }
+    btif_acm_check_and_start_lock_release_timer(btif_acm_initiator.MusicActiveCSetId());
+  }
+
+  //Start the lock release timer here.
+  if ((btif_acm_initiator.MusicActiveCSetId() != INVALID_SET_ID) &&
+      (btif_acm_initiator.GetGroupLockStatus(btif_acm_initiator.MusicActiveCSetId()) == BtifAcmInitiator::kFlagStatusLocked)) {
+    BTIF_TRACE_DEBUG("%s: ", __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+    btif_acm_check_and_start_lock_release_timer(btif_acm_initiator.MusicActiveCSetId());
+  }
+  if (!btif_acm_initiator.IsMusicActiveGroupStarted()) {
+    if (peer_.SetId() == btif_acm_initiator.MusicActiveCSetId())
+      btif_acm_initiator.SetMusicActiveGroupStarted(true);
+  }
+#endif
+
+}
+
+void BtifAcmStateMachine::StateStarted::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+}
+
+bool BtifAcmStateMachine::StateStarted::ProcessEvent(uint32_t event, void* p_data) {
+  tBTIF_ACM* p_acm = (tBTIF_ACM*)p_data;
+
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s music_active_peer=%s voice_active_peer=%s",
+                   __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+                   BtifAcmEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str(),
+                   logbool(peer_.IsPeerActiveForMusic()).c_str(),
+                   logbool(peer_.IsPeerActiveForVoice()).c_str());
+
+  switch (event) {
+    case BTIF_ACM_STOP_STREAM_REQ_EVT:
+    case BTIF_ACM_SUSPEND_STREAM_REQ_EVT: {
+      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+               peer_.PeerAddress().ToString().c_str(),
+               BtifAcmEvent::EventName(event).c_str(),
+               peer_.FlagsToString().c_str());
+      peer_.SetFlags(BtifAcmPeer::kFlagPendingLocalSuspend);
+
+      StreamType type_1;
+      std::vector<StreamType> stop_streams;
+      if (peer_.GetStreamContextType() == CONTEXT_TYPE_MUSIC) {
+        if (current_active_profile_type != WMCP) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_MEDIA,
+                    .direction = ASE_DIRECTION_SINK
+                   };
+          stop_streams.push_back(type_1);
+        } else {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_LIVE,
+                    .direction = ASE_DIRECTION_SRC
+                   };
+          stop_streams.push_back(type_1);
+        }
+      } else if (peer_.GetStreamContextType() == CONTEXT_TYPE_VOICE) {
+        StreamType type_2;
+        type_1 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                  .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                  .direction = ASE_DIRECTION_SINK
+                 };
+        type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                  .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                  .direction = ASE_DIRECTION_SRC
+                 };
+        stop_streams.push_back(type_2);
+        stop_streams.push_back(type_1);
+      }
+      if(btif_acm_initiator.IsConnUpdateEnabled()) {
+        //Cancel the timer if start streamng comes before
+        // 5 seconds while moving the interval to relaxed mode.
+        if (alarm_is_scheduled(btif_acm_initiator.AcmConnIntervalTimer())) {
+           btif_acm_check_and_cancel_conn_Interval_timer();
+        }
+        else {
+          peer_.SetConnUpdateMode(BtifAcmPeer::kFlagAggresiveMode);
+        }
+      }
+
+      if (!sUcastClientInterface) break;
+        sUcastClientInterface->Stop(peer_.PeerAddress(), stop_streams);
+    }
+    break;
+
+    case BTIF_ACM_DISCONNECT_REQ_EVT: {
+      int contextType = p_acm->state_info.stream_type.type;
+      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s contextType=%d", __PRETTY_FUNCTION__,
+               peer_.PeerAddress().ToString().c_str(),
+               BtifAcmEvent::EventName(event).c_str(),
+               peer_.FlagsToString().c_str(), contextType);
+
+      tBTIF_ACM_CONN_DISC* p_bta_data = (tBTIF_ACM_CONN_DISC*)p_data;
+      std::vector<StreamType> disconnect_streams;
+      if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC) {
+        StreamType type_1;
+        if (p_bta_data->profileType & (BAP|GCP)) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_MEDIA,
+                    .direction = ASE_DIRECTION_SINK
+                   };
+          disconnect_streams.push_back(type_1);
+        }
+        if (p_bta_data->profileType & WMCP) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_LIVE,
+                    .direction = ASE_DIRECTION_SRC
+                   };
+          disconnect_streams.push_back(type_1);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_MUSIC_VOICE) {
+        StreamType type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SRC
+                            };
+        StreamType type_3 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SINK
+                            };
+        disconnect_streams.push_back(type_3);
+        disconnect_streams.push_back(type_2);
+        StreamType type_1;
+        if (p_bta_data->profileType & (BAP|GCP)) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_MEDIA,
+                    .direction = ASE_DIRECTION_SINK
+                    };
+          disconnect_streams.push_back(type_1);
+        }
+        if (p_bta_data->profileType & WMCP) {
+          type_1 = {.type = CONTENT_TYPE_MEDIA,
+                    .audio_context = CONTENT_TYPE_LIVE,
+                    .direction = ASE_DIRECTION_SRC
+                    };
+          disconnect_streams.push_back(type_1);
+        }
+      } else if (p_bta_data->contextType == CONTEXT_TYPE_VOICE) {
+        StreamType type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SRC
+                            };
+        StreamType type_3 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                             .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                             .direction = ASE_DIRECTION_SINK
+                            };
+        disconnect_streams.push_back(type_3);
+        disconnect_streams.push_back(type_2);
+      }
+      LOG(WARNING) << __func__ << " size of disconnect_streams " << disconnect_streams.size();
+      if (!sUcastClientInterface) break;
+      sUcastClientInterface->Disconnect(peer_.PeerAddress(), disconnect_streams);
+
+      // Inform the application that we are disconnecting
+      if ((p_bta_data->contextType == CONTEXT_TYPE_MUSIC) && ((peer_.GetPeerVoiceRxState() == StreamState::CONNECTED) ||
+          (peer_.GetPeerVoiceTxState() == StreamState::CONNECTED))) {
+        LOG(WARNING) << __func__ << " voice connected move in opened state ";
+        peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+      } else if ((p_bta_data->contextType == CONTEXT_TYPE_VOICE) && ((peer_.GetPeerMusicTxState() == StreamState::CONNECTED) ||
+          (peer_.GetPeerMusicRxState() == StreamState::CONNECTED))) {
+        LOG(WARNING) << __func__ << " Music connected remain in started state ";
+      } else {
+        LOG(WARNING) << __func__ << " Move in closing state ";
+        peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+      }
+    }
+    break;
+
+    case BTA_ACM_STOP_EVT: {
+      int contextType = p_acm->state_info.stream_type.type;
+      LOG_INFO(LOG_TAG, "%s: Peer %s : event=%s flags=%s", __PRETTY_FUNCTION__,
+               peer_.PeerAddress().ToString().c_str(),
+               BtifAcmEvent::EventName(event).c_str(),
+               peer_.FlagsToString().c_str());
+      if (contextType == CONTENT_TYPE_MEDIA) {
+        BTIF_TRACE_DEBUG("%s: STOPPING event came from BAP for Media, ignore", __func__);
+      } else if (contextType == CONTENT_TYPE_CONVERSATIONAL) {
+        BTIF_TRACE_DEBUG("%s: STOPPING event came from BAP for Voice, ignore", __func__);
+      }
+    }
+    break;
+
+    case BTA_ACM_DISCONNECT_EVT: {
+      int context_type = p_acm->state_info.stream_type.type;
+      if (p_acm->state_info.stream_state == StreamState::DISCONNECTED) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          }
+          if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+              peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            btif_report_connection_state(peer_.PeerAddress(),
+                BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_MUSIC);
+          }
+          if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when Voice Tx+Rx & Media Rx/Tx was disconnected, move in idle state", __func__);
+            peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+          } else {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when either Voice Tx or Rx or Media Rx/Tx is connected, remain in started state", __func__);
+          }
+        } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx, Music Tx & Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx is disconnected but music Tx or Rx still not disconnected,"
+                                 " remain in started state", __func__);
+              }
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Tx, Music Tx & Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Rx is disconnected but music Tx or Rx still not disconnected,"
+                                 " remain in started state", __func__);
+              }
+            }
+          }
+        }
+      } else if (p_acm->state_info.stream_state == StreamState::DISCONNECTING) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          }
+          if ((peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTING) &&
+               (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTING) &&
+               (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING) &&
+               (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING)) {
+              BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnecting state from BAP"
+                               " when Voice Tx+Rx and Media Rx/Tx disconnected/ing, move in closing state", __func__);
+              peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+          } else {
+              if (peer_.GetStreamContextType() == CONTEXT_TYPE_MUSIC) {
+                std::vector<StreamType> disconnect_streams;
+                btif_report_audio_state(peer_.PeerAddress(), BTACM_AUDIO_STATE_STOPPED, CONTEXT_TYPE_MUSIC);
+                BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnecting state from BAP while streaming"
+                    " when either Voice Tx or Rx or Media Rx/Tx is connected, move to opened state", __func__);
+                if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING) {
+                    BTIF_TRACE_DEBUG("%s: Received disconnecting for Music-Tx, initiate for Rx also", __func__);
+                    StreamType type_1;
+                    type_1 = {.type = CONTENT_TYPE_MEDIA,
+                              .audio_context = CONTENT_TYPE_LIVE,
+                              .direction = ASE_DIRECTION_SRC
+                             };
+                    disconnect_streams.push_back(type_1);
+                    if (!sUcastClientInterface) break;
+                    sUcastClientInterface->Disconnect(peer_.PeerAddress(), disconnect_streams);
+                }
+                if (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING) {
+                    BTIF_TRACE_DEBUG("%s: Received disconnecting for Music-Rx, initiate for Tx also", __func__);
+                    StreamType type_1;
+                    type_1 = {.type = CONTENT_TYPE_MEDIA,
+                              .audio_context = CONTENT_TYPE_MEDIA,
+                              .direction = ASE_DIRECTION_SINK
+                             };
+                    disconnect_streams.push_back(type_1);
+                    if (!sUcastClientInterface) break;
+                    sUcastClientInterface->Disconnect(peer_.PeerAddress(), disconnect_streams);
+                }
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnecting state from BAP"
+                    " when either Voice Tx or Rx or Media Rx/Tx is connected, remain in started state", __func__);
+              }
+          }
+          btif_report_connection_state(peer_.PeerAddress(),
+                                  BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_MUSIC);
+        } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTING ||
+                peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED) {
+              if (((peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING)) &&
+                  ((peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING))) {
+                BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Tx,"
+                                 " voice Rx, music Tx+Rx are disconnected/ing move in closing state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+              } else {
+                if (peer_.GetStreamContextType() == CONTEXT_TYPE_VOICE) {
+                  btif_report_audio_state(peer_.PeerAddress(), BTACM_AUDIO_STATE_STOPPED, CONTEXT_TYPE_VOICE);
+                  BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Tx while streaming,"
+                                   " voice Rx is disconncted/ing but music Tx or Rx still not disconnected/ing,"
+                                   " move to opened state", __func__);
+                  peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+                } else {
+                  BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Tx,"
+                                   " voice Rx is disconncted/ing but music Tx or Rx still not disconnected/ing,"
+                                   " remain in started state", __func__);
+                }
+              }
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_VOICE);
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTING ||
+                peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED) {
+              if (((peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING)) &&
+                  ((peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING))) {
+                BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Rx,"
+                                 " voice Tx, music Tx+Rx are disconnected/ing move in closing state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+              } else {
+                if (peer_.GetStreamContextType() == CONTEXT_TYPE_VOICE) {
+                  btif_report_audio_state(peer_.PeerAddress(), BTACM_AUDIO_STATE_STOPPED, CONTEXT_TYPE_VOICE);
+                  BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx while streaming,"
+                                   " voice Tx is disconncted/ing but music Tx or Rx still not disconnected/ing,"
+                                   " move to Opened state", __func__);
+                  peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+                } else {
+                  BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                   " voice Tx is disconncted/ing but music Tx or Rx still not disconnected/ing,"
+                                   " remain in started state", __func__);
+                }
+              }
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_VOICE);
+            }
+          }
+        }
+      }
+    }
+    break;
+
+    case BTA_ACM_CONNECT_EVT: {// above evnt can come and handle for voice/media case
+      int contextType = p_acm->state_info.stream_type.type;
+      LOG_INFO(
+          LOG_TAG, "%s: Peer %s : event=%s context=%d",
+          __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+          BtifAcmEvent::EventName(event).c_str(), contextType);
+      LOG_INFO(
+          LOG_TAG, "%s: context=%d, converted=%d, Streaming context=%d",
+          __PRETTY_FUNCTION__, contextType, btif_acm_bap_to_acm_context(contextType), peer_.GetStreamContextType());
+      if (btif_acm_bap_to_acm_context(contextType) != peer_.GetStreamContextType()) {
+        if (contextType == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            if (p_acm->state_info.stream_state == StreamState::CONNECTED) {
+              BTIF_TRACE_DEBUG("%s: received connected state from BAP for Music Rx, update state", __func__);
+              peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+            } else if (p_acm->state_info.stream_state == StreamState::CONNECTING){
+              BTIF_TRACE_DEBUG("%s: received connecting state from BAP for Music Rx, ignore", __func__);
+              peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            if (p_acm->state_info.stream_state == StreamState::CONNECTED) {
+              BTIF_TRACE_DEBUG("%s: received connected state from BAP for Music Tx, update state", __func__);
+              peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+            } else if (p_acm->state_info.stream_state == StreamState::CONNECTING){
+              BTIF_TRACE_DEBUG("%s: received connecting state from BAP for Music Tx, ignore", __func__);
+              peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+            }
+          }
+          if (p_acm->state_info.stream_state == StreamState::CONNECTED)
+            btif_report_connection_state(peer_.PeerAddress(),
+                    BTACM_CONNECTION_STATE_CONNECTED, CONTEXT_TYPE_MUSIC);
+        } else if (contextType == CONTENT_TYPE_CONVERSATIONAL) {
+          if (p_acm->state_info.stream_state == StreamState::CONNECTED) {
+            BTIF_TRACE_DEBUG("%s: voice context connected, remain in started state", __func__);
+            if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+              peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+              if (peer_.GetPeerVoiceRxState() == StreamState::CONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received connected state from BAP for voice Tx, update state", __func__);
+                btif_report_connection_state(peer_.PeerAddress(),
+                                             BTACM_CONNECTION_STATE_CONNECTED, CONTEXT_TYPE_VOICE);
+              }
+            } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+              peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+              if (peer_.GetPeerVoiceTxState() == StreamState::CONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received connected state from BAP for voice Rx, update state", __func__);
+                btif_report_connection_state(peer_.PeerAddress(),
+                                             BTACM_CONNECTION_STATE_CONNECTED, CONTEXT_TYPE_VOICE);
+              }
+            }
+          } else if (p_acm->state_info.stream_state == StreamState::CONNECTING) {
+            BTIF_TRACE_DEBUG("%s: received connecting state from BAP for voice Tx or Rx, ignore", __func__);
+            if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+              peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+              peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            }
+          }
+        }
+      } else {
+        if (peer_.CheckFlags(BtifAcmPeer::kFlagPendingLocalSuspend)) {
+          peer_.ClearFlags(BtifAcmPeer::kFlagPendingLocalSuspend);
+          BTIF_TRACE_DEBUG("%s: peer device is suspended, send MM any pending ACK", __func__);
+          tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+          if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+            pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+            btif_acm_source_on_suspended(A2DP_CTRL_ACK_SUCCESS);
+          } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+            btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+          } else {
+            BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+          }
+          BTIF_TRACE_DEBUG("%s: report STOP to apps and move to Opened", __func__);
+          btif_report_audio_state(peer_.PeerAddress(), BTACM_AUDIO_STATE_STOPPED, peer_.GetStreamContextType());
+          peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+        }
+      }
+      if (alarm_is_scheduled(btif_acm_initiator.AcmGroupProcedureTimer()))
+        btif_acm_check_and_cancel_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+
+    } break;
+
+    case BTIF_ACM_RECONFIG_REQ_EVT: {
+        BTIF_TRACE_DEBUG("%s: sending stop to BAP before reconfigure", __func__);
+        btif_a2dp_source_end_session(active_bda);
+        peer_.SetFlags(BtifAcmPeer::kFlagPendingLocalSuspend);
+        StreamType type_1;
+        std::vector<StreamType> stop_streams;
+        if (peer_.GetStreamContextType() == CONTEXT_TYPE_MUSIC) {
+          if (current_active_profile_type != WMCP) {
+            type_1 = {.type = CONTENT_TYPE_MEDIA,
+                      .audio_context = CONTENT_TYPE_MEDIA,
+                      .direction = ASE_DIRECTION_SINK
+                     };
+            stop_streams.push_back(type_1);
+          } else {
+            type_1 = {.type = CONTENT_TYPE_MEDIA,
+                      .audio_context = CONTENT_TYPE_LIVE,
+                      .direction = ASE_DIRECTION_SRC
+                     };
+            stop_streams.push_back(type_1);
+          }
+        } else if (peer_.GetStreamContextType() == CONTEXT_TYPE_VOICE) {
+          StreamType type_2;
+          type_1 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                    .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                    .direction = ASE_DIRECTION_SINK
+                   };
+          type_2 = {.type = CONTENT_TYPE_CONVERSATIONAL,
+                    .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                    .direction = ASE_DIRECTION_SRC
+                   };
+          stop_streams.push_back(type_2);
+          stop_streams.push_back(type_1);
+        }
+        if (!sUcastClientInterface) break;
+          sUcastClientInterface->Stop(peer_.PeerAddress(), stop_streams);
+        peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateReconfiguring);
+    }
+    break;
+
+    case BTA_ACM_CONFIG_EVT: {
+       tBTIF_ACM* p_acm_data = (tBTIF_ACM*)p_data;
+       uint16_t contextType = p_acm_data->state_info.stream_type.type;
+       uint16_t peer_latency_ms = 0;
+       uint32_t presen_delay = 0;
+       bool is_update_require = false;
+       if (contextType == CONTENT_TYPE_MEDIA) {
+         if (p_acm_data->state_info.stream_type.audio_context == CONTENT_TYPE_MEDIA) {
+           BTIF_TRACE_DEBUG("%s: compare current_media_config", __PRETTY_FUNCTION__);
+           is_update_require = compare_codec_config_(current_media_config, p_acm_data->config_info.codec_config);
+         } else if (p_acm_data->state_info.stream_type.audio_context == CONTENT_TYPE_LIVE) {
+           BTIF_TRACE_DEBUG("%s: cache current_recording_config", __PRETTY_FUNCTION__);
+           current_recording_config = p_acm_data->config_info.codec_config;
+         }
+         if (mandatory_codec_selected) {
+           BTIF_TRACE_DEBUG("%s: Mandatory codec selected, do not store config", __PRETTY_FUNCTION__);
+         } else {
+           BTIF_TRACE_DEBUG("%s: store configuration", __PRETTY_FUNCTION__);
+         }
+         //Cache the peer latency in WMCP case
+         if (p_acm_data->state_info.stream_type.audio_context == CONTENT_TYPE_LIVE) {
+           BTIF_TRACE_DEBUG("%s: presentation delay[0] = %x", __func__,
+                            p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[0]);
+           BTIF_TRACE_DEBUG("%s: presentation delay[1] = %x", __func__,
+                            p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[1]);
+           BTIF_TRACE_DEBUG("%s: presentation delay[2] = %x", __func__,
+                            p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[2]);
+           presen_delay = static_cast<uint32_t>(p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[0]) |
+                          static_cast<uint32_t>(p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[1] << 8) |
+                          static_cast<uint32_t>(p_acm_data->config_info.qos_config.ascs_configs[0].presentation_delay[2] << 16);
+           BTIF_TRACE_DEBUG("%s: presen_delay = %dus", __func__, presen_delay);
+           peer_latency_ms = presen_delay/1000;
+           BTIF_TRACE_DEBUG("%s: s_to_m latency = %dms", __func__,
+                           p_acm_data->config_info.qos_config.cig_config.max_tport_latency_s_to_m);
+           peer_latency_ms += p_acm_data->config_info.qos_config.cig_config.max_tport_latency_s_to_m;
+           peer_.SetPeerLatency(peer_latency_ms);
+           BTIF_TRACE_DEBUG("%s: cached peer Latency = %dms", __func__, peer_.GetPeerLatency());
+         }
+         if (is_update_require) {
+           current_media_config = p_acm_data->config_info.codec_config;
+           BTIF_TRACE_DEBUG("%s: current_media_config.codec_specific_3: %"
+                                 PRIi64, __func__, current_media_config.codec_specific_3);
+           btif_acm_update_lc3q_params(&current_media_config.codec_specific_3, p_acm_data);
+           btif_acm_report_source_codec_state(peer_.PeerAddress(), current_media_config,
+                                              unicast_codecs_capabilities,
+                                              unicast_codecs_capabilities, CONTEXT_TYPE_MUSIC);
+         }
+       } else if (contextType == CONTENT_TYPE_CONVERSATIONAL &&
+                  p_acm_data->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+         BTIF_TRACE_DEBUG("%s: cache current_voice_config", __PRETTY_FUNCTION__);
+         current_voice_config = p_acm_data->config_info.codec_config;
+         BTIF_TRACE_DEBUG("%s: current_voice_config.codec_specific_3: %"
+                               PRIi64, __func__, current_voice_config.codec_specific_3);
+         btif_acm_update_lc3q_params(&current_voice_config.codec_specific_3, p_acm_data);
+         btif_acm_report_source_codec_state(peer_.PeerAddress(), current_voice_config,
+                                            unicast_codecs_capabilities,
+                                            unicast_codecs_capabilities, CONTEXT_TYPE_VOICE);
+       }
+      //Handle BAP START if reconfig comes in mid of streaming
+      //peer_.SetStreamReconfigInfo(p_acm->acm_reconfig);
+      //TODO: local capabilities
+      //CodecConfig record = p_bta_data->acm_reconfig.codec_config;
+      //saving codec config as negotiated parameter as true
+      //btif_pacs_add_record(peer_.PeerAddress(), true, CodecDirection::CODEC_DIR_SRC, &record);
+
+    } break;
+
+    case BTA_ACM_CONN_UPDATE_TIMEOUT_EVT:
+      peer_.SetConnUpdateMode(BtifAcmPeer::kFlagRelaxedMode);
+      break;
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAcmEvent::EventName(event).c_str());
+      return false;
+  }
+
+  return true;
+}
+
+void BtifAcmStateMachine::StateReconfiguring::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+  if(btif_acm_initiator.IsConnUpdateEnabled()) {
+    //Cancel the timer if running if  not, move to aggressive mode
+    if (alarm_is_scheduled(btif_acm_initiator.AcmConnIntervalTimer())) {
+       btif_acm_check_and_cancel_conn_Interval_timer();
+    } else {
+       BTIF_TRACE_DEBUG("%s: conn timer not running, push aggressive intervals", __func__);
+       peer_.SetConnUpdateMode(BtifAcmPeer::kFlagAggresiveMode);
+    }
+  }
+}
+
+void BtifAcmStateMachine::StateReconfiguring::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+}
+
+bool BtifAcmStateMachine::StateReconfiguring::ProcessEvent(uint32_t event, void* p_data) {
+  tBTIF_ACM* p_acm = (tBTIF_ACM*)p_data;
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s music_active_peer=%s voice_active_peer=%s",
+                   __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+                   BtifAcmEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str(),
+                   logbool(peer_.IsPeerActiveForMusic()).c_str(),
+                   logbool(peer_.IsPeerActiveForVoice()).c_str());
+
+  switch (event) {
+    case BTIF_ACM_SUSPEND_STREAM_REQ_EVT:
+
+    case BTA_ACM_STOP_EVT: {
+        BTIF_TRACE_DEBUG("%s: STOPPING event from BAP, ignore", __func__);
+    } break;
+
+    case BTA_ACM_RECONFIG_EVT: {
+        BTIF_TRACE_DEBUG("%s: received reconfiguring state from BAP, ignore", __func__);
+    } break;
+
+    case BTA_ACM_CONFIG_EVT: {
+       uint16_t contextType = p_acm->state_info.stream_type.type;
+       uint16_t peer_latency_ms = 0;
+       uint32_t presen_delay = 0;
+       bool is_update_require = false;
+       if (contextType == CONTENT_TYPE_MEDIA) {
+         if (p_acm->state_info.stream_type.audio_context == CONTENT_TYPE_MEDIA) {
+           BTIF_TRACE_DEBUG("%s: compare current_media_config", __PRETTY_FUNCTION__);
+           is_update_require = compare_codec_config_(current_media_config, p_acm->config_info.codec_config);
+         } else if (p_acm->state_info.stream_type.audio_context == CONTENT_TYPE_LIVE) {
+           BTIF_TRACE_DEBUG("%s: cache current_recording_config", __PRETTY_FUNCTION__);
+           current_recording_config = p_acm->config_info.codec_config;
+         }
+         //Cache the peer latency in WMCP case
+         if (peer_.GetRcfgProfileType() == WMCP) {
+           BTIF_TRACE_DEBUG("%s: presentation delay[0] = %x", __func__,
+                            p_acm->config_info.qos_config.ascs_configs[0].presentation_delay[0]);
+           BTIF_TRACE_DEBUG("%s: presentation delay[1] = %x", __func__,
+                            p_acm->config_info.qos_config.ascs_configs[0].presentation_delay[1]);
+           BTIF_TRACE_DEBUG("%s: presentation delay[2] = %x", __func__,
+                            p_acm->config_info.qos_config.ascs_configs[0].presentation_delay[2]);
+           presen_delay = static_cast<uint32_t>(p_acm->config_info.qos_config.ascs_configs[0].presentation_delay[0]) |
+                          static_cast<uint32_t>(p_acm->config_info.qos_config.ascs_configs[0].presentation_delay[1] << 8) |
+                          static_cast<uint32_t>(p_acm->config_info.qos_config.ascs_configs[0].presentation_delay[2] << 16);
+           BTIF_TRACE_DEBUG("%s: presen_delay = %dus", __func__, presen_delay);
+           peer_latency_ms = presen_delay/1000;
+           BTIF_TRACE_DEBUG("%s: s_to_m latency = %dms", __func__,
+                           p_acm->config_info.qos_config.cig_config.max_tport_latency_s_to_m);
+           peer_latency_ms += p_acm->config_info.qos_config.cig_config.max_tport_latency_s_to_m;
+           peer_.SetPeerLatency(peer_latency_ms);
+           BTIF_TRACE_DEBUG("%s: cached peer Latency = %dms", __func__, peer_.GetPeerLatency());
+         }
+         if (is_update_require) {
+           current_media_config = p_acm->config_info.codec_config;
+           BTIF_TRACE_DEBUG("%s: current_media_config.codec_specific_3: %"
+                                 PRIi64, __func__, current_media_config.codec_specific_3);
+           btif_acm_update_lc3q_params(&current_media_config.codec_specific_3, p_acm);
+           btif_acm_report_source_codec_state(peer_.PeerAddress(), current_media_config,
+                                              unicast_codecs_capabilities,
+                                              unicast_codecs_capabilities, CONTEXT_TYPE_MUSIC);
+         }
+       } else if (contextType == CONTENT_TYPE_CONVERSATIONAL) {
+         BTIF_TRACE_DEBUG("%s: cache current_voice_config");
+         current_voice_config = p_acm->config_info.codec_config;
+         BTIF_TRACE_DEBUG("%s: current_voice_config.codec_specific_3: %"
+                               PRIi64, __func__, current_voice_config.codec_specific_3);
+         btif_acm_update_lc3q_params(&current_voice_config.codec_specific_3, p_acm);
+       }
+    } break;
+
+    case BTA_ACM_CONNECT_EVT: {
+        uint8_t status = (uint8_t)p_acm->state_info.stream_state;
+        LOG_INFO(
+            LOG_TAG, "%s: Peer %s : event=%s flags=%s status=%d",
+            __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+            BtifAcmEvent::EventName(event).c_str(), peer_.FlagsToString().c_str(),
+            status);
+        if (peer_.CheckFlags(BtifAcmPeer::kFlagPendingReconfigure)) {
+          if (p_acm->state_info.stream_state == StreamState::CONNECTED) {
+            if (p_acm->state_info.stream_type.type == CONTENT_TYPE_MEDIA) {
+              BTIF_TRACE_DEBUG("%s: Reconfig complete, move in opened state", __func__);
+              peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+            } else {
+              if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+                peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+                if (peer_.GetPeerVoiceRxState() == StreamState::CONNECTED) {
+                  BTIF_TRACE_DEBUG("%s: Report Call audio config to apps? move to opened when both Voice Tx and Rx done", __func__);
+                  BTIF_TRACE_DEBUG("%s: received connected state from BAP for voice Tx, move in opened state", __func__);
+                  peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+                }
+              } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+                peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+                if (peer_.GetPeerVoiceTxState() == StreamState::CONNECTED) {
+                  BTIF_TRACE_DEBUG("%s: Report Call audio config to apps? move to opened when both Voice Tx and Rx done", __func__);
+                  BTIF_TRACE_DEBUG("%s: received connected state from BAP for voice Rx, move in opened state", __func__);
+                  peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateOpened);
+                }
+              }
+            }
+          }
+          break;
+        }
+        if (peer_.CheckFlags(BtifAcmPeer::kFlagPendingLocalSuspend)) {
+          peer_.ClearFlags(BtifAcmPeer::kFlagPendingLocalSuspend);
+          BTIF_TRACE_DEBUG("%s: peer device is suspended, send MM any pending ACK", __func__);
+          tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+          if (pending_cmd == A2DP_CTRL_CMD_STOP || pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+            btif_acm_source_on_suspended(A2DP_CTRL_ACK_SUCCESS);
+          } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+            btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+          } else {
+           BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+          }
+          if (alarm_is_scheduled(btif_acm_initiator.AcmGroupProcedureTimer()))
+            btif_acm_check_and_cancel_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+          btif_report_audio_state(peer_.PeerAddress(), BTACM_AUDIO_STATE_STOPPED, peer_.GetStreamContextType());
+          std::vector<StreamReconfig> reconf_streams;
+          StreamReconfig reconf_info;
+          CodecQosConfig cfg;
+          reconf_info.stream_type.type = CONTENT_TYPE_MEDIA;
+          // TODO to change audio context based on use case ( media or gaming or Live audio)
+          if (peer_.GetRcfgProfileType() != WMCP) {
+            reconf_info.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+            reconf_info.stream_type.direction = ASE_DIRECTION_SINK;
+          } else {
+            reconf_info.stream_type.audio_context = CONTENT_TYPE_LIVE;
+            reconf_info.stream_type.direction = ASE_DIRECTION_SRC;
+          }
+          reconf_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+          cfg = peer_.get_peer_media_codec_qos_config();
+          reconf_info.codec_qos_config_pair.push_back(cfg);
+          reconf_streams.push_back(reconf_info);
+          peer_.SetFlags(BtifAcmPeer::kFlagPendingReconfigure);
+          if (!sUcastClientInterface) break;
+            sUcastClientInterface->Reconfigure(peer_.PeerAddress(), reconf_streams);
+        }
+    } break;
+
+    case BTA_ACM_DISCONNECT_EVT: {
+      int context_type = p_acm->state_info.stream_type.type;
+      if (p_acm->state_info.stream_state == StreamState::DISCONNECTED) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          }
+          if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+              peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            btif_report_connection_state(peer_.PeerAddress(),
+                BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_MUSIC);
+          }
+          if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when Voice Tx+Rx & Media Rx/Tx was disconnected, move in idle state", __func__);
+            peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+          } else {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when either Voice Tx or Rx or Media Rx/Tx is connected, remain in reconfiguring state", __func__);
+          }
+        } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx, Music Tx & Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx is disconnected but music Tx or Rx still not disconnected,"
+                                 " remain in reconfiguring state", __func__);
+              }
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Tx, Music Tx & Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Rx is disconnected but music Tx or Rx still not disconnected,"
+                                 " remain in reconfiguring state", __func__);
+              }
+            }
+          }
+        }
+      } else if (p_acm->state_info.stream_state == StreamState::DISCONNECTING) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          }
+          btif_report_connection_state(peer_.PeerAddress(),
+                                  BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_MUSIC);
+          if ((peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTING) &&
+               (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTING) &&
+               (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING) &&
+               (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED ||
+                peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING)) {
+              BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnecting state from BAP"
+                               " when Voice Tx+Rx and Media Rx/Tx disconnected/ing, move in closing state", __func__);
+              peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+          } else {
+              BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnecting state from BAP"
+                               " when either Voice Tx or Rx or Media Rx/Tx is connected, remain in reconfiguring state", __func__);
+          }
+        } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTING ||
+                peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_VOICE);
+              if (((peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING)) &&
+                  ((peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING))) {
+                BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Tx,"
+                                 " voice Rx, music Tx+Rx are disconnected/ing move in closing state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Tx,"
+                                 " voice Rx is disconncted/ing but music Tx or Rx still not disconnected/ing,"
+                                 " remain in reconfiguring state", __func__);
+              }
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTING ||
+                peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTING, CONTEXT_TYPE_VOICE);
+              if (((peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTING)) &&
+                  ((peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) ||
+                  (peer_.GetPeerMusicRxState() == StreamState::DISCONNECTING))) {
+                BTIF_TRACE_DEBUG("%s: received disconnecting state from BAP for voice Rx,"
+                                 " voice Tx, music Tx+Rx are disconnected/ing move in closing state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateClosing);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Tx is disconncted/ing but music Tx or Rx still not disconnected/ing,"
+                                 " remain in reconfiguring state", __func__);
+              }
+            }
+          }
+        }
+      }
+    } break;
+
+    case BTA_ACM_CONN_UPDATE_TIMEOUT_EVT:
+      peer_.SetConnUpdateMode(BtifAcmPeer::kFlagRelaxedMode);
+      break;
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAcmEvent::EventName(event).c_str());
+      return false;
+  }
+  return true;
+}
+
+void BtifAcmStateMachine::StateClosing::OnEnter() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+  if(btif_acm_initiator.IsConnUpdateEnabled()) {
+    //Cancel the timer if running if  not, move to aggressive mode
+    if (alarm_is_scheduled(btif_acm_initiator.AcmConnIntervalTimer())) {
+      btif_acm_check_and_cancel_conn_Interval_timer();
+    }
+    else {
+      BTIF_TRACE_DEBUG("%s: conn timer not running, push aggressive intervals", __func__);
+      peer_.SetConnUpdateMode(BtifAcmPeer::kFlagAggresiveMode);
+    }
+  }
+
+}
+
+void BtifAcmStateMachine::StateClosing::OnExit() {
+  BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
+                   peer_.PeerAddress().ToString().c_str());
+}
+
+bool BtifAcmStateMachine::StateClosing::ProcessEvent(uint32_t event, void* p_data) {
+  tBTIF_ACM* p_acm = (tBTIF_ACM*)p_data;
+  BTIF_TRACE_DEBUG("%s: Peer %s : event=%s flags=%s music_active_peer=%s voice_active_peer=%s",
+                   __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str(),
+                   BtifAcmEvent::EventName(event).c_str(),
+                   peer_.FlagsToString().c_str(),
+                   logbool(peer_.IsPeerActiveForMusic()).c_str(),
+                   logbool(peer_.IsPeerActiveForVoice()).c_str());
+
+  switch (event) {
+    case BTIF_ACM_SUSPEND_STREAM_REQ_EVT:
+    case BTIF_ACM_START_STREAM_REQ_EVT:
+    case BTA_ACM_STOP_EVT:
+    case BTIF_ACM_STOP_STREAM_REQ_EVT:
+      break;
+
+    case BTA_ACM_DISCONNECT_EVT: {
+      int context_type = p_acm->state_info.stream_type.type;
+      if (p_acm->state_info.stream_state == StreamState::DISCONNECTED) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+          }
+          if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+              peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            btif_report_connection_state(peer_.PeerAddress(),
+                BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_MUSIC);
+          }
+          if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+               peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when Voice Tx+Rx & Media Rx/Tx was disconnected, move in idle state", __func__);
+            peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+          } else {
+            BTIF_TRACE_DEBUG("%s: received Media Tx/Rx disconnected state from BAP"
+                      " when either Voice Tx or Rx or Media Rx/Tx is connected, remain in closing state", __func__);
+          }
+        } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceRxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx, Music Tx & Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Tx,"
+                                 " voice Rx is disconnected but music Tx or Rx still not disconnected,"
+                                 " remain in closing state", __func__);
+              }
+            }
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+            if (peer_.GetPeerVoiceTxState() == StreamState::DISCONNECTED) {
+              btif_report_connection_state(peer_.PeerAddress(), BTACM_CONNECTION_STATE_DISCONNECTED, CONTEXT_TYPE_VOICE);
+              if (peer_.GetPeerMusicTxState() == StreamState::DISCONNECTED &&
+                  peer_.GetPeerMusicRxState() == StreamState::DISCONNECTED) {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Tx, Music Tx & Rx are disconnected move in idle state", __func__);
+                peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+              } else {
+                BTIF_TRACE_DEBUG("%s: received disconnected state from BAP for voice Rx,"
+                                 " voice Rx is disconnected but music Tx or Rx still not disconnected,"
+                                 " remain in closing state", __func__);
+              }
+            }
+          }
+        }
+      } else if (p_acm->state_info.stream_state == StreamState::DISCONNECTING) {
+        if (context_type == CONTENT_TYPE_MEDIA) {
+          BTIF_TRACE_DEBUG("%s: received Music Tx or Rx disconnecting state from BAP, ignore", __func__);
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK)
+            peer_.SetPeerMusicTxState(p_acm->state_info.stream_state);
+          else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC)
+            peer_.SetPeerMusicRxState(p_acm->state_info.stream_state);
+        } else if (context_type == CONTENT_TYPE_CONVERSATIONAL) {
+          BTIF_TRACE_DEBUG("%s: received voice Tx or Rx disconnecting state from BAP, ignore", __func__);
+          if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SINK) {
+            peer_.SetPeerVoiceTxState(p_acm->state_info.stream_state);
+          } else if (p_acm->state_info.stream_type.direction == ASE_DIRECTION_SRC) {
+            peer_.SetPeerVoiceRxState(p_acm->state_info.stream_state);
+          }
+        }
+      }
+    }
+    break;
+
+    case BTA_ACM_CONN_UPDATE_TIMEOUT_EVT:
+      peer_.SetConnUpdateMode(BtifAcmPeer::kFlagRelaxedMode);
+      break;
+
+    default:
+      BTIF_TRACE_WARNING("%s: Peer %s : Unhandled event=%s",
+                         __PRETTY_FUNCTION__,
+                         peer_.PeerAddress().ToString().c_str(),
+                         BtifAcmEvent::EventName(event).c_str());
+      peer_.StateMachine().TransitionTo(BtifAcmStateMachine::kStateIdle);
+      return false;
+  }
+  return true;
+}
+
+void btif_acm_update_lc3q_params(int64_t* cs3, tBTIF_ACM* p_data) {
+
+  /* ==================================================================
+   * CS3: Res  |LC3Q-len| QTI  | VMT | VML | ver/For_Als |LC3Q-support
+   * ==================================================================
+   *      0x00 |0B      | 000A | FF  | 0F  | 01/03       | 10
+   * ==================================================================
+   * CS4:    Res
+   * ==============================
+   *     0x00,00,00,00,00,00,00,00
+   * ============================== */
+
+  if (GetVendorMetaDataLc3QPref(
+                         &p_data->config_info.codec_config)) {
+    *cs3 &= ~((int64_t)0xFF << (LE_AUDIO_CS_3_1ST_BYTE_INDEX * 8));
+    *cs3 |=  ((int64_t)0x10 << (LE_AUDIO_CS_3_1ST_BYTE_INDEX * 8));
+
+    uint8_t lc3q_ver = GetVendorMetaDataLc3QVer(&p_data->config_info.codec_config);
+    BTIF_TRACE_DEBUG("%s: lc3q_ver: %d", __func__, lc3q_ver);
+    *cs3 &= ~((int64_t)0xFF << (LE_AUDIO_CS_3_2ND_BYTE_INDEX * 8));
+    *cs3 |=  ((int64_t)lc3q_ver << (LE_AUDIO_CS_3_2ND_BYTE_INDEX * 8));
+
+    //*cs3 &= ~((int64_t)LE_AUDIO_MASK);
+    *cs3 |=  (int64_t)LE_AUDIO_AVAILABLE_LICENSED;
+
+    *cs3 &= ~((int64_t)0xFF << (LE_AUDIO_CS_3_3RD_BYTE_INDEX * 8));
+    *cs3 |=  ((int64_t)0x0F << (LE_AUDIO_CS_3_3RD_BYTE_INDEX * 8));
+
+    *cs3 &= ~((int64_t)0xFF << (LE_AUDIO_CS_3_4TH_BYTE_INDEX * 8));
+    *cs3 |=  ((int64_t)0xFF << (LE_AUDIO_CS_3_4TH_BYTE_INDEX * 8));
+
+    *cs3 &= ~((int64_t)0xFFFF << (LE_AUDIO_CS_3_5TH_BYTE_INDEX * 8));
+    *cs3 |=  ((int64_t)0x000A << (LE_AUDIO_CS_3_5TH_BYTE_INDEX * 8));
+
+    *cs3 &= ~((int64_t)0xFF << (LE_AUDIO_CS_3_7TH_BYTE_INDEX * 8));
+    *cs3 |=  ((int64_t)0x0B << (LE_AUDIO_CS_3_7TH_BYTE_INDEX * 8));
+
+    CodecConfig temp = unicast_codecs_capabilities.back();
+    unicast_codecs_capabilities.pop_back();
+    temp.codec_specific_3 = *cs3;
+    unicast_codecs_capabilities.push_back(temp);
+  }
+  BTIF_TRACE_DEBUG("%s: cs3: %" PRIi64, __func__, *cs3);
+  BTIF_TRACE_DEBUG("%s: cs3= 0x%" PRIx64, __func__, *cs3);
+}
+
+static void btif_report_connection_state(const RawAddress& peer_address,
+                                         btacm_connection_state_t state, uint16_t contextType) {
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%d contextType=%d", __func__,
+           peer_address.ToString().c_str(), state, contextType);
+  if (btif_acm_initiator.Enabled()) {
+    do_in_jni_thread(FROM_HERE,
+                     Bind(btif_acm_initiator.Callbacks()->connection_state_cb,
+                          peer_address, state, contextType));
+  }
+}
+
+static void btif_report_audio_state(const RawAddress& peer_address,
+                                    btacm_audio_state_t state, uint16_t contextType) {
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%d contextType=%d", __func__,
+           peer_address.ToString().c_str(), state, contextType);
+  if (btif_acm_initiator.Enabled()) {
+    do_in_jni_thread(FROM_HERE,
+                     Bind(btif_acm_initiator.Callbacks()->audio_state_cb,
+                          peer_address, state, contextType));
+  }
+}
+
+void btif_acm_report_source_codec_state(
+    const RawAddress& peer_address,
+    const CodecConfig& codec_config,
+    const std::vector<CodecConfig>& codecs_local_capabilities,
+    const std::vector<CodecConfig>&
+        codecs_selectable_capabilities, int contextType) {
+  BTIF_TRACE_EVENT("%s: peer_address=%s contextType=%d", __func__,
+                   peer_address.ToString().c_str(), contextType);
+  if (btif_acm_initiator.Enabled()) {
+    do_in_jni_thread(FROM_HERE,
+                     Bind(btif_acm_initiator.Callbacks()->audio_config_cb, peer_address,
+                          codec_config, codecs_local_capabilities,
+                          codecs_selectable_capabilities, contextType));
+  }
+}
+
+static void btif_acm_handle_evt(uint16_t event, char* p_param) {
+  BtifAcmPeer* peer = nullptr;
+  BTIF_TRACE_DEBUG("Handle the ACM event = %d ", event);
+  switch (event) {
+    case BTIF_ACM_DISCONNECT_REQ_EVT: {
+        if (p_param == NULL) {
+          BTIF_TRACE_ERROR("%s: Invalid p_param, dropping event: %d", __func__, event);
+          return;
+        }
+        tBTIF_ACM_CONN_DISC* p_acm = (tBTIF_ACM_CONN_DISC*)p_param;
+        peer = btif_acm_initiator.FindOrCreatePeer(p_acm->bd_addr);
+        if (peer == nullptr) {
+          BTIF_TRACE_ERROR(
+              "%s: Cannot find peer for peer_address=%s"
+              ": event dropped: %d",
+              __func__, p_acm->bd_addr.ToString().c_str(),
+              event);
+          return;
+        } else {
+          BTIF_TRACE_EVENT(
+              "%s: BTIF_ACM_DISCONNECT_REQ_EVT peer_address=%s"
+              ": contextType=%d",
+              __func__, p_acm->bd_addr.ToString().c_str(),
+              p_acm->contextType);
+        }
+        break;
+    }
+    case BTIF_ACM_START_STREAM_REQ_EVT:
+    case BTIF_ACM_SUSPEND_STREAM_REQ_EVT:
+    case BTIF_ACM_STOP_STREAM_REQ_EVT: {
+        if (p_param == NULL) {
+          BTIF_TRACE_ERROR("%s: Invalid p_param, dropping event: %d", __func__, event);
+          return;
+        }
+        tBTIF_ACM_CONN_DISC* p_acm = (tBTIF_ACM_CONN_DISC*)p_param;
+        peer = btif_acm_initiator.FindOrCreatePeer(p_acm->bd_addr);
+        if (peer == nullptr) {
+          BTIF_TRACE_ERROR("%s: Cannot find peer for peer_address=%s"
+                           ": event dropped: %d",
+                           __func__, p_acm->bd_addr.ToString().c_str(), event);
+          return;
+        }
+    } break;
+    case BTA_ACM_DISCONNECT_EVT:
+    case BTA_ACM_CONNECT_EVT:
+    case BTA_ACM_START_EVT:
+    case BTA_ACM_STOP_EVT:
+    case BTA_ACM_RECONFIG_EVT: {
+        if (p_param == NULL) {
+          BTIF_TRACE_ERROR("%s: Invalid p_param, dropping event: %d", __func__, event);
+          return;
+        }
+        tBTA_ACM_STATE_INFO* p_acm = (tBTA_ACM_STATE_INFO*)p_param;
+        peer = btif_acm_initiator.FindOrCreatePeer(p_acm->bd_addr);
+        if (peer == nullptr) {
+          BTIF_TRACE_ERROR("%s: Cannot find or create peer for peer_address=%s"
+                           ": event dropped: %d",
+                           __func__, p_acm->bd_addr.ToString().c_str(), event);
+          return;
+        }
+    } break;
+    case BTA_ACM_CONFIG_EVT: {
+        if (p_param == NULL) {
+          BTIF_TRACE_ERROR("%s: Invalid p_param, dropping event: %d", __func__, event);
+          return;
+        }
+        tBTA_ACM_CONFIG_INFO* p_acm = (tBTA_ACM_CONFIG_INFO*)p_param;
+        peer = btif_acm_initiator.FindPeer(p_acm->bd_addr);
+        if (peer == nullptr) {
+          BTIF_TRACE_ERROR("%s: Cannot find or create peer for peer_address=%s"
+                           ": event dropped: %d",
+                           __func__, p_acm->bd_addr.ToString().c_str(), event);
+          return;
+        }
+    } break;
+    case BTIF_ACM_RECONFIG_REQ_EVT: {
+        if (p_param == NULL) {
+          BTIF_TRACE_ERROR("%s: Invalid p_param, dropping event: %d", __func__, event);
+          return;
+        }
+        tBTIF_ACM_RECONFIG* p_acm = (tBTIF_ACM_RECONFIG*)p_param;
+        peer = btif_acm_initiator.FindPeer(p_acm->bd_addr);
+        if (peer == nullptr) {
+          BTIF_TRACE_ERROR("%s: Cannot find or create peer for peer_address=%s"
+                           ": event dropped: %d",
+                           __func__, p_acm->bd_addr.ToString().c_str(), event);
+          return;
+        }
+    } break;
+
+    case BTA_ACM_CONN_UPDATE_TIMEOUT_EVT: {
+        if (p_param == NULL) {
+          BTIF_TRACE_ERROR("%s: Invalid p_param, dropping event: %d", __func__, event);
+          return;
+        }
+        tBTA_ACM_CONN_UPDATE_TIMEOUT_INFO * p_acm =
+                                  (tBTA_ACM_CONN_UPDATE_TIMEOUT_INFO *)p_param;
+        peer = btif_acm_initiator.FindPeer(p_acm->bd_addr);
+        if (peer == nullptr) {
+          BTIF_TRACE_ERROR("%s: Cannot find or create peer for peer_address=%s"
+                           ": event dropped: %d",
+                           __func__, p_acm->bd_addr.ToString().c_str(), event);
+          return;
+        }
+    } break;
+
+    default :
+        BTIF_TRACE_DEBUG("UNHandled ACM event = %d ", event);
+        break;
+  }
+  peer->StateMachine().ProcessEvent(event, (void*)p_param);
+}
+
+/**
+ * Process BTA CSIP events. The processing is done on the JNI
+ * thread.
+ */
+static void btif_acm_handle_bta_csip_event(uint16_t evt, char* p_param) {
+  BtifCsipEvent btif_csip_event(evt, p_param, sizeof(tBTA_CSIP_DATA));
+  tBTA_CSIP_EVT event = btif_csip_event.Event();
+  tBTA_CSIP_DATA* p_data = (tBTA_CSIP_DATA*)btif_csip_event.Data();
+  BTIF_TRACE_DEBUG("%s: event=%s", __func__, btif_csip_event.ToString().c_str());
+
+  switch (event) {
+    case BTA_CSIP_LOCK_STATUS_CHANGED_EVT: {
+      const tBTA_LOCK_STATUS_CHANGED& lock_status_param = p_data->lock_status_param;
+      BTIF_TRACE_DEBUG("%s: app_id=%d, set_id=%d, status=%d ", __func__,
+                       lock_status_param.app_id, lock_status_param.set_id,
+                       lock_status_param.status);
+
+      std::vector<RawAddress> set_members =lock_status_param.addr;
+
+      for (int j = 0; j < (int)set_members.size(); j++) {
+        BTIF_TRACE_DEBUG("%s: address =%s", __func__, set_members[j].ToString().c_str());
+      }
+
+      BTIF_TRACE_DEBUG("%s: Get current lock status: %d ", __func__,
+                        btif_acm_initiator.GetGroupLockStatus(lock_status_param.set_id));
+      if (btif_acm_initiator.GetGroupLockStatus(lock_status_param.set_id) == BtifAcmInitiator::kFlagStatusPendingLock) {
+        BTIF_TRACE_DEBUG("%s: lock was awaited for this set ", __func__);
+      }
+
+      if (btif_acm_initiator.GetGroupLockStatus(lock_status_param.set_id) == BtifAcmInitiator::kFlagStatusPendingUnlock) {
+        BTIF_TRACE_DEBUG("%s: Unlock was awaited for this set ", __func__);
+      }
+
+      BTIF_TRACE_DEBUG("%s: Get CSIP app id: %d ", __func__,
+                        btif_acm_initiator.GetCsipAppId());
+      if (btif_acm_initiator.GetCsipAppId() != lock_status_param.app_id) {
+        BTIF_TRACE_DEBUG("%s: app id mismatch ERROR!!! ", __func__);
+        tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+        if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+           pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+          btif_acm_source_on_suspended(A2DP_CTRL_ACK_FAILURE);
+        } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+          btif_acm_on_started(A2DP_CTRL_ACK_FAILURE);
+        } else {
+          BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+        }
+        return;
+      }
+
+      switch (lock_status_param.status) {
+        case LOCK_RELEASED:
+            BTIF_TRACE_DEBUG("%s: unlocked attempt succeeded ", __func__);
+            btif_acm_initiator.SetOrUpdateGroupLockStatus(lock_status_param.set_id, BtifAcmInitiator::kFlagStatusUnlocked);
+            break;
+        case LOCK_RELEASED_TIMEOUT:
+            BTIF_TRACE_DEBUG("%s: peer unlocked due to timeout ", __func__);
+            //in this case evaluate which device has sent TO and how to use it ?
+            btif_acm_initiator.SetOrUpdateGroupLockStatus(lock_status_param.set_id, BtifAcmInitiator::kFlagStatusUnlocked);
+            break;
+        case ALL_LOCKS_ACQUIRED:
+            btif_acm_initiator.SetOrUpdateGroupLockStatus(lock_status_param.set_id, BtifAcmInitiator::kFlagStatusLocked);
+            btif_acm_handle_csip_status_locked(lock_status_param.addr, lock_status_param.set_id);
+            BTIF_TRACE_DEBUG("%s: All locks acquired ", __func__);
+            break;
+        case SOME_LOCKS_ACQUIRED_REASON_TIMEOUT:
+            //proceed to continue use case;
+        /*case SOME_LOCKS_ACQUIRED_REASON_DISC:
+            //proceed to continue use case;
+            BTIF_TRACE_DEBUG("%s: locked attempt succeeded with status = %d", __func__, lock_status_param.status);
+            BTIF_TRACE_DEBUG("%s: locked set member count = %d, setsize = %d",
+                                      __func__, (lock_status_param.addr).size(), setSize);
+            btif_acm_initiator.music_active_set_locked_dev_count_ += (lock_status_param.addr).size();
+            btif_acm_initiator.locked_devices.insert(btif_acm_initiator.locked_devices.end(),
+                                 lock_status_param.addr.begin(), lock_status_param.addr.end());
+            btif_acm_handle_csip_status_locked(lock_status_param.addr, lock_status_param.set_id);
+            if (btif_acm_initiator.music_active_set_locked_dev_count_ < setSize) {
+              btif_acm_initiator.SetOrUpdateGroupLockStatus(lock_status_param.set_id, BtifAcmInitiator::kFlagStatusSubsetLocked);
+            } else {
+              btif_acm_initiator.SetOrUpdateGroupLockStatus(lock_status_param.set_id, BtifAcmInitiator::kFlagStatusLocked);
+            }
+            break;*/
+        case LOCK_DENIED: {
+            //proceed to discontinue use case;
+            tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+            if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+               pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+              btif_acm_source_on_suspended(A2DP_CTRL_ACK_FAILURE);
+            } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+              btif_acm_on_started(A2DP_CTRL_ACK_FAILURE);
+            } else {
+              BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+            }
+            btif_acm_check_and_cancel_group_procedure_timer(lock_status_param.set_id);
+            btif_acm_initiator.SetOrUpdateGroupLockStatus(lock_status_param.set_id, BtifAcmInitiator::kFlagStatusUnlocked);
+        } break;
+        case INVALID_REQUEST_PARAMS: {
+            BTIF_TRACE_DEBUG("%s: invalid lock request ", __func__);
+            tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+            if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+               pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+              btif_acm_source_on_suspended(A2DP_CTRL_ACK_FAILURE);
+            } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+              btif_acm_on_started(A2DP_CTRL_ACK_FAILURE);
+            } else {
+              BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+            }
+            btif_acm_check_and_cancel_group_procedure_timer(lock_status_param.set_id);
+            if (btif_acm_initiator.GetGroupLockStatus(lock_status_param.set_id) == BtifAcmInitiator::kFlagStatusPendingLock)
+              btif_acm_initiator.SetOrUpdateGroupLockStatus(lock_status_param.set_id, BtifAcmInitiator::kFlagStatusUnlocked);
+            else
+              btif_acm_initiator.SetOrUpdateGroupLockStatus(lock_status_param.set_id, BtifAcmInitiator::kFlagStatusLocked);
+        } break;
+        default:
+        break;
+      }
+    } break;
+    case BTA_CSIP_SET_MEMBER_FOUND_EVT: {
+      const tBTA_SET_MEMBER_FOUND& set_member_param = p_data->set_member_param;
+      BTIF_TRACE_DEBUG("%s: set_id=%d, uuid=%d ", __func__,
+                     set_member_param.set_id,
+                     set_member_param.uuid);
+    } break;
+
+    case BTA_CSIP_LOCK_AVAILABLE_EVT: {
+      const tBTA_LOCK_AVAILABLE& lock_available_param = p_data->lock_available_param;
+      BTIF_TRACE_DEBUG("%s: app_id=%d, set_id=%d ", __func__,
+                   lock_available_param.app_id, lock_available_param.set_id);
+    } break;
+  }
+}
+
+static void btif_acm_handle_csip_status_locked(std::vector<RawAddress> addr, uint8_t setId) {
+  if (addr.empty()) {
+    BTIF_TRACE_ERROR("%s: vector size is empty", __func__);
+    return;
+  }
+  tA2DP_CTRL_CMD pending_cmd;// = A2DP_CTRL_CMD_START;//TODO: change to None
+  pending_cmd =  btif_ahim_get_pending_command();
+  std::vector<RawAddress>::iterator itr;
+  int req = 0;
+  if (pending_cmd == A2DP_CTRL_CMD_START) {
+    req = BTIF_ACM_START_STREAM_REQ_EVT;
+  } else if (pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+    req = BTIF_ACM_SUSPEND_STREAM_REQ_EVT;
+  } else if (pending_cmd == A2DP_CTRL_CMD_STOP) {
+    req = BTIF_ACM_STOP_STREAM_REQ_EVT;
+  } else {
+    BTIF_TRACE_EVENT("%s: No pending command, check if this list of peers belong to MusicActive streaming started group", __func__);
+//if (btif_acm_initiator.IsMusicActiveGroupStarted() && (setId == btif_acm_initiator.MusicActiveCSetId()))
+//req = BTIF_ACM_START_STREAM_REQ_EVT;
+  }
+  if (req) {
+    for (itr = addr.begin(); itr != addr.end(); itr++) {
+      btif_acm_initiator_dispatch_sm_event(*itr, static_cast<btif_acm_sm_event_t>(req));
+    }
+  }
+//  BtifAcmPeer* peer_ = btif_acm_initiator.FindPeer(peer_address);
+  /*if ((peer_.IsPeerActiveForMusic() || !btif_acm_stream_started_ready())) {
+    // Immediately stop transmission of frames while suspend is pending
+    if (req == BTIF_ACM_STOP_STREAM_REQ_EVT) {
+      //btif_acm_on_stopped(nullptr);
+    } else if (req == BTIF_ACM_SUSPEND_STREAM_REQ_EVT) {
+      // ensure tx frames are immediately suspended
+      //btif_acm_source_set_tx_flush(true);
+    }
+  }*/
+}
+
+static void btif_acm_check_and_start_conn_Interval_timer(BtifAcmPeer* peer) {
+
+  btif_acm_check_and_cancel_conn_Interval_timer();
+  BTIF_TRACE_DEBUG("%s: ", __func__);
+
+  alarm_set_on_mloop(btif_acm_initiator.AcmConnIntervalTimer(),
+                     BtifAcmInitiator::kTimeoutConnIntervalMs,
+                     btif_acm_initiator_conn_Interval_timer_timeout,
+                     (void *)peer);
+}
+
+static void btif_acm_check_and_cancel_conn_Interval_timer() {
+
+  BTIF_TRACE_DEBUG("%s: ", __func__);
+  if (alarm_is_scheduled(btif_acm_initiator.AcmConnIntervalTimer())) {
+    alarm_cancel(btif_acm_initiator.AcmConnIntervalTimer());
+  }
+}
+
+
+static void btif_acm_initiator_conn_Interval_timer_timeout(void *data) {
+
+  BTIF_TRACE_DEBUG("%s: ", __func__);
+  BtifAcmPeer *peer = (BtifAcmPeer *)data;
+  tBTA_ACM_CONN_UPDATE_TIMEOUT_INFO p_data;
+  p_data.bd_addr = peer->PeerAddress();
+  btif_transfer_context(btif_acm_handle_evt, BTA_ACM_CONN_UPDATE_TIMEOUT_EVT,
+                        (char*)&p_data,
+                        sizeof(tBTA_ACM_CONN_UPDATE_TIMEOUT_INFO), NULL);
+}
+
+static void btif_acm_check_and_start_group_procedure_timer(uint8_t setId) {
+  uint8_t *arg = NULL;
+  arg = (uint8_t *) osi_malloc(sizeof(uint8_t));
+  BTIF_TRACE_DEBUG("%s: ", __func__);
+  btif_acm_check_and_cancel_group_procedure_timer(setId);
+
+  *arg = setId;
+  alarm_set_on_mloop(btif_acm_initiator.AcmGroupProcedureTimer(),
+                     BtifAcmInitiator::kTimeoutAcmGroupProcedureMs,
+                     btif_acm_initiator_group_procedure_timer_timeout,
+                     (void*) arg);
+
+}
+
+static void btif_acm_check_and_cancel_group_procedure_timer(uint8_t setId) {
+  if (alarm_is_scheduled(btif_acm_initiator.AcmGroupProcedureTimer())) {
+    BTIF_TRACE_ERROR("%s: acm group procedure already running for setId = %d, cancel", __func__, setId);
+    alarm_cancel(btif_acm_initiator.AcmGroupProcedureTimer());
+  }
+}
+
+static void btif_acm_initiator_group_procedure_timer_timeout(void *data) {
+  BTIF_TRACE_DEBUG("%s: ", __func__);
+  tBTA_CSIP_CSET cset_info; // need to do memset ?
+  memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+  std::vector<RawAddress> streaming_devices;
+  std::vector<RawAddress> non_streaming_devices;
+  uint8_t *arg = (uint8_t*) data;
+  if (!arg) {
+    BTIF_TRACE_ERROR("%s: coordinate arg is null, return", __func__);
+    return;
+  }
+  uint8_t setId = *arg;
+  if (setId == INVALID_SET_ID) {
+    BTIF_TRACE_ERROR("%s: coordinate SetId is invalid, return", __func__);
+    if (arg) osi_free(arg);
+    return;
+  }
+
+  cset_info = BTA_CsipGetCoordinatedSet(setId);
+  if (cset_info.size == 0) {
+    BTIF_TRACE_ERROR("%s: CSET info size is zero, return", __func__);
+    if (arg) osi_free(arg);
+    return;
+  }
+  std::vector<RawAddress>::iterator itr;
+  BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+  if ((cset_info.set_members).size() > 0) {
+    for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+      //BTIF_TRACE_DEBUG("%s: address = %s", __func__, itr->ToString().c_str());
+      BtifAcmPeer* peer = btif_acm_initiator.FindPeer(*itr);
+      if ((peer == nullptr) || (peer != nullptr && !peer->IsStreaming())) {
+        non_streaming_devices.push_back(*itr);
+      } else {
+        streaming_devices.push_back(*itr);
+      }
+    }
+  }
+
+  if (streaming_devices.size() > 0) {
+    tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+    if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+       pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+      btif_acm_source_on_suspended(A2DP_CTRL_ACK_SUCCESS);
+    } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+      btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+    } else {
+      BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+    }
+    BTIF_TRACE_DEBUG("%s: Get music active setid: %d", __func__,
+                        btif_acm_initiator.MusicActiveCSetId());
+    btif_acm_check_and_start_lock_release_timer(btif_acm_initiator.MusicActiveCSetId());
+    if (streaming_devices.size() < (cset_info.set_members).size()) {
+      // this case should continue with mono mode since all set members are not streaming
+    }
+  } else {
+    tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+    if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+       pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+      btif_acm_source_on_suspended(A2DP_CTRL_ACK_FAILURE);
+    } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+      btif_acm_on_started(A2DP_CTRL_ACK_FAILURE);
+    } else {
+      BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+    }
+  }
+
+  if (non_streaming_devices.size() > 0) //do we need to unlock and then disconnect ??
+   // le_Acl_disconnect (non_streaming_devices);
+
+  if (arg) osi_free(arg);
+}
+
+static void btif_acm_check_and_start_lock_release_timer(uint8_t setId) {
+  uint8_t *arg = NULL;
+  arg = (uint8_t *) osi_malloc(sizeof(uint8_t));
+
+  btif_acm_check_and_cancel_lock_release_timer(setId);
+
+  *arg = setId;
+  alarm_set_on_mloop(btif_acm_initiator.MusicSetLockReleaseTimer(),
+                           BtifAcmPeer::kTimeoutLockReleaseMs,
+                           btif_acm_initiator_lock_release_timer_timeout,
+                           (void*) arg);
+}
+
+static void btif_acm_check_and_cancel_lock_release_timer(uint8_t setId) {
+  if (alarm_is_scheduled(btif_acm_initiator.MusicSetLockReleaseTimer())) {
+    BTIF_TRACE_ERROR("%s: lock release already running for setId = %d, cancel ", __func__, setId);
+    alarm_cancel(btif_acm_initiator.MusicSetLockReleaseTimer());
+  }
+}
+
+static void btif_acm_initiator_lock_release_timer_timeout(void *data) {
+  uint8_t *arg = (uint8_t*) data;
+  if (!arg) {
+    BTIF_TRACE_ERROR("%s: coordinate arg is null, return", __func__);
+    return;
+  }
+  uint8_t setId = *arg;
+  if (setId == INVALID_SET_ID) {
+    BTIF_TRACE_ERROR("%s: coordinate SetId is invalid, return", __func__);
+    if (arg) osi_free(arg);
+    return;
+  }
+  if ((btif_acm_initiator.GetGroupLockStatus(setId) != BtifAcmInitiator::kFlagStatusLocked) ||
+      (btif_acm_initiator.GetGroupLockStatus(setId) != BtifAcmInitiator::kFlagStatusSubsetLocked)) {
+    BTIF_TRACE_ERROR("%s: SetId = %d Lock Status = %d returning",
+                      __func__, setId, btif_acm_initiator.GetGroupLockStatus(setId));
+    if (arg) osi_free(arg);
+    return;
+  }
+  if (!btif_acm_request_csip_unlock(setId)) {
+    BTIF_TRACE_ERROR("%s: error unlocking", __func__);
+  }
+  if (arg) osi_free(arg);
+}
+
+static void bta_csip_callback(tBTA_CSIP_EVT event, tBTA_CSIP_DATA* p_data) {
+  BTIF_TRACE_DEBUG("%s: event: %d", __func__, event);
+  btif_transfer_context(btif_acm_handle_bta_csip_event, event, (char*)p_data,
+                        sizeof(tBTA_CSIP_DATA), NULL);
+}
+
+// Initializes the ACM interface for initiator mode
+static bt_status_t init_acm_initiator(
+    btacm_initiator_callbacks_t* callbacks, int max_connected_acceptors,
+    const std::vector<CodecConfig>& codec_priorities) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  return btif_acm_initiator.Init(callbacks, max_connected_acceptors,
+                                 codec_priorities);
+}
+
+// Establishes the BAP connection with the remote acceptor device
+static void connect_int(uint16_t uuid, char* p_param) {
+    tBTIF_ACM_CONN_DISC connection;
+    memset(&connection, 0, sizeof(tBTIF_ACM_CONN_DISC));
+    memcpy(&connection, p_param, sizeof(connection));
+    RawAddress peer_address = RawAddress::kEmpty;
+    BtifAcmPeer* peer = nullptr;
+    peer_address = connection.bd_addr;
+    if (uuid == ACM_UUID) {
+      peer = btif_acm_initiator.FindOrCreatePeer(peer_address);
+    }
+    if (peer == nullptr) {
+      BTIF_TRACE_ERROR("%s: peer is NULL", __func__);
+      return;
+    }
+    peer->SetContextType(connection.contextType);
+    peer->SetProfileType(connection.profileType);
+    BTIF_TRACE_DEBUG("%s: cummulative_profile_type %d", __func__, peer->GetProfileType());
+    //peer->SetPrefContextType(preferredContext);
+    peer->StateMachine().ProcessEvent(BTIF_ACM_CONNECT_REQ_EVT, &connection);
+}
+
+// Set the active peer for contexttype
+static void set_acm_active_peer_int(const RawAddress& peer_address,
+                                    uint16_t contextType, uint16_t profileType,
+                                    std::promise<void> peer_ready_promise) {
+  BTIF_TRACE_EVENT("%s: peer_address=%s", __func__, peer_address.ToString().c_str());
+  if (peer_address.IsEmpty()) {
+    int setid = INVALID_SET_ID;
+    if (contextType == CONTEXT_TYPE_MUSIC)
+      setid = btif_acm_initiator.MusicActiveCSetId();
+    else if (contextType == CONTEXT_TYPE_VOICE)
+      setid = btif_acm_initiator.VoiceActiveCSetId();
+
+    if (setid < INVALID_SET_ID) {
+      tBTA_CSIP_CSET cset_info;
+      memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+      cset_info = BTA_CsipGetCoordinatedSet(setid);
+      if (cset_info.size != 0) {
+        std::vector<RawAddress>::iterator itr;
+        BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+        if ((cset_info.set_members).size() > 0) {
+          for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+            BtifAcmPeer* peer = btif_acm_initiator.FindPeer(*itr);
+            if (peer != nullptr && peer->IsStreaming() &&
+                    (contextType == peer->GetStreamContextType())) {
+              BTIF_TRACE_DEBUG("%s: peer is streaming %s ", __func__, peer->PeerAddress().ToString().c_str());
+              btif_acm_initiator_dispatch_sm_event(*itr, BTIF_ACM_STOP_STREAM_REQ_EVT);
+            }
+          }
+        }
+      }
+    } else {
+      BTIF_TRACE_DEBUG("%s: set active for twm device ", __func__);
+      BtifAcmPeer* peer = nullptr;
+      if (contextType == CONTEXT_TYPE_MUSIC)
+        peer = btif_acm_initiator.FindPeer(btif_acm_initiator.MusicActivePeer());
+      else if (contextType == CONTEXT_TYPE_VOICE)
+        peer = btif_acm_initiator.FindPeer(btif_acm_initiator.VoiceActivePeer());
+      if (peer != nullptr && peer->IsStreaming() &&
+              (contextType == peer->GetStreamContextType())) {
+        BTIF_TRACE_DEBUG("%s: peer is streaming %s ", __func__, peer->PeerAddress().ToString().c_str());
+        if (contextType == CONTEXT_TYPE_MUSIC)
+          btif_acm_initiator_dispatch_sm_event(btif_acm_initiator.MusicActivePeer(), BTIF_ACM_STOP_STREAM_REQ_EVT);
+        else if (contextType == CONTEXT_TYPE_VOICE)
+          btif_acm_initiator_dispatch_sm_event(btif_acm_initiator.VoiceActivePeer(), BTIF_ACM_STOP_STREAM_REQ_EVT);
+      }
+    }
+  }
+  if (!btif_acm_initiator.SetAcmActivePeer(peer_address, contextType, profileType,
+                                           std::move(peer_ready_promise))) {
+    BTIF_TRACE_ERROR("%s: Error setting %s as active peer", __func__,
+                     peer_address.ToString().c_str());
+  }
+}
+
+static bt_status_t connect_acm_initiator(const RawAddress& peer_address,
+                                         uint16_t contextType, uint16_t profileType,
+                                         uint16_t preferredContext) {
+  BTIF_TRACE_EVENT("%s: Peer %s contextType=%d profileType=%d preferredContext=%d", __func__,
+    peer_address.ToString().c_str(), contextType, profileType, preferredContext);
+
+  if (!btif_acm_initiator.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF ACM Initiator is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+
+  tBTIF_ACM_CONN_DISC conn;
+  conn.contextType = contextType;
+  conn.profileType = profileType;
+  conn.bd_addr = peer_address;
+  return btif_transfer_context(connect_int, ACM_UUID, (char*)&conn,
+                               sizeof(tBTIF_ACM_CONN_DISC), NULL);
+}
+
+static bt_status_t disconnect_acm_initiator(const RawAddress& peer_address,
+                                                        uint16_t contextType) {
+  BTIF_TRACE_EVENT("%s: Peer %s contextType=%d", __func__,
+                     peer_address.ToString().c_str(), contextType);
+
+  if (!btif_acm_initiator.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF ACM Initiator is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+
+  BtifAcmPeer* peer = btif_acm_initiator.FindOrCreatePeer(peer_address);
+  if (peer == nullptr) {
+    BTIF_TRACE_ERROR("%s: peer is NULL", __func__);
+    return BT_STATUS_FAIL;
+  }
+
+  tBTIF_ACM_CONN_DISC disc;
+  peer->ResetContextType(contextType);
+  if (contextType == CONTEXT_TYPE_MUSIC) {
+    peer->ResetProfileType(BAP|GCP|WMCP);
+    disc.profileType = BAP|GCP|WMCP;
+  } else if (contextType == CONTEXT_TYPE_VOICE) {
+    peer->ResetProfileType(BAP_CALL);
+    disc.profileType = BAP_CALL;
+  } else if (contextType == CONTEXT_TYPE_MUSIC_VOICE) {
+    peer->ResetProfileType(BAP|GCP|WMCP|BAP_CALL);
+    disc.profileType = BAP|GCP|WMCP|BAP_CALL;
+  }
+  BTIF_TRACE_DEBUG("%s: cummulative_profile_type %d", __func__, peer->GetProfileType());
+
+  disc.bd_addr = peer_address;
+  disc.contextType = contextType;
+  btif_transfer_context(btif_acm_handle_evt, BTIF_ACM_DISCONNECT_REQ_EVT, (char*)&disc,
+                        sizeof(tBTIF_ACM_CONN_DISC), NULL);
+  return BT_STATUS_SUCCESS;
+}
+
+static bt_status_t set_active_acm_initiator(const RawAddress& peer_address,
+                                            uint16_t profileType) {
+  uint16_t contextType = CONTEXT_TYPE_MUSIC;
+  if (profileType == BAP || profileType == GCP || profileType == WMCP)
+    contextType = CONTEXT_TYPE_MUSIC;
+  else if (profileType == BAP_CALL)
+    contextType = CONTEXT_TYPE_VOICE;
+
+  BTIF_TRACE_EVENT("%s: Peer %s contextType=%d profileType=%d", __func__,
+                    peer_address.ToString().c_str(), contextType, profileType);
+  if (!btif_acm_initiator.Enabled()) {
+    LOG(WARNING) << __func__ << ": BTIF ACM Initiator is not enabled";
+    return BT_STATUS_NOT_READY;
+  }
+
+  BtifAcmPeer* peer = nullptr;
+  if (contextType == CONTEXT_TYPE_MUSIC) {
+    peer = btif_acm_initiator.FindPeer(btif_acm_initiator.MusicActivePeer());
+    if ((peer != nullptr) && (peer->GetStreamContextType() == CONTEXT_TYPE_MUSIC) &&
+        (peer->CheckFlags(BtifAcmPeer::kFlagPendingStart | BtifAcmPeer::kFlagPendingLocalSuspend |
+                          BtifAcmPeer::kFlagPendingReconfigure))) {
+      LOG(WARNING) << __func__ << ": Active music device is pending start or suspend or reconfig";
+      return BT_STATUS_NOT_READY;
+    }
+  } else if (contextType == CONTEXT_TYPE_VOICE) {
+    peer = btif_acm_initiator.FindPeer(btif_acm_initiator.VoiceActivePeer());
+    if ((peer != nullptr) && (peer->GetStreamContextType() == CONTEXT_TYPE_VOICE) &&
+        (peer->CheckFlags(BtifAcmPeer::kFlagPendingStart |
+                          BtifAcmPeer::kFlagPendingLocalSuspend))) {
+      LOG(WARNING) << __func__ << ": Active voice device is pending start or suspend";
+      return BT_STATUS_NOT_READY;
+    }
+  }
+  std::promise<void> peer_ready_promise;
+  std::future<void> peer_ready_future = peer_ready_promise.get_future();
+  set_acm_active_peer_int(peer_address, contextType, profileType,
+                          std::move(peer_ready_promise));
+  return BT_STATUS_SUCCESS;
+}
+
+static bt_status_t start_stream_acm_initiator(const RawAddress& peer_address,
+                                              uint16_t contextType) {
+  LOG_INFO(LOG_TAG, "%s: Peer %s", __func__, peer_address.ToString().c_str());
+
+  if (!btif_acm_initiator.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF ACM Initiator is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+  int id = btif_acm_initiator.VoiceActiveCSetId();
+  if (id < INVALID_SET_ID) {
+    tBTA_CSIP_CSET cset_info;
+    memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+    cset_info = BTA_CsipGetCoordinatedSet(id);
+    std::vector<RawAddress>::iterator itr;
+    BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+    if ((cset_info.set_members).size() > 0) {
+      for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+         BTIF_TRACE_DEBUG("%s: Sending start request ", __func__);
+         BtifAcmPeer* p = btif_acm_initiator.FindPeer(*itr);
+         if (p && p->IsConnected()) {
+           p->SetStreamContextType(contextType);
+           btif_acm_initiator_dispatch_sm_event(*itr, BTIF_ACM_START_STREAM_REQ_EVT);
+         }
+      }
+    }
+    btif_acm_check_and_start_group_procedure_timer(btif_acm_initiator.VoiceActiveCSetId());
+  } else {
+    BTIF_TRACE_DEBUG("%s: Sending start to twm device ", __func__);
+    BtifAcmPeer* p = btif_acm_initiator.FindPeer(btif_acm_initiator.VoiceActivePeer());
+    if (p != nullptr && p->IsConnected()) {
+      p->SetStreamContextType(CONTEXT_TYPE_VOICE);
+      btif_acm_initiator_dispatch_sm_event(btif_acm_initiator.VoiceActivePeer(), BTIF_ACM_START_STREAM_REQ_EVT);
+    } else {
+      BTIF_TRACE_DEBUG("%s: Unable to send start to twm device ", __func__);
+    }
+  }
+  return BT_STATUS_SUCCESS;
+}
+
+static bt_status_t stop_stream_acm_initiator(const RawAddress& peer_address,
+                                             uint16_t contextType) {
+  LOG_INFO(LOG_TAG, "%s: Peer %s", __func__, peer_address.ToString().c_str());
+
+  if (!btif_acm_initiator.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF ACM Initiator is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+
+  int id = btif_acm_initiator.VoiceActiveCSetId();
+  if (id < INVALID_SET_ID) {
+    tBTA_CSIP_CSET cset_info;
+    memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+    cset_info = BTA_CsipGetCoordinatedSet(id);
+    std::vector<RawAddress>::iterator itr;
+    BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+    if ((cset_info.set_members).size() > 0) {
+      for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+         BTIF_TRACE_DEBUG("%s: Sending stop request ", __func__);
+         BtifAcmPeer* p = btif_acm_initiator.FindPeer(*itr);
+         if (p && p->IsConnected()) {
+           p->SetStreamContextType(contextType);
+           btif_acm_initiator_dispatch_sm_event(*itr, BTIF_ACM_STOP_STREAM_REQ_EVT);
+         }
+      }
+    }
+    btif_acm_check_and_start_group_procedure_timer(btif_acm_initiator.VoiceActiveCSetId());
+  } else {
+    BTIF_TRACE_DEBUG("%s: Sending stop to twm device ", __func__);
+    BtifAcmPeer* p = btif_acm_initiator.FindPeer(btif_acm_initiator.VoiceActivePeer());
+    if (p != nullptr && p->IsConnected()) {
+      p->SetStreamContextType(CONTEXT_TYPE_VOICE);
+      btif_acm_initiator_dispatch_sm_event(btif_acm_initiator.VoiceActivePeer(), BTIF_ACM_STOP_STREAM_REQ_EVT);
+    } else {
+      BTIF_TRACE_DEBUG("%s: Unable to send stop to twm device ", __func__);
+    }
+  }
+  return BT_STATUS_SUCCESS;
+}
+
+static bt_status_t codec_config_acm_initiator(const RawAddress& peer_address,
+                                std::vector<CodecConfig> codec_preferences,
+                                uint16_t contextType, uint16_t profileType) {
+  BTIF_TRACE_EVENT("%s", __func__);
+
+  if (!btif_acm_initiator.Enabled()) {
+    LOG(WARNING) << __func__ << ": BTIF ACM Initiator is not enabled";
+    return BT_STATUS_NOT_READY;
+  }
+
+  if (peer_address.IsEmpty()) {
+    LOG(WARNING) << __func__ << ": BTIF ACM Initiator, peer empty";
+    return BT_STATUS_PARM_INVALID;
+  }
+
+  std::promise<void> peer_ready_promise;
+  std::future<void> peer_ready_future = peer_ready_promise.get_future();
+  bt_status_t status = BT_STATUS_SUCCESS;
+  if (status == BT_STATUS_SUCCESS) {
+    peer_ready_future.wait();
+  } else {
+    LOG(WARNING) << __func__ << ": BTIF ACM Initiator fails to config codec";
+  }
+  return status;
+}
+
+static bt_status_t change_codec_config_acm_initiator(const RawAddress& peer_address,
+                                                     char* msg) {
+  BTIF_TRACE_DEBUG("%s: codec change string: %s", __func__, msg);
+  tBTIF_ACM_RECONFIG data;
+  if (!btif_acm_initiator.Enabled()) {
+    LOG(WARNING) << __func__ << ": BTIF ACM Initiator is not enabled";
+    return BT_STATUS_NOT_READY;
+  }
+
+  if (peer_address.IsEmpty()) {
+    LOG(WARNING) << __func__ << ": BTIF ACM Initiator, peer empty";
+    return BT_STATUS_PARM_INVALID;
+  }
+  BtifAcmPeer* peer = btif_acm_initiator.FindPeer(peer_address);
+  if (peer == nullptr)
+    LOG(ERROR) << __func__ << ": BTIF ACM Initiator, peer is null";
+    return BT_STATUS_FAIL;
+
+  CodecQosConfig codec_qos_cfg;
+  memset(&codec_qos_cfg, 0, sizeof(codec_qos_cfg));
+  if (!strcmp(msg, "GCP_TX") && peer->GetContextType() == CONTEXT_TYPE_MUSIC) {
+    data.bd_addr = peer_address;
+    data.streams_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.direction = ASE_DIRECTION_SINK;
+    data.streams_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+    if (peer->IsStereoHsType()) {
+      SelectCodecQosConfig(peer_address, GCP, MEDIA_CONTEXT, SNK, STEREO_HS_CONFIG_1);
+    } else {
+      SelectCodecQosConfig(peer_address, GCP, MEDIA_CONTEXT, SNK, EB_CONFIG);
+    }
+    codec_qos_cfg = peer->get_peer_media_codec_qos_config();
+    data.streams_info.codec_qos_config_pair.push_back(codec_qos_cfg);
+  } else if (!strcmp(msg, "GCP_TX_RX") && peer->GetContextType() == CONTEXT_TYPE_MUSIC) {
+    data.bd_addr = peer_address;
+    data.streams_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.direction = ASE_DIRECTION_SINK;
+    data.streams_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+    SelectCodecQosConfig(peer_address, GCP, MEDIA_CONTEXT, SNK, EB_CONFIG);
+    codec_qos_cfg = peer->get_peer_media_codec_qos_config();
+    codec_qos_cfg.qos_config.cig_config.cig_id++;
+    codec_qos_cfg.qos_config.ascs_configs[0].cig_id++;
+    peer->set_peer_media_qos_config(codec_qos_cfg.qos_config);
+    peer->set_peer_media_codec_qos_config(codec_qos_cfg);
+    data.streams_info.codec_qos_config_pair.push_back(codec_qos_cfg);
+  } else if (!strcmp(msg, "MEDIA_TX")) {
+    data.bd_addr = peer_address;
+    data.streams_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    data.streams_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+    data.streams_info.stream_type.direction = ASE_DIRECTION_SINK;
+    if (peer->IsStereoHsType()) {
+      SelectCodecQosConfig(peer_address, BAP, MEDIA_CONTEXT, SNK, STEREO_HS_CONFIG_1);
+    } else {
+      SelectCodecQosConfig(peer_address, BAP, MEDIA_CONTEXT, SNK, EB_CONFIG);
+    }
+    codec_qos_cfg = peer->get_peer_media_codec_qos_config();
+    data.streams_info.codec_qos_config_pair.push_back(codec_qos_cfg);
+  } else if (!strcmp(msg, "MEDIA_RX")) {
+    data.bd_addr = peer_address;
+    data.streams_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.audio_context = CONTENT_TYPE_LIVE; //Live Audio Context
+    data.streams_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+    data.streams_info.stream_type.direction = ASE_DIRECTION_SRC;
+    SelectCodecQosConfig(peer_address, WMCP, MEDIA_CONTEXT, SRC, EB_CONFIG);
+    codec_qos_cfg = peer->get_peer_media_codec_qos_config();
+    data.streams_info.codec_qos_config_pair.push_back(codec_qos_cfg);
+  }
+  print_codec_parameters(codec_qos_cfg.codec_config);
+  print_qos_parameters(codec_qos_cfg.qos_config);
+  btif_transfer_context(btif_acm_handle_evt, BTIF_ACM_RECONFIG_REQ_EVT, (char*)&data,
+                        sizeof(tBTIF_ACM_RECONFIG), NULL);
+  return BT_STATUS_SUCCESS;
+}
+
+bool reconfig_acm_initiator(const RawAddress& peer_address, int profileType) {
+  BTIF_TRACE_DEBUG("%s: profileType: %d", __func__, profileType);
+  tBTIF_ACM_RECONFIG data;
+  if (!btif_acm_initiator.Enabled()) {
+    LOG(WARNING) << __func__ << ": BTIF ACM Initiator is not enabled";
+    return false;
+  }
+
+  if (peer_address.IsEmpty()) {
+    LOG(WARNING) << __func__ << ": BTIF ACM Initiator, peer empty";
+    return false;
+  }
+  BtifAcmPeer* peer = btif_acm_initiator.FindPeer(peer_address);
+  if (peer == nullptr) {
+    LOG(ERROR) << __func__ << ": BTIF ACM Initiator, peer is null";
+    return false;
+  }
+
+  CodecQosConfig codec_qos_cfg;
+  memset(&codec_qos_cfg, 0, sizeof(codec_qos_cfg));
+  if ((profileType == GCP) && (peer->GetContextType() & CONTEXT_TYPE_MUSIC)) {
+    data.bd_addr = peer_address;
+    data.streams_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.direction = ASE_DIRECTION_SINK;
+    data.streams_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+    if (peer->IsStereoHsType()) {
+      SelectCodecQosConfig(peer_address, GCP, MEDIA_CONTEXT, SNK, STEREO_HS_CONFIG_1);
+    } else {
+      SelectCodecQosConfig(peer_address, GCP, MEDIA_CONTEXT, SNK, EB_CONFIG);
+    }
+    codec_qos_cfg = peer->get_peer_media_codec_qos_config();
+    data.streams_info.codec_qos_config_pair.push_back(codec_qos_cfg);
+  } else if ((profileType == GCP_TX_RX) && (peer->GetContextType() & CONTEXT_TYPE_MUSIC)) {
+    data.bd_addr = peer_address;
+    data.streams_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.direction = ASE_DIRECTION_SINK;
+    data.streams_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+    SelectCodecQosConfig(peer_address, GCP, MEDIA_CONTEXT, SNK, EB_CONFIG);
+    codec_qos_cfg = peer->get_peer_media_codec_qos_config();
+    codec_qos_cfg.qos_config.cig_config.cig_id++;
+    codec_qos_cfg.qos_config.ascs_configs[0].cig_id++;
+    peer->set_peer_media_qos_config(codec_qos_cfg.qos_config);
+    peer->set_peer_media_codec_qos_config(codec_qos_cfg);
+    data.streams_info.codec_qos_config_pair.push_back(codec_qos_cfg);
+  } else if ((profileType == BAP) && (peer->GetContextType() & CONTEXT_TYPE_MUSIC)) {
+    data.bd_addr = peer_address;
+    data.streams_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.direction = ASE_DIRECTION_SINK;
+    data.streams_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+    if (peer->IsStereoHsType()) {
+      SelectCodecQosConfig(peer_address, BAP, MEDIA_CONTEXT, SNK, STEREO_HS_CONFIG_1);
+    } else {
+      SelectCodecQosConfig(peer_address, BAP, MEDIA_CONTEXT, SNK, EB_CONFIG);
+    }
+    codec_qos_cfg = peer->get_peer_media_codec_qos_config();
+    data.streams_info.codec_qos_config_pair.push_back(codec_qos_cfg);
+  } else if ((profileType == WMCP) && (peer->GetContextType() & CONTEXT_TYPE_MUSIC)) {
+    data.bd_addr = peer_address;
+    data.streams_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    data.streams_info.stream_type.audio_context = CONTENT_TYPE_LIVE; //Live Audio Context
+    data.streams_info.stream_type.direction = ASE_DIRECTION_SRC;
+    data.streams_info.reconf_type = bluetooth::bap::ucast::StreamReconfigType::CODEC_CONFIG;
+    if (peer->IsStereoHsType()) {
+      SelectCodecQosConfig(peer_address, WMCP, MEDIA_CONTEXT, SRC, STEREO_HS_CONFIG_1);
+    } else {
+      SelectCodecQosConfig(peer_address, WMCP, MEDIA_CONTEXT, SRC, EB_CONFIG);
+    }
+    codec_qos_cfg = peer->get_peer_media_codec_qos_config();
+    data.streams_info.codec_qos_config_pair.push_back(codec_qos_cfg);
+  } else if ((profileType == BAP_CALL) && (peer->GetContextType() & CONTEXT_TYPE_VOICE)) {
+    data.bd_addr = peer_address;
+    data.streams_info.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+  }
+
+  peer->SetRcfgProfileType(profileType);
+  if (profileType != BAP_CALL) {
+    print_codec_parameters(codec_qos_cfg.codec_config);
+    print_qos_parameters(codec_qos_cfg.qos_config);
+  }
+  btif_transfer_context(btif_acm_handle_evt, BTIF_ACM_RECONFIG_REQ_EVT, (char*)&data,
+                        sizeof(tBTIF_ACM_RECONFIG), NULL);
+  return true;
+}
+
+static void cleanup_acm_initiator(void) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  do_in_bta_thread(FROM_HERE, Bind(&BtifAcmInitiator::Cleanup,
+                                   base::Unretained(&btif_acm_initiator)));
+}
+
+static const btacm_initiator_interface_t bt_acm_initiator_interface = {
+    sizeof(btacm_initiator_interface_t),
+    init_acm_initiator,
+    connect_acm_initiator,
+    disconnect_acm_initiator,
+    set_active_acm_initiator,
+    start_stream_acm_initiator,
+    stop_stream_acm_initiator,
+    codec_config_acm_initiator,
+    change_codec_config_acm_initiator,
+    cleanup_acm_initiator,
+};
+
+RawAddress btif_acm_initiator_music_active_peer(void) {
+  return btif_acm_initiator.MusicActivePeer();
+}
+
+RawAddress btif_acm_initiator_voice_active_peer(void) {
+  return btif_acm_initiator.VoiceActivePeer();
+}
+
+bool btif_acm_request_csip_lock(uint8_t setId) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+  tBTA_CSIP_CSET cset_info; // need to do memset ?
+  memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+  cset_info = BTA_CsipGetCoordinatedSet(setId);
+  /*if (cset_info.p_srvc_uuid != ACM_UUID) {
+    return false;
+  }*/
+  if (cset_info.size > cset_info.total_discovered) {
+    LOG_INFO(LOG_TAG, "%s not complete set discovered yet. size = %d discovered = %d",
+                    __func__, cset_info.size, cset_info.total_discovered);
+  }
+  if (setId == cset_info.set_id) {
+    LOG_INFO(LOG_TAG, "%s correct set id", __func__);
+  } else {
+    return false;
+  }
+
+  btif_acm_check_and_cancel_lock_release_timer(setId);
+
+  //Aquire lock for entire group.
+  tBTA_SET_LOCK_PARAMS lock_params; //need to do memset ?
+  lock_params.app_id = btif_acm_initiator.GetCsipAppId();
+  lock_params.set_id = cset_info.set_id;
+  lock_params.lock_value = LOCK_VALUE;//For lock
+  lock_params.members_addr = cset_info.set_members;
+  BTA_CsipSetLockValue (lock_params);
+  btif_acm_initiator.SetLockFlags(BtifAcmInitiator::kFlagStatusPendingLock);
+  btif_acm_initiator.SetOrUpdateGroupLockStatus(cset_info.set_id,
+                     btif_acm_initiator.CheckLockFlags(BtifAcmInitiator::kFlagStatusPendingLock));
+  return true;
+}
+
+bool btif_acm_request_csip_unlock(uint8_t setId) {
+  tBTA_CSIP_CSET cset_info; // need to do memset ?
+  memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+  cset_info = BTA_CsipGetCoordinatedSet(setId);
+  /*if (cset_info.p_srvc_uuid != Uuid::FromString("2B86")) {
+    return false;
+  }*/
+  if (cset_info.size > cset_info.total_discovered) {
+    LOG_INFO(LOG_TAG, "%s not complete set discovered yet. size = %d discovered = %d",
+                    __func__, cset_info.size, cset_info.total_discovered);
+  }
+  if (setId == cset_info.set_id) {
+    LOG_INFO(LOG_TAG, "%s correct app id", __func__);
+  } else {
+    return false;
+  }
+  //Aquire lock for entire group.
+  tBTA_SET_LOCK_PARAMS lock_params; //need to do memset ?
+  lock_params.app_id = btif_acm_initiator.GetCsipAppId();
+  lock_params.set_id = cset_info.set_id;
+  lock_params.lock_value = UNLOCK_VALUE;//For Unlock
+  lock_params.members_addr = cset_info.set_members;
+  BTA_CsipSetLockValue (lock_params);
+  btif_acm_initiator.SetLockFlags(BtifAcmInitiator::kFlagStatusPendingUnlock);
+  btif_acm_initiator.SetOrUpdateGroupLockStatus(cset_info.set_id,
+                btif_acm_initiator.CheckLockFlags(BtifAcmInitiator::kFlagStatusPendingUnlock));
+  return true;
+}
+
+bool btif_acm_is_call_active(void) {
+  BtifAcmPeer* peer = nullptr;
+  peer = btif_acm_initiator.FindPeer(btif_acm_initiator.VoiceActivePeer());
+  if (peer != nullptr && (peer->IsStreaming() || peer->CheckFlags(BtifAcmPeer::kFlagPendingStart)) &&
+          (peer->GetStreamContextType() == CONTEXT_TYPE_VOICE))
+    return true;
+
+  return false;
+}
+
+void btif_acm_stream_start(void) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+  if (!btif_acm_initiator.Enabled())
+    return;
+  bool ret = false;
+  if (false/*btif_acm_initiator.IsCsipRegistered() && (btif_acm_initiator.MusicActiveCSetId() != INVALID_SET_ID)*/) {
+    ret = btif_acm_request_csip_lock(btif_acm_initiator.MusicActiveCSetId());
+    if (ret == false) {
+      tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+      if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+          pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+        btif_acm_source_on_suspended(A2DP_CTRL_ACK_FAILURE);
+      } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+        btif_acm_on_started(A2DP_CTRL_ACK_FAILURE);
+      } else {
+        BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+      }
+      return;
+    }
+    //call below in lock changed success CB
+    //should be dispatched to list of peers in active music group.
+    btif_acm_check_and_start_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+  } else {
+    int id = btif_acm_initiator.MusicActiveCSetId();
+    if (id < INVALID_SET_ID) {
+      bool send_neg_ack = true;
+      tBTA_CSIP_CSET cset_info;
+      memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+      cset_info = BTA_CsipGetCoordinatedSet(id);
+      std::vector<RawAddress>::iterator itr;
+      BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+      if ((cset_info.set_members).size() > 0) {
+        for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+           BTIF_TRACE_DEBUG("%s: Sending start request ", __func__);
+           BtifAcmPeer* p = btif_acm_initiator.FindPeer(*itr);
+           if (p != nullptr && p->IsConnected()) {
+             send_neg_ack = false;
+             p->SetStreamContextType(CONTEXT_TYPE_MUSIC);
+             btif_acm_initiator_dispatch_sm_event(*itr, BTIF_ACM_START_STREAM_REQ_EVT);
+           }
+        }
+      }
+      if (send_neg_ack) {
+        tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+        if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+            pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+          btif_acm_source_on_suspended(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+        } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+          btif_acm_on_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+        } else {
+          BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+        }
+        return;
+      }
+      btif_acm_check_and_start_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+    } else {
+      BTIF_TRACE_DEBUG("%s: Sending start to twm device ", __func__);
+      BtifAcmPeer* p = btif_acm_initiator.FindPeer(btif_acm_initiator_music_active_peer());
+
+      if (p != nullptr && p->IsStreaming()) {
+        BTIF_TRACE_DEBUG("%s: Already streaming ongoing", __func__);
+        btif_acm_on_started(A2DP_CTRL_ACK_SUCCESS);
+        return;
+      }
+
+      if (p != nullptr && p->IsConnected()) {
+        p->SetStreamContextType(CONTEXT_TYPE_MUSIC);
+        btif_acm_initiator_dispatch_sm_event(btif_acm_initiator_music_active_peer(), BTIF_ACM_START_STREAM_REQ_EVT);
+      } else {
+        tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+        if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+            pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+          btif_acm_source_on_suspended(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+        } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+          btif_acm_on_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+        } else {
+          BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+        }
+      }
+    }
+  }
+}
+
+void btif_acm_stream_stop(void) {
+  LOG_INFO(LOG_TAG, "%s ", __func__);
+  bool ret = false;
+  if (false /*btif_acm_initiator.IsCsipRegistered() && (btif_acm_initiator.MusicActiveCSetId() != INVALID_SET_ID)*/) {
+    ret = btif_acm_request_csip_lock(btif_acm_initiator.MusicActiveCSetId());
+    if (ret == false) {
+      tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+      if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+          pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+        btif_acm_source_on_suspended(A2DP_CTRL_ACK_FAILURE);
+      } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+        btif_acm_on_started(A2DP_CTRL_ACK_FAILURE);
+      } else {
+        BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+      }
+      return;
+    }
+    btif_acm_check_and_start_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+  } else {
+    BTIF_TRACE_DEBUG("%s: Sending stop to twm device ", __func__);
+    BtifAcmPeer* p = btif_acm_initiator.FindPeer(btif_acm_initiator_music_active_peer());
+    if (p != nullptr && p->IsConnected()) {
+      p->SetStreamContextType(CONTEXT_TYPE_MUSIC);
+      btif_acm_initiator_dispatch_sm_event(btif_acm_initiator_music_active_peer(), BTIF_ACM_STOP_STREAM_REQ_EVT);
+    } else {
+      tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+      if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+          pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+        btif_acm_source_on_suspended(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+      } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+        btif_acm_on_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+      } else {
+        BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+      }
+    }
+  }
+}
+
+void btif_acm_stream_suspend(void) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+  if (!btif_acm_initiator.Enabled())
+    return;
+  bool ret = false;
+  if (false /*btif_acm_initiator.IsCsipRegistered() && (btif_acm_initiator.MusicActiveCSetId() != INVALID_SET_ID)*/) {
+    ret = btif_acm_request_csip_lock(btif_acm_initiator.MusicActiveCSetId());
+    //call below in lock changed success CB.
+    //should be dispatched to list of peers in active music group.
+    if (ret == false) {
+      tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+      if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+          pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+        btif_acm_source_on_suspended(A2DP_CTRL_ACK_FAILURE);
+      } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+        btif_acm_on_started(A2DP_CTRL_ACK_FAILURE);
+      } else {
+        BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+      }
+      return;
+    }
+    btif_acm_check_and_start_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+  } else {
+    int id = btif_acm_initiator.MusicActiveCSetId();
+    if (id < INVALID_SET_ID) {
+      bool send_neg_ack = true;
+      tBTA_CSIP_CSET cset_info; // need to do memset ?
+      memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
+      cset_info = BTA_CsipGetCoordinatedSet(id);
+      std::vector<RawAddress>::iterator itr;
+      BTIF_TRACE_DEBUG("%s: size of set members %d", __func__, (cset_info.set_members).size());
+      if ((cset_info.set_members).size() > 0) {
+        for (itr =(cset_info.set_members).begin(); itr != (cset_info.set_members).end(); itr++) {
+           BTIF_TRACE_DEBUG("%s: Sending suspend request ", __func__);
+           BtifAcmPeer* p = btif_acm_initiator.FindPeer(*itr);
+           if (p != nullptr && p->IsConnected()) {
+             send_neg_ack = false;
+             p->SetStreamContextType(CONTEXT_TYPE_MUSIC);
+             btif_acm_initiator_dispatch_sm_event(*itr, BTIF_ACM_SUSPEND_STREAM_REQ_EVT);
+           }
+        }
+      }
+      if (send_neg_ack) {
+        tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+        if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+            pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+          btif_acm_source_on_suspended(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+        } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+          btif_acm_on_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+        } else {
+          BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+        }
+        return;
+      }
+      btif_acm_check_and_start_group_procedure_timer(btif_acm_initiator.MusicActiveCSetId());
+    } else {
+      BTIF_TRACE_DEBUG("%s: Sending suspend to twm device ", __func__);
+      BtifAcmPeer* p = btif_acm_initiator.FindPeer(btif_acm_initiator_music_active_peer());
+      if (p != nullptr && p->IsConnected()) {
+        p->SetStreamContextType(CONTEXT_TYPE_MUSIC);
+        btif_acm_initiator_dispatch_sm_event(btif_acm_initiator_music_active_peer(), BTIF_ACM_SUSPEND_STREAM_REQ_EVT);
+      } else {
+        tA2DP_CTRL_CMD pending_cmd = btif_ahim_get_pending_command();
+        if (pending_cmd == A2DP_CTRL_CMD_STOP ||
+            pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
+          btif_acm_source_on_suspended(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+        } else if (pending_cmd == A2DP_CTRL_CMD_START) {
+          btif_acm_on_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+        } else {
+          BTIF_TRACE_DEBUG("%s: no pending command to ack mm audio", __func__);
+        }
+      }
+    }
+  }
+}
+
+void btif_acm_disconnect(const RawAddress& peer_address, int context_type) {
+  LOG_INFO(LOG_TAG, "%s: peer %s", __func__, peer_address.ToString().c_str());
+  disconnect_acm_initiator(peer_address, context_type);
+}
+
+static void btif_acm_initiator_dispatch_sm_event(const RawAddress& peer_address,
+                                                 btif_acm_sm_event_t event) {
+  BtifAcmEvent btif_acm_event(event, nullptr, 0);
+  BTIF_TRACE_EVENT("%s: peer_address=%s event=%s", __func__,
+                   peer_address.ToString().c_str(),
+                   btif_acm_event.ToString().c_str());
+
+  btif_transfer_context(btif_acm_handle_evt, event, (char *)&peer_address,
+                        sizeof(RawAddress), NULL);
+}
+
+bt_status_t btif_acm_initiator_execute_service(bool enable) {
+  BTIF_TRACE_EVENT("%s: service: %s", __func__,
+                   (enable) ? "enable" : "disable");
+
+  if (enable) {
+    BTA_RegisterCsipApp(bta_csip_callback, base::Bind([](uint8_t status, uint8_t app_id) {
+                                             if (status != BTA_CSIP_SUCCESS) {
+                                                LOG(ERROR) << "Can't register CSIP module ";
+                                                return;
+                                             }
+                                             BTIF_TRACE_DEBUG("App ID: %d", app_id);
+                                             btif_acm_initiator.SetCsipAppId(app_id);
+                                             btif_acm_initiator.SetCsipRegistration(true);} ));
+    return BT_STATUS_SUCCESS;
+  }
+
+  // Disable the service
+  //BTA_UnregisterCsipApp();
+  return BT_STATUS_FAIL;
+}
+
+// Get the ACM callback interface for ACM Initiator profile
+const btacm_initiator_interface_t* btif_acm_initiator_get_interface(void) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  return &bt_acm_initiator_interface;
+}
+
+uint16_t btif_acm_get_active_device_latency() {
+  BtifAcmPeer* peer = btif_acm_initiator.FindMusicActivePeer();
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: peer is NULL", __func__);
+    return 0;
+  } else {
+    return peer->GetPeerLatency();
+  }
+}
+
+static void SelectCodecQosConfig(const RawAddress& bd_addr,
+                                 int profile_type, int context_type,
+                                 int direction, int config_type) {
+
+  BTIF_TRACE_DEBUG("%s: Peer %s , context type: %d, profile_type: %d,"
+                   " direction: %d config_type %d", __func__,
+                   bd_addr.ToString().c_str(), context_type,
+                   profile_type, direction, config_type);
+
+  BtifAcmPeer* peer = btif_acm_initiator.FindPeer(bd_addr);
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: peer is NULL", __func__);
+    return;
+  }
+
+  uint8_t CigId = peer->CigId();
+  uint8_t set_size = 0;
+  tBTA_CSIP_CSET cset_info;
+  memset(&cset_info, 0, sizeof(cset_info));
+  cset_info = BTA_CsipGetCoordinatedSet(peer->SetId());
+  BTIF_TRACE_DEBUG("%s: cset members size: %d",
+                    __func__, (uint8_t)(cset_info.size));
+
+  if (cset_info.size == 0) {
+    BTIF_TRACE_WARNING("%s: this shud be case for stereo-HS, config_type %d",
+                                             __func__, config_type);
+    set_size = (config_type == STEREO_HS_CONFIG_1) ? 2 : 1;
+  } else {
+    set_size = cset_info.size;
+  }
+
+  CodecConfig codec_config_;
+  CodecQosConfig codec_qos_config;
+  QosConfig qos_configs;
+  CISConfig cis_config;
+  std::vector<QoSConfig> vmcp_qos_config;
+  BTIF_TRACE_WARNING("%s: going for best config", __func__);
+  memset(&codec_config_, 0, sizeof(codec_config_));
+  select_best_codec_config(bd_addr, context_type, profile_type,
+                            &codec_config_, direction, config_type);
+  codec_qos_config.codec_config = codec_config_;
+  BTIF_TRACE_DEBUG("%s: sample rate : %d, frame_duration: %d, octets: %d, ",
+                   __func__, static_cast<uint16_t>(codec_config_.sample_rate),
+                   GetFrameDuration(&codec_config_),
+                   GetOctsPerFrame(&codec_config_));
+
+  if (context_type == MEDIA_CONTEXT) {
+    if (profile_type != WMCP) {
+      vmcp_qos_config = get_qos_params_for_codec(profile_type,
+                                      MEDIA_LL_CONTEXT,
+                                      codec_config_.sample_rate,
+                                      GetFrameDuration(&codec_config_),
+                                      GetOctsPerFrame(&codec_config_));
+    } else {
+      vmcp_qos_config = get_qos_params_for_codec(profile_type,
+                                      MEDIA_HR_CONTEXT,
+                                      codec_config_.sample_rate,
+                                      GetFrameDuration(&codec_config_),
+                                      GetOctsPerFrame(&codec_config_));
+    }
+  } else if (context_type == VOICE_CONTEXT) {
+    vmcp_qos_config = get_qos_params_for_codec(profile_type,
+                                      VOICE_CONTEXT,
+                                      codec_config_.sample_rate,
+                                      GetFrameDuration(&codec_config_),
+                                      GetOctsPerFrame(&codec_config_));
+  }
+  BTIF_TRACE_DEBUG("%s: vmcp qos size: %d",
+                           __func__, (uint8_t)vmcp_qos_config.size());
+
+  bool qhs_enable = false;
+  char qhs_value[PROPERTY_VALUE_MAX] = "false";
+  property_get("persist.vendor.btstack.qhs_enable", qhs_value, "false");
+  if (!strncmp("true", qhs_value, 4)) {
+    if (btm_acl_qhs_phy_supported(bd_addr, BT_TRANSPORT_LE)) {
+      qhs_enable = true;
+    }
+  } else {
+    qhs_enable = false;
+  }
+
+  //TODO: fill cig id and cis count from
+  //Currently it is a single size vector
+  for (uint8_t j = 0; j < (uint8_t)vmcp_qos_config.size(); j++) {
+    if (vmcp_qos_config[j].mandatory == 0) {
+      uint32_t sdu_interval = vmcp_qos_config[j].sdu_int_micro_secs;
+      codec_qos_config.qos_config.cig_config = {
+                  .cig_id = CigId,
+                  .cis_count = set_size,
+                  .packing = 0x01, // interleaved
+                  .framing =  vmcp_qos_config[j].framing, // unframed
+                  .max_tport_latency_m_to_s = vmcp_qos_config[j].max_trans_lat,
+                  .max_tport_latency_s_to_m = vmcp_qos_config[j].max_trans_lat,
+                  .sdu_interval_m_to_s = {
+                             static_cast<uint8_t>(sdu_interval & 0xFF),
+                             static_cast<uint8_t>((sdu_interval >> 8)& 0xFF),
+                             static_cast<uint8_t>((sdu_interval >> 16)& 0xFF)
+                           },
+                  .sdu_interval_s_to_m = {
+                             static_cast<uint8_t>(sdu_interval & 0xFF),
+                             static_cast<uint8_t>((sdu_interval >> 8)& 0xFF),
+                             static_cast<uint8_t>((sdu_interval >> 16)& 0xFF)
+                           }
+                  };
+      BTIF_TRACE_DEBUG("%s: framing: %d, transport latency: %d"
+                       " sdu_interval: %d", __func__,
+                        vmcp_qos_config[j].framing,
+                        vmcp_qos_config[j].max_trans_lat,
+                        vmcp_qos_config[j].sdu_int_micro_secs);
+      BTIF_TRACE_DEBUG("%s: CIG: packing: %d, transport latency m to s: %d,"
+              " transport latency s to m: %d", __func__,
+              codec_qos_config.qos_config.cig_config.packing,
+              codec_qos_config.qos_config.cig_config.max_tport_latency_m_to_s,
+              codec_qos_config.qos_config.cig_config.max_tport_latency_s_to_m);
+      BTIF_TRACE_DEBUG("%s: Filled CIG config ", __func__);
+    }
+  }
+
+  for (uint8_t i = 0; i < set_size; i++) {
+    //Currently it is a single size vector
+    uint8_t check_memset = 0;
+    for (uint8_t j = 0; j < (uint8_t)vmcp_qos_config.size(); j++) {
+      if (vmcp_qos_config[j].mandatory == 0) {
+        memset(&cis_config, 0, sizeof(cis_config));
+        if (!check_memset)
+          check_memset = 1;
+        cis_config.cis_id = i;
+        if (profile_type != WMCP)
+          cis_config.max_sdu_m_to_s = vmcp_qos_config[j].max_sdu_size;
+        else
+          cis_config.max_sdu_m_to_s = 0;
+        if ((context_type == VOICE_CONTEXT) || (profile_type == WMCP))
+          cis_config.max_sdu_s_to_m = vmcp_qos_config[j].max_sdu_size;
+        else
+          cis_config.max_sdu_s_to_m = 0;
+
+        BTIF_TRACE_DEBUG("%s: qhs_enable: %d", __func__, qhs_enable);
+
+        if (qhs_enable) {
+          cis_config.phy_m_to_s = LE_QHS_PHY;
+          cis_config.phy_s_to_m = LE_QHS_PHY;
+        } else {
+          cis_config.phy_m_to_s = LE_2M_PHY;//2mbps
+          cis_config.phy_s_to_m = LE_2M_PHY;
+        }
+        cis_config.rtn_m_to_s = vmcp_qos_config[j].retrans_num;
+        cis_config.rtn_s_to_m = vmcp_qos_config[j].retrans_num;
+      }
+    }
+    if (!check_memset)
+      memset(&cis_config, 0, sizeof(cis_config));
+    codec_qos_config.qos_config.cis_configs.push_back(cis_config);
+    BTIF_TRACE_DEBUG("%s: Filled CIS config for %d", __func__, i);
+  }
+
+  for (uint8_t j = 0; j < (uint8_t)vmcp_qos_config.size(); j++) {
+    if (vmcp_qos_config[j].mandatory == 0) {
+      uint32_t presen_delay = vmcp_qos_config[j].presentation_delay;
+      ASCSConfig ascs_config_1 = {
+                      .cig_id = CigId,
+                      .cis_id = peer->CisId(),
+                      .target_latency = 0x03,//Target higher reliability
+                      .bi_directional = false,
+                      .presentation_delay = {static_cast<uint8_t>(presen_delay & 0xFF),
+                                             static_cast<uint8_t>((presen_delay >> 8)& 0xFF),
+                                             static_cast<uint8_t>((presen_delay >> 16)& 0xFF)}
+                      };
+      codec_qos_config.qos_config.ascs_configs.push_back(ascs_config_1);
+      BTIF_TRACE_DEBUG("%s: presentation delay = %d", __func__, presen_delay);
+      BTIF_TRACE_DEBUG("%s: Filled ASCS config for %d", __func__, ascs_config_1.cis_id);
+      if (config_type == STEREO_HS_CONFIG_1) {
+        ASCSConfig ascs_config_2 = ascs_config_1;
+        ascs_config_2.cis_id = peer->CisId()+1;
+        codec_qos_config.qos_config.ascs_configs.push_back(ascs_config_2);
+        BTIF_TRACE_DEBUG("%s: Filled ASCS config for %d", __func__, ascs_config_2.cis_id);
+      }
+    }
+  }
+
+  if (profile_type == BAP) {
+    if (context_type == VOICE_CONTEXT) {
+      if (direction == SNK) {
+        codec_qos_config.qos_config.cig_config.cig_id = CigId + 2;
+        codec_qos_config.qos_config.ascs_configs[0].cig_id = CigId + 2;
+        codec_qos_config.qos_config.ascs_configs[0].target_latency = 0x01;
+        codec_qos_config.qos_config.ascs_configs[0].bi_directional = true;
+        if (config_type == STEREO_HS_CONFIG_1) {
+          codec_qos_config.qos_config.ascs_configs[1].cig_id = CigId + 2;
+          codec_qos_config.qos_config.ascs_configs[1].target_latency = 0x01;
+          codec_qos_config.qos_config.ascs_configs[1].bi_directional = true;
+        }
+        peer->set_peer_voice_tx_codec_config(codec_config_);
+        peer->set_peer_voice_tx_qos_config(codec_qos_config.qos_config);
+        peer->set_peer_voice_tx_codec_qos_config(codec_qos_config);
+      } else if (direction == SRC) {
+        codec_qos_config.qos_config.cig_config.cig_id = CigId + 2;
+        codec_qos_config.qos_config.ascs_configs[0].cig_id = CigId + 2;
+        codec_qos_config.qos_config.ascs_configs[0].target_latency = 0x01;
+        codec_qos_config.qos_config.ascs_configs[0].bi_directional = true;
+        if (config_type == STEREO_HS_CONFIG_1) {
+          codec_qos_config.qos_config.ascs_configs[1].cig_id = CigId + 2;
+          codec_qos_config.qos_config.ascs_configs[1].target_latency = 0x01;
+          codec_qos_config.qos_config.ascs_configs[1].bi_directional = true;
+        }
+        peer->set_peer_voice_rx_codec_config(codec_config_);
+        peer->set_peer_voice_rx_qos_config(codec_qos_config.qos_config);
+        peer->set_peer_voice_rx_codec_qos_config(codec_qos_config);
+      }
+    } else {
+      peer->set_peer_media_codec_config(codec_config_);
+      peer->set_peer_media_qos_config(codec_qos_config.qos_config);
+      peer->set_peer_media_codec_qos_config(codec_qos_config);
+    }
+  } else if (profile_type == GCP) {
+    if (context_type == VOICE_CONTEXT) {
+      codec_qos_config.qos_config.cig_config.cig_id = CigId + 1;
+      codec_qos_config.qos_config.ascs_configs[0].cig_id = CigId + 1;
+      codec_qos_config.qos_config.ascs_configs[0].target_latency = 0x01;
+      codec_qos_config.qos_config.ascs_configs[0].bi_directional = true;
+      peer->set_peer_voice_rx_codec_config(codec_config_);
+      peer->set_peer_voice_rx_qos_config(codec_qos_config.qos_config);
+      peer->set_peer_voice_rx_codec_qos_config(codec_qos_config);
+    } else {
+      peer->set_peer_media_codec_config(codec_config_);
+      peer->set_peer_media_qos_config(codec_qos_config.qos_config);
+      peer->set_peer_media_codec_qos_config(codec_qos_config);
+    }
+  } else if (profile_type == WMCP) {
+    if (context_type == MEDIA_CONTEXT) {
+      codec_qos_config.qos_config.cig_config.cig_id = CigId + 3;
+      codec_qos_config.qos_config.ascs_configs[0].cig_id = CigId + 3;
+      if (config_type == STEREO_HS_CONFIG_1)
+        codec_qos_config.qos_config.ascs_configs[1].cig_id = CigId + 3;
+      peer->set_peer_media_codec_config(codec_config_);
+      peer->set_peer_media_qos_config(codec_qos_config.qos_config);
+      peer->set_peer_media_codec_qos_config(codec_qos_config);
+    }
+  }
+  //print_codec_parameters(codec_config_);
+  //print_qos_parameters(codec_qos_config.qos_config);
+}
+
+static bool select_best_sample_rate(uint16_t samp_freq, CodecConfig *result_config) {
+  BTIF_TRACE_DEBUG("%s: samp_freq: %d", __func__, samp_freq);
+  if (samp_freq & static_cast<uint16_t>(CodecSampleRate::CODEC_SAMPLE_RATE_48000)) {
+    result_config->sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    return true;
+  }
+  if (samp_freq & static_cast<uint16_t>(CodecSampleRate::CODEC_SAMPLE_RATE_44100)) {
+    result_config->sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_44100;
+    return true;
+  }
+  if (samp_freq & static_cast<uint16_t>(CodecSampleRate::CODEC_SAMPLE_RATE_32000)) {
+    result_config->sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_32000;
+    return true;
+  }
+  if (samp_freq & static_cast<uint16_t>(CodecSampleRate::CODEC_SAMPLE_RATE_24000)) {
+    result_config->sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_24000;
+    return true;
+  }
+  if (samp_freq & static_cast<uint16_t>(CodecSampleRate::CODEC_SAMPLE_RATE_16000)) {
+    result_config->sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+    return true;
+  }
+  if (samp_freq & static_cast<uint16_t>(CodecSampleRate::CODEC_SAMPLE_RATE_8000)) {
+    result_config->sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_8000;
+    return true;
+  }
+  return false;
+}
+
+static bool select_best_frame_dura(uint8_t frame_dura,
+                                   CodecConfig *result_config) {
+  BTIF_TRACE_DEBUG("%s: frame_duration: %d", __func__, frame_dura);
+  if (frame_dura & static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10)) {
+    BTIF_TRACE_DEBUG("%s: selecting 10ms as best frame duration", __func__);
+    UpdateFrameDuration(result_config,
+                   static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    return true;
+  }
+
+  if ((frame_dura &
+       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_7_5)) == 0) {
+    BTIF_TRACE_DEBUG("%s: selecting 7.5ms as best frame duration", __func__);
+    UpdateFrameDuration(result_config,
+               static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_7_5));
+    return true;
+  }
+  return true;
+}
+
+void select_best_codec_config(const RawAddress& bd_addr,
+                              uint16_t context_type,
+                              uint8_t profile_type,
+                              CodecConfig *codec_config,
+                              int dir, int config_type) {
+
+    BTIF_TRACE_DEBUG("%s: select best codec config for context type: %d,"
+                     " profile type %d config_type %d", __func__,
+                     context_type, profile_type, config_type);
+
+    CodecConfig result_codec_config;
+    uint16_t vmcp_samp_freq = 0;
+    uint8_t vmcp_fram_dur = 0;
+    uint32_t vmcp_octets_per_frame = 0;
+    std::vector<CodecConfig> pac_record;
+    std::vector<CodecConfig> local_codec_config;
+    memset(&result_codec_config, 0, sizeof(result_codec_config));
+    bool pac_found = false;
+    uint16_t audio_context_type = CONTENT_TYPE_UNSPECIFIED;
+
+    bool is_lc3q_supported = false;
+    char lc3q_value[PROPERTY_VALUE_MAX] = "false";
+    property_get("persist.vendor.service.bt.is_lc3q_supported", lc3q_value, "false");
+    if (!strncmp("true", lc3q_value, 4)) {
+      is_lc3q_supported = true;
+    } else {
+      is_lc3q_supported = false;
+    }
+    BTIF_TRACE_IMP("%s: is_lc3q_supported: %d", __func__, is_lc3q_supported);
+
+    if (context_type == MEDIA_CONTEXT) {
+      audio_context_type |=
+        (profile_type == WMCP) ? CONTENT_TYPE_LIVE : CONTENT_TYPE_MEDIA;
+    } else if (context_type == VOICE_CONTEXT) {
+      audio_context_type |= CONTENT_TYPE_CONVERSATIONAL;
+    }
+    BTIF_TRACE_IMP("%s: audio_context_type: %d", __func__, audio_context_type);
+
+    pac_found = btif_bap_get_records(bd_addr, REC_TYPE_CAPABILITY, audio_context_type,
+        ((dir == SRC) ? CodecDirection::CODEC_DIR_SRC : CodecDirection::CODEC_DIR_SINK),
+        &pac_record);
+
+    if (pac_found) {
+      BTIF_TRACE_DEBUG("%s: PAC record found, select best codec config", __func__);
+      uint16_t peer_samp_freq = 0;
+      uint8_t peer_channel_mode = 0;
+      uint8_t peer_fram_dur = 0;
+      uint16_t peer_min_octets_per_frame = 0;
+      uint16_t peer_max_octets_per_frame = 0;
+      uint8_t peer_max_sup_lc3_frames = 0;
+      uint16_t peer_preferred_context = 0;
+      bool peer_lc3q_pref = 0;
+      uint8_t peer_lc3q_ver = 0;
+
+      //currently differentiating based on frequency later we will do on context type
+      for (auto it = pac_record.begin(); it != pac_record.end(); ++it) {
+        if (it->codec_type == CodecIndex::CODEC_INDEX_SOURCE_LC3) {
+          //performing only for MUSIC context type based on 44.1KHz and 48KHz
+          BTIF_TRACE_DEBUG("%s: pac_record sample_rate: %d", __func__, it->sample_rate);
+          if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_48000) {
+            peer_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_44100) {
+            peer_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_32000) {
+            peer_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_24000) {
+            peer_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_16000) {
+            peer_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_8000) {
+            peer_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          }
+        }
+      }
+
+      local_codec_config = get_all_codec_configs(profile_type, context_type);
+      BTIF_TRACE_DEBUG("%s: vmcp codec size: %d",
+                           __func__, (uint8_t)local_codec_config.size());
+      for (auto it = local_codec_config.begin();
+                             it != local_codec_config.end(); ++it) {
+        if (it->codec_type == CodecIndex::CODEC_INDEX_SOURCE_LC3) {
+          BTIF_TRACE_DEBUG("%s: local_codec_config sample_rate: %d",
+                                           __func__, it->sample_rate);
+          if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_48000) {
+            vmcp_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_44100) {
+            vmcp_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_32000) {
+            vmcp_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_24000) {
+            vmcp_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_16000) {
+            vmcp_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_8000) {
+            vmcp_samp_freq |= static_cast<uint16_t>(it->sample_rate);
+          }
+        }
+      }
+
+      select_best_sample_rate(peer_samp_freq & vmcp_samp_freq,
+                                                   &result_codec_config);
+      for (auto it = pac_record.begin(); it != pac_record.end(); ++it) {
+        if (it->codec_type == CodecIndex::CODEC_INDEX_SOURCE_LC3) {
+           BTIF_TRACE_DEBUG("%s: pac_record sample_rate: %d,"
+                            " result_codec_config.sample_rate: %d",
+                            __func__, it->sample_rate,
+                            result_codec_config.sample_rate);
+            if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_48000 &&
+                result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_48000) {
+              BTIF_TRACE_DEBUG("%s: selecting 48KHz for config", __func__);
+              peer_channel_mode = static_cast<uint8_t>(it->channel_mode);
+              peer_fram_dur = GetCapaSupFrameDurations(&(*it));
+              peer_min_octets_per_frame = GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF;
+              peer_max_octets_per_frame = (GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF0000) >> 16;
+              peer_max_sup_lc3_frames = GetCapaMaxSupLc3Frames(&(*it));
+              peer_lc3q_pref = GetCapaVendorMetaDataLc3QPref(&(*it));
+              peer_lc3q_ver = GetCapaVendorMetaDataLc3QVer(&(*it));
+              peer_preferred_context = GetCapaPreferredContexts(&(*it));
+              break;
+            } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_44100 &&
+                result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_44100) {
+              BTIF_TRACE_DEBUG("%s: selecting 44.1KHz for config", __func__);
+              peer_channel_mode = static_cast<uint8_t>(it->channel_mode);
+              peer_fram_dur = GetCapaSupFrameDurations(&(*it));
+              peer_min_octets_per_frame = GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF;
+              peer_max_octets_per_frame = (GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF0000) >> 16;
+              peer_max_sup_lc3_frames = GetCapaMaxSupLc3Frames(&(*it));
+              peer_lc3q_pref = GetCapaVendorMetaDataLc3QPref(&(*it));
+              peer_lc3q_ver = GetCapaVendorMetaDataLc3QVer(&(*it));
+              peer_preferred_context = GetCapaPreferredContexts(&(*it));
+              break;
+            } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_32000 &&
+                result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_32000) {
+              BTIF_TRACE_DEBUG("%s: selecting 32KHz for config", __func__);
+              peer_channel_mode = static_cast<uint8_t>(it->channel_mode);
+              peer_fram_dur = GetCapaSupFrameDurations(&(*it));
+              peer_min_octets_per_frame = GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF;
+              peer_max_octets_per_frame = (GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF0000) >> 16;
+              peer_max_sup_lc3_frames = GetCapaMaxSupLc3Frames(&(*it));
+              peer_lc3q_pref = GetCapaVendorMetaDataLc3QPref(&(*it));
+              peer_lc3q_ver = GetCapaVendorMetaDataLc3QVer(&(*it));
+              peer_preferred_context = GetCapaPreferredContexts(&(*it));
+              break;
+            } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_24000 &&
+                result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_24000) {
+              BTIF_TRACE_DEBUG("%s: selecting 24KHz for config", __func__);
+              peer_channel_mode = static_cast<uint8_t>(it->channel_mode);
+              peer_fram_dur = GetCapaSupFrameDurations(&(*it));
+              peer_min_octets_per_frame = GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF;
+              peer_max_octets_per_frame = (GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF0000) >> 16;
+              peer_max_sup_lc3_frames = GetCapaMaxSupLc3Frames(&(*it));
+              peer_lc3q_pref = GetCapaVendorMetaDataLc3QPref(&(*it));
+              peer_lc3q_ver = GetCapaVendorMetaDataLc3QVer(&(*it));
+              peer_preferred_context = GetCapaPreferredContexts(&(*it));
+              break;
+            } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_16000 &&
+                result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_16000) {
+              BTIF_TRACE_DEBUG("%s: selecting 16KHz for config", __func__);
+              peer_channel_mode = static_cast<uint8_t>(it->channel_mode);
+              peer_fram_dur = GetCapaSupFrameDurations(&(*it));
+              peer_min_octets_per_frame = GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF;
+              peer_max_octets_per_frame = (GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF0000) >> 16;
+              peer_max_sup_lc3_frames = GetCapaMaxSupLc3Frames(&(*it));
+              peer_lc3q_pref = GetCapaVendorMetaDataLc3QPref(&(*it));
+              peer_lc3q_ver = GetCapaVendorMetaDataLc3QVer(&(*it));
+              peer_preferred_context = GetCapaPreferredContexts(&(*it));
+              break;
+            } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_8000 &&
+                result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_8000) {
+              BTIF_TRACE_DEBUG("%s: selecting 8KHz for config", __func__);
+              peer_channel_mode = static_cast<uint8_t>(it->channel_mode);
+              peer_fram_dur = GetCapaSupFrameDurations(&(*it));
+              peer_min_octets_per_frame = GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF;
+              peer_max_octets_per_frame = (GetCapaSupOctsPerFrame(&(*it)) & 0xFFFF0000) >> 16;
+              peer_max_sup_lc3_frames = GetCapaMaxSupLc3Frames(&(*it));
+              peer_lc3q_pref = GetCapaVendorMetaDataLc3QPref(&(*it));
+              peer_lc3q_ver = GetCapaVendorMetaDataLc3QVer(&(*it));
+              peer_preferred_context = GetCapaPreferredContexts(&(*it));
+              break;
+            }
+          }
+        }
+
+      BTIF_TRACE_DEBUG("%s: PAC parameters, peer supported sample_freqncies=%d,"
+                       " channel_mode=%d, frame_dura=%d", __func__,
+                        peer_samp_freq, peer_channel_mode, peer_fram_dur);
+      BTIF_TRACE_DEBUG("%s: PAC parameters, min_octets_per_frame=%d,"
+                       " max_octets_per_frame=%d, peer_max_sup_lc3_frames=%d",
+                       __func__, peer_min_octets_per_frame, peer_max_octets_per_frame,
+                       peer_max_sup_lc3_frames);
+      BTIF_TRACE_DEBUG("%s: PAC parameters, peer_preferred_context=%d",
+                       __func__, peer_preferred_context);
+      BTIF_TRACE_DEBUG("%s: PAC parameters, peer_lc3q_pref=%d, peer_lc3q_ver=%d",
+                                        __func__, peer_lc3q_pref, peer_lc3q_ver);
+
+      local_codec_config = get_all_codec_configs(profile_type, context_type);
+      BTIF_TRACE_DEBUG("%s: vmcp codec size: %d",
+                        __func__, (uint8_t)local_codec_config.size());
+      for (auto it = local_codec_config.begin();
+                             it != local_codec_config.end(); ++it) {
+        if (it->codec_type == CodecIndex::CODEC_INDEX_SOURCE_LC3) {
+          BTIF_TRACE_DEBUG("%s: local_codec_config sample_rate: %d,"
+                           " result_codec_config.sample_rate: %d",
+                           __func__, it->sample_rate, result_codec_config.sample_rate);
+          if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_48000 &&
+              result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_48000) {
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_44100 &&
+              result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_44100) {
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_32000 &&
+              result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_32000) {
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_24000 &&
+              result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_24000) {
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_16000 &&
+              result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_16000) {
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_8000 &&
+              result_codec_config.sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_8000) {
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          }
+        }
+      }
+
+      if (config_type == STEREO_HS_CONFIG_2)
+        vmcp_octets_per_frame = vmcp_octets_per_frame * 2;
+
+      BTIF_TRACE_DEBUG("%s: VMCP parameters, sample_freq=%d,"
+                       " frame_duration=%d, octets=%d", __func__,
+                        vmcp_samp_freq, vmcp_fram_dur, vmcp_octets_per_frame);
+
+      result_codec_config.codec_type = CodecIndex::CODEC_INDEX_SOURCE_LC3;
+      result_codec_config.codec_priority = CodecPriority::CODEC_PRIORITY_DEFAULT;
+
+      if (config_type == STEREO_HS_CONFIG_2) {
+        result_codec_config.channel_mode = CodecChannelMode::CODEC_CHANNEL_MODE_STEREO;
+      } else {
+        result_codec_config.channel_mode = CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+      }
+
+      select_best_frame_dura((peer_fram_dur >> 1) & vmcp_fram_dur, &result_codec_config);
+
+      if (vmcp_octets_per_frame < peer_min_octets_per_frame ||
+          vmcp_octets_per_frame > peer_max_octets_per_frame) {
+        BTIF_TRACE_DEBUG("%s: octets per frame is out of bound: %d ",
+                             __func__, vmcp_octets_per_frame);
+        UpdateOctsPerFrame(&result_codec_config, vmcp_octets_per_frame);
+      } else {
+        BTIF_TRACE_DEBUG("%s: octets per frame is in limit update 100 octets: %d ",
+                              __func__, vmcp_octets_per_frame);
+        UpdateOctsPerFrame(&result_codec_config, vmcp_octets_per_frame);//TODO: make this as peer octets
+      }
+
+      if (is_lc3q_supported) {
+        UpdateLc3QPreference(&result_codec_config, true);
+      }
+      UpdateCapaMaxSupLc3Frames(&result_codec_config, peer_max_sup_lc3_frames);
+      UpdateLc3BlocksPerSdu(&result_codec_config, 1);
+      UpdatePreferredAudioContext(&result_codec_config, audio_context_type);
+      *codec_config = result_codec_config;
+    } else {
+      BTIF_TRACE_DEBUG("%s: PAC record not found, select mandatory config", __func__);
+      mandatory_codec_selected = true;
+      std::vector<CodecConfig> codec_pref_config;
+      codec_pref_config = get_all_codec_configs(profile_type, context_type);
+      BTIF_TRACE_DEBUG("%s: vmcp codec size %d", __func__, (uint8_t)codec_pref_config.size());
+      for (auto it = codec_pref_config.begin(); it != codec_pref_config.end(); ++it) {
+        if (it->codec_type == CodecIndex::CODEC_INDEX_SOURCE_LC3) {
+          if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_48000) {
+            BTIF_TRACE_DEBUG("%s: selecting 48KHz from VMCP", __func__);
+            result_codec_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_44100) {
+            BTIF_TRACE_DEBUG("%s: selecting 44.1KHz from VMCP", __func__);
+            result_codec_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_44100;
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_32000) {
+            BTIF_TRACE_DEBUG("%s: selecting 32KHz from VMCP", __func__);
+            result_codec_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_32000;
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_24000) {
+            BTIF_TRACE_DEBUG("%s: selecting 24KHz from VMCP", __func__);
+            result_codec_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_24000;
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_16000) {
+            BTIF_TRACE_DEBUG("%s: selecting 16KHz from VMCP", __func__);
+            result_codec_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          } else if (it->sample_rate == CodecSampleRate::CODEC_SAMPLE_RATE_8000) {
+            BTIF_TRACE_DEBUG("%s: selecting 8KHz from VMCP", __func__);
+            result_codec_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_8000;
+            vmcp_fram_dur |= GetFrameDuration(&(*it));
+            if (vmcp_octets_per_frame < GetOctsPerFrame(&(*it)))
+              vmcp_octets_per_frame = GetOctsPerFrame(&(*it));
+          }
+        }
+      }
+
+      if (config_type == STEREO_HS_CONFIG_2)
+        vmcp_octets_per_frame = vmcp_octets_per_frame * 2;
+
+      BTIF_TRACE_DEBUG("%s: VMCP parameters, frame_duration=%d, octets=%d",
+                         __func__, vmcp_fram_dur, vmcp_octets_per_frame);
+
+      result_codec_config.codec_type = CodecIndex::CODEC_INDEX_SOURCE_LC3;
+      result_codec_config.codec_priority = CodecPriority::CODEC_PRIORITY_DEFAULT;
+      if (config_type == STEREO_HS_CONFIG_2)
+        result_codec_config.channel_mode = CodecChannelMode::CODEC_CHANNEL_MODE_STEREO;
+      else
+        result_codec_config.channel_mode = CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+      //select_best_sample_rate(peer_samp_freq & vmcp_samp_freq, &result_codec_config, context_type);
+      select_best_frame_dura(vmcp_fram_dur, &result_codec_config);
+      UpdateOctsPerFrame(&result_codec_config, vmcp_octets_per_frame);
+      if (is_lc3q_supported) {
+        UpdateLc3QPreference(&result_codec_config, true);
+      }
+      UpdateLc3BlocksPerSdu(&result_codec_config, 1);//currently making it for media case
+      UpdatePreferredAudioContext(&result_codec_config, audio_context_type);
+      BTIF_TRACE_DEBUG("%s: saved codec config", __func__);
+      *codec_config = result_codec_config;
+    }
+}
+
+uint16_t btif_acm_get_sample_rate() {
+  if (current_active_config.sample_rate !=
+                CodecSampleRate::CODEC_SAMPLE_RATE_NONE) {
+    BTIF_TRACE_DEBUG("[ACM]:%s: sample_rate = %d",
+                     __func__, current_active_config.sample_rate);
+    return static_cast<uint16_t>(current_active_config.sample_rate);
+  } else {
+    BTIF_TRACE_DEBUG("[ACM]:%s: default sample_rate = %d",
+                     __func__, default_config.sample_rate);
+    return static_cast<uint16_t>(default_config.sample_rate);
+  }
+}
+
+uint8_t btif_acm_get_ch_mode() {
+  if (current_active_config.channel_mode !=
+         CodecChannelMode::CODEC_CHANNEL_MODE_NONE) {
+    BTIF_TRACE_DEBUG("[ACM]:%s: channel mode = %d",
+                 __func__, current_active_config.channel_mode);
+    return static_cast<uint8_t>(current_active_config.channel_mode);
+  } else {
+    BTIF_TRACE_DEBUG("[ACM]:%s: channel mode = %d",
+                      __func__, default_config.channel_mode);
+    return static_cast<uint8_t>(default_config.channel_mode);
+  }
+}
+
+uint32_t btif_acm_get_bitrate() {
+    //based on bitrate set (100(80kbps), 120 (96kbps), 155 (128kbps))
+    uint32_t bitrate = 0;
+    uint16_t octets = static_cast<int>(GetOctsPerFrame(&current_active_config));
+    BTIF_TRACE_DEBUG("[ACM]:%s: octets = %d",__func__, octets);
+
+    switch (octets) {
+        case 26:
+          bitrate = 27800;
+          break;
+
+        case 30:
+          if (btif_acm_get_sample_rate() ==
+              (static_cast<uint16_t>(CodecSampleRate::CODEC_SAMPLE_RATE_8000))) {
+            bitrate = 24000;
+          } else {
+            bitrate = 32000;
+          }
+          break;
+
+        case 40:
+          bitrate = 32000;
+          break;
+
+        case 45:
+          bitrate = 48000;
+          break;
+
+        case 60:
+          if (btif_acm_get_sample_rate() ==
+              (static_cast<uint16_t>(CodecSampleRate::CODEC_SAMPLE_RATE_24000))) {
+            bitrate = 48000;
+          } else {
+            bitrate = 64000;
+          }
+          break;
+
+        case 80:
+          bitrate = 64000;
+          break;
+
+        case 98:
+        case 130:
+          bitrate = 95550;
+          break;
+
+        case 75:
+        case 100:
+          bitrate = 80000;
+          break;
+
+        case 90:
+        case 120:
+          bitrate = 96000;
+          break;
+
+        case 117:
+        case 155:
+          bitrate = 124000;
+          break;
+
+        default:
+          bitrate = 124000;
+          break;
+    }
+    BTIF_TRACE_DEBUG("[ACM]%s: bitrate = %d",__func__,bitrate);
+    return bitrate;
+}
+
+uint32_t btif_acm_get_octets(uint32_t bit_rate) {
+    uint32_t octets = 0;
+    octets = GetOctsPerFrame(&current_active_config);
+    BTIF_TRACE_DEBUG("[ACM]%s: octets = %d",__func__,octets);
+    return octets;
+}
+
+uint16_t btif_acm_get_framelength() {
+  uint16_t frame_duration;
+  switch (GetFrameDuration(&current_active_config)) {
+      case 0:
+        frame_duration = 7500; //7.5msec
+        break;
+
+      case 1:
+        frame_duration = 10000; //10msec
+        break;
+
+      default:
+        frame_duration = 10000;
+  }
+  BTIF_TRACE_DEBUG("[ACM]%s: frame duration = %d",
+                                 __func__,frame_duration);
+  return frame_duration;
+}
+
+uint16_t btif_acm_get_current_active_profile() {
+    return current_active_profile_type;
+}
+uint8_t btif_acm_get_ch_count() {//update channel mode based on device connection
+    uint8_t ch_mode = 0;
+    if (current_active_config.channel_mode ==
+        CodecChannelMode::CODEC_CHANNEL_MODE_STEREO) {
+      ch_mode = 0x02;
+    } else if (current_active_config.channel_mode ==
+               CodecChannelMode::CODEC_CHANNEL_MODE_MONO) {
+      ch_mode = 0x01;
+    }
+    BTIF_TRACE_DEBUG("[ACM]%s: channel count = %d",__func__,ch_mode);
+    return ch_mode;
+}
+
+bool btif_acm_is_codec_type_lc3q() {
+  BTIF_TRACE_DEBUG("[ACM]%s",__func__);
+  return GetVendorMetaDataLc3QPref(&current_active_config);
+}
+
+uint8_t btif_acm_lc3q_ver() {
+  BTIF_TRACE_DEBUG("[ACM]%s",__func__);
+  return GetVendorMetaDataLc3QVer(&current_active_config);
+}
+
+uint16_t btif_acm_bap_to_acm_context(uint16_t bap_context) {
+  switch (bap_context) {
+    case CONTENT_TYPE_MEDIA:
+    case CONTENT_TYPE_LIVE:
+      return CONTEXT_TYPE_MUSIC;
+
+    case CONTENT_TYPE_CONVERSATIONAL:
+      return CONTEXT_TYPE_VOICE;
+
+    default:
+      BTIF_TRACE_DEBUG("%s: Unknown bap context",__func__);
+      return CONTEXT_TYPE_UNKNOWN;
+  }
+}
+
+static void btif_debug_acm_peer_dump(int fd, const BtifAcmPeer& peer) {
+  std::string state_str;
+  int state = peer.StateMachine().StateId();
+  switch (state) {
+    case BtifAcmStateMachine::kStateIdle:
+      state_str = "Idle";
+      break;
+
+    case BtifAcmStateMachine::kStateOpening:
+      state_str = "Opening";
+      break;
+
+    case BtifAcmStateMachine::kStateOpened:
+      state_str = "Opened";
+      break;
+
+    case BtifAcmStateMachine::kStateStarted:
+      state_str = "Started";
+      break;
+
+    case BtifAcmStateMachine::kStateReconfiguring:
+      state_str = "Reconfiguring";
+      break;
+
+    case BtifAcmStateMachine::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, "    State Machine: %s\n", state_str.c_str());
+  dprintf(fd, "    Flags: %s\n", peer.FlagsToString().c_str());
+
+}
+
+bool compare_codec_config_(CodecConfig &first, CodecConfig &second) {
+    if (first.codec_type != second.codec_type) {
+      BTIF_TRACE_DEBUG("[ACM] Codec type mismatch %s",__func__);
+      return true;
+    } else if (first.sample_rate != second.sample_rate) {
+      BTIF_TRACE_DEBUG("[ACM] Sample rate mismatch %s",__func__);
+      return true;
+    } else if (first.bits_per_sample != second.bits_per_sample) {
+      BTIF_TRACE_DEBUG("[ACM] Bits per sample mismatch %s",__func__);
+      return true;
+    } else if (first.channel_mode != second.channel_mode) {
+      BTIF_TRACE_DEBUG("[ACM] Channel mode mismatch %s",__func__);
+      return true;
+    } else {
+      uint8_t frame_first = GetFrameDuration(&first);
+      uint8_t frame_second = GetFrameDuration(&second);
+      if (frame_first != frame_second) {
+        BTIF_TRACE_DEBUG("[ACM] frame duration mismatch %s",__func__);
+        return true;
+      }
+      uint8_t lc3blockspersdu_first = GetLc3BlocksPerSdu(&first);
+      uint8_t lc3blockspersdu_second = GetLc3BlocksPerSdu(&second);
+      if (lc3blockspersdu_first != lc3blockspersdu_second) {
+        BTIF_TRACE_DEBUG("[ACM] LC3blocks per SDU mismatch %s",__func__);
+        return true;
+      }
+      uint16_t octets_first = GetOctsPerFrame(&first);
+      uint16_t octets_second = GetOctsPerFrame(&second);
+      if (octets_first != octets_second) {
+        BTIF_TRACE_DEBUG("[ACM] LC3 octets mismatch %s",__func__);
+        return true;
+      }
+      return false;
+    }
+}
+
+void print_codec_parameters(CodecConfig config) {
+  uint8_t frame = GetFrameDuration(&config);
+  uint8_t lc3blockspersdu = GetLc3BlocksPerSdu(&config);
+  uint16_t octets = GetOctsPerFrame(&config);
+  bool vendormetadatalc3qpref = GetCapaVendorMetaDataLc3QPref(&config);
+  uint8_t vendormetadatalc3qver = GetCapaVendorMetaDataLc3QVer(&config);
+  LOG_DEBUG(
+    LOG_TAG,
+    "codec_type=%d codec_priority=%d "
+    "sample_rate=0x%x bits_per_sample=0x%x "
+    "channel_mode=0x%x",
+    config.codec_type, config.codec_priority,
+    config.sample_rate, config.bits_per_sample,
+    config.channel_mode);
+  LOG_DEBUG(
+    LOG_TAG,
+    "frame_duration=%d, lc3_blocks_per_SDU=%d,"
+    " octets_per_frame=%d, vendormetadatalc3qpref=%d,"
+    " vendormetadatalc3qver=%d ",
+    frame, lc3blockspersdu, octets,
+    vendormetadatalc3qpref, vendormetadatalc3qver);
+}
+
+void print_qos_parameters(QosConfig qos) {
+    LOG_DEBUG(
+    LOG_TAG,
+    "CIG --> cig_id=%d cis_count=%d "
+    "packing=%d framing=%d "
+    "max_tport_latency_m_to_s=%d "
+    "max_tport_latency_s_to_m=%d "
+    "sdu_interval_m_to_s[0] = %x "
+    "sdu_interval_m_to_s[1] = %x "
+    "sdu_interval_m_to_s[2] = %x ",
+    qos.cig_config.cig_id, qos.cig_config.cis_count,
+    qos.cig_config.packing, qos.cig_config.framing,
+    qos.cig_config.max_tport_latency_m_to_s,
+    qos.cig_config.max_tport_latency_s_to_m,
+    qos.cig_config.sdu_interval_m_to_s[0],
+    qos.cig_config.sdu_interval_m_to_s[1],
+    qos.cig_config.sdu_interval_m_to_s[2]);
+    for (auto it = qos.cis_configs.begin(); it != qos.cis_configs.end(); ++it) {
+      LOG_DEBUG(
+      LOG_TAG,
+      "CIS --> cis_id = %d max_sdu_m_to_s = %d "
+      "max_sdu_s_to_m=%d "
+      "phy_m_to_s = %d "
+      "phy_s_to_m = %d "
+      "rtn_m_to_s = %d "
+      "rtn_s_to_m = %d",
+      it->cis_id, it->max_sdu_m_to_s,
+      it->max_sdu_s_to_m,
+      it->phy_m_to_s, it->phy_s_to_m,
+      it->rtn_m_to_s, it->rtn_s_to_m);
+    }
+    for (auto it = qos.ascs_configs.begin(); it != qos.ascs_configs.end(); ++it) {
+      LOG_DEBUG(
+        LOG_TAG,
+        "ASCS --> cig_id = %d cis_id = %d "
+        "target_latency=%d "
+        "bi_directional = %d "
+        "presentation_delay[0] = %x "
+        "presentation_delay[1] = %x "
+        "presentation_delay[2] = %x ",
+        it->cig_id,
+        it->cis_id,
+        it->target_latency,
+        it->bi_directional,
+        it->presentation_delay[0],
+        it->presentation_delay[1],
+        it->presentation_delay[2]);
+    }
+}
+
+static void btif_debug_acm_initiator_dump(int fd) {
+  bool enabled = btif_acm_initiator.Enabled();
+
+  dprintf(fd, "\nA2DP Source State: %s\n", (enabled) ? "Enabled" : "Disabled");
+  if (!enabled) return;
+  //dprintf(fd, "  Active peer: %s\n",
+    //      btif_acm_initiator.ActivePeer().ToString().c_str());
+  for (auto it : btif_acm_initiator.Peers()) {
+    const BtifAcmPeer* peer = it.second;
+    btif_debug_acm_peer_dump(fd, *peer);
+  }
+}
+
+void btif_debug_acm_dump(int fd) {
+  btif_debug_acm_initiator_dump(fd);
+}
diff --git a/le_audio/system/bt/btif/src/btif_acm_source.cc b/le_audio/system/bt/btif/src/btif_acm_source.cc
new file mode 100644
index 0000000..f9b99bf
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_acm_source.cc
@@ -0,0 +1,242 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 <hardware/bluetooth.h>
+#include "bt_trace.h"
+#include "btif_acm_source.h"
+#include "btif_ahim.h"
+#include "btif_acm.h"
+#include "osi/include/thread.h"
+
+extern thread_t* get_worker_thread();
+
+#if AHIM_ENABLED
+
+
+void btif_acm_process_request(tA2DP_CTRL_CMD cmd)
+{
+  tA2DP_CTRL_ACK status = A2DP_CTRL_ACK_FAILURE;
+  // update pending command
+  btif_ahim_update_pending_command(cmd, AUDIO_GROUP_MGR);
+
+  BTIF_TRACE_IMP("%s: cmd %u", __func__, cmd);
+
+  switch (cmd) {
+    case A2DP_CTRL_CMD_START:
+    {
+      if (btif_acm_is_call_active()) {
+        BTIF_TRACE_IMP("%s: call active, return incall_failure", __func__);
+        status = A2DP_CTRL_ACK_INCALL_FAILURE;
+      } else {
+        // ACM is in right state
+        status = A2DP_CTRL_ACK_PENDING;
+        btif_acm_stream_start();
+      }
+      btif_ahim_ack_stream_started(status, AUDIO_GROUP_MGR);
+      break;
+    }
+
+    case A2DP_CTRL_CMD_SUSPEND:
+    {
+      if (btif_acm_is_call_active()) {
+        BTIF_TRACE_IMP("%s: call active, return success", __func__);
+        status = A2DP_CTRL_ACK_SUCCESS;
+      } else {
+        status = A2DP_CTRL_ACK_PENDING;
+        btif_acm_stream_suspend();
+      }
+      btif_ahim_ack_stream_suspended(status, AUDIO_GROUP_MGR);
+      break;
+    }
+
+    case A2DP_CTRL_CMD_STOP:
+    {
+      status = A2DP_CTRL_ACK_SUCCESS;
+      if (btif_acm_is_call_active()) {
+        BTIF_TRACE_IMP("%s: call active, return success", __func__);
+      } else {
+        btif_acm_stream_stop();
+      }
+      btif_ahim_ack_stream_suspended(status, AUDIO_GROUP_MGR);
+      break;
+    }
+    default:
+      APPL_TRACE_ERROR("%s: unsupported command %d", __func__, cmd);
+      break;
+  }
+}
+
+
+void btif_acm_handle_event(uint16_t event, char* p_param)
+{
+
+  switch(event) {
+    case BTIF_ACM_PROCESS_HIDL_REQ_EVT:
+      btif_acm_process_request((tA2DP_CTRL_CMD ) *p_param);
+      break;
+    default:
+      BTIF_TRACE_IMP("%s: unhandled event", __func__);
+      break;
+  }
+}
+
+void process_hidl_req_acm(tA2DP_CTRL_CMD cmd)
+{
+   btif_transfer_context(btif_acm_handle_event, BTIF_ACM_PROCESS_HIDL_REQ_EVT, (char*)&cmd, sizeof(cmd), NULL);
+}
+
+static btif_ahim_client_callbacks_t sAhimAcmCallbacks = {
+  1, // mode
+  process_hidl_req_acm,
+  btif_acm_get_sample_rate,
+  btif_acm_get_ch_mode,
+  btif_acm_get_bitrate,
+  btif_acm_get_octets,
+  btif_acm_get_framelength,
+  btif_acm_get_ch_count,
+  nullptr,
+  btif_acm_get_current_active_profile,
+  btif_acm_is_codec_type_lc3q,
+  btif_acm_lc3q_ver
+};
+
+void btif_register_cb()
+{
+  reg_cb_with_ahim(AUDIO_GROUP_MGR, &sAhimAcmCallbacks);
+}
+
+bt_status_t btif_acm_source_setup_codec() {
+  APPL_TRACE_EVENT("%s", __func__);
+
+  bt_status_t status = BT_STATUS_FAIL;
+
+
+  APPL_TRACE_EVENT("%s ## setup_codec ##", __func__);
+  btif_ahim_setup_codec(AUDIO_GROUP_MGR);
+
+  // TODO: check the status
+  return status;
+}
+
+bool btif_acm_source_start_session(const RawAddress& peer_address) {
+  bt_status_t status = BT_STATUS_FAIL;
+  APPL_TRACE_DEBUG("%s: starting session for BD addr %s",__func__,
+        peer_address.ToString().c_str());
+
+  // initialize hal.
+  btif_ahim_init_hal(get_worker_thread(), AUDIO_GROUP_MGR);
+
+  status = btif_acm_source_setup_codec();
+
+  btif_ahim_start_session();
+
+  return true;
+}
+
+bool btif_acm_source_end_session(const RawAddress& peer_address) {
+  APPL_TRACE_DEBUG("%s: starting session for BD addr %s",__func__,
+        peer_address.ToString().c_str());
+
+  btif_ahim_end_session();
+
+  return true;
+}
+
+bool btif_acm_source_restart_session(const RawAddress& old_peer_address,
+                                      const RawAddress& new_peer_address) {
+  bool is_streaming = btif_ahim_is_streaming();
+  SessionType session_type = btif_ahim_get_session_type();
+
+  APPL_TRACE_IMP("%s: old_peer_address=%s, new_peer_address=%s, is_streaming=%d ",
+      __func__, old_peer_address.ToString().c_str(),
+    new_peer_address.ToString().c_str(), is_streaming);
+
+   // TODO: do we need to check for new empty address
+  //CHECK(!new_peer_address.IsEmpty());
+
+  // If the old active peer was valid or if session is not
+  // unknown, end the old session.
+  if (!old_peer_address.IsEmpty() ||
+    session_type != SessionType::UNKNOWN) {
+    btif_acm_source_end_session(old_peer_address);
+  }
+
+  btif_acm_source_start_session(new_peer_address);
+
+  return true;
+}
+
+bool btif_acm_update_sink_latency_change(uint16_t sink_latency) {
+  APPL_TRACE_DEBUG("%s: update_sink_latency %d for active session ",__func__,
+                                sink_latency);
+
+  btif_ahim_set_remote_delay(sink_latency);
+
+  return true;
+}
+
+void btif_acm_source_command_ack(tA2DP_CTRL_CMD cmd, tA2DP_CTRL_ACK status) {
+  switch (cmd) {
+    case A2DP_CTRL_CMD_START:
+      btif_ahim_ack_stream_started(status, AUDIO_GROUP_MGR);
+      break;
+    case A2DP_CTRL_CMD_SUSPEND:
+    case A2DP_CTRL_CMD_STOP:
+      btif_ahim_ack_stream_suspended(status, AUDIO_GROUP_MGR);
+      break;
+    default:
+      break;
+  }
+}
+
+void btif_acm_source_on_stopped(tA2DP_CTRL_ACK status) {
+  APPL_TRACE_EVENT("%s: status %u", __func__, status);
+
+  btif_ahim_ack_stream_suspended(status, AUDIO_GROUP_MGR);
+
+  btif_ahim_reset_pending_command(AUDIO_GROUP_MGR);
+}
+
+void btif_acm_source_on_suspended(tA2DP_CTRL_ACK status) {
+  APPL_TRACE_EVENT("%s: status %u", __func__, status);
+
+  btif_ahim_ack_stream_suspended(status, AUDIO_GROUP_MGR);
+
+  btif_ahim_reset_pending_command(AUDIO_GROUP_MGR);
+}
+
+bool btif_acm_on_started(tA2DP_CTRL_ACK status) {
+  APPL_TRACE_EVENT("%s: status %u", __func__, status);
+  bool retval = false;
+
+  if(0/* TODO: check if call is in progress*/) {
+    APPL_TRACE_WARNING("%s: call in progress, sending failure", __func__);
+    btif_ahim_ack_stream_started(A2DP_CTRL_ACK_INCALL_FAILURE, AUDIO_GROUP_MGR);
+  }
+  else {
+    btif_ahim_ack_stream_started(status, AUDIO_GROUP_MGR);
+    retval = true;
+  }
+
+  btif_ahim_reset_pending_command(AUDIO_GROUP_MGR);
+  return retval;
+}
+
+
+#endif // AHIM_ENABLED
diff --git a/le_audio/system/bt/btif/src/btif_apm.cc b/le_audio/system/bt/btif/src/btif_apm.cc
new file mode 100644
index 0000000..02f5a6f
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_apm.cc
@@ -0,0 +1,189 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+
+#define LOG_TAG "btif_apm"
+
+#include <base/logging.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_apm.h>
+
+#include "bt_common.h"
+#include "btif_common.h"
+#include "btif_ahim.h"
+
+#define A2DP_PROFILE 0x0001
+#define BROADCAST_BREDR 0x0400
+#define BROADCAST_LE 0x0800
+#define ACTIVE_VOICE_PROFILE_HFP 0x0002
+
+std::mutex apm_mutex;
+btapm_initiator_callbacks_t* callbacks_;
+
+static bt_status_t init(btapm_initiator_callbacks_t* callbacks);
+static void cleanup();
+static bt_status_t update_active_device(const RawAddress& bd_addr, uint16_t profile, uint16_t audio_type);
+static bt_status_t set_content_control_id(uint16_t content_control_id, uint16_t audio_type);
+static bool apm_enabled = false;
+
+
+#define CHECK_BTAPM_INIT()                                                   \
+  do {                                                                       \
+    if (!apm_enabled) {                                                  \
+      BTIF_TRACE_WARNING("%s: BTAV not initialized", __func__);              \
+      return BT_STATUS_NOT_READY;                                            \
+    }                                                                        \
+  } while (0)
+
+typedef enum {
+  BTIF_APM_AUDIO_TYPE_VOICE = 0x0,
+  BTIF_APM_AUDIO_TYPE_MEDIA,
+
+  BTIF_APM_AUDIO_TYPE_SIZE
+} btif_av_state_t;
+
+typedef struct {
+  RawAddress peer_bda;
+  int profile;
+} btif_apm_device_profile_combo_t;
+
+typedef struct {
+  RawAddress peer_bda;
+} btif_apm_get_active_profile;
+
+int active_profile_info;
+
+static btif_apm_device_profile_combo_t active_device_profile[BTIF_APM_AUDIO_TYPE_SIZE];
+static uint16_t content_control_id[BTIF_APM_AUDIO_TYPE_SIZE];
+
+static void btif_update_active_device(uint16_t audio_type, char* param);
+void btif_get_active_device(btif_av_state_t audio_type, RawAddress* peer_bda);
+static void btif_update_content_control(uint16_t audio_type, char* param);
+uint16_t btif_get_content_control_id(btif_av_state_t audio_type);
+
+static void btif_update_active_device(uint16_t audio_type, char* param) {
+  btif_apm_device_profile_combo_t new_device_profile;
+  if(audio_type != BTIF_APM_AUDIO_TYPE_MEDIA)
+    return;
+
+  memcpy(&new_device_profile, param, sizeof(new_device_profile));
+  active_device_profile[audio_type].peer_bda = new_device_profile.peer_bda;
+  active_device_profile[audio_type].profile = new_device_profile.profile;
+  BTIF_TRACE_WARNING("%s() New Active Device: %s, Profile: %x\n", __func__,
+                active_device_profile[audio_type].peer_bda.ToString().c_str(),
+                active_device_profile[audio_type].profile);
+  if(active_device_profile[audio_type].profile == A2DP_PROFILE) {
+    btif_ahim_update_current_profile(A2DP);
+  } else if(active_device_profile[audio_type].profile == BROADCAST_LE) {
+    btif_ahim_update_current_profile(BROADCAST);
+  } else {
+    btif_ahim_update_current_profile(AUDIO_GROUP_MGR);
+  }
+}
+
+void btif_get_active_device(btif_av_state_t audio_type, RawAddress* peer_bda) {
+  if(audio_type >= BTIF_APM_AUDIO_TYPE_SIZE)
+    return;
+  peer_bda = &active_device_profile[audio_type].peer_bda;
+}
+
+static void btif_update_content_control(uint16_t audio_type, char* param) {
+  if(audio_type >= BTIF_APM_AUDIO_TYPE_SIZE)
+    return;
+  uint16_t cc_id = (uint16_t)(*param);
+  content_control_id[audio_type] = cc_id;
+  /*Update ACM here*/
+}
+
+uint16_t btif_get_content_control_id(btif_av_state_t audio_type) {
+  if(audio_type >= BTIF_APM_AUDIO_TYPE_SIZE)
+    return 0;
+  return content_control_id[audio_type];
+}
+
+static const bt_apm_interface_t bt_apm_interface = {
+    sizeof(bt_apm_interface_t),
+    init,
+    update_active_device,
+    set_content_control_id,
+    cleanup,
+};
+
+const bt_apm_interface_t* btif_apm_get_interface(void) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  return &bt_apm_interface;
+}
+
+static bt_status_t init(btapm_initiator_callbacks_t* callbacks) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  callbacks_  = callbacks;
+  apm_enabled = true;
+  
+  return BT_STATUS_SUCCESS;
+}
+
+static void cleanup() {
+  BTIF_TRACE_EVENT("%s", __func__);
+  apm_enabled = false;
+}
+
+static bt_status_t update_active_device(const RawAddress& bd_addr, uint16_t profile, uint16_t audio_type) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  CHECK_BTAPM_INIT();
+  btif_apm_device_profile_combo_t new_device_profile;
+  new_device_profile.peer_bda = bd_addr;
+  new_device_profile.profile = profile;
+
+  std::unique_lock<std::mutex> guard(apm_mutex);
+
+  return btif_transfer_context(btif_update_active_device, (uint8_t)audio_type,
+            (char *)&new_device_profile, sizeof(btif_apm_device_profile_combo_t), NULL);
+}
+
+static bt_status_t set_content_control_id(uint16_t content_control_id, uint16_t audio_type) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  CHECK_BTAPM_INIT();
+
+  std::unique_lock<std::mutex> guard(apm_mutex);
+
+  return btif_transfer_context(btif_update_content_control,
+     (uint8_t)audio_type, (char *)&content_control_id, sizeof(content_control_id), NULL);
+}
+
+void call_active_profile_info(const RawAddress& bd_addr, uint16_t audio_type) {
+  if (apm_enabled == true) {
+     BTIF_TRACE_WARNING("%s", __func__);
+     active_profile_info = callbacks_->active_profile_cb(bd_addr, audio_type);
+     BTIF_TRACE_WARNING("%s: profile info is %d", __func__, active_profile_info);
+  }
+}
+
+int get_active_profile(const RawAddress& bd_addr, uint16_t audio_type) {
+  if (apm_enabled == true) {
+     BTIF_TRACE_WARNING("%s: active profile is %d ", __func__, active_profile_info);
+     return active_profile_info;
+  }
+  else {
+     BTIF_TRACE_WARNING("%s: APM is not enabled, returning HFP as active profile %d ",
+                                     __func__, ACTIVE_VOICE_PROFILE_HFP);
+     return ACTIVE_VOICE_PROFILE_HFP;
+  }
+}
+
diff --git a/le_audio/system/bt/btif/src/btif_ascs_client.cc b/le_audio/system/bt/btif/src/btif_ascs_client.cc
new file mode 100644
index 0000000..d5853e6
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_ascs_client.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#include "bta_closure_api.h"
+#include "bta_ascs_client_api.h"
+#include "btif_common.h"
+#include "btif_storage.h"
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_ascs_client.h>
+#include "osi/include/thread.h"
+
+using base::Bind;
+using base::Unretained;
+using bluetooth::bap::ascs::AscsClient;
+using bluetooth::bap::ascs::GattState;
+using bluetooth::bap::ascs::AscsClientCallbacks;
+using bluetooth::bap::ascs::AscsClientInterface;
+using bluetooth::bap::ascs::AseOpId;
+using bluetooth::bap::ascs::AseOpStatus;
+using bluetooth::bap::ascs::AseParams;
+using bluetooth::bap::ascs::AseCodecConfigOp;
+using bluetooth::bap::ascs::AseQosConfigOp;
+using bluetooth::bap::ascs::AseEnableOp;
+using bluetooth::bap::ascs::AseDisableOp;
+using bluetooth::bap::ascs::AseStartReadyOp;
+using bluetooth::bap::ascs::AseStopReadyOp;
+using bluetooth::bap::ascs::AseReleaseOp;
+using bluetooth::bap::ascs::AseUpdateMetadataOp;
+
+namespace {
+
+class AscsClientInterfaceImpl;
+std::unique_ptr<AscsClientInterface> AscsClientInstance;
+
+class AscsClientInterfaceImpl
+    : public AscsClientInterface,
+      public AscsClientCallbacks {
+  ~AscsClientInterfaceImpl() = default;
+
+  void Init(AscsClientCallbacks* callbacks) override {
+    DVLOG(2) << __func__;
+    this->callbacks = callbacks;
+
+    do_in_bta_thread(
+        FROM_HERE,
+        Bind(&AscsClient::Init, this));
+  }
+
+  void OnAscsInitialized(int status, int client_id) override {
+    do_in_jni_thread(FROM_HERE, Bind(&AscsClientCallbacks::OnAscsInitialized,
+                                     Unretained(callbacks), status,
+                                     client_id));
+  }
+
+  void OnConnectionState(const RawAddress& address,
+                         GattState state) override {
+    DVLOG(2) << __func__ << " address: " << address;
+    do_in_jni_thread(FROM_HERE, Bind(&AscsClientCallbacks::OnConnectionState,
+                                     Unretained(callbacks), address, state));
+  }
+
+  void OnAseOpFailed(const RawAddress& address, AseOpId ase_op_id,
+                     std::vector<AseOpStatus> status) override {
+    do_in_jni_thread(FROM_HERE,
+                     Bind(&AscsClientCallbacks::OnAseOpFailed,
+                          Unretained(callbacks),
+                          address, ase_op_id, status));
+  }
+
+  void OnAseState(const RawAddress& address, AseParams ase) override {
+    do_in_jni_thread(FROM_HERE,
+                     Bind(&AscsClientCallbacks::OnAseState,
+                          Unretained(callbacks), address, ase));
+  }
+
+  void OnSearchComplete(int status,
+                        const RawAddress& address,
+                        std::vector<AseParams> sink_ase_list,
+                        std::vector<AseParams> src_ase_list) override {
+    do_in_jni_thread(FROM_HERE, Bind(&AscsClientCallbacks::OnSearchComplete,
+                                     Unretained(callbacks),
+                                     status,
+                                     address,
+                                     sink_ase_list,
+                                     src_ase_list));
+  }
+
+  void Connect(uint16_t client_id, const RawAddress& address) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::Connect,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, false));
+  }
+
+  void Disconnect(uint16_t client_id, const RawAddress& address) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::Disconnect,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address));
+  }
+
+  void StartDiscovery(uint16_t client_id, const RawAddress& address) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::StartDiscovery,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address));
+  }
+
+  void GetAseState(uint16_t client_id, const RawAddress& address,
+                   uint8_t ase_id) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::GetAseState,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, ase_id));
+  }
+
+  void CodecConfig(uint16_t client_id, const RawAddress& address,
+                   std::vector<AseCodecConfigOp> codec_configs) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::CodecConfig,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, codec_configs));
+  }
+
+  void QosConfig(uint16_t client_id, const RawAddress& address,
+                 std::vector<AseQosConfigOp> qos_configs) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::QosConfig,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, qos_configs));
+  }
+
+  void Enable(uint16_t client_id, const RawAddress& address,
+              std::vector<AseEnableOp> enable_ops) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::Enable,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, enable_ops));
+  }
+
+  void Disable(uint16_t client_id, const RawAddress& address,
+               std::vector<AseDisableOp> disable_ops) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::Disable,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, disable_ops));
+  }
+
+  void StartReady(uint16_t client_id, const RawAddress& address,
+                  std::vector<AseStartReadyOp> start_ready_ops) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::StartReady,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, start_ready_ops));
+  }
+
+  void StopReady(uint16_t client_id, const RawAddress& address,
+                 std::vector<AseStopReadyOp> stop_ready_ops) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::StopReady,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, stop_ready_ops));
+  }
+
+  void Release(uint16_t client_id, const RawAddress& address,
+               std::vector<AseReleaseOp> release_ops) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::Release,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, release_ops));
+  }
+
+  void UpdateStream(uint16_t client_id, const RawAddress& address,
+                    std::vector<AseUpdateMetadataOp> metadata_ops) override {
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::UpdateStream,
+                                      Unretained(AscsClient::Get()),
+                                      client_id, address, metadata_ops));
+  }
+
+  void Cleanup(uint16_t client_id) override {
+    DVLOG(2) << __func__;
+    do_in_bta_thread(FROM_HERE, Bind(&AscsClient::CleanUp, client_id));
+  }
+
+ private:
+  AscsClientCallbacks* callbacks;
+};
+
+}  // namespace
+
+AscsClientInterface* btif_ascs_client_get_interface() {
+  if (!AscsClientInstance)
+    AscsClientInstance.reset(new AscsClientInterfaceImpl());
+
+  return AscsClientInstance.get();
+}
diff --git a/le_audio/system/bt/btif/src/btif_bap_broadcast.cc b/le_audio/system/bt/btif/src/btif_bap_broadcast.cc
new file mode 100644
index 0000000..0bd393c
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_bap_broadcast.cc
@@ -0,0 +1,1822 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "btif_bap_broadcast"
+
+#include "btif_bap_broadcast.h"
+
+#include <base/logging.h>
+#include <string.h>
+#include <base/bind.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_bap_ba.h>
+
+#include "bt_common.h"
+#include "bt_utils.h"
+#include "btif_storage.h"
+#include "btif_a2dp.h"
+#include "btif_hf.h"
+#include "btif_a2dp_control.h"
+#include "btif_util.h"
+#include "btu.h"
+#include "osi/include/allocator.h"
+#include "osi/include/osi.h"
+#include "osi/include/properties.h"
+#include "btif/include/btif_a2dp_source.h"
+#include "device/include/interop.h"
+#include "device/include/controller.h"
+#include "btif_bat.h"
+#include "btif_av.h"
+#include "hcimsgs.h"
+#include "btif_config.h"
+#include "audio_a2dp_hw/include/audio_a2dp_hw.h"
+#include <time.h>
+#include <hardware/ble_advertiser.h>
+#include <hardware/bt_gatt.h>
+#include "btm_ble_api.h"
+#include "btm_ble_api_types.h"
+#include "ble_advertiser.h"
+#if (OFF_TARGET_TEST_ENABLED == FALSE)
+#include "audio_hal_interface/a2dp_encoding.h"
+#endif
+#include "controller.h"
+#if (OFF_TARGET_TEST_ENABLED == TRUE)
+#include "log/log.h"
+#include "service/a2dp_hal_sim/audio_a2dp_hal_stub.h"
+#endif
+#include "state_machine.h"
+
+#define BIG_COMPILE 1
+
+#define BTIF_BAP_BA_NUM_CB       1
+#define kDefaultMaxBroadcastSupported 1
+#define BTIF_BAP_BA_NUM_BMS 1
+
+#define INPUT_DATAPATH 0x01
+#define OUTPUT_DATAPATH 0x02
+#define BROADCAST_SPLIT_STEREO 2
+#define BROADCAST_MONO_JOINT 1
+#define BROADCAST_MODE_HR 0x1000
+#define BROADCAST_MODE_LL 0x2000
+/*****************************************************************************
+ *  Local type definitions
+ *****************************************************************************/
+ typedef enum {
+   BIG_TERMINATED = 0,
+   BIG_CREATING,
+   BIG_CREATED,
+   BIG_RECONFIG,
+   BIG_TERMINATING,
+   BIG_DISABLING,
+ } btif_big_state_t;
+
+std::vector<btav_a2dp_codec_config_t> broadcast_codecs_capabilities;
+btav_a2dp_codec_index_t lc3_codec_id = (btav_a2dp_codec_index_t)9;
+static const btav_a2dp_codec_config_t broadcast_local_capability =
+                           {lc3_codec_id, BTAV_A2DP_CODEC_PRIORITY_DEFAULT,
+                           (BTAV_A2DP_CODEC_SAMPLE_RATE_48000 |
+                            BTAV_A2DP_CODEC_SAMPLE_RATE_24000 |
+                            BTAV_A2DP_CODEC_SAMPLE_RATE_16000),
+                           BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24,
+                           ((btav_a2dp_codec_channel_mode_t)(BTAV_A2DP_CODEC_CHANNEL_MODE_MONO |
+                           BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO |
+                           BTBAP_CODEC_CHANNEL_MODE_JOINT_STEREO)), 0, 0, 0, 0};
+
+static btav_a2dp_codec_config_t default_config;
+static btav_a2dp_codec_config_t current_config;
+static int mBisMultiplier = 0;
+//static bool isSplitEnabled = false;
+static bool notify_key_generated = false;
+Octet16 encryption_key;
+std::vector<uint8_t> mBroadcastID(3,0);
+struct keyCalculator {
+  Octet16 rand;
+};
+uint8_t enc_keylength = 16;
+char local_param[3];
+RawAddress mBapBADevice = RawAddress({0xFA, 0xCE, 0xFA, 0xCE, 0xFA, 0xCE});
+std::mutex session_wait_;
+std::condition_variable session_wait_cv_;
+bool mSession_wait;
+bool mEncryptionEnabled = true;
+bool restart_session = false;
+extern int btif_max_av_clients;
+extern const btgatt_interface_t* btif_gatt_get_interface();
+extern int btif_av_get_latest_device_idx_to_start();
+extern thread_t* get_worker_thread();
+int total_bises = 0;
+typedef enum {
+  iso_unknown = 0,
+  setup_iso = 1,
+  remove_iso = 2,
+}tBAP_BA_ISO_CMD;
+
+typedef struct {
+  uint32_t sdu_int;
+  uint16_t max_sdu;
+  uint16_t max_transport_latency;
+  uint8_t rtn;
+  uint8_t phy;
+  uint8_t packing;
+  uint8_t framing;
+} tBAP_BA_BIG_PARAMS;
+
+tBAP_BA_BIG_PARAMS mBigParams = {10000, 100, 10, 2, 2/*LE 2M*/, 1/*Interleaved*/, 0/*unframed*/};
+#define PATH_ID 1
+tBAP_BA_ISO_CMD pending_cmd = iso_unknown;
+int current_handle = -1;
+int current_iso_index = 0;
+
+int config_req_handle = -1;
+/**
+ * Local functions
+ */
+static void btif_bap_ba_generate_enc_key_local(int length);
+static void btif_bap_ba_create_big(int adv_id);
+static void btif_bap_ba_terminate_big(int adv_id, int big_handle);
+static bool btif_bap_ba_setup_iso_datapath(int big_handle);
+static bool btif_bap_ba_remove_iso_datapath(int big_handle);
+static void btif_bap_ba_process_iso_setup(uint8_t status, uint16_t bis_handle);
+static void btif_bap_ba_update_big_params();
+static void btif_bap_ba_handle_event(uint32_t event, char* p_param);
+static void init_local_capabilities();
+static void btif_report_broadcast_state(int adv_id,
+                                btbap_broadcast_state_t state);
+static void btif_report_broadcast_audio_state(int adv_id,
+                                btbap_broadcast_audio_state_t state);
+static void btif_report_audio_config(int adv_id,
+                                 btav_a2dp_codec_config_t codec_config);
+static void btif_report_setup_big(int setup, int adv_id, int big_handle, int num_bises);
+static void btif_report_broadcast_id();
+static void btif_bap_process_request(tA2DP_CTRL_CMD cmd);
+static void btif_broadcast_process_hidl_event(tA2DP_CTRL_CMD cmd);
+static uint16_t btif_bap_get_transport_latency();
+static void btif_bap_ba_copy_broadcast_id();
+static void btif_bap_ba_generate_broadcast_id();
+static void btif_bap_ba_signal_session_ready() {
+  std::unique_lock<std::mutex> guard(session_wait_);
+  if(!mSession_wait) {
+    mSession_wait = true;
+    session_wait_cv_.notify_all();
+  } else {
+   BTIF_TRACE_WARNING("%s: already signalled ",__func__);
+  }
+}
+
+const char* dump_bap_ba_sm_event_name(btif_bap_broadcast_sm_event_t event) {
+  switch ((int)event) {
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_ENABLE_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_DISABLE_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_START_STREAM_REQ_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_STOP_STREAM_REQ_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_SUSPEND_STREAM_REQ_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_SOURCE_CONFIG_REQ_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_CLEANUP_REQ_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_SET_ACTIVE_REQ_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_REMOVE_ACTIVE_REQ_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_SETUP_ISO_DATAPATH_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_REMOVE_ISO_DATAPATH_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_GENERATE_ENC_KEY_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_BISES_SETUP_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_BISES_REMOVE_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_BIG_SETUP_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_BIG_REMOVED_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_PROCESS_HIDL_REQ_EVT)
+    CASE_RETURN_STR(BTIF_BAP_BROADCAST_SETUP_NEXT_BIS_EVENT)
+    CASE_RETURN_STR(BTIF_SM_ENTER_EVT)
+    CASE_RETURN_STR(BTIF_SM_EXIT_EVT)
+    default:
+      return "UNKNOWN_EVENT";
+  }
+}
+
+void btif_bap_broadcast_update_source_codec(void *p_data) {
+  btav_a2dp_codec_config_t * codec_req = (btav_a2dp_codec_config_t*)p_data;
+  if (codec_req->sample_rate != current_config.sample_rate ||
+    codec_req->channel_mode != current_config.channel_mode ||
+    codec_req->codec_specific_1 != current_config.codec_specific_1 ||
+    codec_req->codec_specific_2 != current_config.codec_specific_2) {
+    restart_session = true;
+  }
+
+  if (codec_req->codec_specific_4 > 0) {
+    if (current_config.codec_specific_4 == BROADCAST_MODE_HR) {
+      mBigParams.max_transport_latency = 60;
+    } else if (codec_req->codec_specific_4 == BROADCAST_MODE_LL) {
+      mBigParams.max_transport_latency = 20;
+    }
+  }
+  memcpy(&current_config, codec_req, sizeof(btav_a2dp_codec_config_t));
+  BTIF_TRACE_DEBUG("[BapBroadcast]%s:sample rate: %d",__func__, current_config.sample_rate);
+  BTIF_TRACE_DEBUG("[BapBroadcast]%s:channel mode: %d",__func__, current_config.channel_mode);
+  BTIF_TRACE_DEBUG("[BapBroadcast]%s:cs1: %d",__func__, current_config.codec_specific_1);
+  btif_bap_ba_update_big_params();
+}
+
+void reverseCode(uint8_t *array) {
+  uint8_t *p_array = array;
+  for (int i = 0; i < 8; i++) {
+    uint8_t temp = p_array[i];
+    p_array[i] = p_array[15-i];
+    p_array[15-i] = temp;
+  }
+}
+
+bool isUnencrypted(uint8_t *array) {
+  uint8_t *p_array = array;
+  for (int i = 0; i < 16; i++) {
+    if (p_array[i] != 0x00) {
+      return false;
+    }
+  }
+  BTIF_TRACE_DEBUG("[BapBroadcast]: isUnencrypted is true");
+  return true;
+}
+class BtifBapBroadcaster;
+
+class BtifBapBroadcastStateMachine : public bluetooth::common::StateMachine{
+ public:
+  enum {
+    kStateIdle,     // Broadcast idle
+    kStateConfigured,  // Broadcast configured
+    kStateStreaming,   // Broadcast streaming
+  };
+
+  class StateIdle : public State {
+   public:
+    StateIdle(BtifBapBroadcastStateMachine& sm)
+        : State(sm, kStateIdle), bms_(sm.Bms()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifBapBroadcaster& bms_;
+  };
+
+  class StateConfigured : public State {
+   public:
+    StateConfigured(BtifBapBroadcastStateMachine& sm)
+        : State(sm, kStateConfigured), bms_(sm.Bms()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifBapBroadcaster& bms_;
+  };
+
+  class StateStreaming : public State {
+   public:
+    StateStreaming(BtifBapBroadcastStateMachine& sm)
+        : State(sm, kStateStreaming), bms_(sm.Bms()) {}
+    void OnEnter() override;
+    void OnExit() override;
+    bool ProcessEvent(uint32_t event, void* p_data) override;
+
+   private:
+    BtifBapBroadcaster& bms_;
+  };
+
+  BtifBapBroadcastStateMachine(BtifBapBroadcaster& bms) : bms_(bms) {
+    state_idle_ = new StateIdle(*this);
+    state_configured_ = new StateConfigured(*this);
+    state_streaming_ = new StateStreaming(*this);
+
+    AddState(state_idle_);
+    AddState(state_configured_);
+    AddState(state_streaming_);
+    SetInitialState(state_idle_);
+  }
+  BtifBapBroadcaster& Bms() { return bms_; }
+  private:
+  BtifBapBroadcaster& bms_;
+  StateIdle* state_idle_;
+  StateConfigured* state_configured_;
+  StateStreaming* state_streaming_;
+};
+
+class BtifBapBroadcaster{
+ public:
+  enum {
+    kFlagBIGPending = 0x1,
+    kFlagISOPending = 0x2,
+    kFlagISOError = 0x4,
+    KFlagISOSetup = 0x8,
+  };
+
+  BtifBapBroadcaster(int adv_handle, int big_handle)
+  :adv_handle_(adv_handle),
+   big_handle_(big_handle),
+   state_machine_(*this),
+   flags_(0),
+   big_state_(BIG_TERMINATED){}
+
+  ~BtifBapBroadcaster();
+
+  bt_status_t Init();
+  void Cleanup();
+
+  bool CanBeDeleted() {return (
+                             (state_machine_.StateId() == BtifBapBroadcastStateMachine::kStateIdle) &&
+                             (state_machine_.PreviousStateId() != BtifBapBroadcastStateMachine::kStateInvalid)); };
+
+  int AdvHandle() const { return adv_handle_; }
+  int BIGHandle() const { return big_handle_; }
+  void SetBIGHandle(int handle) { big_handle_ = handle; }
+  void SetAdvHandle(int handle) { adv_handle_ = handle; }
+  BtifBapBroadcastStateMachine& StateMachine() { return state_machine_; }
+  const BtifBapBroadcastStateMachine& StateMachine() const { return state_machine_; }
+  bool CheckFlags(uint8_t bitflags_mask) const {
+    return ((flags_ & bitflags_mask) != 0);
+  }
+
+  void ClearFlag(uint8_t bitflags_mask) { flags_ &= ~bitflags_mask;}
+
+  void ClearAllFlags() { flags_ = 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; }
+
+  void SetNumBises(uint8_t num_bises) {num_bises_ = num_bises; }
+
+  uint8_t NumBises() const { return num_bises_; }
+
+  void SetBIGState(btif_big_state_t state) { big_state_ = state; }
+
+  btif_big_state_t BIGState() const { return big_state_; }
+
+  /*void SetMandatoryCodecPreferred(bool preferred) {
+    mandatory_codec_preferred_ = preferred;
+  }
+  bool IsMandatoryCodecPreferred() const { return mandatory_codec_preferred_; }*/
+
+  std::vector<uint16_t> GetBISHandles() const { return bis_handle_list_;}
+  void SetBISHandles(std::vector<uint16_t> handle_list) { bis_handle_list_ = handle_list; }
+
+ private:
+  int adv_handle_;
+  int big_handle_;  // SEP type of peer device
+  uint8_t num_bises_;
+  BtifBapBroadcastStateMachine state_machine_;
+  uint8_t flags_;
+  btif_big_state_t big_state_;
+  //bool mandatory_codec_preferred_ = false;
+  std::vector<uint16_t> bis_handle_list_;
+};
+//BtifBapBroadcaster::BtifBapBroadcaster(int adv_handle, int big_handle)
+//  :adv_handle_(adv_handle), big_handle_(big_handle){}
+
+class BtifBapBroadcastSource{
+ public:
+  // The PeerId is used as AppId for BTA_AvRegister() purpose
+  static constexpr uint8_t kPeerIdMin = 0;
+  static constexpr uint8_t kPeerIdMax = BTIF_BAP_BA_NUM_BMS;
+  public:
+   enum {
+     kFlagIdle = 0x1,
+     kFlagConfigured = 0x2,
+     kFlagStreaming = 0x4,
+     KFlagDisabling = 0x8,
+   };
+  BtifBapBroadcastSource()
+      : callbacks_(nullptr),
+        enabled_(false),
+        offload_enabled_(false),
+        max_broadcast_(kDefaultMaxBroadcastSupported) {}
+  ~BtifBapBroadcastSource();
+
+  bt_status_t Init(
+      btbap_broadcast_callbacks_t* callbacks, int max_broadcast,
+      btav_a2dp_codec_config_t codec_config,int mode);
+
+  bt_status_t EnableBroadcast(btav_a2dp_codec_config_t codec_config);
+  bt_status_t DisableBroadcast(int adv_handle);
+  void Cleanup();
+  void CleanupIdleBms();
+  btbap_broadcast_callbacks_t* Callbacks() { return callbacks_; }
+  void SetEnabled(bool state) { enabled_ = state; }
+  bool Enabled() const { return enabled_; }
+  bool BapBroadcastOffloadEnabled() const { return offload_enabled_; }
+
+  BtifBapBroadcaster* FindBmsFromAdvHandle(uint8_t adv_handle);
+//  BtifBapBroadcaster* FindEmptyBms();
+  BtifBapBroadcaster* FindBmsFromBIGHandle(uint8_t big_handle);
+  BtifBapBroadcaster* FindStreamingBms();
+  BtifBapBroadcaster* FindConfiguredBms();
+  BtifBapBroadcaster* CreateBMS(int adv_handle);
+  //void SetDefaultConfig(btav_a2dp_codec_config_t config) { default_config_ = config; }
+  btav_a2dp_codec_config_t GetDefaultConfig () const { return default_config_; }
+  //void SetCurrentConfig (btav_a2dp_codec_config_t config) { current_config_ = config; }
+  btav_a2dp_codec_config_t GetCurrentConfig() const { return current_config_; }
+  bt_status_t SetEncryption(int length);
+  bt_status_t SetBroadcastActive(bool setup, uint8_t adv_id);
+  bool BroadcastActive() const { return ((broadcast_state_ == kFlagConfigured)
+                                           ||(broadcast_state_ == kFlagStreaming)); }
+  void SetBroadcastState(uint8_t flag) { broadcast_state_ = flag; }
+  uint8_t GetBroadcastState() { return broadcast_state_; }
+  bt_status_t SetUserConfig(uint8_t adv_hdl, btav_a2dp_codec_config_t codec_config);
+
+  const std::map<uint8_t/*adv_handle*/, BtifBapBroadcaster*>& Bms() const { return bms_; }
+
+ private:
+  void CleanupAllBms();
+
+  btbap_broadcast_callbacks_t* callbacks_;
+  bool enabled_;
+  bool offload_enabled_;
+  int max_broadcast_;
+  std::map<uint8_t, BtifBapBroadcaster*> bms_;
+  btav_a2dp_codec_config_t default_config_;
+  btav_a2dp_codec_config_t current_config_;
+  uint8_t broadcast_state_;
+};
+
+static BtifBapBroadcastSource btif_bap_bms;
+
+bt_status_t BtifBapBroadcaster::Init() {
+  state_machine_.Start();
+  return BT_STATUS_SUCCESS;
+}
+
+void BtifBapBroadcaster::Cleanup() {
+  state_machine_.Quit();
+}
+
+void BtifBapBroadcastStateMachine::StateIdle::OnEnter() {
+  BTIF_TRACE_IMP("%s", __PRETTY_FUNCTION__);
+
+  bms_.ClearAllFlags();
+  bms_.SetAdvHandle(-1);
+  bms_.SetBIGHandle(-1);
+  bms_.SetBIGState(BIG_TERMINATED);
+  btif_bap_bms.SetBroadcastState(BtifBapBroadcastSource::kFlagIdle);
+  btif_bap_bms.SetEnabled(false);
+  btif_bap_bms.CleanupIdleBms();
+}
+
+void BtifBapBroadcastStateMachine::StateIdle::OnExit() {
+  BTIF_TRACE_IMP("%s", __PRETTY_FUNCTION__);
+}
+
+bool BtifBapBroadcastStateMachine::StateIdle::ProcessEvent(uint32_t event, void* p_data) {
+  BTIF_TRACE_IMP("[BapBroadcast]:%s: event = %s",__func__,
+                   dump_bap_ba_sm_event_name((btif_bap_broadcast_sm_event_t)event));
+  switch (event) {
+    case BTIF_BAP_BROADCAST_SET_ACTIVE_REQ_EVT:
+     bms_.StateMachine().TransitionTo(BtifBapBroadcastStateMachine::kStateConfigured);
+     break;
+    case BTIF_BAP_BROADCAST_SOURCE_CONFIG_REQ_EVT:
+      //copy config
+      break;
+    default:
+      BTIF_TRACE_WARNING("%s: unhandled event=%s", __func__,
+                             dump_bap_ba_sm_event_name((btif_bap_broadcast_sm_event_t)event));
+      return false;
+  }
+ return true;
+}
+
+void BtifBapBroadcastStateMachine::StateConfigured::OnEnter() {
+  BTIF_TRACE_IMP("%s", __PRETTY_FUNCTION__);
+
+  // Inform the application that we are entering connecting state
+  btif_bap_bms.SetBroadcastState(BtifBapBroadcastSource::kFlagConfigured);
+  btif_bap_bms.SetEnabled(true);
+  if (bms_.BIGState() == BIG_TERMINATED) {
+#if AHIM_ENABLED
+    btif_ahim_init_hal(get_worker_thread(), BROADCAST);
+    btif_ahim_start_session();
+#else
+    btif_a2dp_source_restart_session(RawAddress::kEmpty, mBapBADevice);
+#endif
+    btif_bap_ba_signal_session_ready();
+    btif_bap_ba_update_big_params();
+  } else if (bms_.BIGState() == BIG_DISABLING) {
+    ProcessEvent(BTIF_BAP_BROADCAST_DISABLE_EVT, nullptr);
+    return;
+  }
+  bms_.SetBIGState(BIG_TERMINATED);
+  bms_.ClearAllFlags();
+  btif_report_broadcast_state(bms_.AdvHandle(), BTBAP_BROADCAST_STATE_CONFIGURED);
+}
+
+void BtifBapBroadcastStateMachine::StateConfigured::OnExit() {
+  BTIF_TRACE_IMP("%s", __PRETTY_FUNCTION__);
+}
+
+bool BtifBapBroadcastStateMachine::StateConfigured::ProcessEvent(uint32_t event,
+                                                    void* p_data) {
+ BTIF_TRACE_IMP("[BapBroadcast]:%s: event = %s",__func__,
+                            dump_bap_ba_sm_event_name((btif_bap_broadcast_sm_event_t)event));
+ switch (event) {
+
+   case BTIF_BAP_BROADCAST_DISABLE_EVT:
+     BTIF_TRACE_DEBUG("[BapBroadcast]:BTIF_BAP_BROADCAST_DISABLE_EVT, moving to idle");
+     if (bms_.CheckFlags(BtifBapBroadcaster::kFlagBIGPending) ||
+       bms_.CheckFlags(BtifBapBroadcaster::kFlagISOPending)) {
+#if AHIM_ENABLED
+       btif_ahim_ack_stream_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS, BROADCAST);
+       btif_ahim_reset_pending_command(BROADCAST);
+#else
+       bluetooth::audio::a2dp::ack_stream_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+       bluetooth::audio::a2dp::reset_pending_command();
+#endif
+     }
+     bms_.SetBIGState(BIG_DISABLING);
+     bms_.ClearFlag(BtifBapBroadcaster::kFlagISOPending);
+     btif_report_broadcast_state(bms_.AdvHandle(), BTBAP_BROADCAST_STATE_IDLE);
+     bms_.StateMachine().TransitionTo(BtifBapBroadcastStateMachine::kStateIdle);
+     break;
+   case BTIF_BAP_BROADCAST_SET_ACTIVE_REQ_EVT:
+     BTIF_TRACE_DEBUG("Not handled in configured state");
+     break;
+   case BTIF_BAP_BROADCAST_START_STREAM_REQ_EVT:
+     if (bms_.CheckFlags(BtifBapBroadcaster::kFlagBIGPending) ||
+       bms_.CheckFlags(BtifBapBroadcaster::kFlagISOPending)) {
+       BTIF_TRACE_DEBUG("[BapBroadcast]%s: BIG/ISO setup pending, dup req",__func__);
+#if AHIM_ENABLED
+       btif_ahim_ack_stream_started(A2DP_CTRL_ACK_PENDING, BROADCAST);
+#else
+       btif_ahim_ack_stream_started(A2DP_CTRL_ACK_PENDING, BROADCAST);
+#endif
+       break;
+     }
+     bms_.SetFlags(BtifBapBroadcaster::kFlagBIGPending);
+     bms_.SetNumBises(btif_bap_broadcast_get_ch_count());
+     bms_.SetBIGState(BIG_CREATING);
+     btif_bap_ba_create_big(bms_.AdvHandle());
+     break;
+   case BTIF_BAP_BROADCAST_SETUP_ISO_DATAPATH_EVT:
+     {
+       if (bms_.CheckFlags(BtifBapBroadcaster::kFlagBIGPending))
+         bms_.ClearFlag(BtifBapBroadcaster::kFlagBIGPending);
+       bms_.SetFlags(BtifBapBroadcaster::kFlagISOPending);
+       total_bises = bms_.NumBises();
+       current_iso_index = 0;
+       current_handle = bms_.BIGHandle();
+       btif_bap_ba_setup_iso_datapath(current_handle);
+     }
+     break;
+   case BTIF_BAP_BROADCAST_REMOVE_ISO_DATAPATH_EVT:
+     total_bises = bms_.NumBises();
+     current_iso_index = 0;
+     current_handle = bms_.BIGHandle();
+     btif_bap_ba_remove_iso_datapath(current_handle);
+     break;
+   case BTIF_BAP_BROADCAST_BISES_SETUP_EVT:
+     {
+       char *p_p = (char *) p_data;
+       p_p++;
+       uint8_t status = *p_p;
+       if (status != BT_STATUS_SUCCESS &&
+         bms_.CheckFlags(BtifBapBroadcaster::kFlagISOPending)) {
+         BTIF_TRACE_ERROR("[BapBroadcast]%s: setup iso failed",__func__);
+         bms_.ClearAllFlags();
+         bms_.SetFlags(BtifBapBroadcaster::kFlagISOError);
+#if AHIM_ENABLED
+         btif_ahim_ack_stream_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS, BROADCAST);
+         btif_ahim_reset_pending_command(BROADCAST);
+#else
+         bluetooth::audio::a2dp::ack_stream_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
+         bluetooth::audio::a2dp::reset_pending_command();
+#endif
+
+         btif_bap_ba_terminate_big(bms_.AdvHandle(), bms_.BIGHandle());
+         break;
+       }
+       bms_.ClearFlag(BtifBapBroadcaster::kFlagISOPending);
+       bms_.SetFlags(BtifBapBroadcaster::KFlagISOSetup);
+#if AHIM_ENABLED
+       btif_ahim_ack_stream_started(A2DP_CTRL_ACK_SUCCESS, BROADCAST);
+       btif_ahim_reset_pending_command(BROADCAST);
+#else
+       bluetooth::audio::a2dp::ack_stream_started(A2DP_CTRL_ACK_SUCCESS);
+       bluetooth::audio::a2dp::reset_pending_command();
+#endif
+       btif_report_setup_big(1, bms_.AdvHandle(), bms_.BIGHandle(), bms_.NumBises());
+       btif_report_broadcast_state(bms_.AdvHandle(), BTBAP_BROADCAST_STATE_STREAMING);
+       bms_.StateMachine().TransitionTo(BtifBapBroadcastStateMachine::kStateStreaming);
+     }
+     break;
+   case BTIF_BAP_BROADCAST_BISES_REMOVE_EVT:
+#if AHIM_ENABLED
+     btif_ahim_ack_stream_started(A2DP_CTRL_ACK_SUCCESS, BROADCAST);
+     btif_ahim_reset_pending_command(BROADCAST);
+#else
+     bluetooth::audio::a2dp::ack_stream_started(A2DP_CTRL_ACK_SUCCESS);
+     bluetooth::audio::a2dp::reset_pending_command();
+#endif
+     break;
+   case BTIF_BAP_BROADCAST_BIG_SETUP_EVT:
+     break;
+   case BTIF_BAP_BROADCAST_BIG_REMOVED_EVT:
+     btif_report_setup_big(0, bms_.AdvHandle(), -1, 0);
+     if (bms_.CheckFlags(BtifBapBroadcaster::kFlagISOError)) {
+       bms_.StateMachine().TransitionTo(BtifBapBroadcastStateMachine::kStateIdle);
+       btif_report_broadcast_state(bms_.AdvHandle(),BTBAP_BROADCAST_STATE_IDLE);
+     }
+     break;
+     case BTIF_BAP_BROADCAST_SOURCE_CONFIG_REQ_EVT:
+       btif_bap_broadcast_update_source_codec(p_data);
+       if (restart_session) {
+#if AHIM_ENABLED
+         btif_ahim_end_session();
+         btif_ahim_init_hal(get_worker_thread(), BROADCAST);
+         btif_ahim_start_session();
+#else
+         btif_a2dp_source_restart_session(RawAddress::kEmpty, mBapBADevice);
+#endif
+         btif_report_audio_config(bms_.AdvHandle(), current_config);
+       }
+       break;
+     case BTIF_BAP_BROADCAST_SUSPEND_STREAM_REQ_EVT:
+#if AHIM_ENABLED
+       btif_ahim_ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS, BROADCAST);
+       btif_ahim_reset_pending_command(BROADCAST);
+#else
+       bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS);
+       bluetooth::audio::a2dp::reset_pending_command();
+#endif
+       BTIF_TRACE_WARNING("%s: SUSPEND_STEAM_REQ unhandled in Configured state", __func__);
+       break;
+   default:
+     BTIF_TRACE_WARNING("%s: unhandled event=%s", __func__,
+                              dump_bap_ba_sm_event_name((btif_bap_broadcast_sm_event_t)event));
+     return false;
+ }
+ return true;
+
+}
+
+void BtifBapBroadcastStateMachine::StateStreaming::OnEnter() {
+  BTIF_TRACE_IMP("%s", __PRETTY_FUNCTION__);
+  btif_bap_bms.SetBroadcastState(BtifBapBroadcastSource::kFlagStreaming);
+  btif_report_broadcast_audio_state(bms_.AdvHandle(), BTBAP_BROADCAST__AUDIO_STATE_STARTED);
+}
+
+void BtifBapBroadcastStateMachine::StateStreaming::OnExit() {
+  BTIF_TRACE_IMP("%s", __PRETTY_FUNCTION__);
+}
+
+bool BtifBapBroadcastStateMachine::StateStreaming::ProcessEvent(uint32_t event,
+                                                   void* p_data) {
+  BTIF_TRACE_IMP("[BapBroadcast]:%s: event = %s",__func__,
+                            dump_bap_ba_sm_event_name((btif_bap_broadcast_sm_event_t)event));
+  switch (event) {
+    case BTIF_BAP_BROADCAST_DISABLE_EVT:
+      if (bms_.BIGState() == BIG_CREATED) {
+        bms_.SetBIGState(BIG_DISABLING);
+        btif_bap_bms.SetBroadcastState(BtifBapBroadcastSource::KFlagDisabling);
+        if (bms_.CheckFlags(BtifBapBroadcaster::KFlagISOSetup)) {
+          btif_bap_ba_terminate_big(bms_.AdvHandle(), bms_.BIGHandle());
+        }
+      } else {
+        bms_.SetBIGState(BIG_DISABLING);
+        BTIF_TRACE_DEBUG("[BapBroadcast]: BIG Terminate under process");
+      }
+      break;
+    case BTIF_BAP_BROADCAST_SUSPEND_STREAM_REQ_EVT:
+      if (bms_.BIGState() != BIG_CREATED) {
+        BTIF_TRACE_WARNING("[BapBroadcast]:%s: BIG is getting terminated already",__func__);
+#if AHIM_ENABLED
+        btif_ahim_ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS, BROADCAST);
+        btif_ahim_reset_pending_command(BROADCAST);
+#else
+        bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS);
+        bluetooth::audio::a2dp::reset_pending_command();
+#endif
+        break;
+      }
+      bms_.SetFlags(BtifBapBroadcaster::kFlagISOPending);
+      total_bises = bms_.NumBises();
+      current_iso_index = 0;
+      current_handle = bms_.BIGHandle();
+      btif_bap_ba_remove_iso_datapath(current_handle);
+
+      break;
+    case BTIF_BAP_BROADCAST_SETUP_ISO_DATAPATH_EVT:
+      {
+        char *p_p = (char *)p_data;
+        if (*p_p) {
+          BTIF_TRACE_WARNING("[BapBroadcast]:%s: We shouldn't be in streaming state if ISO datapath is not setup yet",__func__);
+        } else {
+          if (bms_.CheckFlags(BtifBapBroadcaster::KFlagISOSetup)) {
+            BTIF_TRACE_WARNING("[BapBroadcast] We shouldn't be here, ISO Datapath is removed first before BIG");
+            btif_bap_ba_remove_iso_datapath(bms_.BIGHandle());
+          } else {
+            BTIF_TRACE_WARNING("[BapBroadcast]:%s:IsoDatapah is already removed",__func__);
+            bms_.StateMachine().TransitionTo(BtifBapBroadcastStateMachine::kStateConfigured);
+          }
+        }
+      }
+      break;
+    case BTIF_BAP_BROADCAST_REMOVE_ISO_DATAPATH_EVT:
+      btif_bap_ba_terminate_big(bms_.AdvHandle(), bms_.BIGHandle());
+      break;
+    case BTIF_BAP_BROADCAST_BISES_REMOVE_EVT:
+      bms_.ClearFlag(BtifBapBroadcaster::kFlagISOPending);
+      if (bms_.BIGState() == BIG_CREATED)
+        bms_.SetBIGState(BIG_TERMINATING);
+      btif_bap_ba_terminate_big(bms_.AdvHandle(), bms_.BIGHandle());
+      break;
+    case BTIF_BAP_BROADCAST_BIG_REMOVED_EVT:
+      if (bms_.BIGState() == BIG_DISABLING) {
+        btif_report_broadcast_state(bms_.AdvHandle(), BTBAP_BROADCAST_STATE_IDLE);
+        bms_.StateMachine().TransitionTo(BtifBapBroadcastStateMachine::kStateIdle);
+      } else if (bms_.BIGState() == BIG_TERMINATING) {
+        bms_.StateMachine().TransitionTo(BtifBapBroadcastStateMachine::kStateConfigured);
+      } else if (bms_.BIGState() == BIG_RECONFIG) {
+#if AHIM_ENABLED
+        btif_ahim_end_session();
+        btif_ahim_init_hal(get_worker_thread(), BROADCAST);
+        btif_ahim_start_session();
+#else
+        btif_a2dp_source_restart_session(RawAddress::kEmpty, mBapBADevice);
+#endif
+        btif_report_audio_config(bms_.AdvHandle(), current_config);
+        bms_.StateMachine().TransitionTo(BtifBapBroadcastStateMachine::kStateConfigured);
+        break;
+      }
+#if AHIM_ENABLED
+      btif_ahim_ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS, BROADCAST);
+      btif_ahim_reset_pending_command(BROADCAST);
+#else
+      bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS);
+      bluetooth::audio::a2dp::reset_pending_command();
+#endif
+
+      break;
+    case BTIF_BAP_BROADCAST_SOURCE_CONFIG_REQ_EVT:
+      btif_bap_broadcast_update_source_codec(p_data);
+      if (restart_session) {
+        bms_.SetBIGState(BIG_RECONFIG);
+        btif_bap_ba_terminate_big(bms_.AdvHandle(), bms_.BIGHandle());
+      }
+      break;
+    default:
+      BTIF_TRACE_WARNING("%s: unhandled event=%s", __func__,
+                              dump_bap_ba_sm_event_name((btif_bap_broadcast_sm_event_t)event));
+      return false;
+  }
+  return true;
+}
+
+static btif_ahim_client_callbacks_t sAhimBroadcastCallbacks = {
+  2, // mode
+  btif_broadcast_process_hidl_event,
+  btif_bap_broadcast_get_sample_rate,
+  btif_bap_broadcast_get_ch_mode,
+  btif_bap_broadcast_get_bitrate,
+  btif_bap_broadcast_get_mtu,
+  btif_bap_broadcast_get_framelength,
+  btif_bap_broadcast_get_ch_count,
+  btif_bap_broadcast_is_simulcast_enabled,
+  nullptr,
+  nullptr,
+  nullptr
+};
+
+bt_status_t BtifBapBroadcastSource::Init(btbap_broadcast_callbacks_t* callbacks,
+                              int max_broadcast,
+                              btav_a2dp_codec_config_t codec_config,int mode) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s", __func__);
+  char value[PROPERTY_VALUE_MAX] = {'\0'};
+  if (mode == 1) offload_enabled_ = true;
+  callbacks_ = callbacks;
+  default_config_ = codec_config;
+  memset(encryption_key.data(), 0, OCTET16_LEN);
+  init_local_capabilities();
+  osi_property_get("persist.vendor.btstack.partial_simulcast",value,"false");
+  if (strcmp(value, "true") == 0) {
+      BTIF_TRACE_IMP("[BapBroadcast]%s:Partial simulcast enabled",__func__);
+      mBisMultiplier = 2;
+  } else  {
+      mBisMultiplier = 1;
+  }
+  osi_property_get("persist.vendor.btstack.transport_latency",value,"0");
+  mBigParams.max_transport_latency = atoi(value);
+  osi_property_get("persist.vendor.btstack.bis_rtn",value,"2");
+  mBigParams.rtn = atoi(value);
+  BTIF_TRACE_IMP("%s: transport_latency: %d, rtn: %d",
+                  __func__, mBigParams.max_transport_latency, mBigParams.rtn);
+  BTIF_TRACE_IMP("%s: Fetch broadcast encryption key", __func__);
+
+  size_t length = OCTET16_LEN;
+  bool ret = btif_config_get_bin("Adapter", "BAP_BA_ENC_KEY", encryption_key.data(), &length);
+
+  if (!ret) {
+    btif_bap_ba_generate_enc_key_local(OCTET16_LEN);
+  } else {
+    reverseCode(encryption_key.data());
+    if (isUnencrypted(encryption_key.data())) {
+      mEncryptionEnabled = false;
+    }
+    for (int i = 0; i < OCTET16_LEN; i++) {
+      BTIF_TRACE_IMP("[bapbroadcast]%s: encryption_key[%d] = %d",__func__,i,encryption_key[i]);
+    }
+  }
+#if AHIM_ENABLED
+  reg_cb_with_ahim(BROADCAST, &sAhimBroadcastCallbacks);
+#endif
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t BtifBapBroadcastSource::EnableBroadcast(btav_a2dp_codec_config_t codec_config) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s", __func__);
+  current_config_ = codec_config;
+  btif_bap_ba_generate_broadcast_id();
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t BtifBapBroadcastSource::DisableBroadcast(int adv_handle) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s", __func__);
+  local_param[0] = adv_handle;
+  do_in_bta_thread(
+      FROM_HERE, base::Bind(&btif_bap_ba_handle_event,
+                            BTIF_BAP_BROADCAST_DISABLE_EVT, local_param));
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t BtifBapBroadcastSource::SetEncryption(int length) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s", __func__);
+  local_param[0] = length;
+  do_in_bta_thread(
+    FROM_HERE, base::Bind(&btif_bap_ba_handle_event,
+                          BTIF_BAP_BROADCAST_GENERATE_ENC_KEY_EVT, local_param));
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t BtifBapBroadcastSource::SetBroadcastActive(bool setup, uint8_t adv_id) {
+  if (btif_a2dp_source_is_hal_v2_supported()) {
+    std::unique_lock<std::mutex> guard(session_wait_);
+    mSession_wait = false;
+    if (setup) {
+      do_in_bta_thread(
+       FROM_HERE, base::Bind(&btif_bap_ba_handle_event,
+                             BTIF_BAP_BROADCAST_SET_ACTIVE_REQ_EVT, (char *)&adv_id));
+    } else {
+     do_in_bta_thread(
+      FROM_HERE, base::Bind(&btif_bap_ba_handle_event,
+                            BTIF_BAP_BROADCAST_REMOVE_ACTIVE_REQ_EVT,(char *)&adv_id));
+    }
+    BTIF_TRACE_EVENT("%s: wating for signal",__func__);
+    session_wait_cv_.wait_for(guard, std::chrono::milliseconds(1000),
+                     []{return mSession_wait;});
+    BTIF_TRACE_EVENT("%s: done with signal",__func__);
+    return BT_STATUS_SUCCESS;
+  }
+  return BT_STATUS_SUCCESS;
+}
+void BtifBapBroadcastSource::Cleanup() {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s", __func__);
+  //while(!bms_.empty()) {
+  for (auto it = bms_.begin();it != bms_.end();){
+    BtifBapBroadcaster *bms = it->second;
+    auto prev_it = it++;
+    bms->Cleanup();
+    bms_.erase(prev_it);
+    //delete bms;
+  }
+}
+
+void BtifBapBroadcastSource::CleanupIdleBms() {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s", __func__);
+  for (auto it = bms_.begin();it != bms_.end();){
+    BtifBapBroadcaster *bms = it->second;
+    auto prev_it = it++;
+    if (bms->CanBeDeleted()) {
+      BTIF_TRACE_DEBUG("[BapBroadcast]%s: Cleaning up idle bms", __func__);
+      bms->Cleanup();
+      bms_.erase(prev_it);
+      //delete bms;
+    }
+    //delete bms;
+  }
+  BTIF_TRACE_DEBUG("[BapBroadcast]%s:Exit",__func__);
+}
+
+bt_status_t BtifBapBroadcastSource::SetUserConfig(uint8_t adv_handle,
+                                           btav_a2dp_codec_config_t codec_config) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s", __func__);
+  config_req_handle = (int) adv_handle;
+  do_in_bta_thread(
+    FROM_HERE, base::Bind(&btif_bap_ba_handle_event,
+                          BTIF_BAP_BROADCAST_SOURCE_CONFIG_REQ_EVT, (char *)&codec_config));
+  return BT_STATUS_SUCCESS;
+}
+BtifBapBroadcaster * BtifBapBroadcastSource::CreateBMS(int adv_handle) {
+  BtifBapBroadcaster *bms = new BtifBapBroadcaster(adv_handle, -1);
+//  bms_.insert(bms);
+  bms_.insert(std::make_pair(adv_handle, bms));
+  bms->Init();
+  return bms;
+}
+
+BtifBapBroadcaster * BtifBapBroadcastSource::FindBmsFromAdvHandle(uint8_t adv_handle) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s: adv_handle = %d", __func__, adv_handle);
+  for(auto it : bms_) {
+    BtifBapBroadcaster *bms = it.second;
+    if (bms->AdvHandle() == adv_handle)
+      return bms;
+  }
+  return nullptr;
+}
+
+BtifBapBroadcaster * BtifBapBroadcastSource::FindBmsFromBIGHandle(uint8_t big_handle) {
+  for(auto it : bms_) {
+    BtifBapBroadcaster *bms = it.second;
+    if (bms->BIGHandle() == big_handle)
+      return bms;
+  }
+  return nullptr;
+}
+
+BtifBapBroadcaster * BtifBapBroadcastSource::FindStreamingBms() {
+ for(auto it : bms_) {
+   BtifBapBroadcaster *bms = it.second;
+   if (bms->StateMachine().StateId() == BtifBapBroadcastStateMachine::kStateStreaming)
+     return bms;
+ }
+ return nullptr;
+}
+
+BtifBapBroadcaster * BtifBapBroadcastSource::FindConfiguredBms() {
+ for(auto it : bms_) {
+   BtifBapBroadcaster *bms = it.second;
+   if (bms->StateMachine().StateId() == BtifBapBroadcastStateMachine::kStateConfigured)
+     return bms;
+ }
+ return nullptr;
+}
+BtifBapBroadcastSource::~BtifBapBroadcastSource(){}
+/*****************************************************************************
+ *  Local event handlers
+ *****************************************************************************/
+void print_config(btav_a2dp_codec_config_t config) {
+  BTIF_TRACE_WARNING("[BapBroadcast]%d: Sampling rate = %d", __func__,config.sample_rate);
+  BTIF_TRACE_WARNING("[BapBroadcast]%d: channel mode = %d", __func__, config.channel_mode);
+  BTIF_TRACE_WARNING("[BapBroadcast]%d: codec_specific_1 = %d", __func__, config.codec_specific_1);
+  BTIF_TRACE_WARNING("[BapBroadcast]%d: codec_specific_2 = %d", __func__, config.codec_specific_2);
+}
+
+static void btif_report_encyption_key() {
+    do_in_jni_thread(FROM_HERE,
+                     base::Bind(btif_bap_bms.Callbacks()->enc_key_cb,
+      std::string(reinterpret_cast<const char*>(encryption_key.data()), OCTET16_LEN)));
+}
+
+static void btif_report_broadcast_state(int adv_id,
+                                btbap_broadcast_state_t state) {
+  if (btif_bap_bms.Enabled()) {
+    do_in_jni_thread(FROM_HERE,
+                     base::Bind(btif_bap_bms.Callbacks()->broadcast_state_cb,
+                               adv_id, state));
+  }
+}
+
+static void btif_report_broadcast_audio_state(int adv_id,
+                                    btbap_broadcast_audio_state_t state) {
+  if (btif_bap_bms.Enabled()) {
+    do_in_jni_thread(FROM_HERE,
+                     base::Bind(btif_bap_bms.Callbacks()->audio_state_cb,
+                               adv_id, state));
+  }
+}
+
+static void btif_report_audio_config(int adv_id,
+                                 btav_a2dp_codec_config_t codec_config) {
+  if (btif_bap_bms.Enabled()) {
+    do_in_jni_thread(FROM_HERE,
+                     base::Bind(btif_bap_bms.Callbacks()->audio_config_cb,
+                               adv_id, codec_config, broadcast_codecs_capabilities));
+  }
+}
+
+static void btif_report_setup_big(int setup, int adv_id, int big_handle, int num_bises) {
+
+  BtifBapBroadcaster *bms = btif_bap_bms.FindBmsFromAdvHandle(adv_id);
+  if (bms == nullptr) return;
+  if (btif_bap_bms.Enabled()) {
+    do_in_jni_thread(FROM_HERE,
+                     base::Bind(btif_bap_bms.Callbacks()->create_big_cb, setup,
+                               adv_id, big_handle, num_bises, bms->GetBISHandles()));
+  }
+
+}
+
+static void btif_report_broadcast_id() {
+  do_in_jni_thread(FROM_HERE,
+                   base::Bind(btif_bap_bms.Callbacks()->broadcast_id_cb, mBroadcastID));
+}
+
+static void btif_bap_ba_handle_event(uint32_t event, char* p_param) {
+    int big_handle, adv_id;
+    big_handle = adv_id = 0;
+    BtifBapBroadcaster *broadcaster;
+    BTIF_TRACE_DEBUG("[BapBroadcast]:%s: event %s",
+           __func__, dump_bap_ba_sm_event_name((btif_bap_broadcast_sm_event_t)event));
+    switch(event) {
+      case BTIF_BAP_BROADCAST_DISABLE_EVT:
+        adv_id = (int)*p_param;
+        broadcaster = btif_bap_bms.FindBmsFromAdvHandle(adv_id);
+        if (broadcaster == nullptr) {
+          BTIF_TRACE_ERROR("[BapBroadcast]:%s:invalid index, Broadcast is already disabled",__func__);
+          return;
+        }
+        break;
+      case BTIF_BAP_BROADCAST_START_STREAM_REQ_EVT:
+        broadcaster = btif_bap_bms.FindConfiguredBms();
+        if (broadcaster == nullptr) {
+          BTIF_TRACE_ERROR("[BapBroadcast]:%s:cannot find empty index",__func__);
+          return;
+        }
+        break;
+      case BTIF_BAP_BROADCAST_STOP_STREAM_REQ_EVT:
+      case BTIF_BAP_BROADCAST_SUSPEND_STREAM_REQ_EVT:
+        broadcaster = btif_bap_bms.FindStreamingBms();
+        if (broadcaster == nullptr) {
+          BTIF_TRACE_ERROR("[BapBroadcast]:%s:cannot find empty index",__func__);
+          return;
+        }
+        break;
+      case BTIF_BAP_BROADCAST_SOURCE_CONFIG_REQ_EVT:
+        broadcaster = btif_bap_bms.FindBmsFromAdvHandle(config_req_handle);
+        if (broadcaster == nullptr) {
+          BTIF_TRACE_ERROR("[BapBroadcast]:%s:cannot find empty index",__func__);
+          return;
+        }
+        break;
+      case BTIF_BAP_BROADCAST_CLEANUP_REQ_EVT:
+        broadcaster = btif_bap_bms.FindStreamingBms(); //TODO:add proper check
+        if (broadcaster == nullptr) {
+          BTIF_TRACE_ERROR("[BapBroadcast]:%s:cannot find empty index",__func__);
+          return;
+        }
+        break;
+      case BTIF_BAP_BROADCAST_SET_ACTIVE_REQ_EVT:
+        {
+          char *p_p = p_param;
+          int adv_handle = (int)*p_p;
+          BTIF_TRACE_ERROR("[BapBroadcast]:%s:adv_handle %d",__func__,adv_handle);
+          broadcaster = btif_bap_bms.CreateBMS((int)adv_handle);
+          if (broadcaster == nullptr) {
+            BTIF_TRACE_ERROR("[BapBroadcast]:%s: cannot find empty index",__func__);
+            return;
+          }
+          broadcaster->SetAdvHandle(adv_handle);
+          BTIF_TRACE_ERROR("[BapBroadcast]:%s:adv_id = %d, big_handle = %d",__func__, adv_handle);
+        }
+        break;
+      case BTIF_BAP_BROADCAST_REMOVE_ACTIVE_REQ_EVT:
+        BTIF_TRACE_DEBUG("[BapBroadcast]:%s:End session", __func__);
+#if AHIM_ENABLED
+        btif_ahim_end_session();
+#else
+        bluetooth::audio::a2dp::end_session();
+#endif
+        btif_bap_ba_signal_session_ready();
+        return;
+      case BTIF_BAP_BROADCAST_SETUP_ISO_DATAPATH_EVT:
+        {
+          char *p_p = p_param;
+          int adv_handle = (int)*p_p;
+          BTIF_TRACE_DEBUG("[BapBroadcast]:%s:adv_handle = %d",__func__,adv_handle);
+          broadcaster = btif_bap_bms.FindBmsFromAdvHandle(adv_handle);
+          if (broadcaster == nullptr) {
+            BTIF_TRACE_ERROR("[BapBroadcast]:%s: cannot find empty index",__func__);
+            return;
+          }
+        }
+        break;
+      case BTIF_BAP_BROADCAST_REMOVE_ISO_DATAPATH_EVT:
+        {
+          char *p_p = p_param;
+          adv_id = *p_p++;
+          big_handle = *p_p;
+          broadcaster = btif_bap_bms.FindBmsFromBIGHandle(big_handle);
+          if (broadcaster == nullptr) {
+            BTIF_TRACE_ERROR("[BapBroadcast]:%s: cannot find empty index",__func__);
+            return;
+          }
+        }
+        break;
+      case BTIF_BAP_BROADCAST_GENERATE_ENC_KEY_EVT:
+        enc_keylength = (uint8_t)(*p_param);
+        notify_key_generated = true;
+        btif_bap_ba_generate_enc_key_local(enc_keylength);
+        return;
+      case BTIF_BAP_BROADCAST_BISES_SETUP_EVT:
+      case BTIF_BAP_BROADCAST_BISES_REMOVE_EVT:
+        big_handle = *p_param;
+        broadcaster = btif_bap_bms.FindBmsFromBIGHandle(big_handle);
+        if (broadcaster == nullptr) {
+          BTIF_TRACE_ERROR("[BapBroadcast]:%s: cannot find empty index",__func__);
+          return;
+        }
+        break;
+      case BTIF_BAP_BROADCAST_BIG_REMOVED_EVT:
+        adv_id = (int)*p_param;
+        btif_report_setup_big(0, adv_id,-1, 0);
+        broadcaster = btif_bap_bms.FindBmsFromAdvHandle(adv_id);
+        if (broadcaster == nullptr) {
+          BTIF_TRACE_ERROR("[BapBroadcast]:%s: cannot find empty index",__func__);
+          return;
+        }
+        break;
+      case BTIF_BAP_BROADCAST_PROCESS_HIDL_REQ_EVT:
+        btif_bap_process_request((tA2DP_CTRL_CMD ) *p_param);
+        return;
+      case BTIF_BAP_BROADCAST_SETUP_NEXT_BIS_EVENT:
+        {
+          char *p = p_param;
+          uint8_t status = *p++;
+          uint16_t bis_handle = *p++;
+          bis_handle = (bis_handle | (*p <<8));
+          btif_bap_ba_process_iso_setup(status, bis_handle);
+        return;
+        }
+      default:
+        BTIF_TRACE_ERROR("[BapBroadcast]:%s: invalid event = %d",__func__, event);
+        return;
+    }
+    if (broadcaster == nullptr) {
+      BTIF_TRACE_ERROR("[BapBroadcast]:%s:Invalid broadcaster",__func__);
+    }
+    broadcaster->StateMachine().ProcessEvent(event, (void*)p_param);
+}
+
+static void btif_bap_process_request(tA2DP_CTRL_CMD cmd) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s",__func__);
+  tA2DP_CTRL_ACK status = A2DP_CTRL_ACK_FAILURE;
+  //BtifBapBroadcaster *broadcaster;
+  uint32_t event = 0;
+#if AHIM_ENABLED
+  btif_ahim_update_pending_command(cmd, BROADCAST);
+#else
+  bluetooth::audio::a2dp::update_pending_command(cmd);
+#endif
+
+  switch(cmd) {
+    case A2DP_CTRL_CMD_START:
+      if (!bluetooth::headset::btif_hf_is_call_vr_idle()) {
+        status = A2DP_CTRL_ACK_INCALL_FAILURE;
+        break;
+      }
+      if (btif_bap_bms.FindStreamingBms() != nullptr) {
+        BTIF_TRACE_DEBUG("%s: Broadcast already streaming, crash recover(?)",__func__);
+        status = A2DP_CTRL_ACK_SUCCESS;
+        break;
+      }
+      if (btif_bap_bms.FindConfiguredBms() == nullptr) {
+        BTIF_TRACE_DEBUG("%s: Broadcast is disabled",__func__);
+        status = A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS;
+        break;
+      }
+      btif_bap_ba_dispatch_sm_event(BTIF_BAP_BROADCAST_START_STREAM_REQ_EVT, NULL, 0);
+      event = BTIF_BAP_BROADCAST_START_STREAM_REQ_EVT;
+      //broadcaster = btif_bap_bms.FindConfiguredBms();
+      status = A2DP_CTRL_ACK_PENDING;
+      break;
+    case A2DP_CTRL_CMD_STOP:
+      btif_bap_ba_dispatch_sm_event(BTIF_BAP_BROADCAST_STOP_STREAM_REQ_EVT, NULL, 0);
+      //broadcaster = btif_bap_bms.FindStreamingBms();
+      status = A2DP_CTRL_ACK_SUCCESS;
+      break;
+    case A2DP_CTRL_CMD_SUSPEND:
+      if (btif_bap_bms.FindStreamingBms() != nullptr) {
+        btif_bap_ba_dispatch_sm_event(BTIF_BAP_BROADCAST_SUSPEND_STREAM_REQ_EVT, NULL, 0);
+        //broadcaster = btif_bap_bms.FindStreamingBms();
+        status = A2DP_CTRL_ACK_PENDING;
+      } else {
+        status = A2DP_CTRL_ACK_SUCCESS;
+      }
+      break;
+    default:
+      APPL_TRACE_ERROR("UNSUPPORTED CMD (%d)", cmd);
+      status = A2DP_CTRL_ACK_FAILURE;
+      break;
+  }
+    // send the response now based on status
+  switch (cmd) {
+    case A2DP_CTRL_CMD_START:
+#if AHIM_ENABLED
+      btif_ahim_ack_stream_started(status, BROADCAST);
+#else
+      bluetooth::audio::a2dp::ack_stream_started(status);
+#endif
+      break;
+    case A2DP_CTRL_CMD_SUSPEND:
+    case A2DP_CTRL_CMD_STOP:
+#if AHIM_ENABLED
+      btif_ahim_ack_stream_suspended(status, BROADCAST);
+#else
+      bluetooth::audio::a2dp::ack_stream_suspended(status);
+#endif
+      break;
+    default:
+      break;
+  }
+  if (status != A2DP_CTRL_ACK_PENDING) {
+#if AHIM_ENABLED
+    btif_ahim_reset_pending_command(BROADCAST);
+#else
+    bluetooth::audio::a2dp::reset_pending_command();
+#endif
+  }
+}
+
+static bool btif_bap_is_broadcaster_valid(uint8_t big_handle) {
+  BTIF_TRACE_DEBUG("[BapBroadcat]%s: handle = %d",__func__, big_handle);
+  BtifBapBroadcaster *bms = btif_bap_bms.FindBmsFromBIGHandle(big_handle);
+  if (bms == nullptr) return false;
+  if (pending_cmd == setup_iso) {
+    if (!bms->CheckFlags(BtifBapBroadcaster::kFlagISOPending) ||
+      bms->StateMachine().StateId() != BtifBapBroadcastStateMachine::kStateConfigured) {
+      BTIF_TRACE_WARNING("[BapBroadcast]:%s Broadcast disabled",__func__);
+      return false;
+    }
+  } else {
+    if (bms->BIGState() != BIG_CREATED) {
+      BTIF_TRACE_WARNING("[BapBroadcast]:%s Broadcast disabled",__func__);
+      return false;
+    }
+  }
+  return true;
+}
+
+static void btif_bap_ba_process_iso_setup(uint8_t status, uint16_t bis_handle) {
+  BTIF_TRACE_WARNING("[BapBroadcast]:%s",__func__);
+  if (!btif_bap_is_broadcaster_valid(current_handle)) return;
+  if (pending_cmd == setup_iso) {
+    local_param[0] = current_handle;
+    local_param[1] = status;
+    if (!btif_bap_ba_setup_iso_datapath(current_handle)) {
+       BTIF_TRACE_WARNING("[BapBroadcast]:%s: notify bis setup",__func__);
+       pending_cmd = iso_unknown;
+       btif_bap_ba_handle_event(BTIF_BAP_BROADCAST_BISES_SETUP_EVT, (char *)local_param);
+     }
+  } else if (pending_cmd == remove_iso) {
+    if (!btif_bap_ba_remove_iso_datapath(current_handle)) {
+      local_param[0] = current_handle;
+      local_param[1] = status;
+      pending_cmd = iso_unknown;
+      BTIF_TRACE_WARNING("[BapBroadcast]:%s: notify bis removed",__func__);
+      btif_bap_ba_handle_event(BTIF_BAP_BROADCAST_BISES_REMOVE_EVT, (char *)local_param);
+    }
+  }
+}
+static void btif_bap_ba_isodatapath_setup_cb(uint8_t status, uint16_t bis_handle) {
+  BTIF_TRACE_WARNING("[BapBroadcast]:%s, status = %d for handle = %d",__func__, status, bis_handle);
+  if (!btif_bap_is_broadcaster_valid(current_handle)) return;
+  if (pending_cmd == setup_iso) {
+    memset(local_param, 0, 3);
+    local_param[0] = status;
+    local_param[1] = bis_handle & 0x00FF;
+    local_param[2] = (bis_handle & 0xFF00) >> 8;
+    if (status == 0) {
+      total_bises--;
+      btif_bap_ba_handle_event(BTIF_BAP_BROADCAST_SETUP_NEXT_BIS_EVENT, (char *)local_param);
+    } else {
+      local_param[0] = current_handle;
+      local_param[1] = status;
+      btif_bap_ba_handle_event(BTIF_BAP_BROADCAST_BISES_SETUP_EVT, (char *)local_param);
+    }
+  } else if (pending_cmd == remove_iso) {
+    memset(local_param, 0, 3);
+    local_param[0] = status;
+    local_param[1] = bis_handle & 0x00FF;
+    local_param[2] = (bis_handle & 0xFF00) >> 8;
+    btif_bap_ba_handle_event(BTIF_BAP_BROADCAST_SETUP_NEXT_BIS_EVENT, (char*)local_param);
+  }
+}
+
+static bool btif_bap_ba_setup_iso_datapath(int big_handle) {
+  BTIF_TRACE_WARNING("[BapBroadcast]:%s",__func__);
+  BtifBapBroadcaster *bms = btif_bap_bms.FindBmsFromBIGHandle(big_handle);
+  if (bms == nullptr) {
+    BTIF_TRACE_WARNING("[BapBroadcast]:%s bms is null",__func__);
+    return false;
+  }
+  if (!btif_bap_is_broadcaster_valid(big_handle)) return false;
+  if (current_iso_index == bms->NumBises()) {
+    BTIF_TRACE_WARNING("[BapBroadcast]:%s completed",__func__);
+    return false;
+  }
+  pending_cmd = setup_iso;
+  std::vector<uint16_t> BisHandles = bms->GetBISHandles();
+  tBTM_BLE_SET_ISO_DATA_PATH_PARAM *p_param = new tBTM_BLE_SET_ISO_DATA_PATH_PARAM;
+  p_param->conn_handle = BisHandles[current_iso_index++];
+  p_param->data_path_dir = 0;
+  p_param->data_path_id = PATH_ID;//6;
+  p_param->codec_id[0] = 6;
+  p_param->cont_delay[0] = 0;
+  p_param->cont_delay[1] = 0;
+  p_param->cont_delay[2] = 0;
+  p_param->codec_config_length = 0;
+  //param.codec_config = NULL;
+  p_param->p_cb = (tBTM_BLE_SETUP_ISO_DATA_PATH_CMPL_CB*)&btif_bap_ba_isodatapath_setup_cb;
+
+  BTIF_TRACE_WARNING("[BapBroadcast]:%s for handle = %d",__func__, p_param->conn_handle);
+  do_in_bta_thread(FROM_HERE,base::Bind(base::IgnoreResult(&BTM_BleSetIsoDataPath), std::move(p_param)));
+  return true;
+}
+
+static bool btif_bap_ba_remove_iso_datapath(int big_handle) {
+  BTIF_TRACE_WARNING("[BapBroadcast]:%s",__func__);
+  BtifBapBroadcaster *bms = btif_bap_bms.FindBmsFromBIGHandle(big_handle);
+  if (bms == nullptr) {
+    BTIF_TRACE_WARNING("[BapBroadcast]%s: broadcaster not found",__func__);
+    return false;
+  }
+  if (current_iso_index == bms->NumBises()) {
+    return false;
+  }
+  pending_cmd = remove_iso;
+  std::vector<uint16_t> BisHandles = bms->GetBISHandles();
+  uint16_t bis_handle = BisHandles[current_iso_index++];
+  do_in_bta_thread(FROM_HERE, base::Bind(base::IgnoreResult(&BTM_BleRemoveIsoDataPath), bis_handle,
+                          INPUT_DATAPATH,&btif_bap_ba_isodatapath_setup_cb));
+   return true;
+}
+
+void btif_bap_ba_creat_big_cb(uint8_t adv_id, uint8_t status, uint8_t big_handle,
+        uint32_t sync_delay, uint32_t transport_latency, uint8_t phy, uint8_t nse, uint8_t bn, uint8_t pto,
+        uint8_t irc, uint16_t max_pdu, uint16_t iso_int, uint8_t num_bis, std::vector<uint16_t> conn_handle_list) {
+  BTIF_TRACE_IMP("[BapBroadcast]%s: callback: status = %d, adv_id = %d",__func__, status, adv_id);
+  if (status == BT_STATUS_SUCCESS) {
+    BtifBapBroadcaster *bms = btif_bap_bms.FindBmsFromAdvHandle(adv_id);
+    if (bms == nullptr) {
+      BTIF_TRACE_ERROR("%s: broadcaster not found",__func__);
+      return;
+    }
+    if (bms->StateMachine().StateId() != BtifBapBroadcastStateMachine::kStateConfigured ||
+      bms->BIGState() != BIG_CREATING) {
+      BTIF_TRACE_WARNING("[BapBroadcast]%s: Broadcast is disabling",__func__);
+      return;
+    }
+    bms->SetBIGHandle(big_handle);
+    BTIF_TRACE_DEBUG("[BapBroadcast]%s: callback: big_handle = %d",__func__, bms->BIGHandle());
+    bms->SetNumBises(num_bis);
+    BTIF_TRACE_DEBUG("[BapBroadcast]%s: callback: num_bis = %d",__func__, bms->NumBises());
+    bms->SetBISHandles(conn_handle_list);
+    bms->SetBIGState(BIG_CREATED);
+    local_param[0] = adv_id;
+    do_in_bta_thread(FROM_HERE,
+                     base::Bind(&btif_bap_ba_handle_event,
+                     BTIF_BAP_BROADCAST_SETUP_ISO_DATAPATH_EVT, local_param));
+  } else {
+    local_param[0] = adv_id;
+    do_in_bta_thread(FROM_HERE,
+                     base::Bind(&btif_bap_ba_handle_event,
+                     BTIF_BAP_BROADCAST_DISABLE_EVT, local_param));
+  }
+}
+
+static void btif_bap_ba_create_big(int adv_handle) {
+  BTIF_TRACE_IMP("[BapBroadcast]:%s",__func__);
+  //char ba_enc[PROPERTY_VALUE_MAX] = {0};
+#if BIG_COMPILE
+  BtifBapBroadcaster *bms = btif_bap_bms.FindBmsFromAdvHandle(adv_handle);
+  if (bms == nullptr) {
+    BTIF_TRACE_ERROR("%s: broadcaster not found",__func__);
+    return;
+  }
+  CreateBIGParameters param;
+  param.adv_handle = adv_handle;
+  param.num_bis = bms->NumBises();
+  param.sdu_int = mBigParams.sdu_int;
+  param.max_sdu = mBigParams.max_sdu;
+  param.max_transport_latency = btif_bap_get_transport_latency();
+  param.rtn = mBigParams.rtn;
+  param.phy = mBigParams.phy;
+  param.packing = mBigParams.packing;;//0 : sequential, 1: interleaved
+  param.framing = mBigParams.framing;
+  //osi_property_get("persist.bluetooth.ba_encryption", ba_enc, "true");
+  //if (!(strncmp(ba_enc,"true",4))) {
+  if (mEncryptionEnabled) {
+    //mEncryptionEnabled = true;
+    param.encryption = 0x01;
+  } else {
+    mEncryptionEnabled = false;
+    param.encryption = 0x00;
+  }
+  uint8_t code[16] = {0};
+  if (mEncryptionEnabled) {
+      memcpy(&code[0], encryption_key.data(),encryption_key.size());
+  }
+  for(int i =0; i < 16; i++) {
+    param.broadcast_code.push_back(code[15-i]);
+    BTIF_TRACE_VERBOSE("[BapBroadcast]%s: code[%d] = %x, bc[%d] = %d",__func__,i,code[i],i,param.broadcast_code[i]);
+  }
+
+  btif_gatt_get_interface()->advertiser->CreateBIG(adv_handle, param,
+                      base::Bind(&btif_bap_ba_creat_big_cb));
+#endif /* BIG_COMPILE */
+}
+
+#if BIG_COMPILE
+static void btif_bap_ba_terminate_big_cb(uint8_t status, uint8_t adv_id,
+                                        uint8_t big_handle, uint8_t reason) {
+  BTIF_TRACE_IMP("[BapBroadcast]:%s",__func__);
+  local_param[0] = adv_id;
+  do_in_bta_thread(FROM_HERE,
+                   base::Bind(&btif_bap_ba_handle_event,
+                   BTIF_BAP_BROADCAST_BIG_REMOVED_EVT, /*(char*)&adv_id)*/local_param));
+
+}
+#endif /* BIG_COMPILE */
+
+static void btif_bap_ba_terminate_big(int adv_handle, int big_handle) {
+  BTIF_TRACE_IMP("[BapBroadcast]:%s",__func__);
+#if BIG_COMPILE
+  int reason = 0x16; //user terminated
+  btif_gatt_get_interface()->advertiser->TerminateBIG(adv_handle, big_handle, reason,
+                     base::Bind(&btif_bap_ba_terminate_big_cb));
+#endif /* BIG_COMPILE */
+}
+
+void btif_bap_ba_dispatch_sm_event(btif_bap_broadcast_sm_event_t event, void* p_data, int len) {
+  BTIF_TRACE_DEBUG("%s: event: %d, len: %d", __FUNCTION__, event, len);
+  do_in_bta_thread(FROM_HERE,
+                   base::Bind(&btif_bap_ba_handle_event, event, (char*)p_data));
+  BTIF_TRACE_DEBUG("%s: event %d sent", __FUNCTION__, event);
+}
+
+void btif_broadcast_process_hidl_event(tA2DP_CTRL_CMD cmd) {
+  btif_bap_ba_dispatch_sm_event(BTIF_BAP_BROADCAST_PROCESS_HIDL_REQ_EVT,
+                                                (char*)&cmd, sizeof(cmd));
+}
+bool btif_bap_broadcast_is_active() {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s",__func__);
+  if (btif_bap_bms.BroadcastActive()) {
+    return true;
+  }
+  return false;
+}
+
+void btif_bap_ba_update_big_params() {
+  uint32_t bitrate = btif_bap_broadcast_get_bitrate();
+  mBigParams.max_sdu = btif_bap_broadcast_get_mtu(bitrate);
+  mBigParams.sdu_int = btif_bap_broadcast_get_framelength();
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s: max_sdu = %d, sdu_int = %d",__func__,
+                       mBigParams.max_sdu, mBigParams.sdu_int);
+}
+uint16_t btif_bap_broadcast_get_sample_rate() {
+  if (current_config.sample_rate != BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
+    BTIF_TRACE_DEBUG("[BapBroadcast]:%s: sample_rate = %d",__func__, current_config.sample_rate);
+    return current_config.sample_rate;
+  } else {
+    BTIF_TRACE_DEBUG("[BapBroadcast]:%s: default sample_rate = %d",__func__, default_config.sample_rate);
+    return default_config.sample_rate;
+  }
+}
+uint8_t btif_bap_broadcast_get_ch_mode() {
+  if (current_config.channel_mode != BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
+    return current_config.channel_mode;
+  } else {
+    return default_config.channel_mode;
+  }
+}
+uint32_t btif_bap_broadcast_get_mtu(uint32_t bitrate) {
+  //based on bitrate set (100(80kbps), 120 (96kbps), 155 (128kbps))
+  uint32_t mtu;
+  switch (bitrate) {
+      case 24000:
+        mtu = 30;
+        break;
+      case 27734:
+        mtu = 26;
+        break;
+      case 48000: {//HAP HQ
+        if (current_config.codec_specific_2 == 0)
+          mtu = 45;
+        else
+          mtu = 60;
+        break;
+      }
+      case 32000: {
+        if (current_config.codec_specific_2 == 0)
+          mtu = 30;
+        else
+          mtu = 40;
+        break;
+      }
+      case 64000: {
+        if (current_config.codec_specific_2 == 0)
+          mtu = 60;
+        else
+          mtu = 80;
+        break;
+      }
+      case 80000: {
+        if (current_config.codec_specific_2 == 0)
+          mtu = 75;
+        else
+          mtu = 100;
+        break;
+      }
+      case 95060:
+        mtu = 97;
+        break;
+      case 95550:
+        mtu = 130;
+        break;
+      case 96000: {
+        if (current_config.codec_specific_2 == 0)
+          mtu = 90;
+        else
+          mtu = 120;
+        break;
+      }
+      case 124800:
+        mtu = 117;
+        break;
+      case 124000:
+        mtu = 155;
+        break;
+      default:
+          mtu = 100;
+  }
+  BTIF_TRACE_DEBUG("[BapBroadcast]%s: mtu = %d",__func__,mtu);
+  return mtu;
+}
+
+uint16_t btif_bap_broadcast_get_framelength() {
+  uint16_t frame_duration;
+  switch (current_config.codec_specific_2) {
+      case 0:
+        frame_duration = 7500; //7.5msec
+        break;
+      case 1:
+        frame_duration = 10000; //10msec
+        break;
+      default:
+        frame_duration = 10000;
+  }
+  BTIF_TRACE_DEBUG("[BapBroadcast]%s: bitrate = %d",__func__,frame_duration);
+  return frame_duration;
+}
+
+uint32_t btif_bap_broadcast_get_bitrate() {
+  //based on bitrate set (100(80kbps), 120 (96kbps), 155 (128kbps))
+  uint32_t bitrate = 0;
+  switch (current_config.codec_specific_1) {
+      case 1000: //32kbps
+        if (current_config.codec_specific_2 == 0) {
+          bitrate = 27734;
+        } else {
+          bitrate = 24000;
+        }
+        break;
+      case 1001: //32kbps
+        bitrate = 32000;
+        break;
+      case 1002: //48kbps
+        bitrate = 48000;
+        break;
+      case 1003: //64kbps
+        bitrate = 64000;
+        break;
+      case 1004: //80kbps
+        bitrate = 80000;
+        break;
+      case 1005: //955.55kbps
+        if (current_config.codec_specific_2 == 0) {
+          bitrate = 95060;
+        } else {
+          bitrate = 95550;
+        }
+        break;
+      case 1006: //96kbps
+        bitrate = 96000;
+        break;
+      case 1007: //96kbps
+        if (current_config.codec_specific_2 == 0) {
+          bitrate = 124800;
+        } else {
+          bitrate = 124000;
+        }
+        break;
+      default:
+        bitrate = 80000;
+  }
+  BTIF_TRACE_DEBUG("[BapBroadcast]%s: bitrate = %d",__func__,bitrate);
+  return bitrate;
+}
+
+uint8_t btif_bap_broadcast_get_ch_count() {
+  if (current_config.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO) {
+    return BROADCAST_SPLIT_STEREO * mBisMultiplier;
+  } else if (current_config.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_MONO ||
+    current_config.channel_mode == BTBAP_CODEC_CHANNEL_MODE_JOINT_STEREO) {
+    return BROADCAST_MONO_JOINT * mBisMultiplier;
+  }
+  return BROADCAST_SPLIT_STEREO;//default split stereo
+}
+
+static uint16_t btif_bap_get_transport_latency() {
+  uint16_t latency;
+  if (mBigParams.max_transport_latency != 0) {
+    BTIF_TRACE_DEBUG("[BapBroadcast]%s: latency set by property: %d",
+                      __func__, mBigParams.max_transport_latency);
+    return mBigParams.max_transport_latency;
+  }
+  switch (current_config.codec_specific_2) {
+    case 0:
+      latency = 45;//45msec for 7.5msec frame duration
+      break;
+    case 1:
+    default:
+      latency = 61;//61msec for 10msec frame duration
+      break;
+  }
+  BTIF_TRACE_DEBUG("[BapBroadcast]%s: transport latency = %d",
+                        __func__, latency);
+  return latency;
+}
+
+bool btif_bap_broadcast_is_simulcast_enabled() {
+  char value[PROPERTY_VALUE_MAX] = {'\0'};
+  osi_property_get("persist.vendor.btstack.partial_simulcast",value,"false");
+  if (strcmp(value, "true") == 0) {
+    BTIF_TRACE_IMP("[BapBroadcast]%s:Partial simulcast enabled",__func__);
+    return true;
+  }
+  return false;
+}
+
+static void btif_bap_ba_copy_broadcast_id() {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s",__func__);
+  for(int j = 0; j < 3; j++) {
+    BTIF_TRACE_DEBUG("Broadcast_ID[%d] = %d",j, mBroadcastID[j]);
+  }
+  btif_report_broadcast_id();
+}
+
+static void btif_bap_ba_generate_broadcast_id() {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s",__func__);
+  btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) {
+   for(int a = 0; a < 3; a++) {
+     uint8_t val = rand[a];
+     BTIF_TRACE_DEBUG("val = %d", val);
+     mBroadcastID[a] = val;
+   }
+   btif_bap_ba_copy_broadcast_id();
+  }));
+}
+
+static void btif_bap_ba_generate_enc_key_local(int length) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s length = %d",__func__, length);
+  srand(time(NULL));
+  int i = 0;
+  uint8_t random_key[OCTET16_LEN] = {0};
+  while (i < length) {
+    uint8_t gen = (uint8_t)(rand() % 256);
+    uint8_t range = (gen % 75) + 48;//Alphanumeric range
+    if ((range > 57 && range < 65) ||
+      (range > 90 && range < 97)) {
+      //Ascii range 58 to 64
+      //:,;, <, =, >, ?, @]
+      //range 91 to 96
+      //[, \, ], ^, _, `
+      BTIF_TRACE_DEBUG("Generate key: Invalid character");
+      continue;
+    }
+    random_key[i] = range;
+    i++;
+  }
+
+  memset(encryption_key.data(), 0, OCTET16_LEN);
+  memcpy(encryption_key.data(),random_key, OCTET16_LEN);
+  reverseCode(random_key);
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s storing new excryption key of length %d",__func__, OCTET16_LEN);
+  if (btif_config_set_bin("Adapter", "BAP_BA_ENC_KEY", /*encryption_key.data()*/random_key, OCTET16_LEN)) {
+     BTIF_TRACE_DEBUG("%s: stored new key", __func__);
+     btif_config_flush();
+  } else {
+    BTIF_TRACE_DEBUG("%s: failed to store new key", __func__);;
+  }
+  if (notify_key_generated) {
+    notify_key_generated = false;
+    btif_report_encyption_key();
+  }
+}
+void init_local_capabilities() {
+  broadcast_codecs_capabilities.push_back(broadcast_local_capability);
+}
+
+static bt_status_t init_broadcast(
+    btbap_broadcast_callbacks_t* callbacks,
+    int max_broadcast, btav_a2dp_codec_config_t codec_config, int mode) {
+  if(max_broadcast > BTIF_BAP_BA_NUM_CB) {
+    BTIF_TRACE_WARNING("%s: App setting maximum allowable broadcast(%d) \
+              to more than limit(%d)",
+            __func__, max_broadcast, BTIF_BAP_BA_NUM_CB);
+    max_broadcast = BTIF_BAP_BA_NUM_CB;
+  }
+  return btif_bap_bms.Init(callbacks, max_broadcast, codec_config, mode);
+}
+static bt_status_t enable_broadcast(btav_a2dp_codec_config_t codec_config) {
+  BTIF_TRACE_IMP("[BapBroadcast]:%s", __func__);
+  current_config = codec_config;
+  print_config(current_config);
+  return btif_bap_bms.EnableBroadcast(codec_config);
+}
+static bt_status_t disable_broadcast(int adv_id) {
+  BTIF_TRACE_IMP("[BapBroadcast]:%s", __func__);
+  return btif_bap_bms.DisableBroadcast(adv_id);
+}
+static bt_status_t set_broadcast_active(bool setup, uint8_t adv_id) {
+  BTIF_TRACE_EVENT("[BapBroadcast]:%s", __func__);
+  return btif_bap_bms.SetBroadcastActive(setup, adv_id);
+}
+static bt_status_t config_codec(uint8_t adv_handle, btav_a2dp_codec_config_t codec_config) {
+  BTIF_TRACE_IMP("[BapBroadcast]:%s", __func__);
+  bool config_changed = false;
+  print_config(codec_config);
+  if (codec_config.sample_rate != current_config.sample_rate ||
+    codec_config.channel_mode != current_config.channel_mode ||
+    codec_config.codec_specific_1 != current_config.codec_specific_1 ||
+    codec_config.codec_specific_2 != current_config.codec_specific_2 ||
+    codec_config.codec_specific_4 > 0) {
+    config_changed = true;
+  }
+
+  if (config_changed) {
+    return btif_bap_bms.SetUserConfig(adv_handle, codec_config);
+  }
+  return BT_STATUS_SUCCESS;
+}
+static bt_status_t set_encryption(bool enabled, uint8_t enc_length) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s: length %d", __func__, enc_length);
+  mEncryptionEnabled = enabled;
+  /*if (!mEncryptionEnabled) {
+    btif_config_remove("Adapter", "BAP_BA_ENC_KEY");
+    return BT_STATUS_SUCCESS;
+  }*/
+  return btif_bap_bms.SetEncryption(enc_length);
+}
+
+static std::string get_encryption_key() {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s", __func__);
+  return std::string(reinterpret_cast<const char*>(encryption_key.data()), OCTET16_LEN);
+}
+
+static bt_status_t setup_audio_data_path(bool enable, uint8_t adv_id,
+                                         uint8_t big_handle, int num_bises, int *bis_handles) {
+  BTIF_TRACE_DEBUG("[BapBroadcast]:%s", __func__);
+  return BT_STATUS_SUCCESS;
+}
+static void cleanup_broadcast() {
+  BTIF_TRACE_ERROR("[BapBroadcast]:%s", __func__);
+  btif_bap_bms.Cleanup();
+}
+
+static const btbap_broadcast_interface_t bt_bap_broadcast_src_interface = {
+    sizeof(btbap_broadcast_interface_t),
+    init_broadcast,
+    enable_broadcast,
+    disable_broadcast,
+    set_broadcast_active,
+    config_codec,
+    setup_audio_data_path,
+    get_encryption_key,
+    set_encryption,
+    cleanup_broadcast,
+};
+
+/*******************************************************************************
+ *
+ * Function         btif_bap_broadcast_get_interface
+ *
+ * Description      Get the Bap broadcast callback interface
+ *
+ * Returns          btbap_broadcast_interface_t
+ *
+ ******************************************************************************/
+const btbap_broadcast_interface_t* btif_bap_broadcast_get_interface(void) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  return &bt_bap_broadcast_src_interface;
+}
+
+
diff --git a/le_audio/system/bt/btif/src/btif_bap_codec_utils.cc b/le_audio/system/bt/btif/src/btif_bap_codec_utils.cc
new file mode 100644
index 0000000..447c3f6
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_bap_codec_utils.cc
@@ -0,0 +1,395 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 "bta_closure_api.h"
+#include "bta_bap_uclient_api.h"
+#include "btif_common.h"
+#include "btif_storage.h"
+#include "osi/include/thread.h"
+
+// Capabilities to be stored on CodecConfig structure :
+// Supported_Sampling_Frequencies  ( codec config sampling rate )
+// Audio_Channel_Counts ( codec config channel mode )
+// Supported_Frame_Durations ( 1st byte of codec specific 1 ) 
+// Max_Supported_LC3_Frames_Per_SDU  ( 2nd byte of codec specific 1)
+// Preferred_Audio_Contexts ( 3&4 bytes of codec specific 1 )
+// Supported_Octets_Per_Codec_Frame ( first 4 bytes codec specific 2 )
+/* Vendor_Specific Metadata ( first 4 bytes of codec specific 3)
+ *    1st byte conveys LC3Q support,
+ *    2nd byte conveys LC3Q version
+ */
+
+// Configurations to be stored in CodecConfig structure :
+// Sampling_Frequency  ( codec config sampling rate )
+// Audio_Channel_Allocation ( codec config channel mode )
+// Frame_Duration  ( 5th bye of codec specific 1 ) 
+// LC3_Blocks_Per_SDU ( 6th byte of codec specific 1 )
+// Preferred_Audio_Contexts ( 7&8 bytes of codec specific 1 )
+// Octets_Per_Codec_Frame ( 2 bytes  )  ( 5&6th bytes of codec specific 2 )
+// LC3Q preferred (1 byte)  ( 7th byte of codec specific 2 )
+/* Vendor_Specific Metadata ( first 4 bytes of codec specific 3)
+ *    1st byte conveys LC3Q support,
+ *    2nd byte conveys LC3Q version
+ */
+
+#include <base/logging.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_pacs_client.h>
+
+using bluetooth::bap::pacs::CodecConfig;
+using bluetooth::bap::pacs::CodecChannelMode;
+
+constexpr uint8_t  CAPA_SUP_FRAME_DUR_INDEX           = 0x00; // CS1
+constexpr uint8_t  CAPA_MAX_SUP_LC3_FRAMES_INDEX      = 0x01; // CS1
+constexpr uint8_t  CAPA_PREF_AUDIO_CONT_INDEX         = 0x02; // CS1
+constexpr uint8_t  CAPA_SUP_OCTS_PER_FRAME_INDEX      = 0x00; // CS2
+
+constexpr uint8_t  CAPA_VENDOR_METADATA_LC3Q_PREF_INDEX = 0x00; // CS3
+constexpr uint8_t  CAPA_VENDOR_METADATA_LC3Q_VER_INDEX  = 0x01; // CS3
+
+constexpr uint8_t  CONFIG_FRAME_DUR_INDEX           = 0x04; // CS1
+constexpr uint8_t  CONFIG_LC3_BLOCKS_INDEX          = 0x05; // CS1
+constexpr uint8_t  CONFIG_PREF_AUDIO_CONT_INDEX     = 0x06; // CS1
+constexpr uint8_t  CONFIG_OCTS_PER_FRAME_INDEX      = 0x04; // CS2
+constexpr uint8_t  CONFIG_LC3Q_PREF_INDEX           = 0x06; // CS2
+constexpr uint8_t  CONFIG_VENDOR_METADATA_LC3Q_PREF_INDEX = 0x00; // CS3
+constexpr uint8_t  CONFIG_VENDOR_METADATA_LC3Q_VER_INDEX  = 0x01; // CS3
+
+// capabilities
+bool UpdateCapaSupFrameDurations(CodecConfig *config , uint8_t sup_frame) {
+  config->codec_specific_1 &= ~(0xFF << (CAPA_SUP_FRAME_DUR_INDEX * 8));
+  config->codec_specific_1 |= sup_frame << (CAPA_SUP_FRAME_DUR_INDEX * 8);
+  return true;
+}
+
+bool UpdateCapaMaxSupLc3Frames(CodecConfig *config,
+                                uint8_t max_sup_lc3_frames) {
+  config->codec_specific_1 &= ~(0xFF << (CAPA_MAX_SUP_LC3_FRAMES_INDEX * 8));
+  config->codec_specific_1 |= max_sup_lc3_frames <<
+                               (CAPA_MAX_SUP_LC3_FRAMES_INDEX * 8);
+  return true;
+}
+
+bool UpdateCapaPreferredContexts(CodecConfig *config, uint16_t contexts) {
+  config->codec_specific_1 &= ~(0xFFFF << (CAPA_PREF_AUDIO_CONT_INDEX * 8));
+  config->codec_specific_1 |= contexts << (CAPA_PREF_AUDIO_CONT_INDEX * 8);
+  return true;
+}
+
+bool UpdateCapaSupOctsPerFrame(CodecConfig *config,
+                              uint32_t octs_per_frame) {
+  config->codec_specific_2 &= ~(0xFFFFFFFF <<
+                          (CAPA_SUP_OCTS_PER_FRAME_INDEX * 8));
+  config->codec_specific_2 |= octs_per_frame <<
+                              (CAPA_SUP_OCTS_PER_FRAME_INDEX * 8);
+  return true;
+}
+
+bool UpdateCapaVendorMetaDataLc3QPref(CodecConfig *config, bool lc3q_pref) {
+  config->codec_specific_3 &= ~(0xFF << (CAPA_VENDOR_METADATA_LC3Q_PREF_INDEX * 8));
+  config->codec_specific_3 |= lc3q_pref << (CAPA_VENDOR_METADATA_LC3Q_PREF_INDEX * 8);
+  return true;
+}
+
+bool UpdateCapaVendorMetaDataLc3QVer(CodecConfig *config, uint8_t lc3q_ver) {
+  config->codec_specific_3 &= ~(0xFF << (CAPA_VENDOR_METADATA_LC3Q_VER_INDEX * 8));
+  config->codec_specific_3 |= lc3q_ver << (CAPA_VENDOR_METADATA_LC3Q_VER_INDEX * 8);
+  return true;
+}
+
+uint8_t GetCapaSupFrameDurations(CodecConfig *config) {
+  return (config->codec_specific_1 >> (8*CAPA_SUP_FRAME_DUR_INDEX)) & 0xff;
+}
+
+uint8_t GetCapaMaxSupLc3Frames(CodecConfig *config) {
+  if (((config->codec_specific_1 >>
+                (8*CAPA_MAX_SUP_LC3_FRAMES_INDEX)) & 0xff) == 0x0) {
+    uint8_t max_chnl_count = 0;
+    LOG(ERROR) << __func__
+               << ": Max Sup LC3 frames is 0, deriving based on chnl count";
+    if(static_cast<uint16_t> (config->channel_mode) &
+       static_cast<uint16_t> (CodecChannelMode::CODEC_CHANNEL_MODE_STEREO)) {
+      max_chnl_count = 2;
+    } else if(static_cast<uint16_t> (config->channel_mode) &
+           static_cast<uint16_t> (CodecChannelMode::CODEC_CHANNEL_MODE_MONO)) {
+      max_chnl_count = 1;
+    }
+    return max_chnl_count;
+  } else {
+    return (config->codec_specific_1 >>
+               (8*CAPA_MAX_SUP_LC3_FRAMES_INDEX)) & 0xff;
+  }
+}
+
+uint16_t GetCapaPreferredContexts(CodecConfig *config) {
+  return (config->codec_specific_1 >>
+                (8*CAPA_PREF_AUDIO_CONT_INDEX)) & 0xffff;
+}
+
+uint32_t GetCapaSupOctsPerFrame(CodecConfig *config) {
+  return (config->codec_specific_2 >>
+                (8*CAPA_SUP_OCTS_PER_FRAME_INDEX)) & 0xffffffff;
+}
+
+bool GetCapaVendorMetaDataLc3QPref(CodecConfig *config) {
+  if (((config->codec_specific_3 >>
+        (8*CAPA_VENDOR_METADATA_LC3Q_PREF_INDEX)) & 0xff) == 0x0) {
+    return false;
+  } else
+    return true;
+}
+
+uint8_t GetCapaVendorMetaDataLc3QVer(CodecConfig *config) {
+  return (config->codec_specific_3 >>
+                (8*CAPA_VENDOR_METADATA_LC3Q_VER_INDEX)) & 0xff;
+}
+
+// Configurations
+bool UpdateFrameDuration(CodecConfig *config , uint8_t frame_dur) {
+  uint64_t value = 0xFF;
+  config->codec_specific_1 &= ~(value << (CONFIG_FRAME_DUR_INDEX*8));
+  config->codec_specific_1 |=  static_cast<uint64_t>(frame_dur)  <<
+                               (CONFIG_FRAME_DUR_INDEX * 8);
+  return true;
+}
+
+bool UpdateLc3BlocksPerSdu(CodecConfig *config, uint8_t lc3_blocks_per_sdu) {
+  uint64_t value = 0xFF;
+  config->codec_specific_1 &= ~(value << (CONFIG_LC3_BLOCKS_INDEX * 8));
+  config->codec_specific_1 |=  static_cast<uint64_t>(lc3_blocks_per_sdu) <<
+                               (CONFIG_LC3_BLOCKS_INDEX * 8);
+  return true;
+}
+
+bool UpdatePreferredAudioContext(CodecConfig *config ,
+                                    uint16_t pref_audio_context) {
+  uint64_t value = 0xFFFF;
+  config->codec_specific_1 &= ~(value << (CONFIG_PREF_AUDIO_CONT_INDEX*8));
+  config->codec_specific_1 |=  static_cast<uint64_t>(pref_audio_context) <<
+                               (CONFIG_PREF_AUDIO_CONT_INDEX * 8);
+  return true;
+}
+
+bool UpdateOctsPerFrame(CodecConfig *config , uint16_t octs_per_frame) {
+  uint64_t value = 0xFFFF;
+  config->codec_specific_2 &= ~(value << (CONFIG_OCTS_PER_FRAME_INDEX * 8));
+  config->codec_specific_2 |=
+                    static_cast<uint64_t>(octs_per_frame) <<
+                    (CONFIG_OCTS_PER_FRAME_INDEX * 8);
+  return true;
+}
+
+bool UpdateLc3QPreference(CodecConfig *config , bool lc3q_pref) {
+  uint64_t value = 0xFF;
+  config->codec_specific_2 &= ~(value << (CONFIG_LC3Q_PREF_INDEX * 8));
+  config->codec_specific_2 |=
+                    static_cast<uint64_t>(lc3q_pref) <<
+                    (CONFIG_LC3Q_PREF_INDEX * 8);
+  LOG(WARNING) << __func__
+               << ": lc3q_pref cs2: " << loghex(config->codec_specific_2);
+  return true;
+}
+
+
+bool UpdateVendorMetaDataLc3QPref(CodecConfig *config, bool lc3q_pref) {
+  uint64_t value = 0xFF;
+  config->codec_specific_3 &= ~(value << (CONFIG_VENDOR_METADATA_LC3Q_PREF_INDEX * 8));
+  config->codec_specific_3 |= static_cast<uint64_t>(lc3q_pref) <<
+                                (CONFIG_VENDOR_METADATA_LC3Q_PREF_INDEX * 8);
+  return true;
+}
+
+bool UpdateVendorMetaDataLc3QVer(CodecConfig *config, uint8_t lc3q_ver) {
+  uint64_t value = 0xFF;
+  config->codec_specific_3 &= ~(value << (CONFIG_VENDOR_METADATA_LC3Q_VER_INDEX * 8));
+  config->codec_specific_3 |= static_cast<uint64_t>(lc3q_ver) <<
+                                  (CONFIG_VENDOR_METADATA_LC3Q_VER_INDEX * 8);
+  return true;
+}
+
+uint8_t GetFrameDuration(CodecConfig *config) {
+  return (config->codec_specific_1 >> (8*CONFIG_FRAME_DUR_INDEX)) & 0xff;
+}
+
+uint8_t GetLc3BlocksPerSdu(CodecConfig *config) {
+  return (config->codec_specific_1 >> (8*CONFIG_LC3_BLOCKS_INDEX)) & 0xff;
+}
+
+uint16_t GetPreferredAudioContext(CodecConfig *config) {
+  return (config->codec_specific_1 >>
+                    (8*CONFIG_PREF_AUDIO_CONT_INDEX)) & 0xffff;
+}
+
+uint16_t GetOctsPerFrame(CodecConfig *config) {
+  return (config->codec_specific_2 >> (8*CONFIG_OCTS_PER_FRAME_INDEX)) & 0xffff;
+}
+
+uint8_t GetLc3QPreference(CodecConfig *config) {
+  LOG(WARNING) << __func__ << ": lc3q_pref cs2: "
+      << loghex((config->codec_specific_2 >> (8*CONFIG_LC3Q_PREF_INDEX)) & 0xff);
+  return (config->codec_specific_2 >>
+                (8*CONFIG_LC3Q_PREF_INDEX)) & 0xff;
+}
+
+uint8_t GetVendorMetaDataLc3QPref(CodecConfig *config) {
+  return (config->codec_specific_3 >>
+                (8*CONFIG_VENDOR_METADATA_LC3Q_PREF_INDEX)) & 0xff;
+}
+
+uint8_t GetVendorMetaDataLc3QVer(CodecConfig *config) {
+  return (config->codec_specific_3 >>
+                (8*CONFIG_VENDOR_METADATA_LC3Q_VER_INDEX)) & 0xff;
+}
+
+bool IsCodecConfigEqual(CodecConfig *src_config, CodecConfig *dst_config) {
+  // first check if passed codec configs are configurations or
+  // capabilities using first byte of codec specific 1
+  if(src_config == nullptr || dst_config == nullptr) {
+    return false;
+  }
+
+  bool is_src_capability = src_config->codec_specific_1 & 0XFF;
+  bool is_dst_capability = dst_config->codec_specific_1 & 0XFF;
+
+  // check the codec type
+  if(src_config->codec_type != dst_config->codec_type) {
+    LOG(ERROR) << __func__ << ": No match for codec type ";
+    return false;
+  }
+
+  // check sample rate
+  if(src_config->sample_rate != dst_config->sample_rate) {
+    LOG(ERROR) << __func__ << ": No match for sample rate";
+    return false;
+  }
+
+  // check channel mode
+  if(!(static_cast<int>(src_config->channel_mode) &
+       static_cast<int>(dst_config->channel_mode))) {
+    LOG(ERROR) << __func__ << ": No match for channel mode ";
+    return false;
+  }
+
+  LOG(WARNING) << __func__
+               << ": is_src_capability: " << loghex(is_src_capability)
+               << ", is_dst_capability: " << loghex(is_dst_capability);
+
+  if(is_src_capability && is_dst_capability) {
+    if(src_config->codec_specific_1 != dst_config->codec_specific_1 ||
+       src_config->codec_specific_2 != dst_config->codec_specific_2 ||
+       src_config->codec_specific_3 != dst_config->codec_specific_3) {
+      LOG(WARNING) << __func__ << ": No match for CS params. ";
+      return false;
+    }
+  } else if (!is_src_capability && !is_dst_capability) {
+    LOG(INFO) << __func__ << ": Comparison for both configs ";
+    uint8_t src_frame_dur = GetFrameDuration(src_config);
+    uint8_t src_lc3_blocks_per_sdu = GetLc3BlocksPerSdu(src_config);
+    uint16_t src_octs_per_frame = GetOctsPerFrame(src_config);
+    uint8_t src_lc3q_pref = GetLc3QPreference(src_config);
+    uint16_t src_pref_audio_context = GetPreferredAudioContext(src_config);
+
+    uint8_t dst_frame_dur = GetFrameDuration(dst_config);
+    uint8_t dst_lc3_blocks_per_sdu = GetLc3BlocksPerSdu(dst_config);
+    uint16_t dst_octs_per_frame = GetOctsPerFrame(dst_config);
+    uint8_t dst_lc3q_pref = GetLc3QPreference(dst_config);
+    uint16_t dst_pref_audio_context = GetPreferredAudioContext(dst_config);
+
+    if(src_frame_dur != dst_frame_dur) {
+      LOG(ERROR) << __func__ << ": Frame Dur not match with existing config ";
+      return false;
+    }
+
+    if(src_lc3_blocks_per_sdu != dst_lc3_blocks_per_sdu) {
+      LOG(ERROR) << __func__ << ": Lc3 blocks not match with existing config ";
+      return false;
+    }
+
+    if(src_octs_per_frame != dst_octs_per_frame) {
+      LOG(ERROR) << __func__ << ": Octs per frame not match with existing config ";
+      return false;
+    }
+
+    if(src_lc3q_pref != dst_lc3q_pref) {
+      LOG(ERROR) << __func__ << ": Lc3Q pref not match with existing config ";
+      return false;
+    }
+
+    if (!(src_pref_audio_context & dst_pref_audio_context)) {
+      LOG(ERROR) << __func__ << ": pref_audio_context not match with existing config ";
+      return false;
+    }
+
+  } else if(is_src_capability || is_dst_capability) {
+    CodecConfig *capa_config = is_src_capability ? src_config:dst_config;
+    CodecConfig *oth_config = is_src_capability ? dst_config:src_config;
+
+    uint8_t capa_sup_frames = GetCapaSupFrameDurations(capa_config);
+    uint8_t capa_max_sup_lc3_frames = GetCapaMaxSupLc3Frames(capa_config);
+    uint16_t capa_min_sup_octs = GetCapaSupOctsPerFrame(capa_config) & 0xFFFF;
+    uint16_t capa_max_sup_octs = (GetCapaSupOctsPerFrame(capa_config)
+                             & 0xFFFF0000) >> 16;
+    bool capa_lc3q_pref = GetCapaVendorMetaDataLc3QPref(capa_config);
+    uint16_t capa_pref_audio_context = GetCapaPreferredContexts(capa_config);
+
+    uint8_t frame_dur = GetFrameDuration(oth_config);
+    uint8_t lc3_blocks_per_sdu = GetLc3BlocksPerSdu(oth_config);
+    uint16_t octs_per_frame = GetOctsPerFrame(oth_config);
+    uint8_t lc3q_pref = GetLc3QPreference(oth_config);
+    uint16_t dst_pref_audio_context = GetPreferredAudioContext(oth_config);
+
+    LOG(WARNING) << __func__
+                 << ": capa_sup_frames: " << loghex(capa_sup_frames)
+                 << ", frame_dur: " << loghex(frame_dur);
+
+    LOG(WARNING) << __func__
+                 << ": capa_lc3q_pref: " << capa_lc3q_pref
+                 << ", lc3q_pref: " << loghex(lc3q_pref);
+
+    LOG(WARNING) << __func__
+                 << ": capa_pref_audio_context: " << capa_pref_audio_context
+                 << ", dst_pref_audio_context: " << dst_pref_audio_context;
+
+    if(!(capa_sup_frames & (0x01 << frame_dur))) {
+      LOG(ERROR) << __func__ << ": No match for frame duration ";
+      return false;
+    }
+
+    if(capa_max_sup_lc3_frames && lc3_blocks_per_sdu) {
+      if(capa_max_sup_lc3_frames < lc3_blocks_per_sdu *
+                      static_cast<uint8_t> (oth_config->channel_mode)) {
+        LOG(ERROR) << __func__ << ": blocks per sdu exceeds the capacity ";
+        return false;
+      }
+    }
+
+    if( octs_per_frame < capa_min_sup_octs ||
+        octs_per_frame > capa_max_sup_octs) {
+      LOG(ERROR) << __func__ << ": octs per frame not in limits ";
+      return true;
+    }
+
+    if (!(capa_pref_audio_context & dst_pref_audio_context)) {
+      LOG(ERROR) << __func__ << ": No Match for Audio context";
+      return false;
+    }
+
+  }
+  return true;
+}
diff --git a/le_audio/system/bt/btif/src/btif_bap_config.cc b/le_audio/system/bt/btif/src/btif_bap_config.cc
new file mode 100644
index 0000000..6daeb6a
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_bap_config.cc
@@ -0,0 +1,860 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/******************************************************************************
+ *
+ *  Copyright (C) 2014 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_btif_bap_config"
+
+#include "btif_bap_config.h"
+#include <base/strings/string_split.h>
+
+#include <base/logging.h>
+#include <ctype.h>
+#include <openssl/rand.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <string>
+
+#include <mutex>
+
+#include "bt_types.h"
+#include "btcore/include/module.h"
+#include "btif_api.h"
+#include "btif_common.h"
+#include "btif_util.h"
+#include "osi/include/alarm.h"
+#include "osi/include/allocator.h"
+#include "osi/include/compat.h"
+#include "osi/include/config.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+#include "osi/include/properties.h"
+#include "btif_bap_codec_utils.h"
+
+#define BT_CONFIG_SOURCE_TAG_NUM 1010001
+
+#define INFO_SECTION "Info"
+#define FILE_TIMESTAMP "TimeCreated"
+#define FILE_SOURCE "FileSource"
+#define TIME_STRING_LENGTH sizeof("YYYY-MM-DD HH:MM:SS")
+#define INDEX_FREE      (0x00)
+#define INDEX_OCCUPIED  (0x01)
+#define MAX_INDEX       (255)
+#define MAX_INDEX_LEN   (0x04)
+#define MAX_SECTION_LEN  (255)
+
+using bluetooth::bap::pacs::CodecSampleRate;
+
+static const char* TIME_STRING_FORMAT = "%Y-%m-%d %H:%M:%S";
+
+// TODO(armansito): Find a better way than searching by a hardcoded path.
+#if defined(OS_GENERIC)
+static const char* CONFIG_FILE_PATH = "bap_config.conf";
+static const char* CONFIG_BACKUP_PATH = "bap_config.bak";
+#else   // !defined(OS_GENERIC)
+static const char* CONFIG_FILE_PATH = "/data/misc/bluedroid/bap_config.conf";
+static const char* CONFIG_BACKUP_PATH = "/data/misc/bluedroid/bap_config.bak";
+#endif  // defined(OS_GENERIC)
+static const period_ms_t CONFIG_SETTLE_PERIOD_MS = 3000;
+
+static void timer_config_save_cb(void* data);
+static void btif_bap_config_write(uint16_t event, char* p_param);
+static bool is_factory_reset(void);
+static void delete_config_files(void);
+static void btif_bap_config_remove_restricted(config_t* config);
+static config_t* btif_bap_config_open(const char* filename);
+
+static enum ConfigSource {
+  NOT_LOADED,
+  ORIGINAL,
+  BACKUP,
+  NEW_FILE,
+  RESET
+} btif_bap_config_source = NOT_LOADED;
+
+//static int btif_bap_config_devices_loaded = -1;
+static char btif_bap_config_time_created[TIME_STRING_LENGTH];
+
+static config_t* config;
+static std::recursive_mutex config_lock;  // protects operations on |config|.
+static alarm_t* config_timer;
+
+#define BAP_DIRECTION_KEY              "Direction"
+#define BAP_CODEC_TYPE_KEY             "CodecType"
+
+#define BAP_RECORD_TYPE_KEY            "RecordType"
+#define BAP_RECORD_TYPE_CAPA            "Capability"
+#define BAP_RECORD_TYPE_CONF           "Configuration"
+
+#define BAP_SAMP_FREQS_KEY             "SamplingRate"
+#define BAP_CONTEXT_TYPE_KEY           "ContextType"
+
+#define BAP_SUPP_FRM_DURATIONS_KEY     "SupFrameDurations"
+#define BAP_SUP_MIN_OCTS_PER_FRAME_KEY "SupMinOctsPerFrame"
+#define BAP_SUP_MAX_OCTS_PER_FRAME_KEY "SupMaxOctsPerFrame"
+#define BAP_MAX_SUP_CODEC_FRAMES_PER_SDU "SupMaxFramesPerSDU"
+#define BAP_LC3Q_SUP_KEY               "LC3QSupport"
+#define BAP_LC3Q_VER_KEY               "LC3QVersion"
+
+#define BAP_CONF_FRAME_DUR_KEY         "ConfiguredFrameDur"
+#define BAP_CONF_OCTS_PER_FRAME_KEY    "ConfiguredOctsPerFrame"
+#define BAP_LC3_FRAMES_PER_SDU_KEY     "Lc3FramesPerSDU"
+#define BAP_CHNL_ALLOCATION_KEY        "ChannelAllocation"
+
+#define BAP_SRC_LOCATIONS_KEY          "SrcLocation"
+#define BAP_SINK_LOCATIONS_KEY         "SinkLocation"
+#define BAP_SUP_AUDIO_CONTEXTS_KEY     "SupAudioContexts"
+
+// Module lifecycle functions
+static future_t* init(void) {
+  std::unique_lock<std::recursive_mutex> lock(config_lock);
+
+  if (is_factory_reset()) delete_config_files();
+
+  std::string file_source;
+
+  config = btif_bap_config_open(CONFIG_FILE_PATH);
+  btif_bap_config_source = ORIGINAL;
+  if (!config) {
+    LOG_WARN(LOG_TAG, "%s unable to load config file: %s; using backup.",
+             __func__, CONFIG_FILE_PATH);
+    config = btif_bap_config_open(CONFIG_BACKUP_PATH);
+    btif_bap_config_source = BACKUP;
+    file_source = "Backup";
+  }
+
+  if (!config) {
+    LOG_ERROR(LOG_TAG,
+              "%s unable to transcode legacy file; creating empty config.",
+              __func__);
+    config = config_new_empty();
+    btif_bap_config_source = NEW_FILE;
+    file_source = "Empty";
+  }
+
+  if (!config) {
+    LOG_ERROR(LOG_TAG, "%s unable to allocate a config object.", __func__);
+    goto error;
+  }
+
+  if (!file_source.empty())
+    config_set_string(config, INFO_SECTION, FILE_SOURCE, file_source.c_str());
+
+  // Cleanup temporary pairings if we have left guest mode
+  if (!is_restricted_mode()) btif_bap_config_remove_restricted(config);
+
+  // Read or set config file creation timestamp
+  const char* time_str;
+  time_str = config_get_string(config, INFO_SECTION, FILE_TIMESTAMP, NULL);
+  if (time_str != NULL) {
+    strlcpy(btif_bap_config_time_created, time_str, TIME_STRING_LENGTH);
+  } else {
+    time_t current_time = time(NULL);
+    struct tm* time_created = localtime(&current_time);
+    if (time_created) {
+      if (strftime(btif_bap_config_time_created, TIME_STRING_LENGTH,
+                   TIME_STRING_FORMAT, time_created)) {
+        config_set_string(config, INFO_SECTION, FILE_TIMESTAMP,
+                          btif_bap_config_time_created);
+      }
+    }
+  }
+  // TODO(sharvil): use a non-wake alarm for this once we have
+  // API support for it. There's no need to wake the system to
+  // write back to disk.
+  config_timer = alarm_new("btif_bap.config");
+  if (!config_timer) {
+    LOG_ERROR(LOG_TAG, "%s unable to create alarm.", __func__);
+    goto error;
+  }
+
+  LOG_EVENT_INT(BT_CONFIG_SOURCE_TAG_NUM, btif_bap_config_source);
+
+  return future_new_immediate(FUTURE_SUCCESS);
+
+error:
+  alarm_free(config_timer);
+  if (config != NULL)
+    config_free(config);
+  config_timer = NULL;
+  config = NULL;
+  btif_bap_config_source = NOT_LOADED;
+  return future_new_immediate(FUTURE_FAIL);
+}
+
+static config_t* btif_bap_config_open(const char* filename) {
+  config_t* config = config_new(filename);
+  if (!config) return NULL;
+
+  return config;
+}
+
+static void btif_bap_config_save(void) {
+  CHECK(config != NULL);
+  CHECK(config_timer != NULL);
+
+  if (config_timer == NULL) {
+    LOG(WARNING) << __func__ << "config_timer is null";
+    return;
+  }
+  alarm_set(config_timer, CONFIG_SETTLE_PERIOD_MS, timer_config_save_cb, NULL);
+}
+
+static void btif_bap_config_flush(void) {
+  CHECK(config != NULL);
+  CHECK(config_timer != NULL);
+
+  alarm_cancel(config_timer);
+  btif_bap_config_write(0, NULL);
+}
+
+bool btif_bap_config_clear(void) {
+  CHECK(config != NULL);
+  CHECK(config_timer != NULL);
+
+  alarm_cancel(config_timer);
+
+  std::unique_lock<std::recursive_mutex> lock(config_lock);
+  if (config != NULL)
+    config_free(config);
+
+  config = config_new_empty();
+  if (config == NULL) return false;
+
+  bool ret = config_save(config, CONFIG_FILE_PATH);
+  btif_bap_config_source = RESET;
+  return ret;
+}
+
+static future_t* shut_down(void) {
+  btif_bap_config_flush();
+  return future_new_immediate(FUTURE_SUCCESS);
+}
+
+static future_t* clean_up(void) {
+  btif_bap_config_flush();
+
+  alarm_free(config_timer);
+  config_timer = NULL;
+
+  std::unique_lock<std::recursive_mutex> lock(config_lock);
+  config_free(config);
+  config = NULL;
+  return future_new_immediate(FUTURE_SUCCESS);
+}
+
+EXPORT_SYMBOL module_t btif_bap_config_module = {.name = BTIF_BAP_CONFIG_MODULE,
+                                             .init = init,
+                                             .start_up = NULL,
+                                             .shut_down = shut_down,
+                                             .clean_up = clean_up};
+
+static void timer_config_save_cb(UNUSED_ATTR void* data) {
+  // Moving file I/O to btif context instead of timer callback because
+  // it usually takes a lot of time to be completed, introducing
+  // delays during A2DP playback causing blips or choppiness.
+  btif_transfer_context(btif_bap_config_write, 0, NULL, 0, NULL);
+}
+
+static void btif_bap_config_write(UNUSED_ATTR uint16_t event,
+                              UNUSED_ATTR char* p_param) {
+  CHECK(config != NULL);
+  CHECK(config_timer != NULL);
+
+  std::unique_lock<std::recursive_mutex> lock(config_lock);
+  rename(CONFIG_FILE_PATH, CONFIG_BACKUP_PATH);
+  if (config == NULL) {
+    LOG(WARNING) << __func__ << "config is null";
+    return;
+  }
+  config_t* config_paired = config_new_clone(config);
+
+  if (config_paired != NULL) {
+    //btif_bap_config_remove_unpaired(config_paired);
+    config_save(config_paired, CONFIG_FILE_PATH);
+    config_free(config_paired);
+  }
+}
+
+static void btif_bap_config_remove_restricted(config_t* config) {
+  CHECK(config != NULL);
+
+  if (config == NULL) {
+    LOG(WARNING) << __func__ << "config is null";
+    return;
+  }
+  const config_section_node_t* snode = config_section_begin(config);
+  for(; snode != config_section_end(config);
+             snode = config_section_next(snode)) {
+    const char* section = config_section_name(snode);
+    // first check the address
+    if (config_has_key(config, section, "Restricted")) {
+      config_remove_section(config, section);
+    }
+  }
+}
+
+static bool is_factory_reset(void) {
+  char factory_reset[PROPERTY_VALUE_MAX] = {0};
+  osi_property_get("persist.bluetooth.factoryreset", factory_reset, "false");
+  return strncmp(factory_reset, "true", 4) == 0;
+}
+
+static void delete_config_files(void) {
+  remove(CONFIG_FILE_PATH);
+  remove(CONFIG_BACKUP_PATH);
+  osi_property_set("persist.bluetooth.factoryreset", "false");
+}
+
+static bool btif_bap_get_section_index(const std::string &section,
+                                       uint16_t *index) {
+  char *temp = nullptr;
+  if (section.length() != 20) return false;
+
+  std::vector<std::string> byte_tokens =
+              base::SplitString(section, ":", base::TRIM_WHITESPACE,
+              base::SPLIT_WANT_ALL);
+
+  LOG(WARNING) << __func__ << ": LC# codec ";
+  if (byte_tokens.size() != 7) return false;
+
+  // get the last nibble
+  const auto& token = byte_tokens[6];
+
+  if (token.length() != 2) return false;
+
+  *index = strtol(token.c_str(), &temp, 16);
+
+  if (*temp != '\0') return false;
+
+  return true;
+}
+
+static bool btif_bap_get_free_section_id(const RawAddress& bd_addr,
+                                         char *section) {
+  uint16_t i = 0;
+  uint8_t index_status[MAX_INDEX] = {0};
+  std::string addrstr = bd_addr.ToString();
+  const char* bdstr = addrstr.c_str();
+
+  // reserve the first index for sink, src, locations
+  index_status[0] = INDEX_OCCUPIED;
+
+  const config_section_node_t* snode = config_section_begin(config);
+  for(; snode != config_section_end(config);
+           snode = config_section_next(snode)) {
+    const char* section = config_section_name(snode);
+    uint16_t index;
+
+    // first check the address
+    if(!strcasestr(section, bdstr)) {
+      continue;
+    }
+
+    if(btif_bap_get_section_index(section, &index)) {
+      index_status[index] = INDEX_OCCUPIED;
+    }
+  }
+
+  // find the unused index
+  for(i = 0; i < MAX_INDEX; i++) {
+    if(index_status[i] == INDEX_FREE) break;
+  }
+
+  if(i != MAX_INDEX) {
+    char index_str[MAX_INDEX_LEN];
+    // form the section entry ( bd address plus index)
+    snprintf(index_str, sizeof(index_str), ":%02x", i);
+    strlcpy(section, bdstr, MAX_SECTION_LEN);
+    strlcat(section, index_str, MAX_SECTION_LEN);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+static bool btif_bap_find_sections(const RawAddress& bd_addr,
+                            btif_bap_record_type_t rec_type,
+                            uint16_t context_type,
+                            CodecDirection direction,
+                            CodecConfig *record,
+                            std::vector<char *> *sections) {
+  std::string addrstr = bd_addr.ToString();
+  const char* bdstr = addrstr.c_str();
+  const config_section_node_t* snode = config_section_begin(config);
+  for(; snode != config_section_end(config);
+             snode = config_section_next(snode)) {
+    const char *section = config_section_name(snode);
+    // first check the address
+    if(!strcasestr(section, bdstr)) {
+      continue;
+    }
+
+    // next check the record type
+    const char* value_str = config_get_string(config, section,
+                                          BAP_RECORD_TYPE_KEY, NULL);
+    if(value_str == nullptr ||
+      ((rec_type == REC_TYPE_CAPABILITY &&
+       strcasecmp(value_str, BAP_RECORD_TYPE_CAPA)) ||
+       (rec_type == REC_TYPE_CONFIGURATION &&
+       strcasecmp(value_str, BAP_RECORD_TYPE_CONF)))) {
+      continue;
+    }
+
+    // next check the record type
+    uint16_t context = config_get_uint16(config, section,
+                                         BAP_CONTEXT_TYPE_KEY, 0XFFFF);
+    LOG(WARNING) << __func__ << ": context " << context
+                             << ": context_type " << context_type;
+    if(context != context_type) {
+      continue;
+    }
+
+    // next check the direction
+    value_str = config_get_string(config, section,
+                                          BAP_DIRECTION_KEY, NULL);
+    if(value_str == nullptr ||
+      ((direction == CodecDirection::CODEC_DIR_SRC &&
+        strcasecmp(value_str, "SRC")) ||
+       (direction == CodecDirection::CODEC_DIR_SINK &&
+        strcasecmp(value_str, "SINK")))) {
+      continue;
+    }
+
+    if(record == nullptr) {
+      sections->push_back((char*) section);
+    } else {
+      // next check codec type
+      value_str = config_get_string(config, section,
+                                    BAP_CODEC_TYPE_KEY, NULL);
+
+      if(value_str == nullptr ||
+        (record->codec_type == CodecIndex::CODEC_INDEX_SOURCE_LC3 &&
+         strcasecmp(value_str, "LC3"))) {
+        continue;
+      }
+
+      // next check the freqency
+      uint16_t value = config_get_uint16(config, section,
+                                         BAP_SAMP_FREQS_KEY, 0);
+      if(value == static_cast<uint16_t> (record->sample_rate)) {
+        sections->push_back((char*) section);
+      }
+    }
+  }
+
+  if(sections->size()) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+static bool btif_bap_update_LC3_codec_info(char *section, CodecConfig *record,
+                                           btif_bap_record_type_t rec_type) {
+  if(section == nullptr || record == nullptr) {
+    return false;
+  }
+
+  if(rec_type == REC_TYPE_CAPABILITY) {
+
+    config_set_string(config, section, BAP_RECORD_TYPE_KEY,
+                      BAP_RECORD_TYPE_CAPA);
+
+    if(record->codec_type == CodecIndex::CODEC_INDEX_SOURCE_LC3) {
+      config_set_string(config, section, BAP_CODEC_TYPE_KEY, "LC3");
+    }
+    // update freqs
+    config_set_uint16(config, section, BAP_SAMP_FREQS_KEY ,
+                     static_cast<uint16_t>(record->sample_rate));
+
+    // update chnl count
+    config_set_uint16(config, section, BAP_CHNL_ALLOCATION_KEY,
+                   static_cast<uint16_t> (record->channel_mode));
+
+    // update supp frames
+    config_set_uint16(config, section, BAP_SUPP_FRM_DURATIONS_KEY ,
+        static_cast<uint16_t> (GetCapaSupFrameDurations(record)));
+
+    // update chnl supp min octs per frame
+    config_set_uint16(config, section, BAP_SUP_MIN_OCTS_PER_FRAME_KEY ,
+        static_cast<uint16_t> (GetCapaSupOctsPerFrame(record) &
+                               0xFFFF));
+
+    // update chnl supp max octs per frame
+    config_set_uint16(config, section, BAP_SUP_MAX_OCTS_PER_FRAME_KEY,
+        static_cast<uint16_t> ((GetCapaSupOctsPerFrame(record) &
+                                0xFFFF0000) >> 16));
+
+    // update max supp codec frames per sdu
+    config_set_uint16(config, section, BAP_MAX_SUP_CODEC_FRAMES_PER_SDU,
+        static_cast<uint16_t> (GetCapaMaxSupLc3Frames(record)));
+
+    // update LC3Q support
+    if (GetCapaVendorMetaDataLc3QPref(record)) {
+      config_set_string(config, section, BAP_LC3Q_SUP_KEY, "true");
+    } else {
+      config_set_string(config, section, BAP_LC3Q_SUP_KEY, "false");
+    }
+
+    // update LC3Q Version
+    config_set_uint16(config, section, BAP_LC3Q_VER_KEY,
+        static_cast<uint16_t> (GetCapaVendorMetaDataLc3QVer(record)));
+  } else {
+
+    config_set_string(config, section, BAP_RECORD_TYPE_KEY,
+                                      BAP_RECORD_TYPE_CONF);
+
+    if(record->codec_type == CodecIndex::CODEC_INDEX_SOURCE_LC3) {
+      config_set_string(config, section, BAP_CODEC_TYPE_KEY, "LC3");
+    }
+
+    // update freqs
+    config_set_uint16(config, section, BAP_SAMP_FREQS_KEY ,
+                     static_cast<uint16_t>(record->sample_rate));
+
+    // update chnl count
+    config_set_uint16(config, section, BAP_CHNL_ALLOCATION_KEY,
+                   static_cast<uint16_t> (record->channel_mode));
+
+    // update configured frame duration
+    config_set_uint16(config, section, BAP_CONF_FRAME_DUR_KEY,
+        static_cast<uint16_t> (GetFrameDuration(record)));
+
+    // update configured octs per frame
+    config_set_uint16(config, section, BAP_CONF_OCTS_PER_FRAME_KEY,
+        static_cast<uint16_t> (GetOctsPerFrame(record)));
+
+    // update LC3 frames per SDU
+    config_set_uint16(config, section, BAP_LC3_FRAMES_PER_SDU_KEY,
+      static_cast<uint16_t> (GetLc3BlocksPerSdu(record)));
+  }
+
+  if (is_restricted_mode()) {
+    LOG(WARNING) << __func__ << ": records will be removed if unrestricted";
+    config_set_uint16(config, section, "Restricted", 1);
+  }
+
+  return true;
+}
+
+bool btif_bap_add_record(const RawAddress& bd_addr,
+                         btif_bap_record_type_t rec_type,
+                         uint16_t context_type,
+                         CodecDirection direction,
+                         CodecConfig *record) {
+  // first check if same record already exists
+  std::unique_lock<std::recursive_mutex> lock(config_lock);
+  std::vector<char *> sections;
+
+  if(btif_bap_find_sections(bd_addr, rec_type, context_type,
+                            direction, record, &sections)) {
+    for (auto it = sections.begin();
+                         it != sections.end(); it++) {
+      btif_bap_update_LC3_codec_info((*it), record , rec_type);
+    }
+  } else {
+    LOG(WARNING) << __func__ << ": section not found";
+    char section[MAX_SECTION_LEN];
+    btif_bap_get_free_section_id(bd_addr, section);
+
+    config_set_uint16(config, section, BAP_CONTEXT_TYPE_KEY, context_type);
+
+    if(direction == CodecDirection::CODEC_DIR_SRC) {
+      config_set_string(config, section, BAP_DIRECTION_KEY, "SRC");
+    } else {
+      config_set_string(config, section, BAP_DIRECTION_KEY, "SINK");
+    }
+    btif_bap_update_LC3_codec_info(section, record , rec_type);
+  }
+  btif_bap_config_save();
+  return true;
+}
+
+bool btif_bap_remove_record(const RawAddress& bd_addr,
+                            btif_bap_record_type_t rec_type,
+                            uint16_t context_type,
+                            CodecDirection direction,
+                            CodecConfig *record) {
+  // first check if same record exists
+  // if exists remove the record by complete section
+  std::unique_lock<std::recursive_mutex> lock(config_lock);
+  bool record_removed = false;
+  std::vector<char *> sections;
+
+  if(btif_bap_find_sections(bd_addr, rec_type, context_type,
+                            direction, record, &sections)) {
+    for (auto it = sections.begin();
+                         it != sections.end(); it++) {
+      config_remove_section(config, (*it));
+    }
+    record_removed = true;
+    btif_bap_config_flush();
+  }
+  return record_removed;
+}
+
+bool btif_bap_remove_record_by_context(const RawAddress& bd_addr,
+                                       btif_bap_record_type_t rec_type,
+                                       uint16_t context_type,
+                                       CodecDirection direction) {
+  // first check if same record exists
+  // if exists remove the record by complete section
+  std::unique_lock<std::recursive_mutex> lock(config_lock);
+  bool record_removed = false;
+  std::vector<char *> sections;
+
+  if(btif_bap_find_sections(bd_addr, rec_type, context_type,
+                            direction, nullptr, &sections)) {
+    for (auto it = sections.begin();
+                         it != sections.end(); it++) {
+      config_remove_section(config, (*it));
+    }
+    record_removed = true;
+    btif_bap_config_flush();
+  }
+  return record_removed;
+}
+
+bool btif_bap_remove_all_records(const RawAddress& bd_addr) {
+  // loop through the file if any record is found delete it
+  std::unique_lock<std::recursive_mutex> lock(config_lock);
+  std::string addrstr = bd_addr.ToString();
+  const char* bdstr = addrstr.c_str();
+  bool record_removed = false;
+  const config_section_node_t* snode = config_section_begin(config);
+  for(; snode != config_section_end(config);
+             snode = config_section_next(snode)) {
+    const char* section = config_section_name(snode);
+    // first check the address
+    if(strcasestr(section, bdstr)) {
+      record_removed = true;
+      config_remove_section(config, section);
+    }
+  }
+  btif_bap_config_flush();
+  return record_removed;
+}
+
+bool btif_bap_get_records(const RawAddress& bd_addr,
+                          btif_bap_record_type_t rec_type,
+                          uint16_t context_type,
+                          CodecDirection direction,
+                          std::vector<CodecConfig> *pac_records) {
+  std::unique_lock<std::recursive_mutex> lock(config_lock);
+  std::vector<char *> sections;
+
+  if(btif_bap_find_sections(bd_addr, rec_type, context_type,
+                            direction, nullptr, &sections)) {
+    for (auto it = sections.begin();
+                         it != sections.end(); it++) {
+      CodecConfig record;
+      memset(&record, 0, sizeof(record));
+
+      if(config_has_key(config, (*it), BAP_SAMP_FREQS_KEY)) {
+        record.sample_rate = static_cast<CodecSampleRate>
+                                (config_get_uint16(config, (*it),
+                                              BAP_SAMP_FREQS_KEY,
+                                              0x00));
+      }
+
+      if (config_has_key(config, (*it), BAP_LC3Q_SUP_KEY)) {
+        bool lc3q_sup = false;
+        const char* is_lc3q_sup = config_get_string(config, (*it),
+                                                BAP_LC3Q_SUP_KEY,
+                                                "");
+        if(!strcmp(is_lc3q_sup, "true")) {
+           lc3q_sup = true;
+        }
+        LOG(WARNING) << __func__ << ": lc3q_sup: " << lc3q_sup;
+        if (lc3q_sup) {
+          UpdateCapaVendorMetaDataLc3QPref(&record, lc3q_sup);
+        }
+      }
+
+      if(config_has_key(config, (*it), BAP_LC3Q_VER_KEY)) {
+        uint16_t lc3q_ver = config_get_uint16(config, (*it),
+                                                BAP_LC3Q_VER_KEY,
+                                                0x00);
+        LOG(WARNING) << __func__ << ": lc3q_ver: " << lc3q_ver;
+        UpdateCapaVendorMetaDataLc3QVer(&record, lc3q_ver);
+      }
+
+      record.codec_type = CodecIndex::CODEC_INDEX_SOURCE_LC3;
+      if(rec_type == REC_TYPE_CAPABILITY) {
+
+        if(config_has_key(config, (*it), BAP_SUPP_FRM_DURATIONS_KEY)) {
+          uint16_t supp_frames = config_get_uint16(config, (*it),
+                                                BAP_SUPP_FRM_DURATIONS_KEY,
+                                                0x00);
+          UpdateCapaSupFrameDurations(&record, supp_frames);
+        }
+
+        // update chnl supp octs per frame
+        if(config_has_key(config, (*it), BAP_SUP_MIN_OCTS_PER_FRAME_KEY) &&
+           config_has_key(config, (*it), BAP_SUP_MAX_OCTS_PER_FRAME_KEY)) {
+          uint16_t sup_min_octs = config_get_uint16(config, (*it),
+                                           BAP_SUP_MIN_OCTS_PER_FRAME_KEY,
+                                           0x00);
+          uint16_t sup_max_octs =  config_get_uint16(config, (*it),
+                                           BAP_SUP_MAX_OCTS_PER_FRAME_KEY,
+                                           0x00);
+          UpdateCapaSupOctsPerFrame(&record, sup_min_octs | sup_max_octs << 16);
+        }
+
+        // update max supp codec frames per sdu
+        if(config_has_key(config, (*it), BAP_MAX_SUP_CODEC_FRAMES_PER_SDU)) {
+          uint16_t max_sup_codec_frames_per_sdu = config_get_uint16(config, (*it),
+                                           BAP_MAX_SUP_CODEC_FRAMES_PER_SDU,
+                                           0x00);
+          UpdateCapaMaxSupLc3Frames(&record, max_sup_codec_frames_per_sdu);
+        }
+
+        // update preferred context type.
+        if(config_has_key(config, (*it), BAP_CONTEXT_TYPE_KEY)) {
+          uint16_t context_type = config_get_uint16(config, (*it),
+                                           BAP_CONTEXT_TYPE_KEY,
+                                           0x00);
+          UpdateCapaPreferredContexts(&record, context_type);
+        }
+      } else {
+
+        if(config_has_key(config, (*it), BAP_CONF_FRAME_DUR_KEY)) {
+          uint16_t conf_frames = config_get_uint16(config, (*it),
+                                                BAP_CONF_FRAME_DUR_KEY,
+                                                0x00);
+          UpdateFrameDuration(&record, conf_frames);
+        }
+
+        if(config_has_key(config, (*it), BAP_CONF_OCTS_PER_FRAME_KEY)) {
+          uint16_t conf_octs_per_frame = config_get_uint16(config, (*it),
+                                                BAP_CONF_OCTS_PER_FRAME_KEY,
+                                                0x00);
+          UpdateOctsPerFrame(&record, conf_octs_per_frame);
+        }
+
+        if(config_has_key(config, (*it), BAP_LC3_FRAMES_PER_SDU_KEY)) {
+          uint16_t lc3_frms_per_sdu = config_get_uint16(config, (*it),
+                                                BAP_LC3_FRAMES_PER_SDU_KEY,
+                                                0x00);
+          UpdateLc3BlocksPerSdu(&record, lc3_frms_per_sdu);
+        }
+      }
+      pac_records->push_back(record);
+    }
+  }
+
+  if(pac_records->size()) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool btif_bap_add_audio_loc(const RawAddress& bd_addr,
+                             CodecDirection direction, uint32_t audio_loc) {
+  // first check if same already exists
+  // if exists update the same entry
+  // audio location will always be stored @ 0th index
+  // form the section entry ( bd address plus index)
+  std::string addrstr = bd_addr.ToString();
+  const char* bdstr = addrstr.c_str();
+  char section[MAX_SECTION_LEN];
+  char index[MAX_INDEX_LEN];
+  snprintf(index, sizeof(index), ":%02x", 0);
+  strlcpy(section, bdstr, sizeof(section));
+  strlcat(section, index, sizeof(section));
+  if(direction == CodecDirection::CODEC_DIR_SRC) {
+    config_set_int(config, section, BAP_SRC_LOCATIONS_KEY, audio_loc);
+  } else {
+    config_set_int(config, section, BAP_SINK_LOCATIONS_KEY, audio_loc);
+  }
+  btif_bap_config_save();
+  return true;
+}
+
+bool btif_bap_rem_audio_loc(const RawAddress& bd_addr,
+                             CodecDirection direction) {
+  // first check if same record already exists
+  // if exists remove the record by complete section
+  std::string addrstr = bd_addr.ToString();
+  const char* bdstr = addrstr.c_str();
+  char section[MAX_SECTION_LEN];
+  char index[MAX_INDEX_LEN];
+  snprintf(index, sizeof(index), ":%02x", 0);
+  strlcpy(section, bdstr, sizeof(section));
+  strlcat(section, index, sizeof(section));
+  if(direction == CodecDirection::CODEC_DIR_SRC) {
+    config_remove_key(config, section, "BAP_SRC_LOCATIONS_KEY");
+  } else {
+    config_remove_key(config, section, "BAP_SINK_LOCATIONS_KEY");
+  }
+  btif_bap_config_flush();
+  return true;
+}
+
+bool btif_bap_add_supp_contexts(const RawAddress& bd_addr,
+                                  uint32_t supp_contexts) {
+  // first check if same already exists
+  // if exists update the same entry
+  // supp contexts will always be stored @ 0th index
+  // form the section entry ( bd address plus index)
+  std::string addrstr = bd_addr.ToString();
+  const char* bdstr = addrstr.c_str();
+  char section[MAX_SECTION_LEN];
+  char index[MAX_INDEX_LEN];
+  snprintf(index, sizeof(index), ":%02x", 0);
+  strlcpy(section, bdstr, sizeof(section));
+  strlcat(section, index, sizeof(section));
+  LOG(WARNING) << __func__ << " supp_contexts "  << supp_contexts;
+  config_set_uint64(config, section,
+                    BAP_SUP_AUDIO_CONTEXTS_KEY, supp_contexts);
+  btif_bap_config_save();
+  return true;
+}
+
+bool btif_bap_get_supp_contexts(const RawAddress& bd_addr,
+                                 uint32_t *supp_contexts) {
+  std::string addrstr = bd_addr.ToString();
+  const char* bdstr = addrstr.c_str();
+  char section[MAX_SECTION_LEN];
+  char index[MAX_INDEX_LEN];
+  snprintf(index, sizeof(index), ":%02x", 0);
+  strlcpy(section, bdstr, sizeof(section));
+  strlcat(section, index, sizeof(section));
+  *supp_contexts = config_get_uint64(config, section,
+                                     BAP_SUP_AUDIO_CONTEXTS_KEY, 0);
+  return true;
+}
+
+bool btif_bap_rem_supp_contexts(const RawAddress& bd_addr) {
+  std::string addrstr = bd_addr.ToString();
+  const char* bdstr = addrstr.c_str();
+  char section[MAX_SECTION_LEN];
+  char index[MAX_INDEX_LEN];
+  snprintf(index, sizeof(index), ":%02x", 0);
+  strlcpy(section, bdstr, sizeof(section));
+  strlcat(section, index, sizeof(section));
+  config_remove_key(config, section, BAP_SUP_AUDIO_CONTEXTS_KEY);
+  btif_bap_config_flush();
+  return true;
+}
diff --git a/le_audio/system/bt/btif/src/btif_bap_uclient.cc b/le_audio/system/bt/btif/src/btif_bap_uclient.cc
new file mode 100644
index 0000000..7a049a5
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_bap_uclient.cc
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#include "bta_closure_api.h"
+#include "bta_bap_uclient_api.h"
+#include "btif_common.h"
+#include "btif_storage.h"
+#include "osi/include/thread.h"
+#include "btif_bap_codec_utils.h"
+
+#include "osi/include/properties.h"
+
+extern void do_in_bta_thread(const base::Location& from_here,
+                             const base::Closure& task);
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_pacs_client.h>
+#include <hardware/bt_bap_uclient.h>
+#include "btif_api.h"
+
+using base::Bind;
+using base::Unretained;
+using bluetooth::bap::ucast::UcastClient;
+using bluetooth::bap::pacs::CodecConfig;
+using bluetooth::bap::pacs::ConnectionState;
+using bluetooth::bap::ucast::UcastClientCallbacks;
+using bluetooth::bap::ucast::UcastClientInterface;
+using bluetooth::bap::ucast::StreamConnect;
+using bluetooth::bap::ucast::StreamType;
+using bluetooth::bap::ucast::StreamStateInfo;
+using bluetooth::bap::ucast::StreamConfigInfo;
+using bluetooth::bap::ucast::StreamReconfig;
+
+namespace bluetooth {
+namespace bap {
+namespace ucast {
+
+class UcastClientInterfaceImpl;
+static std::unique_ptr<UcastClientInterface> UcastClientInstance = nullptr;
+
+class UcastClientInterfaceImpl
+    : public UcastClientInterface,
+      public UcastClientCallbacks {
+  ~UcastClientInterfaceImpl() = default;
+  void Init(UcastClientCallbacks* client_callbacks) override {
+    if(is_initialized) {
+      LOG(WARNING) << __func__ << " Already initialized, return";
+      return;
+    }
+    is_initialized = true;
+    callbacks = client_callbacks;
+    char value[PROPERTY_VALUE_MAX];
+    if(property_get("persist.vendor.service.bt.bap.enable_ucast", value, "false")
+                    && !strcmp(value, "true")) {
+      LOG(INFO) << __func__ << " Registering PACS UUID ";
+      btif_register_uuid_srvc_disc(Uuid::FromString("1850"));
+      btif_register_uuid_srvc_disc(Uuid::FromString("184E"));
+    }
+    do_in_bta_thread( FROM_HERE, Bind(&UcastClient::Initialize, this));
+  }
+
+  void OnStreamState(const RawAddress &address,
+                   std::vector<StreamStateInfo> streams_state_info) override {
+    if(!is_initialized) return;
+    do_in_jni_thread(FROM_HERE, Bind(&UcastClientCallbacks::OnStreamState,
+                                     Unretained(callbacks), address,
+                                     streams_state_info));
+  }
+
+  void OnStreamConfig(const RawAddress &address,
+                std::vector<StreamConfigInfo> streams_config_info) override {
+    if(!is_initialized) return;
+    do_in_jni_thread(FROM_HERE, Bind(&UcastClientCallbacks::OnStreamConfig,
+                                     Unretained(callbacks),
+                                     address, streams_config_info));
+  }
+
+  void OnStreamAvailable(const RawAddress &address,
+                      uint16_t src_audio_contexts,
+                      uint16_t sink_audio_contexts) override {
+    if(!is_initialized) return;
+    do_in_jni_thread(FROM_HERE,
+                     Bind(&UcastClientCallbacks::OnStreamAvailable,
+                          Unretained(callbacks),
+                          address, src_audio_contexts,
+                          sink_audio_contexts));
+  }
+
+  void Reconfigure(const RawAddress& address,
+                   std::vector<StreamReconfig> &streams_info) override {
+    LOG(INFO) << __func__ << " " << address;
+    if(!is_initialized) return;
+    do_in_bta_thread(FROM_HERE, Bind(&UcastClient::Reconfigure,
+                                      Unretained(UcastClient::Get()),
+                                      address, streams_info));
+  }
+
+  void Start(const RawAddress& address,
+             std::vector<StreamType> &streams_info) override {
+    LOG(INFO) << __func__ << " " << address;
+    if(!is_initialized) return;
+    do_in_bta_thread(FROM_HERE, Bind(&UcastClient::Start,
+                                      Unretained(UcastClient::Get()),
+                                      address, streams_info));
+  }
+
+  void Connect(std::vector<RawAddress> &address, bool is_direct,
+               std::vector<StreamConnect> &streams_info) override {
+    for (auto it = address.begin(); it != address.end(); it++) {
+      LOG(INFO) << __func__ << " " << (*it);
+    }
+    if(!is_initialized) return;
+    do_in_bta_thread(FROM_HERE, Bind(&UcastClient::Connect,
+                                      Unretained(UcastClient::Get()),
+                                      address, is_direct, streams_info));
+  }
+
+  void Disconnect(const RawAddress& address,
+                  std::vector<StreamType> &streams_info) override {
+    LOG(INFO) << __func__ << " " << address;
+    if(!is_initialized) return;
+    do_in_bta_thread(FROM_HERE, Bind(&UcastClient::Disconnect,
+                                      Unretained(UcastClient::Get()),
+                                      address, streams_info));
+  }
+
+  void Stop(const RawAddress& address,
+            std::vector<StreamType> &streams_info) override {
+    LOG(INFO) << __func__ << " " << address;
+    if(!is_initialized) return;
+    do_in_bta_thread(FROM_HERE, Bind(&UcastClient::Stop,
+                                      Unretained(UcastClient::Get()),
+                                      address, streams_info));
+  }
+
+  void UpdateStream(const RawAddress& address,
+                    std::vector<StreamUpdate> &update_streams) override {
+    LOG(INFO) << __func__ << " " << address;
+    if(!is_initialized) return;
+    do_in_bta_thread(FROM_HERE, Bind(&UcastClient::UpdateStream,
+                                      Unretained(UcastClient::Get()),
+                                      address, update_streams));
+  }
+
+  void Cleanup() override {
+    if(!is_initialized) return;
+    do_in_bta_thread(FROM_HERE, Bind(&UcastClient::CleanUp));
+    is_initialized = false;
+  }
+
+ private:
+  bool is_initialized = false;;
+  UcastClientCallbacks* callbacks;
+};
+
+UcastClientInterface* btif_bap_uclient_get_interface() {
+  if (!UcastClientInstance)
+    UcastClientInstance.reset(new UcastClientInterfaceImpl());
+  return UcastClientInstance.get();
+}
+
+}  // namespace ucast
+}  // namespace bap
+}  // namespace bluetooth
diff --git a/le_audio/system/bt/btif/src/btif_bap_uclient_test.cc b/le_audio/system/bt/btif/src/btif_bap_uclient_test.cc
new file mode 100644
index 0000000..ffa3745
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_bap_uclient_test.cc
@@ -0,0 +1,2971 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 "bta_closure_api.h"
+#include "bta_bap_uclient_api.h"
+#include "btif_common.h"
+#include "btif_storage.h"
+#include "osi/include/thread.h"
+#include "btif_bap_codec_utils.h"
+
+#include "osi/include/properties.h"
+
+extern void do_in_bta_thread(const base::Location& from_here,
+                             const base::Closure& task);
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_pacs_client.h>
+#include <hardware/bt_bap_uclient.h>
+#include "btif_bap_config.h"
+
+using base::Bind;
+using base::Unretained;
+using bluetooth::bap::ucast::UcastClient;
+using bluetooth::bap::pacs::CodecConfig;
+using bluetooth::bap::pacs::ConnectionState;
+using bluetooth::bap::ucast::UcastClientCallbacks;
+using bluetooth::bap::ucast::UcastClientInterface;
+using bluetooth::bap::ucast::StreamConnect;
+using bluetooth::bap::ucast::StreamType;
+using bluetooth::bap::ucast::StreamStateInfo;
+using bluetooth::bap::ucast::StreamConfigInfo;
+using bluetooth::bap::ucast::StreamReconfig;
+
+using bluetooth::bap::pacs::CodecIndex;
+using bluetooth::bap::pacs::CodecPriority;
+using bluetooth::bap::pacs::CodecSampleRate;
+using bluetooth::bap::pacs::CodecFrameDuration;
+using bluetooth::bap::pacs::CodecChannelMode;
+using bluetooth::bap::ucast::CodecQosConfig;
+using bluetooth::bap::ucast::CISConfig;
+using bluetooth::bap::ucast::CONTENT_TYPE_MEDIA;
+using bluetooth::bap::ucast::CONTENT_TYPE_CONVERSATIONAL;
+using bluetooth::bap::ucast::CONTENT_TYPE_GAME;
+using bluetooth::bap::ucast::CONTENT_TYPE_LIVE;
+using bluetooth::bap::ucast::CONTENT_TYPE_UNSPECIFIED;
+using bluetooth::bap::ucast::ASE_DIRECTION_SRC;
+using bluetooth::bap::ucast::ASE_DIRECTION_SINK;
+using bluetooth::bap::ucast::StreamReconfigType;
+using bluetooth::bap::ucast::ASCSConfig;
+
+static thread_t *test_thread;
+static UcastClientInterface* sUcastClientInterface = nullptr;
+static RawAddress bap_bd_addr;
+
+extern bluetooth::bap::pacs::PacsClientInterface* btif_pacs_client_get_interface();
+
+class UcastClientCallbacksImpl : public UcastClientCallbacks {
+ public:
+  ~UcastClientCallbacksImpl() = default;
+  void OnStreamState(const RawAddress &address,
+                 std::vector<StreamStateInfo> streams_state_info) override {
+    for (auto it = streams_state_info.begin();
+                         it != streams_state_info.end(); it++) {
+      LOG(WARNING) << __func__ << " stream type " << (it->stream_type.type);
+      LOG(WARNING) << __func__ << " stream dir " << loghex(it->stream_type.direction);
+      LOG(WARNING) << __func__ << " stream state " << static_cast<int> (it->stream_state);
+    }
+  }
+  void OnStreamConfig(const RawAddress &address,
+               std::vector<StreamConfigInfo> streams_config_info) override {
+    LOG(WARNING) << __func__;
+    for (auto it = streams_config_info.begin();
+                         it != streams_config_info.end(); it++) {
+      LOG(WARNING) << __func__ << " stream type " << (it->stream_type.type);
+      LOG(WARNING) << __func__ << " stream dir " << loghex(it->stream_type.direction);
+      LOG(WARNING) << __func__ << " location " << static_cast<int> (it->audio_location);
+      btif_bap_add_record(address, REC_TYPE_CONFIGURATION,
+                          it->stream_type.type,
+                          static_cast<CodecDirection> (it->stream_type.direction),
+                          &it->codec_config);
+
+      std::vector<CodecConfig> acm_pac_records;
+
+      btif_bap_get_records(address,
+                                REC_TYPE_CAPABILITY,
+                                CONTENT_TYPE_MEDIA |
+                                CONTENT_TYPE_UNSPECIFIED,
+                                static_cast<CodecDirection>
+                                (it->stream_type.direction),
+                                &acm_pac_records);
+
+      LOG(WARNING) << __func__ << " acm len  to be 3" << (acm_pac_records.size());
+
+      std::vector<CodecConfig> config_pac_records;
+      btif_bap_get_records(address,
+                                REC_TYPE_CONFIGURATION,
+                                CONTENT_TYPE_MEDIA,
+                                static_cast<CodecDirection>
+                                (it->stream_type.direction),
+                                &config_pac_records);
+
+      LOG(WARNING) << __func__ << " configs len to be 1" << (config_pac_records.size());
+
+      btif_bap_remove_record_by_context (address, REC_TYPE_CONFIGURATION,
+                          it->stream_type.type,
+                          static_cast<CodecDirection>
+                           (it->stream_type.direction));
+
+      config_pac_records.clear();
+      btif_bap_get_records(address,
+                                REC_TYPE_CONFIGURATION,
+                                CONTENT_TYPE_MEDIA,
+                                static_cast<CodecDirection>
+                                (it->stream_type.direction),
+                                &config_pac_records);
+
+      LOG(WARNING) << __func__ << " configs len to be 0" << (config_pac_records.size());
+
+#if 0
+      btif_bap_remove_record_by_context (address, REC_TYPE_CAPABILITY,
+                                CONTENT_TYPE_MEDIA |
+                                CONTENT_TYPE_UNSPECIFIED,
+                          static_cast<CodecDirection>
+                           (it->stream_type.direction));
+
+      acm_pac_records.clear();
+      btif_bap_get_records(address,
+                                REC_TYPE_CAPABILITY,
+                                CONTENT_TYPE_MEDIA |
+                                CONTENT_TYPE_UNSPECIFIED,
+                                static_cast<CodecDirection>
+                                (it->stream_type.direction),
+                                &acm_pac_records);
+
+      LOG(WARNING) << __func__ << " acm len to be 0" << (acm_pac_records.size());
+#endif
+    }
+  }
+  void OnStreamAvailable(const RawAddress &address,
+                      uint16_t src_audio_contexts,
+                      uint16_t sink_audio_contexts)  override {
+    LOG(WARNING) << __func__;
+  }
+};
+
+static UcastClientCallbacksImpl sUcastClientCallbacks;
+
+static void event_test_bap_uclient(UNUSED_ATTR void* context) {
+  LOG(INFO) << __func__ << " start " ;
+  sUcastClientInterface = bluetooth::bap::ucast::btif_bap_uclient_get_interface();
+  sUcastClientInterface->Init(&sUcastClientCallbacks);
+  sleep(1);
+
+  StreamConnect conn_info;
+  CodecQosConfig codec_qos_config;
+  codec_qos_config.codec_config.codec_type = CodecIndex::CODEC_INDEX_SOURCE_LC3;
+  codec_qos_config.codec_config.codec_priority =
+                                  CodecPriority::CODEC_PRIORITY_DEFAULT;
+  codec_qos_config.codec_config.sample_rate =
+                                  CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+  codec_qos_config.codec_config.channel_mode =
+                                  CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+  //Frame_Duration
+  UpdateFrameDuration(&codec_qos_config.codec_config,
+                     static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+  // LC3_Blocks_Per_SDU
+  UpdateLc3BlocksPerSdu(&codec_qos_config.codec_config, 1);
+  UpdateOctsPerFrame(&codec_qos_config.codec_config, 100);
+  codec_qos_config.qos_config.cig_config = {
+                                     .cig_id = 1,
+                                     .cis_count = 2,
+                                     .packing = 0x01, // interleaved
+                                     .framing =  0x00, // unframed
+                                     .max_tport_latency_m_to_s =  0x000a,
+                                     .max_tport_latency_s_to_m = 0x000a,
+                                     .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                     .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                    };
+  CISConfig cis_config_1 = {
+                                     .cis_id = 0,
+                                     .max_sdu_m_to_s = 100,
+                                     .max_sdu_s_to_m = 0,
+                                     .phy_m_to_s = 0x02,
+                                     .phy_s_to_m = 0x02,
+                                     .rtn_m_to_s = 0x02,
+                                     .rtn_s_to_m = 0x02
+                           };
+  CISConfig cis_config_2 = {
+                                     .cis_id = 1,
+                                     .max_sdu_m_to_s = 100,
+                                     .max_sdu_s_to_m = 0,
+                                     .phy_m_to_s = 0x02,
+                                     .phy_s_to_m = 0x02,
+                                     .rtn_m_to_s = 0x02,
+                                     .rtn_s_to_m = 0x02
+                           };
+  codec_qos_config.qos_config.cis_configs.push_back(cis_config_1);
+  codec_qos_config.qos_config.cis_configs.push_back(cis_config_2);
+
+  ASCSConfig ascs_config =  {
+    .cig_id = 1,
+    .cis_id = 0,
+    .bi_directional = false,
+    .presentation_delay = {0x20, 0x4E, 0x00}
+  };
+  codec_qos_config.qos_config.ascs_configs.push_back(ascs_config);
+  conn_info.stream_type.type = 0x0004; // media
+  conn_info.stream_type.direction = ASE_DIRECTION_SINK;
+  conn_info.stream_type.audio_context =
+                            CONTENT_TYPE_MEDIA;
+  conn_info.codec_qos_config_pair.push_back(codec_qos_config);
+  std::vector<StreamConnect> streams;
+  streams.push_back(conn_info);
+
+  ASCSConfig ascs_config_2 =  {
+    .cig_id = 1,
+    .cis_id = 1,
+    .bi_directional = false,
+    .presentation_delay = {0x20, 0x4E, 0x00}
+  };
+
+  codec_qos_config.qos_config.ascs_configs.clear();
+  codec_qos_config.qos_config.ascs_configs.push_back(ascs_config_2);
+  StreamConnect conn_info_2;
+  conn_info_2.stream_type.type = 0x0004; // media
+  conn_info_2.stream_type.direction = ASE_DIRECTION_SINK;
+  conn_info_2.stream_type.audio_context =
+                            CONTENT_TYPE_MEDIA;
+  conn_info_2.codec_qos_config_pair.push_back(codec_qos_config);
+  std::vector<StreamConnect> streams_2;
+  streams_2.push_back(conn_info_2);
+
+  // reconfig information
+  std::vector<StreamReconfig> reconf_streams;
+  codec_qos_config.qos_config.ascs_configs[0].cis_id = 1;
+  UpdateFrameDuration(&codec_qos_config.codec_config,
+                    static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_7_5));
+
+  codec_qos_config.qos_config.cig_config = {
+                                     .cig_id = 1,
+                                     .cis_count = 2,
+                                     .packing = 0x01, // interleaved
+                                     .framing =  0x00, // unframed
+                                     .max_tport_latency_m_to_s =  0x000a,
+                                     .max_tport_latency_s_to_m = 0x000a,
+                                     .sdu_interval_m_to_s = {0x4C, 0x1D, 0x00},
+                                     .sdu_interval_s_to_m = {0x4C, 0x1D, 0x00}
+                                    };
+
+  StreamReconfig reconf_info;
+  reconf_info.stream_type.type = 0x0004; // media
+  reconf_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+  reconf_info.stream_type.direction = ASE_DIRECTION_SINK;
+  reconf_info.codec_qos_config_pair.push_back(codec_qos_config);
+  reconf_streams.push_back(reconf_info);
+
+  std::vector<StreamReconfig> reconf_qos_streams;
+  StreamReconfig reconf_qos_info;
+  reconf_qos_info.stream_type.type = 0x0004; // media
+  reconf_qos_info.reconf_type = StreamReconfigType::QOS_CONFIG;
+  reconf_qos_info.stream_type.direction = ASE_DIRECTION_SINK;
+  codec_qos_config.qos_config.cis_configs.clear();
+
+  cis_config_1.rtn_m_to_s = 1;
+  cis_config_1.rtn_s_to_m = 1;
+  cis_config_2.rtn_m_to_s = 1;
+  cis_config_2.rtn_s_to_m = 1;
+  codec_qos_config.qos_config.cis_configs.push_back(cis_config_1);
+  codec_qos_config.qos_config.cis_configs.push_back(cis_config_2);
+  reconf_qos_info.codec_qos_config_pair.push_back(codec_qos_config);
+  reconf_qos_streams.push_back(reconf_qos_info);
+
+  StreamType type_1 = { .type = 0x0004,
+                        .direction = ASE_DIRECTION_SINK
+                      };
+
+  RawAddress bap_bd_addr_2;
+  RawAddress::FromString("00:02:5B:00:FF:01", bap_bd_addr_2);
+
+  std::vector<StreamType> start_streams;
+  start_streams.push_back(type_1);
+
+  char bap_test[150] = "generic";
+
+  property_get("persist.vendor.service.bt.bap.test", bap_test, "generic");
+
+      LOG(INFO) << __func__ << " property" << bap_test;
+
+  if(!strcmp(bap_test, "generic")) {
+    LOG(INFO) << __func__ << " going for generic test case";
+    LOG(INFO) << __func__ << " going for connect";
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, streams_2);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << " going for stream start 1 ";
+    //sUcastClientInterface->Start( bap_bd_addr, start_streams);
+
+    LOG(INFO) << __func__ << " going for stream start 2";
+    //sUcastClientInterface->Start( bap_bd_addr_2, start_streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << "going for stream disconnect 1 ";
+    sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+    LOG(INFO) << __func__ << "going for stream disconnect 2 ";
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, start_streams);
+
+  } else if(!strcmp(bap_test, "stereo_recording_stereo_dual_cis")) {
+    LOG(INFO) << __func__ << " going for rxonly test case";
+    LOG(INFO) << __func__ << " going for connect";
+    StreamConnect conn_info_media_recording;
+
+    CodecQosConfig codec_qos_config_media_rx_1;
+    CodecQosConfig codec_qos_config_media_rx_2;
+
+    CISConfig cis_config_1_media_rx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 0,
+                                       .max_sdu_s_to_m = 100,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                                    };
+
+    CISConfig cis_config_2_media_rx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 0,
+                                       .max_sdu_s_to_m = 100,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                                    };
+
+    CISConfig cis_config_1_media_rx_2 = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 0,
+                                       .max_sdu_s_to_m = 200,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                                    };
+
+    ASCSConfig ascs_config_1 =  {
+                                   .cig_id = 1,
+                                   .cis_id = 0,
+                                   .bi_directional = false,
+                                   .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+
+    ASCSConfig ascs_config_2 =  {
+                                   .cig_id = 1,
+                                   .cis_id = 1,
+                                   .bi_directional = false,
+                                   .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+
+    codec_qos_config_media_rx_1.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_media_rx_1.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_media_rx_1.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    codec_qos_config_media_rx_1.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_media_rx_1.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_media_rx_1.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_media_rx_1.codec_config, 100);
+
+
+    codec_qos_config_media_rx_2 = codec_qos_config_media_rx_1;
+    UpdateOctsPerFrame(&codec_qos_config_media_rx_2.codec_config, 200);
+
+    codec_qos_config_media_rx_1.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s = 0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_media_rx_1.qos_config.cis_configs.push_back(cis_config_1_media_rx);
+    codec_qos_config_media_rx_1.qos_config.cis_configs.push_back(cis_config_2_media_rx);
+
+    codec_qos_config_media_rx_1.qos_config.ascs_configs.push_back(ascs_config_1);
+    codec_qos_config_media_rx_1.qos_config.ascs_configs.push_back(ascs_config_2);
+
+    codec_qos_config_media_rx_2.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 1,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s = 0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_media_rx_2.qos_config.cis_configs.push_back(cis_config_1_media_rx_2);
+
+    codec_qos_config_media_rx_2.qos_config.ascs_configs.push_back(ascs_config_1);
+
+    StreamType type_media_rx = { .type = CONTENT_TYPE_MEDIA,
+                                 .audio_context = CONTENT_TYPE_LIVE,
+                                 .direction = ASE_DIRECTION_SRC
+                               };
+
+    conn_info_media_recording.stream_type.type = CONTENT_TYPE_MEDIA;
+    conn_info_media_recording.stream_type.audio_context = CONTENT_TYPE_LIVE;
+    conn_info_media_recording.stream_type.direction = ASE_DIRECTION_SRC;
+    conn_info_media_recording.codec_qos_config_pair.push_back(codec_qos_config_media_rx_1);
+    conn_info_media_recording.codec_qos_config_pair.push_back(codec_qos_config_media_rx_2);
+
+    std::vector<StreamConnect> media_rx_streams;
+    media_rx_streams.push_back(conn_info_media_recording);
+
+    std::vector<StreamType> streams;
+    streams.push_back(type_media_rx);
+
+
+    sUcastClientInterface->Connect( bap_bd_addr, true, media_rx_streams);
+
+    sleep(15);
+
+    LOG(INFO) << __func__ << " going for stream start 1 ";
+    sUcastClientInterface->Start( bap_bd_addr, streams);
+
+    sleep(10);
+
+    sUcastClientInterface->Stop( bap_bd_addr, streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << "going for stream disconnect 1 ";
+    sUcastClientInterface->Disconnect( bap_bd_addr, streams);
+
+  } else if(!strcmp(bap_test, "dual_streams_media_tx_voice_rx")) {
+    StreamConnect conn_info_media;
+    StreamConnect conn_info_gaming;
+
+    // reconfig information
+    CodecQosConfig codec_qos_config_media_tx_1;
+    CodecQosConfig codec_qos_config_gaming_tx;
+    CodecQosConfig codec_qos_config_gaming_tx_rx;
+
+    CISConfig cis_config_1_media_tx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 155,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+    CISConfig cis_config_2_media_tx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 155,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+    CISConfig cis_config_1_gaming_tx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+    CISConfig cis_config_2_gaming_tx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+    CISConfig cis_config_1_gaming_tx_rx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 40,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                             };
+    CISConfig cis_config_2_gaming_tx_rx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 40,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                             };
+
+    ASCSConfig ascs_config =  {
+                                       .cig_id = 1,
+                                       .cis_id = 0,
+                                       .bi_directional = false,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+
+    codec_qos_config_media_tx_1.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_media_tx_1.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_media_tx_1.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    codec_qos_config_media_tx_1.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_media_tx_1.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_media_tx_1.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_media_tx_1.codec_config, 155);
+    codec_qos_config_media_tx_1.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_media_tx_1.qos_config.cis_configs.push_back(cis_config_1_media_tx);
+    codec_qos_config_media_tx_1.qos_config.cis_configs.push_back(cis_config_2_media_tx);
+
+    codec_qos_config_media_tx_1.qos_config.ascs_configs.push_back(ascs_config);
+    codec_qos_config_gaming_tx.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_gaming_tx.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_gaming_tx.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    codec_qos_config_gaming_tx.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_gaming_tx.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_gaming_tx.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_gaming_tx.codec_config, 100);
+    codec_qos_config_gaming_tx.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_gaming_tx.qos_config.cis_configs.push_back(cis_config_1_gaming_tx);
+    codec_qos_config_gaming_tx.qos_config.cis_configs.push_back(cis_config_2_gaming_tx);
+
+    codec_qos_config_gaming_tx.qos_config.ascs_configs.push_back(ascs_config);
+    codec_qos_config_gaming_tx_rx.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_gaming_tx_rx.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_gaming_tx_rx.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+    codec_qos_config_gaming_tx_rx.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_gaming_tx_rx.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_gaming_tx_rx.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_gaming_tx_rx.codec_config, 40);
+    codec_qos_config_gaming_tx_rx.qos_config.cig_config = {
+                                       .cig_id = 2,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_gaming_tx_rx.qos_config.cis_configs.push_back(cis_config_1_gaming_tx_rx);
+    codec_qos_config_gaming_tx_rx.qos_config.cis_configs.push_back(cis_config_2_gaming_tx_rx);
+    ASCSConfig ascs_config_gaming =  {
+                                       .cig_id = 2,
+                                       .cis_id = 0,
+                                       .bi_directional = true,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+    codec_qos_config_gaming_tx_rx.qos_config.ascs_configs.push_back(ascs_config_gaming);
+    conn_info_media.stream_type.type = CONTENT_TYPE_MEDIA;
+    conn_info_media.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    conn_info_media.stream_type.direction = ASE_DIRECTION_SINK;
+    conn_info_media.codec_qos_config_pair.push_back(codec_qos_config_media_tx_1);
+
+    conn_info_gaming.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_gaming.stream_type.audio_context =
+                            CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_gaming.stream_type.direction = ASE_DIRECTION_SRC;
+    conn_info_gaming.codec_qos_config_pair.push_back(codec_qos_config_gaming_tx_rx);
+
+    std::vector<StreamConnect> dual_streams;
+    dual_streams.push_back(conn_info_media);
+    dual_streams.push_back(conn_info_gaming);
+
+    StreamType type_media = { .type = CONTENT_TYPE_MEDIA,
+                              .audio_context = CONTENT_TYPE_MEDIA,
+                              .direction = ASE_DIRECTION_SINK
+                            };
+
+    StreamType type_voice = { .type = CONTENT_TYPE_CONVERSATIONAL,
+                              .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                              .direction = ASE_DIRECTION_SRC
+                            };
+
+    std::vector<StreamType> media_streams;
+    media_streams.push_back(type_media);
+    std::vector<StreamType> voice_streams;
+    voice_streams.push_back(type_voice);
+
+    LOG(INFO) << __func__ << " going for generic test case";
+    LOG(INFO) << __func__ << " going for connect with media tx and gaming rx";
+    sUcastClientInterface->Connect( bap_bd_addr, true, dual_streams);
+
+    //sUcastClientInterface->Connect( bap_bd_addr_2, true, streams_2);
+
+    sleep(10);
+
+    std::vector<StreamReconfig> reconf_streams;
+
+    StreamReconfig reconf_gaming_tx_info;
+    reconf_gaming_tx_info.stream_type.type = CONTENT_TYPE_MEDIA; // media
+    reconf_gaming_tx_info.stream_type.audio_context = CONTENT_TYPE_MEDIA; // media
+    reconf_gaming_tx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_gaming_tx_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_gaming_tx_info.codec_qos_config_pair.push_back(codec_qos_config_gaming_tx);
+
+    StreamReconfig reconf_gaming_tx_rx_info;
+    reconf_gaming_tx_rx_info.stream_type.type = CONTENT_TYPE_MEDIA; // media
+    reconf_gaming_tx_rx_info.stream_type.audio_context = CONTENT_TYPE_MEDIA; // media
+    reconf_gaming_tx_rx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_gaming_tx_rx_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_gaming_tx_rx_info.codec_qos_config_pair.push_back(codec_qos_config_gaming_tx_rx);
+
+    StreamReconfig reconf_media_info;
+    reconf_media_info.stream_type.type = CONTENT_TYPE_MEDIA; // media
+    reconf_media_info.stream_type.audio_context = CONTENT_TYPE_MEDIA; // media
+    reconf_media_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_media_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_media_info.codec_qos_config_pair.push_back(codec_qos_config_media_tx_1);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream start 1 ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+
+    // switch to Gaming tx
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_gaming_tx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+
+    // switch to media tx
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+
+    // switch to Gaming tx & rx
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_gaming_tx_rx_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+    sUcastClientInterface->Start( bap_bd_addr, voice_streams);
+
+    // switch to Gaming tx (2nd time) (4th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+    sUcastClientInterface->Stop( bap_bd_addr, voice_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_gaming_tx_info.reconf_type = StreamReconfigType::QOS_CONFIG;
+    reconf_streams.push_back(reconf_gaming_tx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+
+
+    // switch to Gaming tx & rx (2nd time) (5th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_gaming_tx_rx_info.reconf_type = StreamReconfigType::QOS_CONFIG;
+    reconf_streams.push_back(reconf_gaming_tx_rx_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+    sUcastClientInterface->Start( bap_bd_addr, voice_streams);
+
+    // switch to media tx (6th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+    sUcastClientInterface->Stop( bap_bd_addr, voice_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for stream disconnect 1 ";
+    sUcastClientInterface->Disconnect( bap_bd_addr, media_streams);
+    sUcastClientInterface->Disconnect( bap_bd_addr, voice_streams);
+    //LOG(INFO) << __func__ << "going for stream disconnect 2 ";
+    //sUcastClientInterface->Disconnect( bap_bd_addr_2, start_streams);
+
+  } else if(!strcmp(bap_test, "tri_streams_media_tx_voice_tx_voice_rx")) {
+    StreamConnect conn_info_media;
+    StreamConnect conn_info_gaming;
+    StreamConnect conn_info_voice_tx;
+    StreamConnect conn_info_voice_rx;
+
+    // reconfig information
+    CodecQosConfig codec_qos_config_media_tx_1;
+    CodecQosConfig codec_qos_config_gaming_tx;
+    CodecQosConfig codec_qos_config_gaming_tx_rx;
+    CodecQosConfig codec_qos_config_gaming_rx;
+    CodecQosConfig codec_qos_config_voice_tx_rx;
+
+    CodecQosConfig codec_qos_config_media_tx_2;
+    CodecQosConfig codec_qos_config_gaming_tx_2;
+    CodecQosConfig codec_qos_config_gaming_tx_rx_2;
+    CodecQosConfig codec_qos_config_gaming_rx_2;
+    CodecQosConfig codec_qos_config_voice_tx_rx_2;
+
+    CISConfig cis_config_1_media_tx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 155,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                                    };
+
+    CISConfig cis_config_2_media_tx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 155,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                                    };
+
+    CISConfig cis_config_1_gaming_tx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                                    };
+
+    CISConfig cis_config_2_gaming_tx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                                    };
+
+    CISConfig cis_config_1_gaming_tx_rx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 40,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                             };
+    CISConfig cis_config_2_gaming_tx_rx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 40,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                             };
+
+    CISConfig cis_config_1_voice_tx_rx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 40,
+                                       .max_sdu_s_to_m = 40,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                             };
+    CISConfig cis_config_2_voice_tx_rx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 40,
+                                       .max_sdu_s_to_m = 40,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                             };
+
+    ASCSConfig ascs_config =  {
+                                       .cig_id = 1,
+                                       .cis_id = 0,
+                                       .bi_directional = false,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+
+    ASCSConfig ascs_config_gaming =  {
+                                       .cig_id = 2,
+                                       .cis_id = 0,
+                                       .bi_directional = true,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                                  };
+
+    ASCSConfig ascs_config_voice =  {
+                                       .cig_id = 3,
+                                       .cis_id = 0,
+                                       .bi_directional = true,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                                  };
+
+    ASCSConfig ascs_config_2 =  {
+                                       .cig_id = 1,
+                                       .cis_id = 1,
+                                       .bi_directional = false,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+
+    ASCSConfig ascs_config_gaming_2 =  {
+                                       .cig_id = 2,
+                                       .cis_id = 1,
+                                       .bi_directional = true,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                                  };
+
+    ASCSConfig ascs_config_voice_2 =  {
+                                       .cig_id = 3,
+                                       .cis_id = 1,
+                                       .bi_directional = true,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                                  };
+
+    codec_qos_config_media_tx_1.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_media_tx_1.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_media_tx_1.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    codec_qos_config_media_tx_1.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_media_tx_1.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_media_tx_1.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_media_tx_1.codec_config, 155);
+    codec_qos_config_media_tx_1.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_media_tx_1.qos_config.cis_configs.push_back(cis_config_1_media_tx);
+    codec_qos_config_media_tx_1.qos_config.cis_configs.push_back(cis_config_2_media_tx);
+
+    codec_qos_config_media_tx_1.qos_config.ascs_configs.push_back(ascs_config);
+
+    codec_qos_config_media_tx_2 = codec_qos_config_media_tx_1;
+    codec_qos_config_media_tx_2.qos_config.ascs_configs.clear();
+    codec_qos_config_media_tx_2.qos_config.ascs_configs.push_back(ascs_config_2);
+
+    codec_qos_config_gaming_tx.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_gaming_tx.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_gaming_tx.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    codec_qos_config_gaming_tx.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_gaming_tx.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_gaming_tx.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_gaming_tx.codec_config, 100);
+    codec_qos_config_gaming_tx.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_gaming_tx.qos_config.cis_configs.push_back(cis_config_1_gaming_tx);
+    codec_qos_config_gaming_tx.qos_config.cis_configs.push_back(cis_config_2_gaming_tx);
+
+    codec_qos_config_gaming_tx.qos_config.ascs_configs.push_back(ascs_config);
+
+
+    codec_qos_config_gaming_tx_2 = codec_qos_config_gaming_tx;
+    codec_qos_config_gaming_tx_2.qos_config.ascs_configs.clear();
+    codec_qos_config_gaming_tx_2.qos_config.ascs_configs.push_back(ascs_config_2);
+
+    codec_qos_config_gaming_tx_rx.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_gaming_tx_rx.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_gaming_tx_rx.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    codec_qos_config_gaming_tx_rx.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_gaming_tx_rx.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_gaming_tx_rx.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_gaming_tx_rx.codec_config, 100);
+    codec_qos_config_gaming_tx_rx.qos_config.cig_config = {
+                                       .cig_id = 2,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_gaming_tx_rx.qos_config.cis_configs.push_back(cis_config_1_gaming_tx_rx);
+    codec_qos_config_gaming_tx_rx.qos_config.cis_configs.push_back(cis_config_2_gaming_tx_rx);
+
+    codec_qos_config_gaming_tx_rx.qos_config.ascs_configs.push_back(ascs_config_gaming);
+
+    codec_qos_config_gaming_tx_rx_2 = codec_qos_config_gaming_tx_rx;
+    codec_qos_config_gaming_tx_rx_2.qos_config.ascs_configs.clear();
+    codec_qos_config_gaming_tx_rx_2.qos_config.ascs_configs.push_back(ascs_config_gaming_2);
+
+
+    codec_qos_config_gaming_rx.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_gaming_rx.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_gaming_rx.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+    codec_qos_config_gaming_rx.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_gaming_rx.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_gaming_rx.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_gaming_rx.codec_config, 40);
+    codec_qos_config_gaming_rx.qos_config.cig_config = {
+                                       .cig_id = 2,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_gaming_rx.qos_config.cis_configs.push_back(cis_config_1_gaming_tx_rx);
+    codec_qos_config_gaming_rx.qos_config.cis_configs.push_back(cis_config_2_gaming_tx_rx);
+
+    codec_qos_config_gaming_rx.qos_config.ascs_configs.push_back(ascs_config_gaming);
+
+    codec_qos_config_gaming_rx_2 = codec_qos_config_gaming_rx;
+    codec_qos_config_gaming_rx_2.qos_config.ascs_configs.clear();
+    codec_qos_config_gaming_rx_2.qos_config.ascs_configs.push_back(ascs_config_gaming_2);
+
+
+    // voice tx rx
+    codec_qos_config_voice_tx_rx.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_voice_tx_rx.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_voice_tx_rx.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+    codec_qos_config_voice_tx_rx.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_voice_tx_rx.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_voice_tx_rx.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_voice_tx_rx.codec_config, 40);
+    codec_qos_config_voice_tx_rx.qos_config.cig_config = {
+                                       .cig_id = 3,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_voice_tx_rx.qos_config.cis_configs.push_back(cis_config_1_voice_tx_rx);
+    codec_qos_config_voice_tx_rx.qos_config.cis_configs.push_back(cis_config_2_voice_tx_rx);
+
+    codec_qos_config_voice_tx_rx.qos_config.ascs_configs.push_back(ascs_config_voice);
+
+    codec_qos_config_voice_tx_rx_2 = codec_qos_config_voice_tx_rx;
+    codec_qos_config_voice_tx_rx_2.qos_config.ascs_configs.clear();
+    codec_qos_config_voice_tx_rx_2.qos_config.ascs_configs.push_back(ascs_config_voice_2);
+
+    conn_info_media.stream_type.type = CONTENT_TYPE_MEDIA;
+    conn_info_media.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    conn_info_media.stream_type.direction = ASE_DIRECTION_SINK;
+    conn_info_media.codec_qos_config_pair.push_back(codec_qos_config_media_tx_1);
+
+    conn_info_gaming.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_gaming.stream_type.audio_context =
+                            CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_gaming.stream_type.direction = ASE_DIRECTION_SRC;
+    conn_info_gaming.codec_qos_config_pair.push_back(codec_qos_config_gaming_tx_rx);
+
+    conn_info_voice_tx.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_voice_tx.stream_type.audio_context =
+                            CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_voice_tx.stream_type.direction = ASE_DIRECTION_SINK;
+    conn_info_voice_tx.codec_qos_config_pair.push_back(codec_qos_config_voice_tx_rx);
+
+    conn_info_voice_rx.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_voice_rx.stream_type.audio_context =
+                            CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_voice_rx.stream_type.direction = ASE_DIRECTION_SRC;
+    conn_info_voice_rx.codec_qos_config_pair.push_back(codec_qos_config_voice_tx_rx);
+
+    std::vector<StreamConnect> tri_streams;
+    tri_streams.push_back(conn_info_media);
+    tri_streams.push_back(conn_info_voice_tx);
+    tri_streams.push_back(conn_info_voice_rx);
+
+    conn_info_media.codec_qos_config_pair.clear();
+    conn_info_media.codec_qos_config_pair.push_back(codec_qos_config_media_tx_2);
+
+    conn_info_voice_tx.codec_qos_config_pair.clear();
+    conn_info_voice_tx.codec_qos_config_pair.push_back(codec_qos_config_voice_tx_rx_2);
+
+    conn_info_voice_rx.codec_qos_config_pair.clear();
+    conn_info_voice_rx.codec_qos_config_pair.push_back(codec_qos_config_voice_tx_rx_2);
+
+    std::vector<StreamConnect> tri_streams_2;
+    tri_streams_2.push_back(conn_info_media);
+    tri_streams_2.push_back(conn_info_voice_tx);
+    tri_streams_2.push_back(conn_info_voice_rx);
+
+    StreamType type_media = { .type = CONTENT_TYPE_MEDIA,
+                              .audio_context = CONTENT_TYPE_MEDIA,
+                              .direction = ASE_DIRECTION_SINK
+                            };
+
+    StreamType type_voice_tx = { .type = CONTENT_TYPE_CONVERSATIONAL,
+                              .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                              .direction = ASE_DIRECTION_SINK
+                            };
+
+    StreamType type_voice_rx = { .type = CONTENT_TYPE_CONVERSATIONAL,
+                              .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                              .direction = ASE_DIRECTION_SRC
+                            };
+
+
+    std::vector<StreamType> media_streams;
+    media_streams.push_back(type_media);
+
+    std::vector<StreamType> gaming_tx_streams;
+    gaming_tx_streams.push_back(type_media);
+
+    std::vector<StreamType> gaming_rx_streams;
+    gaming_rx_streams.push_back(type_voice_rx);
+
+    std::vector<StreamType> voice_streams;
+    voice_streams.push_back(type_voice_tx);
+    voice_streams.push_back(type_voice_rx);
+
+    std::vector<StreamType> all_three_streams;
+    all_three_streams.push_back(type_media);
+    all_three_streams.push_back(type_voice_tx);
+    all_three_streams.push_back(type_voice_rx);
+
+#if 0
+    LOG(INFO) << __func__ << " going for generic test case";
+    LOG(INFO) << __func__ << " going for connect with media tx and voice tx& rx";
+    sUcastClientInterface->Connect( bap_bd_addr, true, tri_streams);
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, tri_streams_2);
+    sleep(15);
+
+    LOG(INFO) << __func__ << "going for stream disconnect all 3 streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr, all_three_streams);
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_three_streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << " going for connect all 3 streams";
+    sUcastClientInterface->Connect( bap_bd_addr, true, tri_streams);
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, tri_streams_2);
+
+    sleep(15);
+
+    LOG(INFO) << __func__ << "going for stream disconnect all 3 streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr, all_three_streams);
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_three_streams);
+
+    sleep(5);
+#endif
+
+    LOG(INFO) << __func__ << " going for connect all 3 streams";
+    sUcastClientInterface->Connect( bap_bd_addr, true, tri_streams);
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, tri_streams_2);
+
+    std::vector<StreamReconfig> reconf_streams;
+    std::vector<StreamReconfig> reconf_streams_2;
+
+    StreamReconfig reconf_gaming_tx_info;
+    reconf_gaming_tx_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_info.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_gaming_tx_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_gaming_tx_info.codec_qos_config_pair.
+                          push_back(codec_qos_config_gaming_tx);
+
+    StreamReconfig reconf_gaming_tx_info_2;
+    reconf_gaming_tx_info_2.stream_type.type = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_info_2.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_info_2.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_gaming_tx_info_2.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_gaming_tx_info_2.codec_qos_config_pair.
+                          push_back(codec_qos_config_gaming_tx_2);
+
+    StreamReconfig reconf_gaming_tx_rx_info;
+    reconf_gaming_tx_rx_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_rx_info.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_rx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_gaming_tx_rx_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_gaming_tx_rx_info.codec_qos_config_pair.
+                         push_back(codec_qos_config_gaming_tx_rx);
+
+    StreamReconfig reconf_gaming_tx_rx_info_2;
+    reconf_gaming_tx_rx_info_2.stream_type.type = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_rx_info_2.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_rx_info_2.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_gaming_tx_rx_info_2.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_gaming_tx_rx_info_2.codec_qos_config_pair.
+                         push_back(codec_qos_config_gaming_tx_rx_2);
+
+    StreamReconfig reconf_gaming_rx_info;
+    reconf_gaming_rx_info.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_gaming_rx_info.stream_type.audio_context =
+                                      CONTENT_TYPE_CONVERSATIONAL;
+    reconf_gaming_rx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_gaming_rx_info.stream_type.direction = ASE_DIRECTION_SRC;
+    reconf_gaming_rx_info.codec_qos_config_pair.
+                           push_back(codec_qos_config_gaming_rx);
+
+    StreamReconfig reconf_gaming_rx_info_2;
+    reconf_gaming_rx_info_2.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_gaming_rx_info_2.stream_type.audio_context =
+                                     CONTENT_TYPE_CONVERSATIONAL;
+    reconf_gaming_rx_info_2.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_gaming_rx_info_2.stream_type.direction = ASE_DIRECTION_SRC;
+    reconf_gaming_rx_info_2.codec_qos_config_pair.
+                           push_back(codec_qos_config_gaming_rx_2);
+
+    StreamReconfig reconf_media_info;
+    reconf_media_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    reconf_media_info.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    reconf_media_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_media_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_media_info.codec_qos_config_pair.push_back(codec_qos_config_media_tx_1);
+
+    StreamReconfig reconf_media_info_2;
+    reconf_media_info_2.stream_type.type = CONTENT_TYPE_MEDIA;
+    reconf_media_info_2.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    reconf_media_info_2.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_media_info_2.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_media_info_2.codec_qos_config_pair.push_back(codec_qos_config_media_tx_2);
+
+
+    StreamReconfig reconf_voice_tx_info;
+    reconf_voice_tx_info.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_tx_info.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_tx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_voice_tx_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_voice_tx_info.codec_qos_config_pair.
+                                 push_back(codec_qos_config_voice_tx_rx);
+
+    StreamReconfig reconf_voice_tx_info_2;
+    reconf_voice_tx_info_2.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_tx_info_2.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_tx_info_2.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_voice_tx_info_2.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_voice_tx_info_2.codec_qos_config_pair.
+                                 push_back(codec_qos_config_voice_tx_rx_2);
+
+    StreamReconfig reconf_voice_rx_info;
+    reconf_voice_rx_info.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_rx_info.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_rx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_voice_rx_info.stream_type.direction = ASE_DIRECTION_SRC;
+    reconf_voice_rx_info.codec_qos_config_pair.
+                       push_back(codec_qos_config_voice_tx_rx);
+
+    StreamReconfig reconf_voice_rx_info_2;
+    reconf_voice_rx_info_2.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_rx_info_2.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_rx_info_2.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_voice_rx_info_2.stream_type.direction = ASE_DIRECTION_SRC;
+    reconf_voice_rx_info_2.codec_qos_config_pair.
+                       push_back(codec_qos_config_voice_tx_rx_2);
+
+    sleep(15);
+    LOG(INFO) << __func__ << " going for stream start 1 ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    // switch to Gaming tx
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_gaming_tx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+    reconf_streams_2.clear();
+    reconf_streams_2.push_back(reconf_gaming_tx_info_2);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    // switch to media tx
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_streams_2.push_back(reconf_media_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    // switch to Gaming tx & rx
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_gaming_tx_rx_info);
+    reconf_streams.push_back(reconf_gaming_rx_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_streams_2.push_back(reconf_gaming_tx_rx_info_2);
+    reconf_streams_2.push_back(reconf_gaming_rx_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, gaming_tx_streams);
+    sUcastClientInterface->Start( bap_bd_addr, gaming_rx_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, gaming_tx_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, gaming_rx_streams);
+
+    // switch to Gaming tx (2nd time) (4th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, gaming_tx_streams);
+    sUcastClientInterface->Stop( bap_bd_addr, gaming_rx_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, gaming_tx_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, gaming_rx_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_gaming_tx_info.reconf_type = StreamReconfigType::QOS_CONFIG;
+    reconf_streams.push_back(reconf_gaming_tx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_gaming_tx_info_2.reconf_type = StreamReconfigType::QOS_CONFIG;
+    reconf_streams_2.push_back(reconf_gaming_tx_info_2);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    // switch to Gaming tx & rx (2nd time) (5th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_gaming_tx_rx_info.reconf_type = StreamReconfigType::QOS_CONFIG;
+    reconf_streams.push_back(reconf_gaming_tx_rx_info);
+    reconf_streams.push_back(reconf_gaming_rx_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_gaming_tx_rx_info_2.reconf_type = StreamReconfigType::QOS_CONFIG;
+    reconf_streams_2.push_back(reconf_gaming_tx_rx_info_2);
+    reconf_streams_2.push_back(reconf_gaming_rx_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, gaming_tx_streams);
+    sUcastClientInterface->Start( bap_bd_addr, gaming_rx_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, gaming_tx_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, gaming_rx_streams);
+
+    // switch to media tx (6th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, gaming_tx_streams);
+    sUcastClientInterface->Stop( bap_bd_addr, gaming_rx_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, gaming_tx_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, gaming_rx_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_streams_2.push_back(reconf_media_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    // switch to voice tx & Rx (7th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_streams_2.push_back(reconf_voice_rx_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, voice_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+    // switch to gaming tx (8th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, voice_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_gaming_tx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_streams.push_back(reconf_gaming_tx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_gaming_tx_info_2.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_streams_2.push_back(reconf_gaming_tx_info_2);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+
+    // switch to voice tx & Rx (9th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_streams_2.push_back(reconf_voice_rx_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, voice_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+     // switch to Gaming tx & rx (10th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, voice_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_gaming_tx_rx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_streams.push_back(reconf_gaming_tx_rx_info);
+    reconf_streams.push_back(reconf_gaming_rx_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_gaming_tx_rx_info_2.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_streams_2.push_back(reconf_gaming_tx_rx_info_2);
+    reconf_streams_2.push_back(reconf_gaming_rx_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, gaming_tx_streams);
+    sUcastClientInterface->Start( bap_bd_addr, gaming_rx_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, gaming_tx_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, gaming_rx_streams);
+
+
+    // switch to voice tx & Rx (11th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, gaming_tx_streams);
+    sUcastClientInterface->Stop( bap_bd_addr, gaming_rx_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, gaming_tx_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, gaming_rx_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_streams_2.push_back(reconf_voice_rx_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, voice_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+    // switch to media tx (12th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, voice_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_streams_2.push_back(reconf_media_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, media_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    // switch to voice tx & Rx (13th)
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream stop 1 ";
+    sUcastClientInterface->Stop( bap_bd_addr, media_streams);
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    // reconfig the first stream
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    reconf_streams_2.clear();
+    reconf_streams_2.push_back(reconf_voice_rx_info_2);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams_2);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream reconfgured .start  ";
+    sUcastClientInterface->Start( bap_bd_addr, voice_streams);
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for stream disconnect 1 ";
+    sUcastClientInterface->Disconnect( bap_bd_addr, all_three_streams);
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_three_streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << " going for connect all 3 streams";
+    sUcastClientInterface->Connect( bap_bd_addr, true, tri_streams);
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, tri_streams_2);
+
+    sleep(15);
+    LOG(INFO) << __func__ << "going for stream disconnect all 3 streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr, all_three_streams);
+
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_three_streams);
+
+    sleep(5);
+
+  } else if(!strcmp(bap_test, "stereo_headset_dual_cis")) {
+    StreamConnect conn_info_media;
+    StreamConnect conn_info_gaming;
+    StreamConnect conn_info_voice_tx;
+    StreamConnect conn_info_voice_rx;
+    StreamConnect conn_info_media_recording;
+
+    // reconfig information
+    CodecQosConfig codec_qos_config_media_tx_1;
+    CodecQosConfig codec_qos_config_media_tx_2;
+    CodecQosConfig codec_qos_config_gaming_tx_1;
+    CodecQosConfig codec_qos_config_gaming_tx_2;
+    CodecQosConfig codec_qos_config_gaming_tx_rx;
+    CodecQosConfig codec_qos_config_voice_tx_rx;
+    CodecQosConfig codec_qos_config_voice_tx_rx_2;
+    CodecQosConfig codec_qos_config_media_rx_1;
+    CodecQosConfig codec_qos_config_media_rx_2;
+
+    CISConfig cis_config_1_media_tx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 155,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+    CISConfig cis_config_2_media_tx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 155,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+    CISConfig cis_config_1_media_tx_2 = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 310,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+
+    CISConfig cis_config_1_gaming_tx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+    CISConfig cis_config_2_gaming_tx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+    CISConfig cis_config_1_gaming_tx_2 = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 200,
+                                       .max_sdu_s_to_m = 0,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                                    };
+
+
+    CISConfig cis_config_1_gaming_tx_rx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 40,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                             };
+    CISConfig cis_config_2_gaming_tx_rx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 100,
+                                       .max_sdu_s_to_m = 40,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                             };
+
+    CISConfig cis_config_1_voice_tx_rx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 80,
+                                       .max_sdu_s_to_m = 80,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                             };
+    CISConfig cis_config_2_voice_tx_rx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 80,
+                                       .max_sdu_s_to_m = 80,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x02,
+                                       .rtn_s_to_m = 0x02
+                             };
+
+    CISConfig cis_config_1_media_rx = {
+                                       .cis_id = 0,
+                                       .max_sdu_m_to_s = 0,
+                                       .max_sdu_s_to_m = 100,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                                    };
+
+    CISConfig cis_config_2_media_rx = {
+                                       .cis_id = 1,
+                                       .max_sdu_m_to_s = 0,
+                                       .max_sdu_s_to_m = 100,
+                                       .phy_m_to_s = 0x02,
+                                       .phy_s_to_m = 0x02,
+                                       .rtn_m_to_s = 0x05,
+                                       .rtn_s_to_m = 0x05
+                                    };
+    ASCSConfig ascs_config_1 =  {
+                                       .cig_id = 1,
+                                       .cis_id = 0,
+                                       .bi_directional = false,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+
+    ASCSConfig ascs_config_2 =  {
+                                       .cig_id = 1,
+                                       .cis_id = 1,
+                                       .bi_directional = false,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+
+    ASCSConfig ascs_config_gaming_1 =  {
+                                       .cig_id = 1,
+                                       .cis_id = 0,
+                                       .bi_directional = true,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                                  };
+
+    ASCSConfig ascs_config_gaming_2 =  {
+                                       .cig_id = 1,
+                                       .cis_id = 1,
+                                       .bi_directional = true,
+                                       .presentation_delay = {0x20, 0x4E, 0x00}
+                                  };
+
+    ASCSConfig ascs_config_voice_1 =  {
+                                       .cig_id = 3,
+                                       .cis_id = 0,
+                                       .bi_directional = true,
+                                       .presentation_delay = {0x40, 0x9C, 0x00}
+                                  };
+
+    ASCSConfig ascs_config_voice_2 =  {
+                                       .cig_id = 3,
+                                       .cis_id = 1,
+                                       .bi_directional = true,
+                                       .presentation_delay = {0x40, 0x9C, 0x00}
+                                  };
+
+    ASCSConfig ascs_config_media_rx_1 =  {
+                                   .cig_id = 4,
+                                   .cis_id = 0,
+                                   .bi_directional = false,
+                                   .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+
+    ASCSConfig ascs_config_media_rx_2 =  {
+                                   .cig_id = 4,
+                                   .cis_id = 1,
+                                   .bi_directional = false,
+                                   .presentation_delay = {0x20, 0x4E, 0x00}
+                              };
+
+    codec_qos_config_media_tx_1.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_media_tx_1.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_media_tx_1.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    codec_qos_config_media_tx_1.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_media_tx_1.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_media_tx_1.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_media_tx_1.codec_config, 155);
+
+    codec_qos_config_media_tx_2 = codec_qos_config_media_tx_1;
+    UpdateOctsPerFrame(&codec_qos_config_media_tx_2.codec_config, 310);
+
+    codec_qos_config_media_tx_1.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_media_tx_1.qos_config.cis_configs.push_back(cis_config_1_media_tx);
+    codec_qos_config_media_tx_1.qos_config.cis_configs.push_back(cis_config_2_media_tx);
+
+    codec_qos_config_media_tx_1.qos_config.ascs_configs.push_back(ascs_config_1);
+    codec_qos_config_media_tx_1.qos_config.ascs_configs.push_back(ascs_config_2);
+
+    codec_qos_config_media_tx_2.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 1,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_media_tx_2.qos_config.cis_configs.push_back(cis_config_1_media_tx_2);
+
+    codec_qos_config_media_tx_2.qos_config.ascs_configs.push_back(ascs_config_1);
+
+    codec_qos_config_gaming_tx_1.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_gaming_tx_1.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_gaming_tx_1.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    codec_qos_config_gaming_tx_1.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_gaming_tx_1.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_gaming_tx_1.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_gaming_tx_1.codec_config, 100);
+
+    codec_qos_config_gaming_tx_2 = codec_qos_config_gaming_tx_1;
+    UpdateOctsPerFrame(&codec_qos_config_gaming_tx_2.codec_config, 200);
+
+    codec_qos_config_gaming_tx_1.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_gaming_tx_1.qos_config.cis_configs.push_back(cis_config_1_gaming_tx);
+    codec_qos_config_gaming_tx_1.qos_config.cis_configs.push_back(cis_config_2_gaming_tx);
+
+    codec_qos_config_gaming_tx_1.qos_config.ascs_configs.push_back(ascs_config_gaming_1);
+    codec_qos_config_gaming_tx_1.qos_config.ascs_configs.push_back(ascs_config_gaming_2);
+
+    codec_qos_config_gaming_tx_2.qos_config.cig_config = {
+                                       .cig_id = 1,
+                                       .cis_count = 1,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_gaming_tx_2.qos_config.cis_configs.push_back(cis_config_1_gaming_tx_2);
+
+    codec_qos_config_gaming_tx_2.qos_config.ascs_configs.push_back(ascs_config_gaming_1);
+
+    codec_qos_config_gaming_tx_rx.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_gaming_tx_rx.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_gaming_tx_rx.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+    codec_qos_config_gaming_tx_rx.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_gaming_tx_rx.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_gaming_tx_rx.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_gaming_tx_rx.codec_config, 40);
+    codec_qos_config_gaming_tx_rx.qos_config.cig_config = {
+                                       .cig_id = 2,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_gaming_tx_rx.qos_config.cis_configs.push_back(cis_config_1_gaming_tx_rx);
+    codec_qos_config_gaming_tx_rx.qos_config.cis_configs.push_back(cis_config_2_gaming_tx_rx);
+
+    codec_qos_config_gaming_tx_rx.qos_config.ascs_configs.push_back(ascs_config_gaming_1);
+
+    // voice tx rx
+    codec_qos_config_voice_tx_rx.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_voice_tx_rx.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_voice_tx_rx.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_32000;
+    codec_qos_config_voice_tx_rx.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_voice_tx_rx.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_voice_tx_rx.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_voice_tx_rx.codec_config, 80);
+    codec_qos_config_voice_tx_rx.qos_config.cig_config = {
+                                       .cig_id = 3,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s =  0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_voice_tx_rx.qos_config.cis_configs.push_back(cis_config_1_voice_tx_rx);
+    codec_qos_config_voice_tx_rx.qos_config.cis_configs.push_back(cis_config_2_voice_tx_rx);
+
+    codec_qos_config_voice_tx_rx.qos_config.ascs_configs.push_back(ascs_config_voice_1);
+    codec_qos_config_voice_tx_rx.qos_config.ascs_configs.push_back(ascs_config_voice_2);
+
+
+    codec_qos_config_media_rx_1.codec_config.codec_type =
+                                    CodecIndex::CODEC_INDEX_SOURCE_LC3;
+    codec_qos_config_media_rx_1.codec_config.codec_priority =
+                                    CodecPriority::CODEC_PRIORITY_DEFAULT;
+    codec_qos_config_media_rx_1.codec_config.sample_rate =
+                                    CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+    codec_qos_config_media_rx_1.codec_config.channel_mode =
+                                    CodecChannelMode::CODEC_CHANNEL_MODE_MONO;
+    //Frame_Duration
+    UpdateFrameDuration(&codec_qos_config_media_rx_1.codec_config,
+                       static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+    // LC3_Blocks_Per_SDU
+    UpdateLc3BlocksPerSdu(&codec_qos_config_media_rx_1.codec_config, 1);
+    UpdateOctsPerFrame(&codec_qos_config_media_rx_1.codec_config, 100);
+
+    codec_qos_config_media_rx_1.qos_config.cig_config = {
+                                       .cig_id = 4,
+                                       .cis_count = 2,
+                                       .packing = 0x01, // interleaved
+                                       .framing =  0x00, // unframed
+                                       .max_tport_latency_m_to_s = 0x000a,
+                                       .max_tport_latency_s_to_m = 0x000a,
+                                       .sdu_interval_m_to_s = {0x10, 0x27, 0x00},
+                                       .sdu_interval_s_to_m = {0x10, 0x27, 0x00}
+                                      };
+
+    codec_qos_config_media_rx_1.qos_config.cis_configs.push_back(cis_config_1_media_rx);
+    codec_qos_config_media_rx_1.qos_config.cis_configs.push_back(cis_config_2_media_rx);
+
+    codec_qos_config_media_rx_1.qos_config.ascs_configs.push_back(ascs_config_media_rx_1);
+    codec_qos_config_media_rx_1.qos_config.ascs_configs.push_back(ascs_config_media_rx_2);
+
+    conn_info_media.stream_type.type = CONTENT_TYPE_MEDIA;
+    conn_info_media.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    conn_info_media.stream_type.direction = ASE_DIRECTION_SINK;
+    conn_info_media.codec_qos_config_pair.push_back(codec_qos_config_media_tx_1);
+    conn_info_media.codec_qos_config_pair.push_back(codec_qos_config_media_tx_2);
+
+    conn_info_gaming.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_gaming.stream_type.audio_context = CONTENT_TYPE_GAME;
+    conn_info_gaming.stream_type.direction = ASE_DIRECTION_SRC;
+    conn_info_gaming.codec_qos_config_pair.push_back(codec_qos_config_gaming_tx_1);
+
+    conn_info_voice_tx.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_voice_tx.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_voice_tx.stream_type.direction = ASE_DIRECTION_SINK;
+    conn_info_voice_tx.codec_qos_config_pair.push_back(codec_qos_config_voice_tx_rx);
+
+    conn_info_voice_rx.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_voice_rx.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    conn_info_voice_rx.stream_type.direction = ASE_DIRECTION_SRC;
+    conn_info_voice_rx.codec_qos_config_pair.push_back(codec_qos_config_voice_tx_rx);
+
+    conn_info_media_recording.stream_type.type = CONTENT_TYPE_MEDIA;
+    conn_info_media_recording.stream_type.audio_context = CONTENT_TYPE_LIVE;
+    conn_info_media_recording.stream_type.direction = ASE_DIRECTION_SRC;
+    conn_info_media_recording.codec_qos_config_pair.push_back(codec_qos_config_media_rx_1);
+
+    std::vector<StreamConnect> four_streams;
+    four_streams.push_back(conn_info_media);
+    four_streams.push_back(conn_info_media_recording);
+    four_streams.push_back(conn_info_voice_tx);
+    four_streams.push_back(conn_info_voice_rx);
+
+    std::vector<StreamConnect> voice_conn_streams;
+    voice_conn_streams.push_back(conn_info_voice_tx);
+    voice_conn_streams.push_back(conn_info_voice_rx);
+
+    std::vector<StreamConnect> media_conn_streams;
+    media_conn_streams.push_back(conn_info_media);
+
+    StreamType type_media = { .type = CONTENT_TYPE_MEDIA,
+                              .audio_context = CONTENT_TYPE_MEDIA,
+                              .direction = ASE_DIRECTION_SINK
+                            };
+
+    StreamType type_gaming_tx = { .type = CONTENT_TYPE_MEDIA,
+                                  .audio_context = CONTENT_TYPE_GAME,
+                                  .direction = ASE_DIRECTION_SINK
+                            };
+
+    StreamType type_voice_tx = { .type = CONTENT_TYPE_CONVERSATIONAL,
+                                 .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                                 .direction = ASE_DIRECTION_SINK
+                            };
+
+    StreamType type_voice_rx = { .type = CONTENT_TYPE_CONVERSATIONAL,
+                                 .audio_context = CONTENT_TYPE_CONVERSATIONAL,
+                                 .direction = ASE_DIRECTION_SRC
+                            };
+
+    StreamType type_media_rx = { .type = CONTENT_TYPE_MEDIA,
+                                 .audio_context = CONTENT_TYPE_LIVE,
+                                 .direction = ASE_DIRECTION_SRC
+                               };
+
+    std::vector<StreamType> media_streams;
+    media_streams.push_back(type_media);
+
+    std::vector<StreamType> gaming_tx_streams;
+    gaming_tx_streams.push_back(type_gaming_tx);
+
+    std::vector<StreamType> media_rx_streams;
+    media_rx_streams.push_back(type_media_rx);
+
+    std::vector<StreamType> voice_streams;
+    voice_streams.push_back(type_voice_tx);
+    voice_streams.push_back(type_voice_rx);
+
+    std::vector<StreamType> all_four_streams;
+    all_four_streams.push_back(type_media);
+    all_four_streams.push_back(type_voice_tx);
+    all_four_streams.push_back(type_voice_rx);
+    all_four_streams.push_back(type_media_rx);
+
+    std::vector<StreamReconfig> reconf_streams;
+
+    StreamReconfig reconf_media_info;
+    reconf_media_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    reconf_media_info.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    reconf_media_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_media_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_media_info.codec_qos_config_pair.push_back(codec_qos_config_media_tx_1);
+    reconf_media_info.codec_qos_config_pair.push_back(codec_qos_config_media_tx_2);
+
+    StreamReconfig reconf_gaming_tx_info;
+    reconf_gaming_tx_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_info.stream_type.audio_context = CONTENT_TYPE_MEDIA;
+    reconf_gaming_tx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_gaming_tx_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_gaming_tx_info.codec_qos_config_pair.push_back(codec_qos_config_gaming_tx_1);
+    reconf_gaming_tx_info.codec_qos_config_pair.push_back(codec_qos_config_gaming_tx_2);
+
+    StreamReconfig reconf_voice_tx_info;
+    reconf_voice_tx_info.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_tx_info.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_tx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_voice_tx_info.stream_type.direction = ASE_DIRECTION_SINK;
+    reconf_voice_tx_info.codec_qos_config_pair.
+                                 push_back(codec_qos_config_voice_tx_rx);
+
+    StreamReconfig reconf_voice_rx_info;
+    reconf_voice_rx_info.stream_type.type = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_rx_info.stream_type.audio_context = CONTENT_TYPE_CONVERSATIONAL;
+    reconf_voice_rx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_voice_rx_info.stream_type.direction = ASE_DIRECTION_SRC;
+    reconf_voice_rx_info.codec_qos_config_pair.
+                       push_back(codec_qos_config_voice_tx_rx);
+
+    StreamReconfig reconf_media_rx_info;
+    reconf_media_rx_info.stream_type.type = CONTENT_TYPE_MEDIA;
+    reconf_media_rx_info.stream_type.audio_context = CONTENT_TYPE_LIVE;
+    reconf_media_rx_info.reconf_type = StreamReconfigType::CODEC_CONFIG;
+    reconf_media_rx_info.stream_type.direction = ASE_DIRECTION_SRC;
+    reconf_media_rx_info.codec_qos_config_pair.push_back(codec_qos_config_media_rx_1);
+
+    LOG(INFO) << __func__ << " going for generic test case";
+    LOG(INFO) << __func__ << " going for connect with voice tx and rx";
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, four_streams);
+
+    sleep(15);
+
+#if 0
+    //  Recording to music switch (12)
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for music  stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << "going for music  stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    //sleep(5);
+    LOG(INFO) << __func__ << "going for stream disconnect voice streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_four_streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << " going for connect with voice tx and rx";
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, four_streams);
+
+    sleep(15);
+
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for music  stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    //sleep(5);
+    LOG(INFO) << __func__ << "going for stream disconnect voice streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_four_streams);
+
+    sleep(10);
+
+
+    LOG(INFO) << __func__ << " going for connect with voice tx and rx";
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, four_streams);
+
+    sleep(15);
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_tx_info);
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for voice stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for voice stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+
+     // Call Tx Rx to Gaming switch (4)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+    sleep(5);
+
+    LOG(INFO) << __func__ << " going for voice stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+    sleep(5);
+
+    LOG(INFO) << __func__ << "going for voice stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+
+     // Call Tx Rx to Gaming switch (4)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+
+    //sleep(5);
+    LOG(INFO) << __func__ << "going for stream disconnect voice streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_four_streams);
+
+    sleep(10);
+
+
+    LOG(INFO) << __func__ << " going for connect with voice tx and rx";
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, four_streams);
+    sleep(15);
+
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_tx_info);
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for voice stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for voice stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+    //sleep(5);
+    LOG(INFO) << __func__ << "going for stream disconnect voice streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_four_streams);
+
+    sleep(10);
+
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, four_streams);
+
+    sleep(1);
+
+    LOG(INFO) << __func__ << "going for stream disconnect voice streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_four_streams);
+
+    sleep(10);
+
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, four_streams);
+
+    sleep(15);
+
+#endif
+#if 1
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_tx_info);
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for voice stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    //usleep(200000);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << "going for voice stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+
+     // Call Tx Rx to Gaming switch (4)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << " going for voice stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << "going for voice stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+
+     // Call Tx Rx to Gaming switch (4)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << "going for voice stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+    //usleep(200000);
+    LOG(INFO) << __func__ << "going for stream disconnect voice streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_four_streams);
+
+    sleep(10);
+
+    sUcastClientInterface->Connect( bap_bd_addr_2, true, four_streams);
+#endif
+
+    sleep(15);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+
+    // Music streaming
+    LOG(INFO) << __func__ << "going for media stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    // music to gaming Tx switch  (1)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_gaming_tx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for gaming stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, gaming_tx_streams);
+
+    // Gaming Tx to music switch  (2)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, gaming_tx_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for gaming stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+     // music to Call Tx Rx switch (3)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_tx_info);
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for voice stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for voice stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+
+     // Call Tx Rx to Gaming switch (4)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_gaming_tx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for gaming stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, gaming_tx_streams);
+
+
+     // Gaming to Cal Audio switch (5)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, gaming_tx_streams);
+
+    sleep(5);
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_tx_info);
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for voice stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for voice stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+
+
+     // Cal Audio to music switch (6)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for music  stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+
+     //  music to Recording switch (7)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_rx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for recording stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_rx_streams);
+
+
+     //  Recording to Gaming switch (8)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_rx_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_gaming_tx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for gaming stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, gaming_tx_streams);
+
+
+     //  Gaming to Recording switch (9)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, gaming_tx_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_rx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for recording stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_rx_streams);
+
+
+     //  Recording to Call Audio switch (10)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_rx_streams);
+
+    sleep(5);
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_voice_tx_info);
+    reconf_streams.push_back(reconf_voice_rx_info);
+    LOG(INFO) << __func__ << " going for voice stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for voice stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, voice_streams);
+
+
+     //  Call Audio to Recording switch (11)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, voice_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_rx_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for recording stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_rx_streams);
+
+
+     //  Recording to music switch (12)
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_rx_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    reconf_streams.clear();
+    reconf_streams.push_back(reconf_media_info);
+    sUcastClientInterface->Reconfigure( bap_bd_addr_2, reconf_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for music  stream start ";
+    sUcastClientInterface->Start( bap_bd_addr_2, media_streams);
+
+    sleep(5);
+    LOG(INFO) << __func__ << "going for media stream stop ";
+    sUcastClientInterface->Stop( bap_bd_addr_2, media_streams);
+
+    //sleep(5);
+    LOG(INFO) << __func__ << "going for stream disconnect voice streams ";
+    sUcastClientInterface->Disconnect( bap_bd_addr_2, all_four_streams);
+
+    sleep(10);
+
+  } else if(!strcmp(bap_test, "connect")) {
+    LOG(INFO) << __func__ << " going for connect test case";
+
+    for ( uint8_t i = 0; i < 5; i++) {
+      LOG(INFO) << __func__ << " iteration " << loghex(i);
+      LOG(INFO) << __func__ << " going for connect";
+      sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+      usleep( i* 1000 * 1000);
+      LOG(INFO) << __func__ << " going for stream disconnect";
+      sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+    }
+  } else if(!strcmp(bap_test, "release_in_streaming")) {
+    LOG(INFO) << __func__ << " going for release_in_streaming test case";
+
+    LOG(INFO) << __func__ << " going for connect";
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << " going for stream start";
+    sUcastClientInterface->Start( bap_bd_addr, start_streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << "going for stream disconnect";
+    sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+
+
+  } else if(!strcmp(bap_test, "release_in_enabling")) {
+    LOG(INFO) << __func__ << " going for release_in_enabling test case";
+
+    LOG(INFO) << __func__ << " going for connect";
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << " going for stream start";
+    sUcastClientInterface->Start( bap_bd_addr, start_streams);
+
+    usleep(150 *1000);
+
+    LOG(INFO) << __func__ << "going for stream disconnect";
+    sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+
+
+  } else if(!strcmp(bap_test, "release_in_disabling")) {
+    LOG(INFO) << __func__ << " going for release_in_disabling test case";
+
+    LOG(INFO) << __func__ << " going for connect";
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << " going for stream start";
+    sUcastClientInterface->Start( bap_bd_addr, start_streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << " going for stream stop";
+    sUcastClientInterface->Stop( bap_bd_addr, start_streams);
+
+    usleep(100 * 1000);
+    LOG(INFO) << __func__ << "going for stream disconnect";
+    sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+
+  } else if(!strcmp(bap_test, "disable_in_enabling")) {
+    LOG(INFO) << __func__ << " going for disable_in_enabling test case";
+
+    LOG(INFO) << __func__ << " going for connect";
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << " going for stream start";
+    sUcastClientInterface->Start( bap_bd_addr, start_streams);
+
+    usleep(200 * 1000);
+    LOG(INFO) << __func__ << "going for stream stop";
+    sUcastClientInterface->Stop( bap_bd_addr, start_streams);
+
+  } else if(!strcmp(bap_test, "disable_in_streaming")) {
+    LOG(INFO) << __func__ << " going for disable_in_streaming test case";
+
+    LOG(INFO) << __func__ << " going for connect";
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << " going for stream start";
+    sUcastClientInterface->Start( bap_bd_addr, start_streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << "going for stream stop";
+    sUcastClientInterface->Stop( bap_bd_addr, start_streams);
+
+  } else if(!strcmp(bap_test, "release_in_codec_configured")) {
+    LOG(INFO) << __func__ << " going for release_in_codec_configured test case";
+
+    LOG(INFO) << __func__ << " going for connect";
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    usleep(2600 * 1000);
+    LOG(INFO) << __func__ << "going for stream disconnect";
+    sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+
+  } else if(!strcmp(bap_test, "release_in_qos_configured")) {
+    LOG(INFO) << __func__ << " going for release_in_qos_configured test case";
+
+    LOG(INFO) << __func__ << " going for connect";
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << "going for stream disconnect";
+    sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+
+  } else if(!strcmp(bap_test, "enable_in_qos_configured")) {
+    LOG(INFO) << __func__ << " going for enable_in_qos_configured test case";
+
+    LOG(INFO) << __func__ << " going for connect";
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    sleep(10);
+
+    LOG(INFO) << __func__ << "going for stream start";
+    sUcastClientInterface->Start( bap_bd_addr, start_streams);
+
+  } else if(!strcmp(bap_test, "start")) {
+    LOG(INFO) << __func__ << " going for start test case";
+
+    LOG(INFO) << __func__ << " going for stop while starting test case";
+
+
+    for ( uint8_t i = 0; i < 5; i++) {
+      LOG(INFO) << __func__ << " iteration " << loghex(i);
+      LOG(INFO) << __func__ << " going for connect";
+      sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+      sleep(5);
+      LOG(INFO) << __func__ << " going for stream start";
+      sUcastClientInterface->Start( bap_bd_addr, start_streams);
+      usleep( i* 200 * 1000);
+      LOG(INFO) << __func__ << " going for stream stop";
+      sUcastClientInterface->Stop( bap_bd_addr, start_streams);
+      sleep(5);
+
+      LOG(INFO) << __func__ << " going for stream disconnect";
+      sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+    }
+
+    LOG(INFO) << __func__ << " going for disconnect while starting test case";
+
+    for ( uint8_t i = 0; i < 5; i++) {
+      LOG(INFO) << __func__ << " iteration " << loghex(i);
+      LOG(INFO) << __func__ << " going for connect";
+      sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+      sleep(5);
+
+      LOG(INFO) << __func__ << " going for stream start";
+      sUcastClientInterface->Start( bap_bd_addr, start_streams);
+      usleep( i* 200 * 1000);
+      LOG(INFO) << __func__ << " going for stream disconnect";
+      sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+      sleep(5);
+    }
+
+  } else if(!strcmp(bap_test, "stop")) {
+    LOG(INFO) << __func__ << " going for stop test case";
+    LOG(INFO) << __func__ << " going for connect";
+
+    for ( uint8_t i = 0; i < 5; i++) {
+      LOG(INFO) << __func__ << " iteration " << loghex(i);
+      LOG(INFO) << __func__ << " going for disconnect while stopping";
+      sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+      sleep(5);
+
+      LOG(INFO) << __func__ << " going for stream start";
+      sUcastClientInterface->Start( bap_bd_addr, start_streams);
+
+      sleep(5);
+
+      LOG(INFO) << __func__ << " going for stream stop";
+      sUcastClientInterface->Stop( bap_bd_addr, start_streams);
+      usleep( i* 200 * 1000);
+
+      LOG(INFO) << __func__ << " going for stream disconnect";
+      sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+    }
+
+  } else if(!strcmp(bap_test, "stop")) {
+    LOG(INFO) << __func__ << " going for stop test case";
+    LOG(INFO) << __func__ << " going for connect";
+
+    for ( uint8_t i = 0; i < 5; i++) {
+      LOG(INFO) << __func__ << " iteration " << loghex(i);
+      LOG(INFO) << __func__ << " going for disconnect while stopping";
+      sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+      sleep(5);
+
+      LOG(INFO) << __func__ << " going for stream start";
+      sUcastClientInterface->Start( bap_bd_addr, start_streams);
+
+      sleep(5);
+
+      LOG(INFO) << __func__ << " going for stream stop";
+      sUcastClientInterface->Stop( bap_bd_addr, start_streams);
+      usleep( i* 200 * 1000);
+
+      LOG(INFO) << __func__ << " going for stream disconnect";
+      sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+    }
+  } else if(!strcmp(bap_test, "reconfigure")) {
+    LOG(INFO) << __func__ << " going for reconfigure test case";
+    LOG(INFO) << __func__ << " going for connect";
+
+    sUcastClientInterface->Connect( bap_bd_addr, true, streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << " going for stream codec reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << " going for stream qos reconfig";
+    sUcastClientInterface->Reconfigure( bap_bd_addr, reconf_qos_streams);
+
+    sleep(5);
+
+    LOG(INFO) << __func__ << " going for stream disconnect";
+    sUcastClientInterface->Disconnect( bap_bd_addr, start_streams);
+
+  }
+
+  LOG(INFO) << __func__ << " Test completed";
+#if 0
+  sleep(2);
+  LOG(INFO) << __func__ << "going for connect";
+  sUcastClientInterface->Connect( bap_bd_addr);
+  sleep(7);
+  LOG(INFO) << __func__ << "going for discovery";
+  sUcastClientInterface->StartDiscovery( bap_bd_addr);
+  sleep(2);
+  LOG(INFO) << __func__ << "going for getAudiocontexts";
+  sUcastClientInterface->GetAvailableAudioContexts( bap_bd_addr);
+  sleep(2);
+  LOG(INFO) << __func__ << "going for disconnect";
+  sUcastClientInterface->Disconnect( bap_bd_addr);
+  sleep(1);
+  sUcastClientInterface->Cleanup();
+  sleep(1);
+  sUcastClientInterface->Init(&sUcastClientCallbacks);
+  sleep(1);
+  LOG(INFO) << __func__ << "going for connect 2 ";
+  sUcastClientInterface->Connect( bap_bd_addr);
+  sleep(7);
+  LOG(INFO) << __func__ << "going for discovery 2";
+  sUcastClientInterface->StartDiscovery( bap_bd_addr);
+  sleep(2);
+  LOG(INFO) << __func__ << "going for getAudiocontexts 2";
+  sUcastClientInterface->GetAvailableAudioContexts( bap_bd_addr);
+  sleep(2);
+    LOG(INFO) << __func__ << "going for disconnect 2";
+  sUcastClientInterface->Disconnect( bap_bd_addr);
+  sleep(1);
+  sUcastClientInterface->Cleanup();
+#endif
+}
+
+bool test_bap_uclient () {
+  RawAddress::FromString("00:02:5b:00:ff:00", bap_bd_addr);
+  test_thread = thread_new("test_bap_uclient");
+  LOG(INFO) << __func__ << "going for test setup";
+  thread_post(test_thread, event_test_bap_uclient, NULL);
+  return true;
+}
+
+// PACS related test code
+using bluetooth::bap::pacs::PacsClientInterface;
+using bluetooth::bap::pacs::PacsClientCallbacks;
+
+static PacsClientInterface* sPacsClientInterface = nullptr;
+static uint16_t pacs_client_id = 0;
+static RawAddress pac_bd_addr;
+
+class PacsClientCallbacksImpl : public PacsClientCallbacks {
+ public:
+  ~PacsClientCallbacksImpl() = default;
+  void OnInitialized(int status,
+                     int client_id) override {
+    LOG(WARNING) << __func__ << client_id;
+    pacs_client_id = client_id;
+  }
+  void OnConnectionState(const RawAddress& bd_addr,
+                         ConnectionState state) override {
+    LOG(WARNING) << __func__;
+    if(state == ConnectionState::CONNECTED)  {
+      LOG(WARNING) << __func__ << "connected";
+    } else if(state == ConnectionState::DISCONNECTED)  {
+      LOG(WARNING) << __func__ << "Disconnected";
+    }
+
+  }
+  void OnAudioContextAvailable(const RawAddress& bd_addr,
+                        uint32_t available_contexts) override {
+    LOG(INFO) << __func__;
+  }
+   void OnSearchComplete(int status, const RawAddress& address,
+            std::vector<bluetooth::bap::pacs::CodecConfig> sink_pac_records,
+            std::vector<bluetooth::bap::pacs::CodecConfig> src_pac_records,
+            uint32_t sink_locations,
+            uint32_t src_locations,
+            uint32_t available_contexts,
+            uint32_t supported_contexts) override {
+    LOG(WARNING) << __func__;
+  }
+};
+
+static PacsClientCallbacksImpl sPacsClientCallbacks;
+
+static void event_test_pacs(UNUSED_ATTR void* context) {
+  sPacsClientInterface = btif_pacs_client_get_interface();
+  sPacsClientInterface->Init(&sPacsClientCallbacks);
+  sleep(1);
+  LOG(INFO) << __func__ << "going for connect";
+  sPacsClientInterface->Connect(pacs_client_id, pac_bd_addr);
+  sleep(7);
+  LOG(INFO) << __func__ << "going for discovery";
+  sPacsClientInterface->StartDiscovery(pacs_client_id, pac_bd_addr);
+  sleep(2);
+  LOG(INFO) << __func__ << "going for getAudiocontexts";
+  sPacsClientInterface->GetAvailableAudioContexts(pacs_client_id, pac_bd_addr);
+  sleep(2);
+    LOG(INFO) << __func__ << "going for disconnect";
+  sPacsClientInterface->Disconnect(pacs_client_id, pac_bd_addr);
+
+  sleep(2);
+  LOG(INFO) << __func__ << "going for connect";
+  sPacsClientInterface->Connect(pacs_client_id, pac_bd_addr);
+  sleep(7);
+  LOG(INFO) << __func__ << "going for discovery";
+  sPacsClientInterface->StartDiscovery(pacs_client_id, pac_bd_addr);
+  sleep(2);
+  LOG(INFO) << __func__ << "going for getAudiocontexts";
+  sPacsClientInterface->GetAvailableAudioContexts(pacs_client_id, pac_bd_addr);
+  sleep(2);
+  LOG(INFO) << __func__ << "going for disconnect";
+  sPacsClientInterface->Disconnect(pacs_client_id, pac_bd_addr);
+
+  sleep(1);
+  sPacsClientInterface->Cleanup(pacs_client_id);
+
+  sleep(1);
+
+  sPacsClientInterface->Init(&sPacsClientCallbacks);
+  sleep(1);
+  LOG(INFO) << __func__ << "going for connect 2 ";
+  sPacsClientInterface->Connect(pacs_client_id, pac_bd_addr);
+  sleep(7);
+  LOG(INFO) << __func__ << "going for discovery 2";
+  sPacsClientInterface->StartDiscovery(pacs_client_id, pac_bd_addr);
+  sleep(2);
+  LOG(INFO) << __func__ << "going for getAudiocontexts 2";
+  sPacsClientInterface->GetAvailableAudioContexts(pacs_client_id, pac_bd_addr);
+  sleep(2);
+    LOG(INFO) << __func__ << "going for disconnect 2";
+  sPacsClientInterface->Disconnect(pacs_client_id, pac_bd_addr);
+
+  sleep(1);
+  sPacsClientInterface->Cleanup(pacs_client_id);
+
+}
+
+bool test_pacs (RawAddress& bd_addr) {
+
+  test_thread = thread_new("test_pacs");
+  pac_bd_addr = bd_addr;
+  thread_post(test_thread, event_test_pacs, NULL);
+  return true;
+}
diff --git a/le_audio/system/bt/btif/src/btif_cc.cc b/le_audio/system/bt/btif/src/btif_cc.cc
new file mode 100644
index 0000000..59c355d
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_cc.cc
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+/* CC Interface */
+#define LOG_TAG "bt_btif_cc"
+
+#include "bt_target.h"
+#include "bta_closure_api.h"
+#include "btif_common.h"
+#include "btif_storage.h"
+#include "bta_cc_api.h"
+
+#include <base/at_exit.h>
+#include <base/bind.h>
+#include <base/threading/thread.h>
+#include <base/callback.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bluetooth_callcontrol_callbacks.h>
+#include <hardware/bluetooth_callcontrol_interface.h>
+
+using base::Bind;
+using base::Unretained;
+using base::Owned;
+using bluetooth::Uuid;
+using std::vector;
+
+using base::Unretained;
+using bluetooth::call_control::CallControllerInterface;
+using bluetooth::call_control::CallControllerCallbacks;
+
+namespace {
+class CallControllerInterfaceImpl;
+std::unique_ptr<CallControllerInterface> CallControllerInstance;
+
+class CallControllerInterfaceImpl
+  : public CallControllerInterface, public CallControllerCallbacks {
+  ~CallControllerInterfaceImpl() = default;
+
+ bt_status_t Init(CallControllerCallbacks* callbacks, Uuid uuid, int max_ccs_clients,
+                              bool inband_ringing_enabled) override {
+
+    LOG(INFO) << __func__ ;
+    this->callbacks = callbacks;
+    do_in_bta_thread(FROM_HERE,Bind(&CallController::Initialize,
+           this, uuid,max_ccs_clients, inband_ringing_enabled));
+    return BT_STATUS_SUCCESS;
+ }
+
+ bt_status_t UpdateBearerName(uint8_t* operator_str) {
+
+    LOG(INFO) << __func__ << ": bearer name " << operator_str;
+    uint8_t* bName = (uint8_t*)malloc(sizeof(uint8_t)*strlen((char*)operator_str)+1);
+    if (bName != NULL) {
+      memcpy(bName, operator_str, strlen((char*)operator_str)+1);
+      do_in_bta_thread(FROM_HERE,Bind(&CallController::BearerInfoName,
+              Unretained(CallController::Get()), bName));
+      free(bName);
+    }
+    return BT_STATUS_SUCCESS;
+ }
+
+ void Cleanup() {
+   LOG(INFO) << __func__ ;
+   do_in_bta_thread(FROM_HERE,Bind(&CallController::CleanUp));
+ }
+
+bt_status_t UpdateBearerTechnology(int bearer_tech) {
+
+  LOG(INFO) << __func__ << ": " << bearer_tech;
+  do_in_bta_thread(FROM_HERE,Bind(&CallController::UpdateBearerTechnology,
+                Unretained(CallController::Get()), bearer_tech));
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t  UpdateSupportedBearerList(uint8_t* supportedbearer_list) {
+
+ LOG(INFO) << __func__ << ": " << supportedbearer_list;
+ uint8_t* sList = (uint8_t*)malloc(sizeof(uint8_t)*strlen((char*)supportedbearer_list)+1);
+ if (sList != NULL) {
+   memcpy(sList, supportedbearer_list, strlen((char*)supportedbearer_list)+1);
+   do_in_bta_thread(FROM_HERE,Bind(&CallController::UpdateSupportedBearerList,
+        Unretained(CallController::Get()), sList));
+   free(sList);
+ }
+ return BT_STATUS_SUCCESS;
+
+}
+bt_status_t UpdateStatusFlags(uint8_t status_flag) {
+  LOG(INFO) << __func__ << ": " << status_flag;
+  do_in_bta_thread(FROM_HERE,Bind(&CallController::UpdateStatusFlags,
+            Unretained(CallController::Get()), status_flag));
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t UpdateSignalStatus(int signal) {
+
+ LOG(INFO) << __func__ << ": " << signal;
+ do_in_bta_thread(FROM_HERE,Bind(&CallController::UpdateBearerSignalStrength,
+           Unretained(CallController::Get()), signal));
+ return BT_STATUS_SUCCESS;
+
+}
+
+bt_status_t CallControlOptionalOpSupported(int feature) {
+
+ LOG(INFO) << __func__ << ": " << feature;
+ do_in_bta_thread(FROM_HERE,Bind(&CallController::CallControlOptionalOpSupported,
+           Unretained(CallController::Get()), feature));
+ return BT_STATUS_SUCCESS;
+}
+
+bt_status_t CallState(int len, std::vector<uint8_t> call_state_list) {
+
+ LOG(INFO) << __func__ << ": ";
+    do_in_bta_thread(FROM_HERE,Bind(&CallController::CallState,
+           Unretained(CallController::Get()), len, std::move(call_state_list)));
+ return BT_STATUS_SUCCESS;
+
+}
+
+void UpdateIncomingCall(int index, uint8_t* Uri) {
+  LOG(INFO) << __func__ << ": " <<Uri;
+  uint8_t* callUri = (uint8_t*)malloc(sizeof(uint8_t)*strlen((char*)Uri)+1);
+  if (callUri != NULL) {
+    memcpy(callUri, Uri, strlen((char*)Uri)+1);
+    do_in_bta_thread(FROM_HERE,Bind(&CallController::UpdateIncomingCall,
+           Unretained(CallController::Get()), index, callUri));
+    free(callUri);
+  }
+}
+
+void IncomingCallTargetUri(int index, uint8_t* target_uri) {
+  LOG(INFO) << __func__ << ": " <<target_uri;
+  uint8_t* callUri = (uint8_t*)malloc(sizeof(uint8_t)*strlen((char*)target_uri)+1);
+  if (callUri != NULL) {
+    memcpy(callUri, target_uri, strlen((char*)target_uri)+1);
+
+    do_in_bta_thread(FROM_HERE,Bind(&CallController::UpdateIncomingCallTargetUri,
+           Unretained(CallController::Get()), index, callUri));
+    free(callUri);
+  }
+}
+
+void Disconnect(const RawAddress& address) {
+
+ LOG(INFO) << __func__ << ": " <<address;
+ do_in_bta_thread(FROM_HERE,Bind(&CallController::Disconnect,
+           Unretained(CallController::Get()), address));
+ return;
+
+}
+
+void ContentControlId(uint32_t ccid) {
+  LOG(INFO) << __func__ << ": " << ccid;
+  do_in_bta_thread(FROM_HERE,
+     Bind(&CallController::ContentControlId, Unretained(CallController::Get()), ccid));
+}
+
+bt_status_t CallControlResponse(uint8_t op, uint8_t index,  uint32_t status, const RawAddress& address) {
+
+ LOG(INFO) << __func__ << ": ";
+ do_in_bta_thread(FROM_HERE,Bind(&CallController::CallControlResponse,
+       Unretained(CallController::Get()), op, index, status, address));
+ return BT_STATUS_SUCCESS;
+}
+
+void SetActiveDevice(const RawAddress& address, int set_id) override {
+    LOG(INFO) << __func__ << ": set_id" << set_id<< ": device"<< address;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&CallController::SetActiveDevice, Unretained(CallController::Get()), address, set_id));
+}
+
+void ConnectionStateCallback(uint8_t state, const RawAddress& address) override {
+
+ LOG(INFO) << __func__ << ": device=" << address << " state=" << state;
+    do_in_jni_thread(FROM_HERE, Bind(&CallControllerCallbacks::ConnectionStateCallback,
+            Unretained(callbacks), state, address));
+
+}
+
+void CallControlCallback(uint8_t op, std::vector<int32_t> indicies, int count, std::vector<uint8_t> uri_data, const RawAddress& address) override {
+
+  LOG(INFO) << __func__ << ": device=" << address << " operation=" << op;
+
+  do_in_jni_thread(FROM_HERE, Bind(&CallControllerCallbacks::CallControlCallback,
+             Unretained(callbacks), op, std::move(indicies), count, std::move(uri_data), address));
+}
+
+ void CallControlInitializedCallback(uint8_t state) override {
+   LOG(INFO) << __func__ << ": state=" << state;
+   do_in_jni_thread(FROM_HERE, Bind(&CallControllerCallbacks::CallControlInitializedCallback,
+              Unretained(callbacks), state));
+ }
+
+ private:
+   CallControllerCallbacks* callbacks;
+  };
+}//namespace
+
+const CallControllerInterface* btif_cc_server_get_interface(void) {
+   LOG(INFO) << __func__;
+   if (!CallControllerInstance)
+     CallControllerInstance.reset(new CallControllerInterfaceImpl());
+   return CallControllerInstance.get();
+}
diff --git a/le_audio/system/bt/btif/src/btif_csip.cc b/le_audio/system/bt/btif/src/btif_csip.cc
new file mode 100644
index 0000000..4700c8e
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_csip.cc
@@ -0,0 +1,252 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ *  Filename:      btif_csip.c
+ *
+ *  Description:   CSIP client implementation (Set Coordinator)
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_btif_csip"
+
+#include <base/at_exit.h>
+#include <base/bind.h>
+#include <base/threading/thread.h>
+#include <bluetooth/uuid.h>
+#include <errno.h>
+#include <hardware/bluetooth.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vector>
+#include "device/include/controller.h"
+#include "bta_csip_api.h"
+
+#include "btif_api.h"
+#include "btif_common.h"
+#include "btif_util.h"
+
+#include <hardware/bt_csip.h>
+
+using base::Bind;
+using bluetooth::Uuid;
+
+static btcsip_callbacks_t* bt_csip_callbacks = NULL;
+
+void btif_new_set_found_cb(tBTA_CSIP_NEW_SET_FOUND params) {
+  HAL_CBACK(bt_csip_callbacks, new_set_found_cb, params.set_id, params.addr,
+            params.size, params.sirk, params.including_srvc_uuid,
+            params.lock_support);
+}
+
+void btif_conn_state_changed_cb(tBTA_CSIP_CONN_STATE_CHANGED params) {
+  HAL_CBACK(bt_csip_callbacks, conn_state_cb, params.app_id, params.addr,
+            params.state, params.status);
+}
+
+void btif_new_set_member_found_cb(tBTA_SET_MEMBER_FOUND params) {
+  HAL_CBACK(bt_csip_callbacks, new_set_member_cb, params.set_id, params.addr);
+}
+
+void btif_lock_status_changed_cb(tBTA_LOCK_STATUS_CHANGED params) {
+  HAL_CBACK(bt_csip_callbacks, lock_status_cb, params.app_id, params.set_id,
+            params.value, params.status, params.addr);
+}
+
+void btif_lock_available_cb(tBTA_LOCK_AVAILABLE params) {
+  HAL_CBACK(bt_csip_callbacks, lock_available_cb, params.app_id, params.set_id,
+            params.addr);
+}
+
+void btif_set_size_changed_cb (tBTA_CSIP_SET_SIZE_CHANGED params) {
+  HAL_CBACK(bt_csip_callbacks, size_changed_cb, params.set_id, params.size,
+            params.addr);
+}
+
+void btif_set_sirk_changed_cb (tBTA_CSIP_SET_SIRK_CHANGED params) {
+  HAL_CBACK(bt_csip_callbacks, sirk_changed_cb, params.set_id, params.sirk,
+            params.addr);
+}
+
+const char* btif_csip_get_event_name(tBTA_CSIP_EVT event) {
+  switch(event) {
+    case BTA_CSIP_LOCK_STATUS_CHANGED_EVT:
+      return "BTA_CSIP_LOCK_STATUS_CHANGED_EVT";
+    case BTA_CSIP_SET_MEMBER_FOUND_EVT:
+      return "BTA_CSIP_SET_MEMBER_FOUND_EVT";
+    case BTA_CSIP_LOCK_AVAILABLE_EVT:
+      return "BTA_CSIP_LOCK_AVAILABLE_EVT";
+    case BTA_CSIP_NEW_SET_FOUND_EVT:
+      return "BTA_CSIP_NEW_SET_FOUND_EVT";
+    case BTA_CSIP_CONN_STATE_CHG_EVT:
+      return "BTA_CSIP_CONN_STATE_CHG_EVT";
+    case BTA_CSIP_SET_SIZE_CHANGED:
+      return "BTA_CSIP_SET_SIZE_CHANGED";
+    case BTA_CSIP_SET_SIRK_CHANGED:
+      return "BTA_CSIP_SET_SIRK_CHANGED";
+    default:
+      return "UNKNOWN_EVENT";
+  }
+}
+
+void btif_csip_evt (tBTA_CSIP_EVT event, tBTA_CSIP_DATA* p_data) {
+  BTIF_TRACE_EVENT("%s: Event = %02x (%s)", __func__, event, btif_csip_get_event_name(event));
+
+  switch (event) {
+    case BTA_CSIP_LOCK_STATUS_CHANGED_EVT: {
+        tBTA_LOCK_STATUS_CHANGED lock_status_params = p_data->lock_status_param;
+        do_in_jni_thread(Bind(btif_lock_status_changed_cb, lock_status_params));
+      }
+      break;
+
+    case BTA_CSIP_LOCK_AVAILABLE_EVT: {
+        tBTA_LOCK_AVAILABLE lock_avl_param = p_data->lock_available_param;
+        do_in_jni_thread(Bind(btif_lock_available_cb, lock_avl_param));
+      }
+      break;
+
+    case BTA_CSIP_NEW_SET_FOUND_EVT: {
+        tBTA_CSIP_NEW_SET_FOUND new_set_params = p_data->new_set_params;
+        memcpy(new_set_params.sirk, p_data->new_set_params.sirk, SIRK_SIZE);
+        do_in_jni_thread(Bind(btif_new_set_found_cb, new_set_params));
+      }
+      break;
+
+    case BTA_CSIP_SET_MEMBER_FOUND_EVT: {
+        tBTA_SET_MEMBER_FOUND new_member_params = p_data->set_member_param;
+        do_in_jni_thread(Bind(btif_new_set_member_found_cb, new_member_params));
+      }
+      break;
+
+    case BTA_CSIP_CONN_STATE_CHG_EVT: {
+        tBTA_CSIP_CONN_STATE_CHANGED conn_params = p_data->conn_params;
+        do_in_jni_thread(Bind(btif_conn_state_changed_cb, conn_params));
+      }
+      break;
+
+   case BTA_CSIP_SET_SIZE_CHANGED: {
+        tBTA_CSIP_SET_SIZE_CHANGED size_chg_param = p_data->size_chg_params;
+        do_in_jni_thread(Bind(btif_set_size_changed_cb, size_chg_param));
+      }
+      break;
+
+   case BTA_CSIP_SET_SIRK_CHANGED: {
+          tBTA_CSIP_SET_SIRK_CHANGED sirk_chg_param = p_data->sirk_chg_params;
+          do_in_jni_thread(Bind(btif_set_sirk_changed_cb, sirk_chg_param));
+      }
+      break;
+
+    default:
+      BTIF_TRACE_ERROR("%s: Unknown event %d", __func__, event);
+  }
+}
+
+/* Initialization of CSIP module on BT ON*/
+bt_status_t btif_csip_init( btcsip_callbacks_t* callbacks ) {
+  bt_csip_callbacks = callbacks;
+
+  do_in_jni_thread(Bind(BTA_CsipEnable, btif_csip_evt));
+  btif_register_uuid_srvc_disc(Uuid::FromString("1846"));
+
+  return BT_STATUS_SUCCESS;
+}
+
+/* Connect call from upper layer for GATT Connecttion to a given Set Member */
+bt_status_t btif_csip_connect (uint8_t app_id, RawAddress *bd_addr) {
+  BTIF_TRACE_EVENT("%s: Address: %s", __func__, bd_addr->ToString().c_str());
+
+  do_in_jni_thread(Bind(BTA_CsipConnect, app_id, *bd_addr));
+
+  return BT_STATUS_SUCCESS;
+}
+
+/* Call from upper layer to disconnect GATT Connection for given Set Member */
+bt_status_t btif_csip_disconnect (uint8_t app_id, RawAddress *bd_addr ) {
+  BTIF_TRACE_EVENT("%s", __func__);
+
+  do_in_jni_thread(Bind(BTA_CsipDisconnect, app_id, *bd_addr));
+
+  return BT_STATUS_SUCCESS;
+}
+
+/** register app/module with CSIP profile */
+bt_status_t btif_csip_app_register (const bluetooth::Uuid& uuid) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  return do_in_jni_thread(Bind(
+    [](const Uuid& uuid) {
+      BTA_RegisterCsipApp(
+          btif_csip_evt,
+          base::Bind(
+              [](const Uuid& uuid, uint8_t status, uint8_t app_id) {
+                do_in_jni_thread(Bind(
+                    [](const Uuid& uuid, uint8_t status, uint8_t app_id) {
+                      HAL_CBACK(bt_csip_callbacks, app_registered_cb,
+                                status, app_id, uuid);
+                    },
+                    uuid, status, app_id));
+              },
+              uuid));
+    }, uuid));
+}
+
+/** unregister csip App/Module */
+bt_status_t btif_csip_app_unregister (uint8_t app_id) {
+  BTIF_TRACE_EVENT("%s", __func__);
+  return do_in_jni_thread(Bind(BTA_UnregisterCsipApp, app_id));
+}
+
+/** change lock value */
+bt_status_t btif_csip_set_lock_value (uint8_t app_id, uint8_t set_id, uint8_t lock_value,
+                                             std::vector<RawAddress> devices) {
+  BTIF_TRACE_EVENT("%s appId = %d setId = %d Lock Value = %02x ", __func__,
+                    app_id, set_id, lock_value);
+  tBTA_SET_LOCK_PARAMS lock_params = {app_id, set_id, lock_value, devices};
+  do_in_jni_thread(Bind(BTA_CsipSetLockValue, lock_params));
+  return BT_STATUS_SUCCESS;
+}
+
+void  btif_csip_cleanup() {
+  BTIF_TRACE_EVENT("%s", __func__);
+  do_in_jni_thread(Bind(BTA_CsipDisable));
+}
+
+const btcsip_interface_t btcsipInterface = {
+    sizeof(btcsipInterface),
+    btif_csip_init,
+    btif_csip_connect,
+    btif_csip_disconnect,
+    btif_csip_app_register,
+    btif_csip_app_unregister,
+    btif_csip_set_lock_value,
+    btif_csip_cleanup,
+};
+
+/*******************************************************************************
+ *
+ * Function         btif_csip_get_interface
+ *
+ * Description      Get the csip callback interface
+ *
+ * Returns          btcsip_interface_t
+ *
+ ******************************************************************************/
+const btcsip_interface_t* btif_csip_get_interface() {
+  BTIF_TRACE_EVENT("%s", __func__);
+  return &btcsipInterface;
+}
diff --git a/le_audio/system/bt/btif/src/btif_dm_adv_audio.cc b/le_audio/system/bt/btif/src/btif_dm_adv_audio.cc
new file mode 100644
index 0000000..a9c32f0
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_dm_adv_audio.cc
@@ -0,0 +1,692 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_btif_dm"
+
+#include "btif_dm.h"
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <iterator>
+#include <map>
+
+#include <mutex>
+
+#include <bluetooth/uuid.h>
+#include "hardware/vendor.h"
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hearing_aid.h>
+
+#include "advertise_data_parser.h"
+#include "bt_common.h"
+#include "bta_closure_api.h"
+#include "bta_csip_api.h"
+#include "bta_gatt_api.h"
+#include "btif_api.h"
+#include "btif_bqr.h"
+#include "btif_config.h"
+#include "btif_dm.h"
+#include "btif_hh.h"
+#include "btif_sdp.h"
+#include "btif_storage.h"
+#include "btif_util.h"
+#include "btu.h"
+#include "bta/include/bta_dm_api.h"
+#include "device/include/controller.h"
+#include "device/include/interop.h"
+#include "internal_include/stack_config.h"
+#include "osi/include/allocator.h"
+#include "osi/include/log.h"
+#include "osi/include/metrics.h"
+#include "osi/include/osi.h"
+#include "osi/include/properties.h"
+#include "stack/btm/btm_int.h"
+#include "stack_config.h"
+#include "stack/sdp/sdpint.h"
+#include "btif_tws_plus.h"
+#include "device/include/device_iot_config.h"
+#include "btif_bap_config.h"
+#include "bta_dm_adv_audio.h"
+#include "btif_dm_adv_audio.h"
+
+using bluetooth::Uuid;
+
+/******************************************************************************
+ *  Constants & Macros
+ *****************************************************************************/
+#define BTIF_DM_GET_REMOTE_PROP(b,t,v,l,p) \
+      {p.type=t;p.val=v;p.len=l;btif_storage_get_remote_device_property(b,&p);}
+
+extern std::vector<bluetooth::Uuid> uuid_srv_disc_search;
+std::unordered_map<RawAddress, uint32_t> adv_audio_device_db;
+extern void bta_dm_adv_audio_gatt_conn(RawAddress p_bd_addr);
+extern void bta_dm_adv_audio_close(RawAddress p_bd_addr);
+extern bool btif_has_ble_keys(const char* bdstr);
+bt_status_t btif_storage_get_remote_device_property(
+        const RawAddress* remote_bd_addr, bt_property_t* property);
+extern tBTA_LEA_PAIRING_DB bta_lea_pairing_cb;
+extern void search_services_copy_cb(uint16_t event, char* p_dest, char* p_src);
+
+extern void bond_state_changed(bt_status_t status, const RawAddress& bd_addr,
+                               bt_bond_state_t state);
+
+#define BTIF_STORAGE_GET_REMOTE_PROP(b, t, v, l, p)     \
+    do {                                                  \
+          (p).type = (t);                                     \
+          (p).val = (v);                                      \
+          (p).len = (l);                                      \
+          btif_storage_get_remote_device_property((b), &(p)); \
+        } while (0)
+
+extern bool check_adv_audio_cod(uint32_t cod);
+extern bool is_remote_support_adv_audio(const RawAddress remote_bdaddr);
+extern bool is_le_audio_service(Uuid uuid);
+extern void bta_adv_audio_update_bond_db(RawAddress p_bd_addr, uint8_t transport);
+
+#define BTIF_DM_MAX_SDP_ATTEMPTS_AFTER_PAIRING 2
+
+
+tBTA_TRANSPORT btif_dm_get_adv_audio_transport(const RawAddress& bd_addr) {
+  tBTM_INQ_INFO* p_inq_info;
+
+  p_inq_info = BTM_InqDbRead(bd_addr);
+  if (p_inq_info != NULL) {
+    BTIF_TRACE_DEBUG("%s, inq_result_type %x",
+        __func__, p_inq_info->results.inq_result_type);
+    if (p_inq_info->results.inq_result_type & BTM_INQ_RESULT_BLE) {
+      return BT_TRANSPORT_LE;
+    }
+  }
+  return BT_TRANSPORT_BR_EDR;
+}
+
+/*******************************************************************************
+ *
+ * Function         btif_set_remote_device_uuid_property
+ *
+ * Description      Store the remote LEA services in config file
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+static void btif_set_remote_device_uuid_property(RawAddress p_addr,
+                                                 int num_uuids,
+                                                 bluetooth::Uuid *new_uuids) {
+
+  Uuid remote_uuids[BT_MAX_NUM_UUIDS];
+  bt_property_t prop;
+
+  for (int j = 0; j < num_uuids; j++) {
+    remote_uuids[j] = new_uuids[j];
+    BTIF_TRACE_EVENT("%s: UUID %s index %d ", __func__,
+      remote_uuids[j].ToString().c_str(), j);
+  }
+  prop.type = (bt_property_type_t)BT_PROPERTY_ADV_AUDIO_UUIDS;
+  prop.val = &remote_uuids[0];
+  prop.len = (num_uuids) * (Uuid::kNumBytes128);
+  Uuid* tmp = (Uuid*)(prop.val);
+  BTIF_TRACE_EVENT("%s: Checking it %s", __func__, tmp->ToString().c_str());
+  int ret = btif_storage_set_remote_device_property(&p_addr, &prop);
+  ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed",
+          ret);
+}
+
+/*******************************************************************************
+ *
+ * Function         btif_dm_lea_search_services_evt
+ *
+ * Description      Executes search services event in btif context
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void btif_dm_lea_search_services_evt(uint16_t event, char* p_param) {
+  tBTA_DM_SEARCH* p_data = (tBTA_DM_SEARCH*)p_param;
+
+  bt_bond_state_t pairing_state = BT_BOND_STATE_NONE;
+  uint8_t sdp_attempts = 0;
+  RawAddress pairing_bd_addr;
+  RawAddress static_bd_addr;
+  btif_get_pairing_cb_info(&pairing_state, &sdp_attempts,
+    &pairing_bd_addr, &static_bd_addr);
+
+  BTIF_TRACE_EVENT("%s:  event = %d", __func__, event);
+  switch (event) {
+    case BTA_DM_DISC_RES_EVT: {
+      uint32_t i = 0, j = 0;
+      bt_property_t prop[2];
+      int num_properties = 0;
+      bt_status_t ret;
+      Uuid remote_uuids[BT_MAX_NUM_UUIDS];
+      Uuid missed_remote_uuids[BT_MAX_NUM_UUIDS];
+      uint8_t missing_uuids_len = 0;
+      bt_property_t remote_uuid_prop;
+
+      RawAddress& bd_addr = p_data->disc_res.bd_addr;
+
+      BTIF_TRACE_DEBUG("%s:(result=0x%x, services 0x%x)", __func__,
+                       p_data->disc_res.result, p_data->disc_res.services);
+
+      /* retry sdp service search, if sdp fails for pairing bd address,
+      ** report sdp results to APP immediately for non pairing addresses
+      */
+      if ((p_data->disc_res.result != BTA_SUCCESS) &&
+          (pairing_state == BT_BOND_STATE_BONDED) &&
+          ((p_data->disc_res.bd_addr == pairing_bd_addr) ||
+          (p_data->disc_res.bd_addr == static_bd_addr)) &&
+          (sdp_attempts > 0)) {
+        if (sdp_attempts < BTIF_DM_MAX_SDP_ATTEMPTS_AFTER_PAIRING) {
+          BTIF_TRACE_WARNING("%s:SDP failed after bonding re-attempting",
+
+                           __func__);
+          btif_inc_sdp_attempts();
+          btif_dm_get_remote_services_by_transport(&bd_addr, BT_TRANSPORT_BR_EDR);
+          return;
+        } else {
+          BTIF_TRACE_WARNING(
+            "%s: SDP reached to maximum attempts, sending bond fail to upper layers",
+            __func__);
+          btif_reset_sdp_attempts();
+          if (bta_remote_device_is_dumo(bd_addr)) {
+            auto itr = bta_lea_pairing_cb.dev_addr_map.find(bd_addr);
+            if (itr != bta_lea_pairing_cb.dev_addr_map.end()) {
+              if ((itr->first != itr->second)) {
+                bta_lea_pairing_cb.is_sdp_discover = false;
+                bond_state_changed(BT_STATUS_FAIL,
+                  bd_addr, BT_BOND_STATE_NONE);
+              } else {
+                btif_reset_pairing_cb();
+                BTIF_TRACE_WARNING("%s: Skipping BOND_NONE for %s", __func__,
+                  bd_addr.ToString().c_str());
+              }
+            } else {
+              BTIF_TRACE_ERROR("%s: SDP shouldnt on random address. Wrong path %s", __func__,
+                  bd_addr.ToString().c_str());
+              btif_reset_pairing_cb();
+              bond_state_changed(BT_STATUS_FAIL,
+                  bd_addr, BT_BOND_STATE_NONE);
+              btif_storage_remove_bonded_device(&bd_addr);
+              BTA_DmRemoveDevice(bd_addr);
+            }
+            return;
+          } else {
+            BTIF_TRACE_ERROR("%s: SDP shouldnt called. Wrong path %s", __func__,
+                bd_addr.ToString().c_str());
+          }
+        }
+      }
+      prop[0].type = BT_PROPERTY_UUIDS;
+      prop[0].len = 0;
+      if ((p_data->disc_res.result == BTA_SUCCESS) &&
+          (p_data->disc_res.num_uuids > 0)) {
+        prop[0].val = p_data->disc_res.p_uuid_list;
+        prop[0].len = p_data->disc_res.num_uuids * Uuid::kNumBytes128;
+
+        for (i = 0; i < p_data->disc_res.num_uuids; i++) {
+          std::string temp = ((p_data->disc_res.p_uuid_list + i))->ToString();
+          LOG_INFO(LOG_TAG, "%s index:%d uuid:%s", __func__, i, temp.c_str());
+        }
+      }
+
+      /* onUuidChanged requires getBondedDevices to be populated.
+      ** bond_state_changed needs to be sent prior to remote_device_property
+      */
+      if ((pairing_state == BT_BOND_STATE_BONDED && sdp_attempts) &&
+          (p_data->disc_res.bd_addr == pairing_bd_addr ||
+           p_data->disc_res.bd_addr == static_bd_addr)) {
+        LOG_INFO(LOG_TAG, "%s: SDP search done for %s", __func__,
+                 bd_addr.ToString().c_str());
+        btif_reset_sdp_attempts();
+        BTA_DmResetPairingflag(bd_addr);
+        btif_reset_pairing_cb();
+
+        // Send one empty UUID to Java to unblock pairing intent when SDP failed
+        // or no UUID is discovered
+        if (p_data->disc_res.result != BTA_SUCCESS ||
+            p_data->disc_res.num_uuids == 0) {
+          LOG_INFO(LOG_TAG,
+                   "%s: SDP failed, send empty UUID to unblock bonding %s",
+                   __func__, bd_addr.ToString().c_str());
+          bt_property_t prop;
+
+          Uuid uuid = {};
+          //Updating in lea_pairing_database
+          if (bta_remote_device_is_dumo(bd_addr)) {
+            tBTA_DEV_PAIRING_CB *p_lea_pair_cb = NULL;
+
+            p_lea_pair_cb = bta_get_lea_pair_cb(bd_addr);
+            if (p_lea_pair_cb) {
+              p_lea_pair_cb->sdp_disc_status = false;
+            }
+          }
+
+          if (btif_dm_get_adv_audio_transport(bd_addr) == BT_TRANSPORT_BR_EDR)
+          {
+            prop.type = BT_PROPERTY_UUIDS;
+          } else {
+            prop.type = (bt_property_type_t)BT_PROPERTY_ADV_AUDIO_UUIDS;
+          }
+          prop.val = &uuid;
+          prop.len = Uuid::kNumBytes128;
+
+          /* Send the event to the BTIF */
+          HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
+                    BT_STATUS_SUCCESS, &bd_addr, 1, &prop);
+          break;
+        }
+      }
+
+      // updates extra uuids which are discovered during
+      // new sdp search to existing uuid list present in conf file.
+      // If conf file has more UUIDs than the sdp search, it will
+      // update the conf file UUIDs as the final UUIDs
+      BTIF_STORAGE_FILL_PROPERTY(&remote_uuid_prop, BT_PROPERTY_UUIDS,
+                                 sizeof(remote_uuids), remote_uuids);
+      btif_storage_get_remote_device_property(&bd_addr,
+                                        &remote_uuid_prop);
+      if(remote_uuid_prop.len && p_data->disc_res.result == BTA_SUCCESS) {
+        // compare now
+        bool uuid_found = false;
+        uint8_t uuid_len = remote_uuid_prop.len / sizeof(Uuid);
+        for (i = 0; i < p_data->disc_res.num_uuids; i++) {
+          uuid_found = false;
+          Uuid* disc_uuid =  reinterpret_cast<Uuid*> (p_data->disc_res.p_uuid_list + i);
+          for (j = 0; j < uuid_len; j++) {
+            Uuid* base_uuid =  reinterpret_cast<Uuid*> (remote_uuid_prop.val) + j;
+            if(*disc_uuid == *base_uuid) {
+              uuid_found = true;
+              break;
+            }
+          }
+          if(!uuid_found) {
+            BTIF_TRACE_WARNING("%s:new uuid found ", __func__);
+            memcpy(&missed_remote_uuids[missing_uuids_len++], disc_uuid, sizeof(Uuid));
+          }
+        }
+
+        // add the missing uuids now
+        if(missing_uuids_len) {
+          BTIF_TRACE_WARNING("%s :missing_uuids_len = %d ", __func__, missing_uuids_len);
+          for (j = 0; j < missing_uuids_len &&
+             (unsigned long)remote_uuid_prop.len < BT_MAX_NUM_UUIDS * sizeof(Uuid); j++) {
+            memcpy(&remote_uuids[uuid_len + j], &missed_remote_uuids[j], sizeof(Uuid));
+            remote_uuid_prop.len += sizeof(Uuid);
+          }
+        }
+
+        prop[0].type = BT_PROPERTY_UUIDS;
+        prop[0].val = remote_uuids;
+        prop[0].len = remote_uuid_prop.len;
+        ret = btif_storage_set_remote_device_property(&bd_addr, &prop[0]);
+        ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed",
+                ret);
+        //Send the UUID values to upper layer as BT_PROPERTY_ADV_AUDIO_UUIDS
+        num_properties++;
+
+        if (bta_is_bredr_primary_transport(bd_addr)) {
+          BTIF_TRACE_WARNING("%s: Initiating LE connection ", __func__);
+          adv_audio_device_db[bd_addr] = MAJOR_LE_AUDIO_VENDOR_COD;
+          bta_le_audio_dev_cb.bond_progress = true;
+          bta_dm_adv_audio_gatt_conn(bd_addr);
+        }
+      } else if (p_data->disc_res.num_uuids != 0) {
+        /* Also write this to the NVRAM */
+        ret = btif_storage_set_remote_device_property(&bd_addr, &prop[0]);
+        ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed",
+                ret);
+        num_properties++;
+      }
+
+      /* Remote name update */
+      if (strlen((const char *) p_data->disc_res.bd_name)) {
+        prop[1].type = BT_PROPERTY_BDNAME;
+        prop[1].val = p_data->disc_res.bd_name;
+        prop[1].len = strlen((char *)p_data->disc_res.bd_name);
+
+        ret = btif_storage_set_remote_device_property(&bd_addr, &prop[1]);
+        ASSERTC(ret == BT_STATUS_SUCCESS, "failed to save remote device property", ret);
+        num_properties++;
+      }
+
+      if (num_properties > 0) {
+        if (btif_dm_get_adv_audio_transport(bd_addr) == BT_TRANSPORT_BR_EDR)
+        {
+          prop[0].type = BT_PROPERTY_UUIDS;
+        } else {
+          prop[0].type = (bt_property_type_t)BT_PROPERTY_ADV_AUDIO_UUIDS;
+        }
+        /* Send the event to the BTIF */
+        HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, BT_STATUS_SUCCESS,
+                  &bd_addr, num_properties, prop);
+      }
+
+      int validAddr = 1;
+      bt_property_t rem_prop;
+      BTIF_STORAGE_GET_REMOTE_PROP(&bd_addr, (bt_property_type_t)BT_PROPERTY_REM_DEViCE_VALID_ADDR,
+                                   &validAddr, sizeof(int),
+                                   rem_prop);
+
+      if (validAddr != 0) {
+        bt_property_t prop_addr;
+        int is_valid = bta_is_adv_audio_valid_bdaddr(bd_addr);
+        prop_addr.type = (bt_property_type_t)BT_PROPERTY_REM_DEViCE_VALID_ADDR;
+        prop_addr.val = (void *)&is_valid;
+        prop_addr.len = sizeof(int);
+        ret = btif_storage_set_remote_device_property(&bd_addr, &prop_addr);
+        ASSERTC(ret == BT_STATUS_SUCCESS, "failed to save remote device property", ret);
+      }
+
+      bt_device_type_t dev_type;
+      dev_type = (bt_device_type_t)BT_DEVICE_TYPE_DUMO;
+      bt_property_t prop_dev;
+      BTIF_STORAGE_FILL_PROPERTY(&prop_dev,
+                        BT_PROPERTY_TYPE_OF_DEVICE, sizeof(dev_type),
+                        &dev_type);
+      ret = btif_storage_set_remote_device_property(&bd_addr, &prop_dev);
+      ASSERTC(ret == BT_STATUS_SUCCESS, "failed to save remote device type",
+        ret);
+
+      HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, BT_STATUS_SUCCESS,
+          &bd_addr, 1, &prop_dev);
+
+      /* If below condition is true, it means LE random advertising
+       * has no ADV audio uuids, but identity address contains adv audio bit
+       * As per current design, if pairing initiated through non adv audio
+       * address then we dont need to fetch ADV audio role and services
+       */
+      if ((btif_get_is_adv_audio_pair_info(bd_addr) == 0)) {
+        prop_dev.type = (bt_property_type_t)BT_PROPERTY_ADV_AUDIO_ACTION_UUID;
+        prop_dev.val = (void *)&validAddr;
+        prop_dev.len = sizeof(uint8_t);
+        HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, BT_STATUS_SUCCESS,
+            &bd_addr, 1, &prop_dev);
+      }
+
+    } break;
+
+    case BTA_DM_DISC_CMPL_EVT:
+      /* fixme */
+      break;
+
+    case BTA_DM_SEARCH_CANCEL_CMPL_EVT:
+      /* no-op */
+      break;
+
+    case BTA_DM_DISC_BLE_RES_EVT: {
+      BTIF_TRACE_DEBUG("%s: service %s", __func__,
+                       p_data->disc_ble_res.service.ToString().c_str());
+      bt_property_t prop;
+      bt_status_t ret;
+      RawAddress& bd_addr = p_data->disc_ble_res.bd_addr;
+        /* Remote name update */
+        if (strnlen((const char*)p_data->disc_ble_res.bd_name, BD_NAME_LEN)) {
+          prop.type = BT_PROPERTY_BDNAME;
+          prop.val = p_data->disc_ble_res.bd_name;
+          prop.len =
+              strnlen((char*)p_data->disc_ble_res.bd_name, BD_NAME_LEN);
+
+          ret = btif_storage_set_remote_device_property(&bd_addr, &prop);
+          ASSERTC(ret == BT_STATUS_SUCCESS,
+                  "failed to save remote device property", ret);
+          /* Send the event to the BTIF */
+          HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, BT_STATUS_SUCCESS,
+                    &bd_addr, 1, &prop);
+        }
+    } break;
+
+    case BTA_DM_LE_AUDIO_SEARCH_CMPL_EVT:
+    {
+      tBTA_DEV_PAIRING_CB *p_lea_pair_cb = NULL;
+      p_lea_pair_cb = bta_get_lea_pair_cb(p_data->disc_ble_res.bd_addr);
+      if (p_lea_pair_cb != NULL) {
+        btif_reset_pairing_cb();
+        bt_property_t prop[5], prop_tmp[2];
+        RawAddress& bd_addr = p_data->disc_ble_res.bd_addr;
+        int num_properties = 0;
+        bool id_addr_action_uuid = false;
+        RawAddress id_addr = bta_get_rem_dev_id_addr(bd_addr);
+
+        prop[num_properties].type = (bt_property_type_t)BT_PROPERTY_ADV_AUDIO_UUIDS;
+        prop[num_properties].val = p_data->adv_audio_disc_cmpl.adv_audio_uuids;
+        prop[num_properties].len = p_data->adv_audio_disc_cmpl.num_uuids *
+          Uuid::kNumBytes128;
+        /* Also write this to the NVRAM */
+        bt_property_t cod_prop1;
+        uint32_t cod_p;
+
+        BTIF_STORAGE_FILL_PROPERTY(&cod_prop1,
+            BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod_p), &cod_p);
+        btif_storage_get_remote_device_property(&bd_addr,
+            &cod_prop1);
+        int ret;
+        cod_p |= MAJOR_LE_AUDIO_VENDOR_COD;
+        BTIF_STORAGE_FILL_PROPERTY(&cod_prop1,
+            BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod_p), &cod_p);
+        ret = btif_storage_set_remote_device_property(&bd_addr, &cod_prop1);
+        ASSERTC(ret == BT_STATUS_SUCCESS,
+            "failed to save remote device property", ret);
+
+        if (bta_remote_device_is_dumo(bd_addr)
+            && (id_addr != bd_addr) && (bta_lea_pairing_cb.is_sdp_discover == true)) {
+          if(id_addr != RawAddress::kEmpty) {
+            BTIF_TRACE_DEBUG("%s: Found BT_PROPERTY_ADV_AUDIO_UUIDS %s",
+                p_data->adv_audio_disc_cmpl.adv_audio_uuids[0].ToString().c_str(),
+                id_addr.ToString().c_str());
+
+            bt_property_t cod_prop;
+            uint32_t cod;
+
+            BTIF_STORAGE_FILL_PROPERTY(&cod_prop,
+                BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod), &cod);
+            btif_storage_get_remote_device_property(&id_addr,
+                &cod_prop);
+            BTIF_TRACE_DEBUG("%s: Cod is %x", __func__, cod);
+            cod |= MAJOR_LE_AUDIO_VENDOR_COD;
+            BTIF_STORAGE_FILL_PROPERTY(&cod_prop,
+                BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod), &cod);
+            ret = btif_storage_set_remote_device_property(&id_addr, &cod_prop);
+            ASSERTC(ret == BT_STATUS_SUCCESS,
+                "failed to save remote device property", ret);
+            num_properties++;
+            prop[num_properties].type = BT_PROPERTY_CLASS_OF_DEVICE;
+            prop[num_properties].val = (void *) &cod;
+            prop[num_properties].len = sizeof(cod);
+
+            num_properties ++;
+
+            BTIF_STORAGE_FILL_PROPERTY(&prop[num_properties],
+                (bt_property_type_t)BT_PROPERTY_REM_DEV_IDENT_BD_ADDR, sizeof(RawAddress), &bd_addr);
+            ret = btif_storage_set_remote_device_property(&id_addr, &prop[num_properties]);
+            ASSERTC(ret == BT_STATUS_SUCCESS,
+                "failed to save remote device property", ret);
+
+            id_addr_action_uuid = true;
+            HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, BT_STATUS_SUCCESS,
+                &id_addr, 3, &prop[0]);
+          }
+        }
+        num_properties = 1;
+
+        BTIF_STORAGE_FILL_PROPERTY(&prop[num_properties],
+            (bt_property_type_t)BT_PROPERTY_REM_DEV_IDENT_BD_ADDR, sizeof(RawAddress), &id_addr);
+
+        ret = btif_storage_set_remote_device_property(&bd_addr, &prop[num_properties]);
+        ASSERTC(ret == BT_STATUS_SUCCESS,
+            "failed to save remote device property", ret);
+
+        HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, BT_STATUS_SUCCESS,
+            &bd_addr, 2, &prop[0]);
+
+        int validAddr = 1;
+        bt_property_t rem_prop;
+        BTIF_STORAGE_GET_REMOTE_PROP(&bd_addr, (bt_property_type_t)BT_PROPERTY_REM_DEViCE_VALID_ADDR,
+            &validAddr, sizeof(int),
+            rem_prop);
+        validAddr = bta_is_adv_audio_valid_bdaddr(bd_addr);
+        BTIF_TRACE_DEBUG("%s: is Valid Address Check value %d bd_addr %s", __func__, validAddr, bd_addr.ToString().c_str());
+        prop_tmp[0].type = (bt_property_type_t)BT_PROPERTY_REM_DEViCE_VALID_ADDR;
+        prop_tmp[0].val = (void *)&validAddr;
+        prop_tmp[0].len = sizeof(int);
+        ret = btif_storage_set_remote_device_property(&bd_addr, &prop_tmp[0]);
+        ASSERTC(ret == BT_STATUS_SUCCESS, "failed to save remote device property", ret);
+
+        prop_tmp[1].type = (bt_property_type_t)BT_PROPERTY_ADV_AUDIO_ACTION_UUID;
+        prop_tmp[1].val = (void *)&validAddr;
+        prop_tmp[1].len = sizeof(uint8_t);
+
+        if (id_addr_action_uuid) {
+          BTIF_TRACE_DEBUG("%s: IDENTITY ADDR ACTION UUID %s ", __func__,
+              id_addr.ToString().c_str());
+          HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, BT_STATUS_SUCCESS,
+              &id_addr, 1, &prop_tmp[1]);
+        }
+
+        HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, BT_STATUS_SUCCESS,
+            &bd_addr, 2, &prop_tmp[0]);
+        if (id_addr_action_uuid) {
+          btif_set_remote_device_uuid_property(id_addr,
+              p_data->adv_audio_disc_cmpl.num_uuids, &p_data->adv_audio_disc_cmpl.adv_audio_uuids[0]);
+        }
+        btif_set_remote_device_uuid_property(bd_addr,
+            p_data->adv_audio_disc_cmpl.num_uuids, &p_data->adv_audio_disc_cmpl.adv_audio_uuids[0]);
+
+        bta_dm_adv_audio_close(bd_addr);
+        bta_dm_reset_lea_pairing_info(bd_addr);
+      } else {
+        BTIF_TRACE_DEBUG("%s: ONCE AGAIN WRITING IDENTITY", __func__);
+      }
+    }
+    break;
+    default: { ASSERTC(0, "unhandled search services event", event); } break;
+  }
+}
+
+/****************************************************************************
+ *
+ * Function        btif_register_uuid_srvc_disc
+ *
+ * Description     Add to UUID to the service search queue
+ *
+ * Returns         void
+ *
+ ****************************************************************************/
+void btif_register_uuid_srvc_disc(bluetooth::Uuid uuid) {
+
+  uuid_srv_disc_search.push_back(uuid);
+  BTIF_TRACE_DEBUG("btif_register_uuid_srvc_disc, no of uuids %d %s",
+  uuid_srv_disc_search.size(), uuid.ToString().c_str());
+}
+
+void btif_dm_release_action_uuid(RawAddress bd_addr) {
+
+  bt_property_t prop_dev;
+  int status = 1;
+  prop_dev.type = (bt_property_type_t)BT_PROPERTY_ADV_AUDIO_ACTION_UUID;
+  prop_dev.val = (void *)&status;
+  prop_dev.len = sizeof(uint8_t);
+  HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, BT_STATUS_SUCCESS,
+      &bd_addr, 1, &prop_dev);
+}
+
+/****************************************************************************
+ *
+ * Function        check_adv_audio_cod
+ *
+ * Description     This API is used to check whether COD contains LE Audio
+ *                 COD or not?
+ *
+ * Returns         bool
+ *
+ ****************************************************************************/
+bool check_adv_audio_cod(uint32_t cod) {
+
+  BTIF_TRACE_DEBUG("check_adv_audio_cod ");
+
+  if (cod & MAJOR_LE_AUDIO_VENDOR_COD) {
+    return true;
+  }
+  return false;
+}
+
+/*******************************************************************************
+ *
+ * Function        is_remote_support_adv_audio
+ *
+ * Description     is remote device is supporting LE audio or not
+ *
+ * Returns         bool
+ *
+ ******************************************************************************/
+
+bool is_remote_support_adv_audio(const RawAddress p_addr) {
+  if (adv_audio_device_db.find(p_addr)
+          != adv_audio_device_db.end()) {
+        BTIF_TRACE_DEBUG("%s  %s LE AUDIO Support ", __func__,
+                  p_addr.ToString().c_str());
+            return true;
+  }
+
+  bool status = bta_is_remote_support_lea(p_addr);
+  if (status) return true;
+
+  bt_property_t cod_prop;
+  uint32_t cod_p;
+
+  BTIF_STORAGE_FILL_PROPERTY(&cod_prop,
+      BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod_p), &cod_p);
+  btif_storage_get_remote_device_property(&p_addr,
+      &cod_prop);
+
+  if ((cod_p & MAJOR_LE_AUDIO_VENDOR_COD)
+      == MAJOR_LE_AUDIO_VENDOR_COD) {
+    BTIF_TRACE_DEBUG("%s ADV AUDIO COD is matched ", __func__);
+    return true;
+  }
+
+  return false;
+}
+
+void bte_dm_adv_audio_search_services_evt(tBTA_DM_SEARCH_EVT event,
+    tBTA_DM_SEARCH* p_data) {
+  BTIF_TRACE_DEBUG(" %s ", __func__);
+  uint16_t param_len = 0;
+  if (p_data) param_len += sizeof(tBTA_DM_SEARCH);
+  switch (event) {
+    case BTA_DM_DISC_RES_EVT: {
+                                if ((p_data && p_data->disc_res.result == BTA_SUCCESS) &&
+                                    (p_data->disc_res.num_uuids > 0)) {
+                                  param_len += (p_data->disc_res.num_uuids * Uuid::kNumBytes128);
+                                }
+                              } break;
+  }
+  /* TODO: The only other member that needs a deep copy is the p_raw_data. But
+   *    * not sure
+   *       * if raw_data is needed. */
+  btif_transfer_context(
+      btif_dm_lea_search_services_evt, event, (char*)p_data, param_len,
+      (param_len > sizeof(tBTA_DM_SEARCH)) ? search_services_copy_cb : NULL);
+}
+
diff --git a/le_audio/system/bt/btif/src/btif_mcp.cc b/le_audio/system/bt/btif/src/btif_mcp.cc
new file mode 100644
index 0000000..35b4ee9
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_mcp.cc
@@ -0,0 +1,185 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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.
+ *
+ ******************************************************************************/
+
+
+/* MCP Interface */
+#define LOG_TAG "bt_btif_mcp"
+
+#include "bt_target.h"
+#include "bta_closure_api.h"
+#include "bta_mcp_api.h"
+#include "btif_common.h"
+#include "btif_storage.h"
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_mcp.h>
+
+using base::Bind;
+using base::Unretained;
+using base::Owned;
+using bluetooth::Uuid;
+using std::vector;
+using base::Bind;
+using base::Unretained;
+
+using bluetooth::mcp_server::McpServerCallbacks;
+using bluetooth::mcp_server::McpServerInterface;
+
+namespace {
+class McpServerInterfaceImpl;
+std::unique_ptr<McpServerInterface> McpServerInstance;
+
+class McpServerInterfaceImpl
+  : public McpServerInterface, public McpServerCallbacks {
+  ~McpServerInterfaceImpl() = default;
+
+  void Init(McpServerCallbacks* callback, Uuid bt_uuid) override {
+    LOG(INFO) << __func__ ;
+    this->callbacks = callback;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::Initialize, this, bt_uuid));
+  }
+
+  void MediaState(uint8_t state) override {
+    LOG(INFO) << __func__ << ": state " << state;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::MediaState, Unretained(McpServer::Get()), state));
+  }
+
+  void MediaPlayerName(uint8_t* name) override {
+    LOG(INFO) << __func__ << ": name" << name;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::MediaPlayerName, Unretained(McpServer::Get()), name));
+  }
+
+  void MediaControlPointOpcodeSupported(uint32_t feature) override {
+    LOG(INFO) << __func__ << ": feature" << feature;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::MediaControlPointOpcodeSupported, Unretained(McpServer::Get()), feature));
+  }
+
+  void MediaControlPoint(uint8_t value) override {
+    LOG(INFO) << __func__ << ": value" << value;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::MediaControlPoint, Unretained(McpServer::Get()), value));
+  }
+
+  void TrackChanged(bool status) override {
+    LOG(INFO) << __func__ << ": status" << status;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::TrackChanged, Unretained(McpServer::Get()), status));
+  }
+
+  void TrackTitle(uint8_t* title) override {
+    LOG(INFO) << __func__ << ": title" << title;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::TrackTitle, Unretained(McpServer::Get()), title));
+  }
+
+  void TrackPosition(int32_t position) override {
+    LOG(INFO) << __func__ << ": position" << position;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::TrackPosition, Unretained(McpServer::Get()), position));
+  }
+
+  void TrackDuration(int32_t duration) override {
+    LOG(INFO) << __func__ << ": duration" << duration;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::TrackDuration, Unretained(McpServer::Get()), duration));
+  }
+
+  void ContentControlId(uint8_t ccid) override {
+    LOG(INFO) << __func__ << ": ccid" << ccid;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::ContentControlId, Unretained(McpServer::Get()), ccid));
+  }
+
+  void PlayingOrderSupported(uint16_t order) override {
+    LOG(INFO) << __func__ << ": order" << order;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::PlayingOrderSupported, Unretained(McpServer::Get()), order));
+  }
+
+  void PlayingOrder(uint8_t value) override {
+    LOG(INFO) << __func__ << ": value" << value;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::PlayingOrder, Unretained(McpServer::Get()), value));
+  }
+
+  void SetActiveDevice(const RawAddress& address, int set_id, int profile) override {
+    LOG(INFO) << __func__ << ": set_id" << set_id<< ": device"<< address;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::SetActiveDevice, Unretained(McpServer::Get()), address, set_id, profile));
+  }
+
+  void DisconnectMcp(const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device"<< address;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::DisconnectMcp, Unretained(McpServer::Get()), address));
+  }
+
+  void BondStateChange(const RawAddress& address, int state) override {
+    LOG(INFO) << __func__ << ": device"<< address << " state : " << state;
+    do_in_bta_thread(FROM_HERE,
+          Bind(&McpServer::BondStateChange, Unretained(McpServer::Get()), address, state));
+  }
+
+  void Cleanup(void) override {
+    LOG(INFO) << __func__;
+    do_in_bta_thread(FROM_HERE, Bind(&McpServer::CleanUp));
+  }
+
+  void OnConnectionStateChange(int status,
+                         const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device=" << address << " state=" << (int)status;
+    do_in_jni_thread(FROM_HERE, Bind(&McpServerCallbacks::OnConnectionStateChange,
+            Unretained(callbacks), status, address));
+  }
+
+  void MediaControlPointChangeReq(uint8_t state,
+                         const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device=" << address << " state=" << (int)state;
+    do_in_jni_thread(FROM_HERE, Bind(&McpServerCallbacks::MediaControlPointChangeReq,
+            Unretained(callbacks), state, address));
+  }
+
+  void TrackPositionChangeReq(int32_t position) override {
+    LOG(INFO) << __func__ << " position=" << (int)position;
+    do_in_jni_thread(FROM_HERE, Bind(&McpServerCallbacks::TrackPositionChangeReq,
+            Unretained(callbacks), position));
+  }
+
+  void PlayingOrderChangeReq(uint32_t order) override {
+    LOG(INFO) << __func__ << ": order=" << order;
+    do_in_jni_thread(FROM_HERE, Bind(&McpServerCallbacks::PlayingOrderChangeReq,
+            Unretained(callbacks), order));
+  }
+
+  private:
+    McpServerCallbacks* callbacks;
+  };
+}//namespace
+
+const McpServerInterface* btif_mcp_server_get_interface(void) {
+   LOG(INFO) << __func__;
+   if (!McpServerInstance)
+     McpServerInstance.reset(new McpServerInterfaceImpl());
+   return McpServerInstance.get();
+}
diff --git a/le_audio/system/bt/btif/src/btif_pacs_client.cc b/le_audio/system/bt/btif/src/btif_pacs_client.cc
new file mode 100644
index 0000000..8c0867a
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_pacs_client.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#include "bta_closure_api.h"
+#include "bta_pacs_client_api.h"
+#include "btif_common.h"
+#include "btif_storage.h"
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_pacs_client.h>
+#include "osi/include/thread.h"
+
+using base::Bind;
+using base::Unretained;
+using bluetooth::bap::pacs::PacsClient;
+using bluetooth::bap::pacs::CodecConfig;
+using bluetooth::bap::pacs::ConnectionState;
+using bluetooth::bap::pacs::PacsClientCallbacks;
+using bluetooth::bap::pacs::PacsClientInterface;
+
+
+namespace {
+
+class PacsClientInterfaceImpl;
+std::unique_ptr<PacsClientInterface> PacsClientInstance;
+
+class PacsClientInterfaceImpl
+    : public PacsClientInterface,
+      public PacsClientCallbacks {
+  ~PacsClientInterfaceImpl() = default;
+
+  void Init(PacsClientCallbacks* callbacks) override {
+    DVLOG(2) << __func__;
+    this->callbacks = callbacks;
+
+    do_in_bta_thread(
+        FROM_HERE,
+        Bind(&PacsClient::Initialize, this));
+  }
+
+  void OnInitialized(int status, int client_id) override {
+    do_in_jni_thread(FROM_HERE, Bind(&PacsClientCallbacks::OnInitialized,
+                                     Unretained(callbacks), status,
+                                     client_id));
+  }
+
+  void OnConnectionState(const RawAddress& address,
+                         ConnectionState state) override {
+    DVLOG(2) << __func__ << " address: " << address;
+    do_in_jni_thread(FROM_HERE, Bind(&PacsClientCallbacks::OnConnectionState,
+                                     Unretained(callbacks), address, state));
+  }
+
+  void OnAudioContextAvailable(const RawAddress& address,
+                        uint32_t available_contexts) override {
+    do_in_jni_thread(FROM_HERE,
+                     Bind(&PacsClientCallbacks::OnAudioContextAvailable,
+                          Unretained(callbacks),
+                          address, available_contexts));
+  }
+
+  void OnSearchComplete(int status,
+                        const RawAddress& address,
+                        std::vector<CodecConfig> sink_pac_records,
+                        std::vector<CodecConfig> src_pac_records,
+                        uint32_t sink_locations,
+                        uint32_t src_locations,
+                        uint32_t available_contexts,
+                        uint32_t supported_contexts) override {
+    do_in_jni_thread(FROM_HERE, Bind(&PacsClientCallbacks::OnSearchComplete,
+                                     Unretained(callbacks),
+                                     status,
+                                     address,
+                                     sink_pac_records,
+                                     src_pac_records,
+                                     sink_locations,
+                                     src_locations,
+                                     available_contexts,
+                                     supported_contexts));
+  }
+
+  void Connect(uint16_t client_id, const RawAddress& address) override {
+    do_in_bta_thread(FROM_HERE, Bind(&PacsClient::Connect,
+                                      Unretained(PacsClient::Get()),
+                                      client_id, address, false));
+  }
+
+  void Disconnect(uint16_t client_id, const RawAddress& address) override {
+    do_in_bta_thread(FROM_HERE, Bind(&PacsClient::Disconnect,
+                                      Unretained(PacsClient::Get()),
+                                      client_id, address));
+  }
+
+  void StartDiscovery(uint16_t client_id,
+                                const RawAddress& address) override {
+    do_in_bta_thread(FROM_HERE, Bind(&PacsClient::StartDiscovery,
+                                      Unretained(PacsClient::Get()),
+                                      client_id, address));
+  }
+
+  void GetAvailableAudioContexts(uint16_t client_id,
+                                    const RawAddress& address) override {
+    do_in_bta_thread(FROM_HERE, Bind(&PacsClient::GetAudioAvailability,
+                                      Unretained(PacsClient::Get()),
+                                      client_id, address));
+  }
+
+  void Cleanup(uint16_t client_id) override {
+    DVLOG(2) << __func__;
+    do_in_bta_thread(FROM_HERE, Bind(&PacsClient::CleanUp, client_id));
+  }
+
+ private:
+  PacsClientCallbacks* callbacks;
+};
+
+}  // namespace
+
+PacsClientInterface* btif_pacs_client_get_interface() {
+  if (!PacsClientInstance)
+    PacsClientInstance.reset(new PacsClientInterfaceImpl());
+
+  return PacsClientInstance.get();
+}
diff --git a/le_audio/system/bt/btif/src/btif_vcp_controller.cc b/le_audio/system/bt/btif/src/btif_vcp_controller.cc
new file mode 100644
index 0000000..10ba2ac
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_vcp_controller.cc
@@ -0,0 +1,131 @@
+/*
+ *Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+/* Volume Control Profile Interface */
+
+#include "bt_target.h"
+#include "bta_closure_api.h"
+#include "bta_vcp_controller_api.h"
+#include "btif_common.h"
+#include "btif_storage.h"
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_vcp_controller.h>
+
+using base::Bind;
+using base::Unretained;
+using bluetooth::vcp_controller::ConnectionState;
+using bluetooth::vcp_controller::VcpControllerCallbacks;
+using bluetooth::vcp_controller::VcpControllerInterface;
+
+namespace {
+class VcpControllerInterfaceImpl;
+std::unique_ptr<VcpControllerInterface> VcpControllerInstance;
+
+class VcpControllerInterfaceImpl
+    : public VcpControllerInterface, public VcpControllerCallbacks {
+  ~VcpControllerInterfaceImpl() = default;
+
+  void Init(VcpControllerCallbacks* callbacks) override {
+    LOG(INFO) << __func__ ;
+    this->callbacks = callbacks;
+
+    do_in_bta_thread(
+        FROM_HERE,
+        Bind(&VcpController::Initialize, this));
+  }
+
+  void OnConnectionState(ConnectionState state,
+                         const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device=" << address << " state=" << (int)state;
+    do_in_jni_thread(FROM_HERE, Bind(&VcpControllerCallbacks::OnConnectionState,
+                                     Unretained(callbacks), state, address));
+  }
+
+  void OnVolumeStateChange(uint8_t volume, uint8_t mute,
+                          const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device=" << address << " volume=" << loghex(volume)
+                        << " mute=" << (int)mute;
+    do_in_jni_thread(FROM_HERE, Bind(&VcpControllerCallbacks::OnVolumeStateChange,
+                                     Unretained(callbacks), volume, mute, address));
+  }
+
+  void OnVolumeFlagsChange(uint8_t flags,
+                          const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device=" << address << " flags=" << loghex(flags);
+    do_in_jni_thread(FROM_HERE, Bind(&VcpControllerCallbacks::OnVolumeFlagsChange,
+                                     Unretained(callbacks), flags, address));
+  }
+
+  void Connect(const RawAddress& address, bool isDirect) override {
+    LOG(INFO) << __func__ << ": device=" << address;
+    do_in_bta_thread(FROM_HERE, Bind(&VcpController::Connect,
+                                      Unretained(VcpController::Get()), address, isDirect));
+  }
+
+  void Disconnect(const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device=" << address;
+    do_in_bta_thread(FROM_HERE, Bind(&VcpController::Disconnect,
+                                      Unretained(VcpController::Get()), address));
+  }
+
+  void SetAbsVolume(uint8_t volume, const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device=" << address << " volume=" << loghex(volume);
+    do_in_bta_thread(FROM_HERE, Bind(&VcpController::SetAbsVolume,
+                                      Unretained(VcpController::Get()), address, volume));
+  }
+
+  void Mute(const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device=" << address;
+    do_in_bta_thread(FROM_HERE, Bind(&VcpController::Mute,
+                                      Unretained(VcpController::Get()), address));
+  }
+
+  void Unmute(const RawAddress& address) override {
+    LOG(INFO) << __func__ << ": device=" << address;
+    do_in_bta_thread(FROM_HERE, Bind(&VcpController::Unmute,
+                                    Unretained(VcpController::Get()), address));
+  }
+
+  void Cleanup(void) override {
+    LOG(INFO) << __func__;
+    do_in_bta_thread(FROM_HERE, Bind(&VcpController::CleanUp));
+  }
+
+ private:
+  VcpControllerCallbacks* callbacks;
+};
+
+}  // namespace
+
+VcpControllerInterface* btif_vcp_get_controller_interface() {
+  LOG(INFO) << __func__;
+  if (!VcpControllerInstance)
+    VcpControllerInstance.reset(new VcpControllerInterfaceImpl());
+
+  return VcpControllerInstance.get();
+}
+
diff --git a/le_audio/system/bt/btif/src/btif_vmcp.cc b/le_audio/system/bt/btif/src/btif_vmcp.cc
new file mode 100644
index 0000000..844ac9b
--- /dev/null
+++ b/le_audio/system/bt/btif/src/btif_vmcp.cc
@@ -0,0 +1,775 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 <iostream>
+#include <string.h>
+#include <vector>
+#include <stack>
+#include <log/log.h>
+
+#include "bt_types.h"
+#include "bt_trace.h"
+
+#include <libxml/parser.h>
+#include "btif_bap_codec_utils.h"
+#include "btif_vmcp.h"
+#include "btif_api.h"
+#include <bluetooth/uuid.h>
+
+using namespace std;
+
+unsigned long voice_codec_count, media_codec_count, qos_settings_count;
+
+// holds the value of current profile being parsed from xml
+uint8_t current_profile = 1;
+
+std::vector<codec_config>vmcp_voice_codec;
+std::vector<codec_config>vmcp_media_codec;
+std::vector<qos_config>vmcp_qos_low_lat_voice;
+std::vector<qos_config>vmcp_qos_low_lat_media;
+std::vector<qos_config>vmcp_qos_high_rel_media;
+
+std::vector<codec_config>bap_voice_codec;
+std::vector<codec_config>bap_media_codec;
+std::vector<qos_config>bap_qos_low_lat_voice;
+std::vector<qos_config>bap_qos_low_lat_media;
+std::vector<qos_config>bap_qos_high_rel_media;
+
+std::vector<codec_config>gcp_voice_codec;
+std::vector<codec_config>gcp_media_codec;
+std::vector<qos_config>gcp_qos_low_lat_voice;
+std::vector<qos_config>gcp_qos_low_lat_media;
+
+std::vector<codec_config>wmcp_media_codec;
+std::vector<qos_config>wmcp_qos_high_rel_media;
+
+vector<CodecConfig> get_all_codec_configs(uint8_t profile, uint8_t context)
+{
+  vector<CodecConfig> ret_config;
+  CodecConfig temp_config;
+  vector<codec_config> *vptr = NULL;
+
+  if (profile == VMCP) {
+    if (context == VOICE_CONTEXT) {
+      vptr = &vmcp_voice_codec;
+    }
+    else if(context == MEDIA_CONTEXT) {
+      vptr = &vmcp_media_codec;
+    } else {
+      // if no valid context is provided, use voice context
+      vptr = &vmcp_voice_codec;
+    }
+  } else if (profile == BAP) {
+    if (context == VOICE_CONTEXT) {
+      vptr = &bap_voice_codec;
+    }
+    else if(context == MEDIA_CONTEXT) {
+      vptr = &bap_media_codec;
+    } else {
+      // if no valid context is provided, use voice context
+      vptr = &bap_voice_codec;
+    }
+  } else if (profile == GCP) {
+    if (context == VOICE_CONTEXT) {
+      vptr = &gcp_voice_codec;
+    }
+    else if(context == MEDIA_CONTEXT) {
+      vptr = &gcp_media_codec;
+    } else {
+      // if no valid context is provided, use media context
+      vptr = &gcp_media_codec;
+    }
+  } else if (profile == WMCP) {
+    if(context == MEDIA_CONTEXT) {
+      vptr = &wmcp_media_codec;
+    } else {
+      // if no valid context is provided, use media context
+      vptr = &wmcp_media_codec;
+    }
+  }
+
+  if (!vptr){
+     return { };
+  }
+
+  for (uint8_t i = 0; i < (uint8_t)vptr->size(); i++) {
+    memset(&temp_config, 0, sizeof(CodecConfig));
+
+    temp_config.codec_type = CodecIndex::CODEC_INDEX_SOURCE_LC3;
+
+    switch (vptr->at(i).freq_in_hz)
+    {
+      case SAMPLE_RATE_8000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_8000;
+        break;
+      case SAMPLE_RATE_16000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+        break;
+      case SAMPLE_RATE_24000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_24000;
+        break;
+      case SAMPLE_RATE_32000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_32000;
+        break;
+      case SAMPLE_RATE_44100:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_44100;
+        break;
+      case SAMPLE_RATE_48000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+        break;
+      default:
+        break;
+    }
+
+    if (vptr->at(i).frame_dur_msecs == FRM_DURATION_7_5_MS)
+      UpdateFrameDuration(&temp_config, static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_7_5));
+    else if (vptr->at(i).frame_dur_msecs == FRM_DURATION_10_MS)
+      UpdateFrameDuration(&temp_config, static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+
+    UpdateOctsPerFrame(&temp_config, vptr->at(i).oct_per_codec_frm);
+
+    ret_config.push_back(temp_config);
+  }
+
+  return ret_config;
+}
+
+vector<CodecConfig> get_preferred_codec_configs(uint8_t profile, uint8_t context)
+{
+  vector<CodecConfig> ret_config;
+  CodecConfig temp_config;
+  vector<codec_config> *vptr = NULL;
+
+  if (profile == VMCP) {
+    if (context == VOICE_CONTEXT) {
+      vptr = &vmcp_voice_codec;
+    }
+    else if(context == MEDIA_CONTEXT) {
+      vptr = &vmcp_media_codec;
+    } else {
+      // if no valid context is provided, use voice context
+      vptr = &vmcp_voice_codec;
+    }
+  } else if (profile == BAP) {
+    if (context == VOICE_CONTEXT) {
+      vptr = &bap_voice_codec;
+    }
+    else if(context == MEDIA_CONTEXT) {
+      vptr = &bap_media_codec;
+    } else {
+      // if no valid context is provided, use voice context
+      vptr = &bap_voice_codec;
+    }
+  } else if (profile == GCP) {
+    if (context == VOICE_CONTEXT) {
+      vptr = &gcp_voice_codec;
+    }
+    else if(context == MEDIA_CONTEXT) {
+      vptr = &gcp_media_codec;
+    } else {
+      // if no valid context is provided, use media context
+      vptr = &gcp_media_codec;
+    }
+  } else if (profile == WMCP) {
+    if(context == MEDIA_CONTEXT) {
+      vptr = &wmcp_media_codec;
+    } else {
+      // if no valid context is provided, use media context
+      vptr = &wmcp_media_codec;
+    }
+  }
+
+  if (!vptr) {
+     return {};
+  }
+
+  for (uint8_t i = 0; i < (uint8_t)vptr->size(); i++) {
+    if (vptr->at(i).mandatory == 1) {
+      memset(&temp_config, 0, sizeof(CodecConfig));
+
+      temp_config.codec_type = CodecIndex::CODEC_INDEX_SOURCE_LC3;
+
+      switch (vptr->at(i).freq_in_hz)
+      {
+        case SAMPLE_RATE_8000:
+          temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_8000;
+          break;
+        case SAMPLE_RATE_16000:
+          temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+          break;
+        case SAMPLE_RATE_24000:
+          temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_24000;
+          break;
+        case SAMPLE_RATE_32000:
+          temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_32000;
+          break;
+        case SAMPLE_RATE_44100:
+          temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_44100;
+          break;
+        case SAMPLE_RATE_48000:
+          temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+          break;
+        default:
+          break;
+      }
+
+      if (vptr->at(i).frame_dur_msecs == FRM_DURATION_7_5_MS)
+        UpdateFrameDuration(&temp_config, static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_7_5));
+      else if (vptr->at(i).frame_dur_msecs == FRM_DURATION_10_MS)
+        UpdateFrameDuration(&temp_config, static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10));
+
+      UpdateOctsPerFrame(&temp_config, vptr->at(i).oct_per_codec_frm);
+
+      ret_config.push_back(temp_config);
+    }
+  }
+
+  return ret_config;
+}
+
+vector<QoSConfig> get_all_qos_params(uint8_t profile, uint8_t context)
+{
+  vector<QoSConfig> ret_config;
+  QoSConfig temp_config;
+  vector<qos_config> *vptr = NULL;
+
+  if (profile == VMCP) {
+    if (context == VOICE_CONTEXT)
+       vptr =  &vmcp_qos_low_lat_voice;
+    else if (context == MEDIA_LL_CONTEXT)
+       vptr = &vmcp_qos_low_lat_media;
+    else if (context == MEDIA_HR_CONTEXT)
+       vptr = &vmcp_qos_high_rel_media;
+  } else if (profile == BAP) {
+    if (context == VOICE_CONTEXT)
+       vptr =  &bap_qos_low_lat_voice;
+    else if (context == MEDIA_LL_CONTEXT)
+       vptr = &bap_qos_low_lat_media;
+    else if (context == MEDIA_HR_CONTEXT)
+       vptr = &bap_qos_high_rel_media;
+  } else if (profile == GCP) {
+    if (context == VOICE_CONTEXT)
+       vptr =  &gcp_qos_low_lat_voice;
+    else if (context == MEDIA_LL_CONTEXT)
+       vptr = &gcp_qos_low_lat_media;
+  } else if (profile == WMCP) {
+    if (context == MEDIA_HR_CONTEXT)
+       vptr = &wmcp_qos_high_rel_media;
+  }
+
+  if (!vptr) {
+     return {};
+  }
+
+  for (uint8_t i = 0; i < (uint8_t)vptr->size(); i++) {
+    memset(&temp_config, 0, sizeof(QoSConfig));
+
+    switch (vptr->at(i).freq_in_hz)
+    {
+      case SAMPLE_RATE_8000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_8000;
+        break;
+      case SAMPLE_RATE_16000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_16000;
+        break;
+      case SAMPLE_RATE_24000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_24000;
+        break;
+      case SAMPLE_RATE_32000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_32000;
+        break;
+      case SAMPLE_RATE_44100:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_44100;
+        break;
+      case SAMPLE_RATE_48000:
+        temp_config.sample_rate = CodecSampleRate::CODEC_SAMPLE_RATE_48000;
+        break;
+      default:
+        break;
+    }
+
+    temp_config.sdu_int_micro_secs = vptr->at(i).sdu_int_micro_secs;
+    temp_config.framing = vptr->at(i).framing;
+    temp_config.max_sdu_size = vptr->at(i).max_sdu_size;
+    temp_config.retrans_num = vptr->at(i).retrans_num;
+    temp_config.max_trans_lat = vptr->at(i).max_trans_lat;
+    temp_config.presentation_delay = vptr->at(i).presentation_delay;
+    temp_config.mandatory = vptr->at(i).mandatory;
+
+    ret_config.push_back(temp_config);
+  }
+
+  return ret_config;
+}
+
+vector<QoSConfig> get_qos_params_for_codec(uint8_t profile, uint8_t context, CodecSampleRate freq, uint8_t frame_dur, uint8_t octets)
+{
+  vector<QoSConfig> ret_config;
+  QoSConfig temp_config;
+  vector<qos_config> *vptr = NULL;
+  uint32_t frame_dur_micro_sec = 0;
+  uint32_t local_freq = 0;
+
+  if (frame_dur == static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_7_5))
+    frame_dur_micro_sec = FRM_DURATION_7_5_MS * 1000;
+  else if (frame_dur == static_cast<uint8_t>(CodecFrameDuration::FRAME_DUR_10))
+    frame_dur_micro_sec = FRM_DURATION_10_MS * 1000;
+
+  switch (freq)
+  {
+    case CodecSampleRate::CODEC_SAMPLE_RATE_8000:
+      local_freq = SAMPLE_RATE_8000;
+      break;
+    case CodecSampleRate::CODEC_SAMPLE_RATE_16000:
+      local_freq = SAMPLE_RATE_16000;
+      break;
+    case CodecSampleRate::CODEC_SAMPLE_RATE_24000:
+      local_freq = SAMPLE_RATE_24000;
+      break;
+    case CodecSampleRate::CODEC_SAMPLE_RATE_32000:
+      local_freq = SAMPLE_RATE_32000;
+      break;
+    case CodecSampleRate::CODEC_SAMPLE_RATE_44100:
+      local_freq = SAMPLE_RATE_44100;
+      break;
+    case CodecSampleRate::CODEC_SAMPLE_RATE_48000:
+      local_freq = SAMPLE_RATE_48000;
+      break;
+    default:
+      break;
+  }
+
+  if (profile == VMCP) {
+    if (context == VOICE_CONTEXT)
+       vptr =  &vmcp_qos_low_lat_voice;
+    else if (context == MEDIA_LL_CONTEXT)
+       vptr = &vmcp_qos_low_lat_media;
+    else if (context == MEDIA_HR_CONTEXT)
+       vptr = &vmcp_qos_high_rel_media;
+  } else if (profile == BAP) {
+    if (context == VOICE_CONTEXT)
+       vptr =  &bap_qos_low_lat_voice;
+    else if (context == MEDIA_LL_CONTEXT) {
+       BTIF_TRACE_IMP("%s: filling BAP LL vptr", __func__);
+       vptr = &bap_qos_low_lat_media;
+    } else if (context == MEDIA_HR_CONTEXT) {
+       BTIF_TRACE_IMP("%s: filling BAP HR vptr", __func__);
+       vptr = &bap_qos_high_rel_media;
+    }
+  } else if (profile == GCP) {
+    if (context == VOICE_CONTEXT)
+       vptr =  &gcp_qos_low_lat_voice;
+    else if (context == MEDIA_LL_CONTEXT)
+       vptr = &gcp_qos_low_lat_media;
+  } else if (profile == WMCP) {
+    if (context == MEDIA_HR_CONTEXT) {
+       BTIF_TRACE_IMP("%s: filling WMCP HR vptr", __func__);
+       vptr = &wmcp_qos_high_rel_media;
+    }
+  }
+
+  if (!vptr) {
+     return { };
+  }
+
+  BTIF_TRACE_IMP("%s: vptr size: %d", __func__, (uint8_t)vptr->size());
+  BTIF_TRACE_IMP("%s: local_freq: %d, frame_dur_micro_sec: %d, octets: %d",
+       __func__, local_freq, frame_dur_micro_sec, octets);
+
+  for (uint8_t i = 0; i < (uint8_t)vptr->size(); i++) {
+    BTIF_TRACE_IMP("%s: freq_in_hz: %d, sdu_int_micro_secs: %d, max_sdu_size: %d",
+       __func__, vptr->at(i).freq_in_hz, vptr->at(i).sdu_int_micro_secs,
+       vptr->at(i).max_sdu_size);
+    if (vptr->at(i).freq_in_hz == local_freq &&
+        vptr->at(i).sdu_int_micro_secs == frame_dur_micro_sec &&
+        vptr->at(i).max_sdu_size == octets) {
+      BTIF_TRACE_IMP("%s: Local and vptr matched.", __func__);
+      memset(&temp_config, 0, sizeof(QoSConfig));
+
+      temp_config.sample_rate = freq;
+      temp_config.sdu_int_micro_secs = vptr->at(i).sdu_int_micro_secs;
+      temp_config.framing = vptr->at(i).framing;
+      temp_config.max_sdu_size = vptr->at(i).max_sdu_size;
+      temp_config.retrans_num = vptr->at(i).retrans_num;
+      temp_config.max_trans_lat = vptr->at(i).max_trans_lat;
+      temp_config.presentation_delay = vptr->at(i).presentation_delay;
+      temp_config.mandatory = vptr->at(i).mandatory;
+
+      ret_config.push_back(temp_config);
+    }
+  }
+
+  return ret_config;
+}
+
+bool is_leaf(xmlNode *node)
+{
+  xmlNode *child = node->children;
+  while(child)
+  {
+    if(child->type == XML_ELEMENT_NODE)
+      return false;
+
+    child = child->next;
+  }
+
+  return true;
+}
+
+void parseCodecConfigs(xmlNode *input_node, int context)
+{
+   stack<xmlNode*> profile_node_stack;
+   unsigned int TempCodecCount = 0;
+   unsigned int TempFieldsCount = 0;
+   xmlNode *FirstChild = xmlFirstElementChild(input_node);
+   unsigned long CodecFields = xmlChildElementCount(FirstChild);
+   codec_config temp_codec_config;
+   memset(&temp_codec_config, 0, sizeof(codec_config));
+
+
+   BTIF_TRACE_IMP("codec Fields count is %ld \n", CodecFields);
+   for (xmlNode *node = input_node->children; node != NULL || !profile_node_stack.empty(); node = node->children)
+   {
+     if (node == NULL)
+     {
+       node = profile_node_stack.top();
+       profile_node_stack.pop();
+     }
+
+     if(node)
+     {
+       if(node->type == XML_ELEMENT_NODE)
+       {
+         if((is_leaf(node)))
+         {
+           string content = (const char*)(xmlNodeGetContent(node));
+           if (content[0] == '\0') {
+               return;
+           }
+           if(!xmlStrcmp(node->name,(const xmlChar*)"SamplingFrequencyInHz"))
+           {
+             temp_codec_config.freq_in_hz = atoi(content.c_str());
+             TempFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"FrameDurationInMicroSecs"))
+           {
+             temp_codec_config.frame_dur_msecs = (float)atoi(content.c_str())/1000;
+             TempFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"OctetsPerCodecFrame"))
+           {
+             temp_codec_config.oct_per_codec_frm = atoi(content.c_str());
+             TempFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"Mandatory"))
+           {
+             temp_codec_config.mandatory = atoi(content.c_str());
+             TempFieldsCount++;
+           }
+         }
+         if(TempFieldsCount == CodecFields)
+         {
+           if (current_profile == VMCP) {
+             if (context == VOICE_CONTEXT) {
+               vmcp_voice_codec.push_back(temp_codec_config);
+             } else if (context == MEDIA_CONTEXT) {
+               vmcp_media_codec.push_back(temp_codec_config);
+             }
+           } else if (current_profile == BAP) {
+             if (context == VOICE_CONTEXT) {
+               bap_voice_codec.push_back(temp_codec_config);
+             } else if (context == MEDIA_CONTEXT) {
+               bap_media_codec.push_back(temp_codec_config);
+             }
+           } else if (current_profile == GCP) {
+             if (context == VOICE_CONTEXT) {
+               gcp_voice_codec.push_back(temp_codec_config);
+             } else if (context == MEDIA_CONTEXT) {
+               gcp_media_codec.push_back(temp_codec_config);
+             }
+           } else if (current_profile == WMCP) {
+             if (context == MEDIA_CONTEXT) {
+               BTIF_TRACE_IMP("%s: parsed codec config for wmcp", __func__);
+               wmcp_media_codec.push_back(temp_codec_config);
+             }
+           }
+
+           TempFieldsCount = 0;
+           TempCodecCount++;
+         }
+       }
+
+       if(node->next != NULL)
+       {
+         profile_node_stack.push(node->next);
+         node = node->next;
+       }
+     } // end of if (node)
+   } // end of for
+   if(context == VOICE_CONTEXT && TempCodecCount == voice_codec_count)
+   {
+     if (current_profile < GCP) {
+       BTIF_TRACE_IMP("All %ld CG codecs are parsed successfully\n", voice_codec_count);
+     } else {
+       BTIF_TRACE_IMP("All %ld GAT Rx codecs are parsed successfully\n", voice_codec_count);
+     }
+   }
+   else if(context == MEDIA_CONTEXT && TempCodecCount == media_codec_count)
+   {
+     if (current_profile < GCP) {
+       BTIF_TRACE_IMP("All %ld UMS codecs are parsed successfully\n", media_codec_count);
+     } else if (current_profile == GCP) {
+       BTIF_TRACE_IMP("All %ld GAT Tx codecs are parsed successfully\n", media_codec_count);
+     } else if (current_profile == WMCP) {
+       BTIF_TRACE_IMP("All %ld WM Rx codecs are parsed successfully\n", media_codec_count);
+     }
+   }
+}
+
+void parseQoSConfigs(xmlNode *QoSInputNode, int context)
+{
+   stack<xmlNode*> QoS_Stack;
+   unsigned int TempQoSCodecCount = 0;
+   unsigned int TempQoSFieldsCount = 0;
+   xmlNode * FirstChild = xmlFirstElementChild(QoSInputNode);
+   unsigned long QoSCodecFields = xmlChildElementCount(FirstChild);
+   qos_config temp_qos_config ;
+   memset(&temp_qos_config, 0, sizeof(qos_config));
+
+   BTIF_TRACE_IMP("QoS Fields count %ld \n", QoSCodecFields);
+   for (xmlNode *node = QoSInputNode->children; node != NULL || !QoS_Stack.empty(); node = node->children)
+   {
+     if (node == NULL)
+     {
+       node = QoS_Stack.top();
+       QoS_Stack.pop();
+     }
+     if(node)
+     {
+       if(node->type == XML_ELEMENT_NODE)
+       {
+         if(is_leaf(node))
+         {
+           string content = (const char*)(xmlNodeGetContent(node));
+           if (content[0] == '\0') {
+               return;
+           }
+           if(!xmlStrcmp(node->name,(const xmlChar*)"SamplingFrequencyInHz"))
+           {
+             temp_qos_config.freq_in_hz = atoi(content.c_str());
+             TempQoSFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"SDUIntervalInMicroSecs"))
+           {
+             temp_qos_config.sdu_int_micro_secs = atoi(content.c_str());
+             TempQoSFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"Framing"))
+           {
+             temp_qos_config.framing = atoi(content.c_str());
+             TempQoSFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"MaxSDUSize"))
+           {
+             temp_qos_config.max_sdu_size = atoi(content.c_str());
+             TempQoSFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"RetransmissionNumber"))
+           {
+             temp_qos_config.retrans_num = atoi(content.c_str());
+             TempQoSFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"MaxTransportLatency"))
+           {
+             temp_qos_config.max_trans_lat = atoi(content.c_str());
+             TempQoSFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"PresentationDelay"))
+           {
+             temp_qos_config.presentation_delay = atoi(content.c_str());
+             TempQoSFieldsCount++;
+           }
+           else if(!xmlStrcmp(node->name, (const xmlChar*)"Mandatory"))
+           {
+             temp_qos_config.mandatory = atoi(content.c_str());
+             TempQoSFieldsCount++;
+           }
+          }
+          if(TempQoSFieldsCount == QoSCodecFields)
+          {
+            if(current_profile == VMCP) {
+              if (context == VOICE_CONTEXT) {
+                vmcp_qos_low_lat_voice.push_back(temp_qos_config);
+              } else if (context == MEDIA_LL_CONTEXT) {
+                vmcp_qos_low_lat_media.push_back(temp_qos_config);
+              } else if (context == MEDIA_HR_CONTEXT) {
+                vmcp_qos_high_rel_media.push_back(temp_qos_config);
+              }
+            } else if(current_profile == BAP) {
+              if (context == VOICE_CONTEXT) {
+                bap_qos_low_lat_voice.push_back(temp_qos_config);
+              } else if (context == MEDIA_LL_CONTEXT) {
+                bap_qos_low_lat_media.push_back(temp_qos_config);
+              } else if (context == MEDIA_HR_CONTEXT) {
+                bap_qos_high_rel_media.push_back(temp_qos_config);
+              }
+            } else if(current_profile == GCP) {
+              if (context == VOICE_CONTEXT) {
+                gcp_qos_low_lat_voice.push_back(temp_qos_config);
+              } else if (context == MEDIA_LL_CONTEXT) {
+                gcp_qos_low_lat_media.push_back(temp_qos_config);
+              }
+            } else if(current_profile == WMCP) {
+              if (context == MEDIA_HR_CONTEXT) {
+                BTIF_TRACE_IMP("%s: parsed qos config for wmcp", __func__);
+                wmcp_qos_high_rel_media.push_back(temp_qos_config);
+              }
+            }
+
+            TempQoSFieldsCount = 0;
+            TempQoSCodecCount++;
+          }
+        }
+       if(node->next != NULL)
+       {
+         QoS_Stack.push(node->next);
+         node = node->next;
+       }
+
+    }
+  }
+  if(TempQoSCodecCount == qos_settings_count)
+  {
+    if(context == VOICE_CONTEXT)
+    {
+      if (current_profile < GCP) {
+        BTIF_TRACE_IMP("All %ld CG Qos Config are parsed successfully\n", qos_settings_count);
+      } else {
+        BTIF_TRACE_IMP("All %ld GAT Rx Qos Config are parsed successfully\n", qos_settings_count);
+      }
+    }
+    else if(context == MEDIA_CONTEXT)
+    {
+      if (current_profile < GCP) {
+        BTIF_TRACE_IMP("All %ld UMS Qos Config are parsed successfully\n", qos_settings_count);
+      } else if (current_profile == GCP) {
+        BTIF_TRACE_IMP("All %ld GAT Tx Qos Config are parsed successfully\n", qos_settings_count);
+      } else if (current_profile == WMCP) {
+        BTIF_TRACE_IMP("All %ld WM Rx Qos Config are parsed successfully\n", qos_settings_count);
+      }
+    }
+  }
+}
+
+void parse_xml(xmlNode *inputNode)
+{
+   stack<xmlNode*> S;
+   for (xmlNode *node = inputNode; node != NULL || !S.empty(); node = node->children)
+   {
+    if (node == NULL)
+    {
+       node = S.top();
+       S.pop();
+    }
+    if (node)
+    {
+      if (node->type == XML_ELEMENT_NODE)
+      {
+        if (!(is_leaf(node)))
+        {
+          string content = (const char *) (xmlNodeGetContent (node));
+          if (content[0] == '\0') {
+              return;
+          }
+          if (!xmlStrcmp (node->name, (const xmlChar *) "VMCP"))
+          {
+             BTIF_TRACE_IMP("VMCP configs being parsed\n");
+             current_profile = VMCP;
+          }
+          if (!xmlStrcmp (node->name, (const xmlChar *) "BAP"))
+          {
+             BTIF_TRACE_IMP("BAP configs being parsed\n");
+             current_profile = BAP;
+          }
+          if (!xmlStrcmp (node->name, (const xmlChar *) "GCP"))
+          {
+             BTIF_TRACE_IMP("GCP configs being parsed\n");
+             current_profile = GCP;
+          }
+          if (!xmlStrcmp (node->name, (const xmlChar *) "WMCP"))
+          {
+             BTIF_TRACE_IMP("WMCP configs being parsed\n");
+             current_profile = WMCP;
+          }
+
+          if (!xmlStrcmp (node->name, (const xmlChar *) "CodecCapabilitiesForVoice"))
+          {
+            voice_codec_count = xmlChildElementCount(node);
+            parseCodecConfigs(node, VOICE_CONTEXT);
+          }
+          else if (!xmlStrcmp (node->name, (const xmlChar *) "CodecCapabilitiesForMedia"))
+          {
+            media_codec_count = xmlChildElementCount(node);
+            parseCodecConfigs(node, MEDIA_CONTEXT);
+          }
+          else if (!xmlStrcmp (node->name, (const xmlChar *) "QosSettingsForLowLatencyVoice"))
+          {
+            qos_settings_count = xmlChildElementCount(node);
+            parseQoSConfigs(node, VOICE_CONTEXT);
+          }
+          else if (!xmlStrcmp (node->name, (const xmlChar *) "QosSettingsForLowLatencyMedia"))
+          {
+            qos_settings_count = xmlChildElementCount(node);
+            parseQoSConfigs(node, MEDIA_LL_CONTEXT);
+          }
+          else if (!xmlStrcmp (node->name, (const xmlChar *) "QosSettingsForHighReliabilityMedia"))
+          {
+            qos_settings_count = xmlChildElementCount(node);
+            parseQoSConfigs(node, MEDIA_HR_CONTEXT);
+          }
+        }
+      }
+      if(node->next != NULL)
+      {
+        S.push(node -> next);
+      }
+    }
+   }
+}
+
+void btif_vmcp_init() {
+  xmlDoc *doc = NULL;
+  xmlNode *root_element = NULL;
+
+  doc = xmlReadFile(LEAUDIO_CONFIG_PATH, NULL, 0);
+  if (doc == NULL) {
+    BTIF_TRACE_ERROR("Could not parse the XML file");
+  }
+
+  root_element = xmlDocGetRootElement(doc);
+  parse_xml(root_element);
+  xmlFreeDoc(doc);
+  xmlCleanupParser();
+
+  //Register Audio Gaming Service UUID (GCP) with Gattc
+  btif_register_uuid_srvc_disc(bluetooth::Uuid::FromString("12994b7e-6d47-4215-8c9e-aae9a1095ba3"));
+
+  //Register Wireless Microphone Configuration Service UUID (WMCP) with Gattc
+  btif_register_uuid_srvc_disc(bluetooth::Uuid::FromString("2587db3c-ce70-4fc9-935f-777ab4188fd7"));
+}
diff --git a/le_audio/system/bt/common/state_machine.h b/le_audio/system/bt/common/state_machine.h
new file mode 100644
index 0000000..62d92d2
--- /dev/null
+++ b/le_audio/system/bt/common/state_machine.h
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <map>
+#include <utility>
+
+#include <base/logging.h>
+
+namespace bluetooth {
+
+namespace common {
+
+/**
+ * State machine used by Bluetooth native stack.
+ */
+class StateMachine {
+ public:
+  enum { kStateInvalid = -1 };
+
+  /**
+   * A class to represent the state in the State Machine.
+   */
+  class State {
+    friend class StateMachine;
+
+   public:
+    /**
+     * Constructor.
+     *
+     * @param sm the State Machine to use
+     * @param state_id the unique State ID. It should be a non-negative number.
+     */
+    State(StateMachine& 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(StateMachine::State* dest_state) {
+      sm_.TransitionTo(dest_state);
+    }
+
+   private:
+    StateMachine& sm_;
+    int state_id_;
+  };
+
+  StateMachine()
+      : initial_state_(nullptr),
+        previous_state_(nullptr),
+        current_state_(nullptr) {}
+  ~StateMachine() {
+    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(StateMachine::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_;
+};
+
+}  // namespace common
+
+}  // namespace bluetooth
diff --git a/le_audio/vhal/include/hardware/bluetooth_callcontrol_callbacks.h b/le_audio/vhal/include/hardware/bluetooth_callcontrol_callbacks.h
new file mode 100644
index 0000000..7137867
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bluetooth_callcontrol_callbacks.h
@@ -0,0 +1,62 @@
+/*
+ *Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace bluetooth {
+namespace call_control {
+
+/**
+ * CCS/GTBS related callbacks invoked from from the Bluetooth native stack
+ * All callbacks are invoked on the JNI thread
+ */
+class CallControllerCallbacks {
+ public:
+  virtual ~CallControllerCallbacks() = default;
+  /**
+   * Callback for notifying the CCS initialization status.
+   *
+   * @param state success if zero, failure otherwise
+   */
+  virtual void CallControlInitializedCallback(uint8_t state
+                                        ) = 0;
+  /**
+   * Callback for connection state change.
+   *
+   * @param state one of the values from btcc_connection_state_t
+   * @param bd_addr remote device address
+   */
+  virtual void ConnectionStateCallback(uint8_t state,
+                                        const RawAddress& address) = 0;
+  /**
+   * Callback for call control operations.
+   *
+   * @param op call control operation initiated from remote
+   * @param indicies Indicies for the call control operations
+   * @param count number of Indicies for the call control operations
+   * @uri uri for the call control operation
+   * @param bd_addr remote device address which initiated the call control op
+   */
+  virtual void CallControlCallback(uint8_t op, std::vector<int32_t> indicies, int count, std::vector<uint8_t> uri_data,
+                                   const RawAddress& address) = 0;
+};
+
+}  // namespace callcontrol
+}  // namespace bluetooth
diff --git a/le_audio/vhal/include/hardware/bluetooth_callcontrol_interface.h b/le_audio/vhal/include/hardware/bluetooth_callcontrol_interface.h
new file mode 100644
index 0000000..c2b7849
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bluetooth_callcontrol_interface.h
@@ -0,0 +1,152 @@
+/*
+ *Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "bluetooth_callcontrol_callbacks.h"
+#include <hardware/bluetooth.h>
+
+#define BT_PROFILE_CC_ID "cc_server"
+
+namespace bluetooth {
+namespace call_control {
+
+/**
+ * Programming interface for CCS/GTBS profiles in the Fluoride stack
+ * Thread-safe
+ */
+class CallControllerInterface {
+ public:
+  virtual ~CallControllerInterface() = default;
+  /**
+   * Initialize the CCS/GTBS Interface
+   *
+   * @param callbacks callbacks for the user of the native stack
+   * @param max_ccs_clients maximum number of CCS/GTBS clients
+   * @param inband_ringing_enabled whether inband ringtone is enabled
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual bt_status_t Init(CallControllerCallbacks* callbacks, Uuid uuid, int max_ccs_clients,
+                           bool inband_ringing_enabled) = 0;
+  /**
+   * Updates telephony bearer name
+   *
+   * @param operator_str bearer name of provider
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual bt_status_t UpdateBearerName(uint8_t* operator_str) = 0;
+
+  /**
+   * Updates the bearer Technogly type
+   *
+   * @param bearer_tech bearer technology type of provider
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual bt_status_t UpdateBearerTechnology(int bearer_tech) = 0;
+
+  /**
+   * Updates telephony network bearers supported
+   *
+   * @param supportedBearers supported bearers list
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual bt_status_t UpdateSupportedBearerList(uint8_t* supportedBearers) = 0;
+
+  /**
+   * Updates optional Call control operations supported
+   *
+   * @param feature bitmask value representing the optional op code supported
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual bt_status_t  CallControlOptionalOpSupported(int feature) = 0;
+
+  /**
+   * Update network signal strength
+   *
+   * @param signal level
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual bt_status_t UpdateSignalStatus(int signal) = 0;
+
+  /**
+   * Update status flag for GTBS
+   *
+   * @param bd_addr remote device address
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual bt_status_t UpdateStatusFlags(uint8_t status_flag) = 0;
+
+  /**
+   * Update Content control for GTBS/CCS
+   *
+   * @param ccid content control Id for GTBS/CCS
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual void ContentControlId(uint32_t ccid) = 0;
+
+  /**
+   * Update the Call State of CCS
+   *
+   * @param len of call state infos
+   * @param call_state_list array of call state list, where each call state
+   *        comprised of index, state, flags
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual bt_status_t CallState(int len, std::vector<uint8_t> call_state_list) = 0;
+
+  /**
+   * Update Incoming call URI for the given Call Index
+   *
+   * @param index index of the call
+   * @param uri representing the Incoming call, will be Incoming call number for telephony
+   * @return BT_STATUS_SUCCESS on success
+   */
+
+  virtual void UpdateIncomingCall(int index, uint8_t* uri) = 0;
+  /**
+   * Response for Call control Operation initiated
+   *
+   * @param op Operation for which response is sent
+   * @param index index of call for operation
+   * @param status status of the operation performed
+   * @return BT_STATUS_SUCCESS on success
+   */
+  virtual bt_status_t CallControlResponse(uint8_t op, uint8_t index, uint32_t status, const RawAddress& address) = 0;
+
+  /**
+   * Set the current active device for GTBS/CCS
+   *
+   * @param active_device_addr remote device address
+   */
+  virtual void SetActiveDevice(const RawAddress& address, int set_id) = 0;
+
+  /**
+   * Disconnect GTBS/CTS profile for remote
+   * @param address remote device address
+   */
+  virtual void Disconnect(const RawAddress& address) = 0;
+   /**
+   * Closes the GTBS/CCS interface.
+   */
+  virtual void Cleanup() = 0;
+};
+
+}  // namespace call_control
+}  // namespace bluetooth
diff --git a/le_audio/vhal/include/hardware/bt_acm.h b/le_audio/vhal/include/hardware/bt_acm.h
new file mode 100644
index 0000000..f32fb90
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bt_acm.h
@@ -0,0 +1,126 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 ANDROID_INCLUDE_BT_ACM_H
+#define ANDROID_INCLUDE_BT_ACM_H
+
+#include <vector>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_pacs_client.h>
+
+__BEGIN_DECLS
+
+#define BT_PROFILE_ACM_ID "bt_acm_proflie"
+using bluetooth::bap::pacs::CodecConfig;
+/* Bluetooth ACM connection states */
+typedef enum {
+  BTACM_CONNECTION_STATE_DISCONNECTED = 0,
+  BTACM_CONNECTION_STATE_CONNECTING,
+  BTACM_CONNECTION_STATE_CONNECTED,
+  BTACM_CONNECTION_STATE_DISCONNECTING
+} btacm_connection_state_t;
+
+/* Bluetooth ACM datapath states */
+typedef enum {
+  BTACM_AUDIO_STATE_REMOTE_SUSPEND = 0,
+  BTACM_AUDIO_STATE_STOPPED,
+  BTACM_AUDIO_STATE_STARTED,
+} btacm_audio_state_t;
+
+/** Callback for connection state change.
+ *  state will have one of the values from btacm_connection_state_t
+ */
+typedef void (*btacm_connection_state_callback)(const RawAddress& bd_addr,
+                                                               btacm_connection_state_t state,
+                                                               uint16_t contextType);
+
+/** Callback for audiopath state change.
+ *  state will have one of the values from btacm_audio_state_t
+ */
+typedef void (*btacm_audio_state_callback)(const RawAddress& bd_addr,
+                                                        btacm_audio_state_t state,
+                                                        uint16_t contextType);
+
+/** Callback for audio configuration change.
+ *  Used only for the ACM Initiator interface.
+ */
+typedef void (*btacm_audio_config_callback)(
+    const RawAddress& bd_addr, CodecConfig codec_config,
+    std::vector<CodecConfig> codecs_local_acmabilities,
+    std::vector<CodecConfig> codecs_selectable_acmabilities,
+    uint16_t contextType);
+
+/** BT-ACM Initiator callback structure. */
+typedef struct {
+  /** set to sizeof(btacm_initiator_callbacks_t) */
+  size_t size;
+  btacm_connection_state_callback connection_state_cb;
+  btacm_audio_state_callback audio_state_cb;
+  btacm_audio_config_callback audio_config_cb;
+} btacm_initiator_callbacks_t;
+
+/** Represents the standard BT-ACM Initiator interface.
+ */
+typedef struct {
+  /** set to sizeof(btacm_source_interface_t) */
+  size_t size;
+  /**
+   * Register the BtAcm callbacks.
+   */
+  bt_status_t (*init)(
+      btacm_initiator_callbacks_t* callbacks, int max_connected_audio_devices,
+      const std::vector<CodecConfig>& codec_priorities);
+
+  /** connect to headset */
+  bt_status_t (*connect)(const RawAddress& bd_addr, uint16_t context_type,
+                         uint16_t profile_type, uint16_t preferred_context);
+
+  /** dis-connect from headset */
+  bt_status_t (*disconnect)(const RawAddress& bd_addr, uint16_t context_type);
+
+  /** sets the connected device as active */
+  bt_status_t (*set_active_device)(const RawAddress& bd_addr,
+                                   uint16_t context_type);
+
+  /** start stream */
+  bt_status_t (*start_stream)(const RawAddress& bd_addr, uint16_t context_type);
+
+  /** stop stream */
+  bt_status_t (*stop_stream)(const RawAddress& bd_addr, uint16_t context_type);
+
+  /** configure the codecs settings preferences */
+  bt_status_t (*config_codec)(
+      const RawAddress& bd_addr,
+      std::vector<CodecConfig> codec_preferences,
+      uint16_t context_type, uint16_t preferred_context);
+
+  /** configure the codec based on ID*/
+  bt_status_t (*change_config_codec)(
+      const RawAddress& bd_addr,
+      char* Id);
+
+  /** Closes the interface. */
+  void (*cleanup)(void);
+
+} btacm_initiator_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_AV_H */
diff --git a/le_audio/vhal/include/hardware/bt_apm.h b/le_audio/vhal/include/hardware/bt_apm.h
new file mode 100644
index 0000000..2eb95e3
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bt_apm.h
@@ -0,0 +1,75 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 ANDROID_INCLUDE_BT_APM_H
+#define ANDROID_INCLUDE_BT_APM_H
+
+#define BT_APM_MODULE_ID "apm"
+
+#include <vector>
+
+#include <hardware/bluetooth.h>
+
+__BEGIN_DECLS
+
+/* Bluetooth APM Audio Types */
+typedef enum {
+    BTAPM_VOICE_AUDIO = 0,
+    BTAPM_MEDIA_AUDIO,
+    BTAPM_BROADCAST_AUDIO
+} btapm_audio_type_t;
+
+void call_active_profile_info(const RawAddress& bd_addr, uint16_t audio_type);
+int get_active_profile(const RawAddress& bd_addr, uint16_t audio_type);
+typedef int (*btapm_active_profile_callback)(const RawAddress& bd_addr, uint16_t audio_type);
+
+
+typedef struct {
+        size_t          size;
+        btapm_active_profile_callback active_profile_cb;
+}btapm_initiator_callbacks_t;
+
+
+
+/** APM interface
+ */
+typedef struct {
+
+    /** set to sizeof(bt_apm_interface_t) */
+    size_t          size;
+    /**
+     * Register the BtAv callbacks.
+     */
+    bt_status_t (*init)(btapm_initiator_callbacks_t* callbacks);
+
+    /** updates new active device to stack */
+    bt_status_t (*active_device_change)(const RawAddress& bd_addr, uint16_t profile, uint16_t audio_type);
+
+    /** send content control id to stack */
+    bt_status_t (*set_content_control_id)(uint16_t content_control_id, uint16_t audio_type);
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+
+}bt_apm_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_APM_H */
+
diff --git a/le_audio/vhal/include/hardware/bt_ascs_client.h b/le_audio/vhal/include/hardware/bt_ascs_client.h
new file mode 100644
index 0000000..cd9341b
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bt_ascs_client.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * 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 ANDROID_INCLUDE_BT_ASCS_CLIENT_H
+#define ANDROID_INCLUDE_BT_ASCS_CLIENT_H
+
+#include <hardware/bluetooth.h>
+
+namespace bluetooth {
+namespace bap {
+namespace ascs {
+
+#define BT_PROFILE_ASCS_CLIENT_ID "bt_ascs_client"
+
+constexpr uint8_t ASE_DIRECTION_SINK   = 0x01;
+constexpr uint8_t ASE_DIRECTION_SOURCE = 0x02;
+
+constexpr uint32_t CONTEXT_TYPE_CONVERSATIONAL  = 0x0002;
+constexpr uint32_t CONTEXT_TYPE_MEDIA           = 0x0004;
+
+constexpr uint8_t  ASE_STATE_IDLE                = 0x00;
+constexpr uint8_t  ASE_STATE_CODEC_CONFIGURED    = 0x01;
+constexpr uint8_t  ASE_STATE_QOS_CONFIGURED      = 0x02;
+constexpr uint8_t  ASE_STATE_ENABLING            = 0x03;
+constexpr uint8_t  ASE_STATE_STREAMING           = 0x04;
+constexpr uint8_t  ASE_STATE_DISABLING           = 0x05;
+constexpr uint8_t  ASE_STATE_RELEASING           = 0x06;
+constexpr uint8_t  ASE_STATE_INVALID             = 0xFF;
+
+typedef uint8_t sdu_interval_t[3];
+typedef uint8_t presentation_delay_t[3];
+typedef uint8_t codec_type_t[5];
+
+
+enum class ASCSEvent {
+  ASCS_DISCOVERY_CMPL_EVT = 0,
+  ASCS_DEV_CONNECTED,
+  ASCS_DEV_DISCONNECTED,
+  ASCS_ASE_STATE,
+};
+
+struct AudioContext {
+  uint8_t length;
+  uint8_t type;
+  uint16_t value;
+};
+
+enum class AseState {
+  IDLE = 0,
+  CODEC_CONFIGURED,
+  QOS_CONFIGURED,
+  ENABLING,
+  STREAMING,
+  DISABLING,
+  RELEASING,
+};
+
+enum class AseOpId {
+  CODEC_CONFIG = 0x01,
+  QOS_CONFIG,
+  ENABLE,
+  START_READY,
+  DISABLE,
+  STOP_READY,
+  UPDATE_META_DATA,
+  RELEASE
+};
+
+enum class GattState {
+  DISCONNECTED = 0,
+  CONNECTING,
+  CONNECTED,
+  DISCONNECTING
+};
+
+struct AseCodecConfigOp {
+  uint8_t ase_id;
+  uint8_t tgt_latency;
+  uint8_t tgt_phy;
+  codec_type_t codec_id;
+  uint8_t codec_params_len;
+  std::vector<uint8_t> codec_params;
+} __attribute__((packed));
+
+struct  AseQosConfigOp {
+  uint8_t ase_id;
+  uint8_t cig_id;
+  uint8_t cis_id;
+  sdu_interval_t sdu_interval;
+  uint8_t framing;
+  uint8_t phy;
+  uint16_t max_sdu_size;
+  uint8_t retrans_number;
+  uint16_t trans_latency;
+  presentation_delay_t present_delay;
+} __attribute__((packed));
+
+struct AseEnableOp {
+  uint8_t ase_id;
+  uint8_t meta_data_len;
+  std::vector<uint8_t> meta_data;
+} __attribute__((packed));
+
+struct AseDisableOp {
+  uint8_t ase_id;
+} __attribute__((packed));
+
+struct AseStartReadyOp {
+  uint8_t ase_id;
+} __attribute__((packed));
+
+struct AseStopReadyOp {
+  uint8_t ase_id;
+} __attribute__((packed));
+
+struct AseReleaseOp {
+  uint8_t ase_id;
+} __attribute__((packed));
+
+struct AseUpdateMetadataOp {
+  uint8_t ase_id;
+  uint8_t meta_data_len;
+  std::vector<uint8_t> meta_data;
+} __attribute__((packed));
+
+struct AseCodecConfigParams {
+  uint8_t framing;
+  uint8_t pref_phy;
+  uint8_t pref_rtn;
+  uint16_t mtl;
+  presentation_delay_t pd_min;
+  presentation_delay_t pd_max;
+  presentation_delay_t pref_pd_min;
+  presentation_delay_t pref_pd_max;
+  codec_type_t codec_id;
+  uint8_t codec_params_len;
+  std::vector<uint8_t> codec_params;
+} __attribute__((packed));
+
+struct  AseQosConfigParams {
+  uint8_t cig_id;
+  uint8_t cis_id;
+  sdu_interval_t sdu_interval;
+  uint8_t framing;
+  uint8_t phy;
+  uint16_t max_sdu_size;
+  uint8_t rtn;
+  uint16_t mtl;
+  presentation_delay_t pd;
+} __attribute__((packed));
+
+struct AseGenericParams {
+  uint8_t cig_id;
+  uint8_t cis_id;
+  uint8_t meta_data_len;
+  std::vector<uint8_t> meta_data;
+} __attribute__((packed));
+
+union AseOp {
+  AseCodecConfigOp codec_config_op;
+  AseQosConfigOp qos_config_op;
+  AseEnableOp enable_op;
+  AseDisableOp disable_op;
+  AseStartReadyOp start_ready_op;
+  AseStopReadyOp stop_ready_op;
+  AseReleaseOp release_op;
+};
+
+struct AseOpStatus {
+  uint8_t ase_id;
+  uint8_t resp_code;
+  uint8_t reason;
+};
+
+struct AseParams {
+  uint8_t ase_id;
+  uint8_t ase_state;
+  AseCodecConfigParams codec_config_params;
+  AseQosConfigParams qos_config_params;
+  AseGenericParams generic_params;
+} __attribute__((packed));
+
+struct AseCpNotification {
+  uint8_t ase_opcode;
+  uint8_t num_ases;
+  std::vector<AseOpStatus> status;
+} __attribute__((packed));
+
+struct Ase {
+  uint16_t ase_handle;
+  uint16_t ase_ccc_handle;
+  AseParams ase_params;
+} __attribute__((packed));
+
+struct AscsDiscoveryDb {
+  std::vector<Ase> ase_list;
+  uint16_t ase_cp_handle;
+  uint16_t ase_cp_ccc_handle;
+  bool service_changed_rcvd;
+  bool active;
+};
+
+class AscsClientCallbacks {
+ public:
+  virtual ~AscsClientCallbacks() = default;
+
+  /** Callback for ascs server registration status */
+  virtual void OnAscsInitialized(int status, int client_id) = 0;
+
+  /** Callback for ascs server connection state change */
+  virtual void OnConnectionState(const RawAddress& address,
+                                 GattState state) = 0;
+
+  /** Callback for ascs server control op failed status */
+  virtual void OnAseOpFailed(const RawAddress& address,
+                             AseOpId ase_op_id,
+                             std::vector<AseOpStatus> status) = 0;
+
+  /** Callback for ascs ase state change */
+  virtual void OnAseState(const RawAddress& address,
+                          AseParams ase) = 0;
+
+  /** Callback for ascs discovery results */
+  virtual void OnSearchComplete(int status, const RawAddress& address,
+                          std::vector<AseParams> sink_ase_list,
+                          std::vector<AseParams> src_ase_list) = 0;
+};
+
+class AscsClientInterface {
+ public:
+  virtual ~AscsClientInterface() = default;
+
+  /** Register the Ascs client callbacks */
+  virtual void Init(AscsClientCallbacks* callbacks) = 0;
+
+  /** Connect to ascs server */
+  virtual void Connect(uint16_t client_id, const RawAddress& address) = 0;
+
+  /** Disconnect ascs server */
+  virtual void Disconnect(uint16_t client_id, const RawAddress& address) = 0;
+
+  virtual void StartDiscovery(uint16_t client_id,
+                              const RawAddress& address) = 0;
+
+  virtual void GetAseState(uint16_t client_id, const RawAddress& address,
+                           uint8_t ase_id) = 0;
+
+  virtual void CodecConfig(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseCodecConfigOp> codec_configs);
+
+  virtual void QosConfig(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseQosConfigOp> qos_configs);
+
+  virtual void Enable(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseEnableOp> enable_ops);
+
+  virtual void Disable(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseDisableOp> disable_ops);
+
+  virtual void StartReady(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseStartReadyOp> start_ready_ops);
+
+  virtual void StopReady(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseStopReadyOp> stop_ready_ops);
+
+  virtual void Release(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseReleaseOp> release_ops);
+
+  virtual void UpdateStream(uint16_t client_id, const RawAddress& address,
+                           std::vector<AseUpdateMetadataOp> metadata_ops);
+
+  /** Closes the interface. */
+  virtual void Cleanup(uint16_t client_id) = 0;
+};
+
+}  // namespace ascs
+}  // namespace bap
+}  // namespace bluetooth
+
+#endif /* ANDROID_INCLUDE_BT_CLIENT_H */
diff --git a/le_audio/vhal/include/hardware/bt_bap_ba.h b/le_audio/vhal/include/hardware/bt_bap_ba.h
new file mode 100644
index 0000000..58c5fbd
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bt_bap_ba.h
@@ -0,0 +1,126 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 ANDROID_INCLUDE_BT_BAP_BA_H
+#define ANDROID_INCLUDE_BT_BAP_BA_H
+
+#include <hardware/bluetooth.h>
+#include "hardware/bt_av.h"
+
+__BEGIN_DECLS
+
+#define BT_PROFILE_BAP_BROADCAST_ID "bap_broadcast"
+
+/* Bluetooth BAP BROADCAST states */
+typedef enum {
+    BTBAP_BROADCAST_STATE_IDLE = 0, //Idle
+    BTBAP_BROADCAST_STATE_CONFIGURED, //Configured
+    BTBAP_BROADCAST_STATE_STREAMING, //Streaming
+} btbap_broadcast_state_t;
+
+/* Bluetooth BAP BROADCAST Audio states */
+typedef enum {
+    BTBAP_BROADCAST_AUDIO_STATE_STOPPED = 0,
+    BTBAP_BROADCAST__AUDIO_STATE_STARTED,
+} btbap_broadcast_audio_state_t;
+
+/** Callback for broadcast state change.
+ *  state will have one of the values from btbap_broadcast_state_t
+ */
+typedef void (* bap_broadcast_state_callback)(int adv_id,
+                                           btbap_broadcast_state_t state);
+
+/** Callback for audiopath state change.
+ *  state will have one of the values from btbap_broadcast_audio_state_t
+ */
+typedef void (* bap_broadcast_audio_state_callback)(int adv_id,
+                                           btbap_broadcast_audio_state_t state);
+
+/** Callback for audio configuration change.
+ */
+typedef void (* bap_broadcast_audio_config_callback)(int adv_id,
+                               btav_a2dp_codec_config_t codec_config,
+                               std::vector<btav_a2dp_codec_config_t> codec_capabilities);
+
+/** Callback for Iso datapath setup or removed.
+ */
+//typedef void (* bap_broadcast_iso_datapath_callback) (int big_handle, int enabled);
+
+/** Callback for encryption key generation notification
+ */
+typedef void (* bap_broadcast_enckey_callback) (std::string key);
+
+/** Callback to create/terminate BIG
+ */
+
+typedef void (* bap_broadcast_setup_big_callback) (int enable, int adv_id, int big_handle,
+                                                       int num_bises, std::vector<uint16_t> bis_handles);
+
+typedef void (* bap_broadcast_bid_callback) (std::vector<uint8_t> broadcast_id);
+
+/** BT-BAP-BROADCAST callback structure. */
+typedef struct {
+    /** set to sizeof(btbap_broadcast_callbacks_t) */
+    size_t      size;
+    bap_broadcast_state_callback  broadcast_state_cb;
+    bap_broadcast_audio_state_callback audio_state_cb;
+    bap_broadcast_audio_config_callback audio_config_cb;
+    //bap_broadcast_iso_datapath_callback iso_datapath_cb;
+    bap_broadcast_enckey_callback enc_key_cb;
+    bap_broadcast_setup_big_callback create_big_cb;
+    bap_broadcast_bid_callback broadcast_id_cb;
+} btbap_broadcast_callbacks_t;
+
+typedef struct {
+
+    /** set to sizeof(btav_source_interface_t) */
+    size_t          size;
+    /**
+     * Register the btbap_broadcast callbacks.
+     */
+    bt_status_t (*init)(btbap_broadcast_callbacks_t* callbacks,
+                int max_broadcast, btav_a2dp_codec_config_t config, int mode);
+
+    /** Enable broadcast with provided codec config */
+    bt_status_t (*enable_broadcast)(btav_a2dp_codec_config_t config);
+
+    /** disable broadcast to move the state machine to idle state */
+    bt_status_t (*disable_broadcast)(int adv_id);
+
+    /** sets bap broadcast as active session */
+    bt_status_t (*set_broadcast_active)(bool enable, uint8_t adv_id);
+
+    /** configure the codecs settings preferences */
+    bt_status_t (*codec_config_change)(uint8_t adv_id, btav_a2dp_codec_config_t config);
+
+    /** Disable ISO datapath */
+    bt_status_t (*setup_audiopath)(bool enable, uint8_t adv_id, uint8_t big_handle, int num_bises, int* bis_handles);
+
+    /** Get stored encryption key */
+    std::string (*get_encryption_key)( void );
+
+    /** Set Encryption with encryption lenght provided */
+    bt_status_t (*set_encryption) (bool enabled, uint8_t key_length);
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+
+} btbap_broadcast_interface_t;
+__END_DECLS
+#endif /*ANDROID_INCLUDE_BT_BAP_BA_H*/
+
diff --git a/le_audio/vhal/include/hardware/bt_bap_uclient.h b/le_audio/vhal/include/hardware/bt_bap_uclient.h
new file mode 100644
index 0000000..7f5a481
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bt_bap_uclient.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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 ANDROID_INCLUDE_BT_BAP_UCLIENT_H
+#define ANDROID_INCLUDE_BT_BAP_UCLIENT_H
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_pacs_client.h>
+
+
+namespace bluetooth {
+namespace bap {
+namespace ucast {
+
+#define BT_PROFILE_BAP_UCLIENT_ID "bt_bap_uclient"
+
+using bluetooth::bap::pacs::CodecConfig;
+
+constexpr uint8_t ASE_DIRECTION_SINK           = 0x01 << 0;
+constexpr uint8_t ASE_DIRECTION_SRC            = 0x01 << 1;
+
+constexpr uint8_t LATENCY_LOW                  = 0x01;
+constexpr uint8_t LATENCY_BALANCED             = 0x02;
+constexpr uint8_t LATENCY_HIGH                 = 0x03;
+
+// Content types
+constexpr uint16_t CONTENT_TYPE_UNSPECIFIED    = 0x0001; // Unspecified
+constexpr uint16_t CONTENT_TYPE_CONVERSATIONAL = 0x0002; // Conversational
+
+// Media(music playback, radio, podcast or movie soundtrack, or tv audio)
+constexpr uint16_t CONTENT_TYPE_MEDIA          = 0x0004;
+constexpr uint16_t CONTENT_TYPE_GAME           = 0x0008; // Game Audio
+constexpr uint16_t CONTENT_TYPE_INSTRUCTIONAL  = 0x0010; // Instructional
+
+// ManMachine(with voice recognition or virtual assistants)
+constexpr uint16_t CONTENT_TYPE_MAN_MACHINE    = 0x0020;
+constexpr uint16_t CONTENT_TYPE_LIVE           = 0x0040; // Live audio
+
+// Sound Effects(including keyboard and touch feedback;
+// menu and user interface sounds; and other system sounds)
+constexpr uint16_t CONTENT_TYPE_SOUND_EFFECTS  = 0x0080;
+
+// Notification and reminder sounds; attention-seeking audio,
+//for example, in beeps signaling the arrival of a message
+constexpr uint16_t CONTENT_TYPE_NOTIFICATIONS  = 0x0100;
+constexpr uint16_t CONTENT_TYPE_RINGTONE       = 0x0200; // Ringtone
+constexpr uint16_t CONTENT_TYPE_ALERT          = 0x0400; // ImmediateAlert
+constexpr uint16_t CONTENT_TYPE_EMERGENCY      = 0x0800; // EmergencyAlert
+
+// Audio locations
+constexpr uint32_t AUDIO_LOC_LEFT              = 0x0001;
+constexpr uint32_t AUDIO_LOC_RIGHT             = 0x0002;
+constexpr uint32_t AUDIO_LOC_CENTER            = 0x0004;
+
+constexpr uint8_t  LE_2M_PHY             = 0x02;
+constexpr uint8_t  LE_QHS_PHY            = 0x80;
+
+typedef uint8_t sdu_interval_t[3];
+typedef uint8_t presentation_delay_t[3];
+typedef uint8_t codec_type_t[5];
+typedef uint8_t codec_config[255];
+
+struct CISConfig {
+  uint8_t cis_id;
+  uint16_t max_sdu_m_to_s;
+  uint16_t max_sdu_s_to_m;
+  uint8_t phy_m_to_s;
+  uint8_t phy_s_to_m;
+  uint8_t rtn_m_to_s;
+  uint8_t rtn_s_to_m;
+};
+
+struct CIGConfig {
+  uint8_t cig_id;
+  uint8_t cis_count;
+  uint8_t packing;
+  uint8_t framing;
+  uint16_t max_tport_latency_m_to_s;
+  uint16_t max_tport_latency_s_to_m;
+  sdu_interval_t sdu_interval_m_to_s;
+  sdu_interval_t sdu_interval_s_to_m;
+};
+
+struct ASCSConfig {
+  uint8_t cig_id;
+  uint8_t cis_id;
+  uint8_t target_latency;
+  bool bi_directional;
+  presentation_delay_t presentation_delay;
+};
+
+struct QosConfig {
+  CIGConfig cig_config;
+  std::vector<CISConfig> cis_configs; // individual CIS configs
+  std::vector<ASCSConfig> ascs_configs;
+};
+
+enum class AseState {
+  IDLE = 0,
+  CODEC_CONFIGURED,
+  QOS_CONFIGURED,
+  ENABLING,
+  STREAMING,
+  DISABLING,
+  RELEASING,
+};
+
+enum class StreamState {
+  DISCONNECTED = 0,
+  CONNECTING,
+  CONNECTED,
+  STARTING,
+  STREAMING,
+  STOPPING,
+  DISCONNECTING,
+  RECONFIGURING,
+  UPDATING
+};
+
+enum class DeviceState {
+  DISCONNECTED = 0,
+  CONNECTING,
+  CONNECTED
+};
+
+enum class StreamDiscReason {
+  REASON_NONE,
+  REASON_USER_DISC
+};
+
+struct CodecQosConfig {
+  CodecConfig codec_config;
+  QosConfig qos_config;
+};
+
+struct StreamType {
+  uint16_t type;
+  uint16_t audio_context;
+  uint8_t  direction;
+};
+
+struct StreamConnect {
+  StreamType stream_type;
+  //CCID_List ccids; //TODO
+  std::vector<CodecQosConfig> codec_qos_config_pair;
+};
+
+enum class StreamReconfigType {
+  CODEC_CONFIG,
+  QOS_CONFIG
+};
+
+struct StreamReconfig {
+  StreamType stream_type;
+  StreamReconfigType reconf_type;
+  std::vector<CodecQosConfig> codec_qos_config_pair;
+};
+
+enum class StreamUpdateType {
+  STREAMING_CONTEXT,
+};
+
+struct StreamUpdate {
+  StreamType stream_type;
+  StreamUpdateType update_type;
+  uint16_t update_value;
+};
+
+struct StreamStateInfo {
+  StreamType stream_type;
+  StreamState stream_state;
+  StreamDiscReason reason;
+};
+
+struct StreamConfigInfo {
+  StreamType stream_type;
+  CodecConfig codec_config; // codec
+  uint32_t audio_location; // location info of remote dev for the stream
+  QosConfig qos_config; // current CIG, CISs configuration
+  std::vector<CodecConfig> codecs_selectable; // pacs codec capabilities
+};
+
+class UcastClientCallbacks {
+ public:
+  virtual ~UcastClientCallbacks() = default;
+
+  virtual void OnStreamState(const RawAddress &address,
+                      std::vector<StreamStateInfo> streams_state_info) = 0;
+
+  virtual void OnStreamConfig(const RawAddress &address,
+                      std::vector<StreamConfigInfo> streams_config_info) = 0;
+
+  virtual void OnStreamAvailable(const RawAddress &address,
+                      uint16_t src_audio_contexts,
+                      uint16_t sink_audio_contexts) = 0;
+};
+
+class UcastClientInterface {
+ public:
+  virtual ~UcastClientInterface() = default;
+
+  /** Register the ucast client callbacks */
+  virtual void Init(UcastClientCallbacks* callbacks) = 0;
+
+  virtual void Connect(std::vector<RawAddress> &address, bool is_direct,
+                       std::vector<StreamConnect> &streams) = 0;
+
+  virtual void Disconnect(const RawAddress& address,
+                       std::vector<StreamType> &streams) = 0;
+
+  virtual void Start(const RawAddress& address,
+                     std::vector<StreamType> &streams) = 0;
+
+  virtual void Stop(const RawAddress& address,
+                    std::vector<StreamType> &streams) = 0;
+
+  virtual void Reconfigure(const RawAddress& address,
+                           std::vector<StreamReconfig> &streams) = 0;
+
+  virtual void UpdateStream(const RawAddress& address,
+                            std::vector<StreamUpdate> &update_streams) = 0;
+
+  /** Closes the interface. */
+  virtual void Cleanup() = 0;
+};
+
+UcastClientInterface* btif_bap_uclient_get_interface();
+
+}  // namespace ucast
+}  // namespace bap
+}  // namespace bluetooth
+
+#endif /* ANDROID_INCLUDE_BT_BAP_UCLIENT_H */
diff --git a/le_audio/vhal/include/hardware/bt_csip.h b/le_audio/vhal/include/hardware/bt_csip.h
new file mode 100644
index 0000000..92eeef1
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bt_csip.h
@@ -0,0 +1,119 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 ANDROID_INCLUDE_BT_CSIP_H
+#define ANDROID_INCLUDE_BT_CSIP_H
+
+#include <stdint.h>
+#include <bluetooth/uuid.h>
+#include <vector>
+
+__BEGIN_DECLS
+
+#define BT_PROFILE_CSIP_CLIENT_ID "csip_client"
+
+/** Callback when app has registered with CSIP Client module
+ */
+typedef void (* btcsip_csip_app_registered_callback)(uint8_t status, uint8_t app_id,
+                                                     const bluetooth::Uuid& app_uuid);
+
+/** Callback when connection state is changed for CSIP Profile
+ */
+typedef void (* btcsip_csip_connection_state_callback)(uint8_t app_id, RawAddress& bd_addr,
+                                                       uint8_t state, uint8_t status);
+
+/** Callback when new set has been identified on remote device
+ */
+typedef void (* btcsip_new_set_found_callback) (uint8_t set_id, RawAddress& bd_addr,
+                                                uint8_t size, uint8_t* sirk,
+                                                const bluetooth::Uuid& p_srvc_uuid,
+                                                bool lock_support);
+
+/** Callback when new set member has been identified
+ */
+typedef void (* btcsip_new_set_member_found_callback) (uint8_t set_id,
+                                                       RawAddress& bd_addr);
+
+/** Callback for lock status changed event to requesting client
+ */
+typedef void (* btcsip_lock_state_changed_callback) (uint8_t app_id, uint8_t set_id,
+                                                     uint8_t value, uint8_t status,
+                                                     std::vector<RawAddress> addr);
+
+/** Callback when lock is available on earlier denying set member
+ */
+typedef void (* btcsip_lock_available_callback) (uint8_t app_id, uint8_t set_id,
+                                                 RawAddress& bd_addr);
+
+/** Callback when size of coordinated set has been changed
+ */
+typedef void (* btcsip_set_size_changed_callback) (uint8_t set_id, uint8_t size,
+                                                   RawAddress& bd_addr);
+
+/** Callback when SIRK of coordinated set has been changed
+ */
+typedef void (* btcsip_set_sirk_changed_callback) (uint8_t set_id, uint8_t* sirk,
+                                                   RawAddress& bd_addr);
+
+/** BT-CSIP callback structure. */
+typedef struct {
+    size_t size;
+    btcsip_csip_app_registered_callback   app_registered_cb;
+    btcsip_csip_connection_state_callback conn_state_cb;
+    btcsip_new_set_found_callback         new_set_found_cb;
+    btcsip_new_set_member_found_callback  new_set_member_cb;
+    btcsip_lock_state_changed_callback    lock_status_cb;
+    btcsip_lock_available_callback        lock_available_cb;
+    btcsip_set_size_changed_callback      size_changed_cb;
+    btcsip_set_sirk_changed_callback      sirk_changed_cb;
+} btcsip_callbacks_t;
+
+/** Represents the standard BT-CSIP interface. */
+typedef struct {
+
+    /** set to sizeof(BtCsipInterface) */
+    size_t size;
+
+    /** Register the BtCsipInterface callbacks
+     */
+    bt_status_t (*init) (btcsip_callbacks_t* callbacks);
+
+    /** CSIP opportunistic gatt client connection*/
+    bt_status_t (*connect) (uint8_t app_id, RawAddress *bd_addr);
+
+    /** disconnect csip gatt connection */
+    bt_status_t (*disconnect) (uint8_t app_id, RawAddress *bd_addr );
+
+    /** register app/module with CSIP profile*/
+    bt_status_t (*register_csip_app) (const bluetooth::Uuid& app_uuid);
+
+    /** unregister app/module with CSIP profile */
+    bt_status_t (*unregister_csip_app) (uint8_t app_id);
+
+    /** change lock value */
+    bt_status_t (*set_lock_value) (uint8_t app_id, uint8_t set_id, uint8_t lock_value,
+                                  std::vector<RawAddress> devices);
+
+    /** Closes the interface. */
+    void  (*cleanup) (void);
+
+} btcsip_interface_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_CSIP_H */
diff --git a/le_audio/vhal/include/hardware/bt_mcp.h b/le_audio/vhal/include/hardware/bt_mcp.h
new file mode 100644
index 0000000..8bde52d
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bt_mcp.h
@@ -0,0 +1,66 @@
+/******************************************************************************
+ *
+ *  Copyright 2021 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 ANDROID_INCLUDE_BT_MCP_H
+#define ANDROID_INCLUDE_BT_MCP_H
+
+
+
+#include <hardware/bluetooth.h>
+#include <vector>
+
+#define BT_PROFILE_MCP_ID "mcs_server"
+
+namespace bluetooth {
+namespace mcp_server {
+
+class McpServerCallbacks {
+  public:
+  virtual ~McpServerCallbacks() = default;
+  virtual void OnConnectionStateChange(int status, const RawAddress& bd_addr) = 0;
+  virtual void MediaControlPointChangeReq(uint8_t state,  const RawAddress& bd_addr) = 0;
+  virtual void TrackPositionChangeReq(int32_t position) = 0;
+  virtual void PlayingOrderChangeReq(uint32_t order) = 0;
+};
+
+
+class McpServerInterface {
+  public:
+    virtual ~McpServerInterface() = default;
+    virtual void Init(McpServerCallbacks* callbacks, Uuid uuid) = 0;
+    virtual void MediaState(uint8_t state) = 0;
+    virtual void MediaPlayerName(uint8_t *name) = 0;
+    virtual void MediaControlPointOpcodeSupported(uint32_t feature) = 0;
+    virtual void MediaControlPoint(uint8_t value) = 0;
+    virtual void TrackChanged(bool status) = 0;
+    virtual void TrackTitle(uint8_t* title) = 0;
+    virtual void TrackPosition(int32_t position) = 0;
+    virtual void TrackDuration(int32_t duration) = 0;
+    virtual void PlayingOrderSupported(uint16_t order) = 0;
+    virtual void PlayingOrder(uint8_t value) = 0;
+    virtual void ContentControlId(uint8_t ccid) = 0;
+    virtual void SetActiveDevice(const RawAddress& address, int set_id, int profile) = 0;
+    virtual void DisconnectMcp(const RawAddress& address) = 0;
+    virtual void BondStateChange(const RawAddress& address, int state) = 0;
+    virtual void Cleanup(void) = 0;
+};
+
+}
+}
+#endif /* ANDROID_INCLUDE_BT_MCP_H */
diff --git a/le_audio/vhal/include/hardware/bt_pacs_client.h b/le_audio/vhal/include/hardware/bt_pacs_client.h
new file mode 100644
index 0000000..7579b72
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bt_pacs_client.h
@@ -0,0 +1,183 @@
+/*
+ * 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 ANDROID_INCLUDE_BT_PACS_CLIENT_H
+#define ANDROID_INCLUDE_BT_PACS_CLIENT_H
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_av.h>
+
+namespace bluetooth {
+namespace bap {
+namespace pacs {
+
+#define BT_PROFILE_PACS_CLIENT_ID "bt_pacs_client"
+
+enum class CodecDirection {
+  CODEC_DIR_SINK = 0x1 << 0,
+  CODEC_DIR_SRC  = 0x1 << 1
+};
+
+enum class CodecCapFrameDuration {
+  FRAME_DUR_7_5         = 0x1 << 0,
+  FRAME_DUR_10          = 0x1 << 1,
+  FRAME_DUR_7_5_PREF    = 0x1 << 4,
+  FRAME_DUR_10_PREF     = 0x1 << 5,
+};
+
+enum class CodecFrameDuration {
+  FRAME_DUR_7_5         = 0x00,
+  FRAME_DUR_10          = 0x01,
+};
+
+enum class CodecCapChnlCount {
+  CHNL_COUNT_ONE         = 0x1 << 0,
+  CHNL_COUNT_TWO         = 0x1 << 1,
+  CHNL_COUNT_THREE       = 0x1 << 2,
+  CHNL_COUNT_FOUR        = 0x1 << 3,
+  CHNL_COUNT_FIVE        = 0x1 << 4,
+  CHNL_COUNT_SIX         = 0x1 << 5,
+  CHNL_COUNT_SEVEN       = 0x1 << 6,
+  CHNL_COUNT_EIGHT       = 0x1 << 7,
+};
+
+enum class ConnectionState {
+  DISCONNECTED = 0,
+  CONNECTING,
+  CONNECTED,
+  DISCONNECTING
+};
+
+enum class CodecIndex {
+  CODEC_INDEX_SOURCE_MIN = 0x09,
+
+  // Add an entry for each source codec here.
+  // NOTE: The values should be same as those listed in the following file:
+  //   BluetoothCodecConfig.java
+  CODEC_INDEX_SOURCE_LC3 = CODEC_INDEX_SOURCE_MIN,
+  CODEC_INDEX_SOURCE_MAX,
+  CODEC_INDEX_MIN = CODEC_INDEX_SOURCE_MIN,
+  CODEC_INDEX_MAX = CODEC_INDEX_SOURCE_MAX,
+};
+
+enum class CodecPriority {
+  // Disable the codec.
+  CODEC_PRIORITY_DISABLED = -1,
+
+  // Reset the codec priority to its default value.
+  CODEC_PRIORITY_DEFAULT = 0,
+
+  // Highest codec priority.
+  CODEC_PRIORITY_HIGHEST = 1000 * 1000
+};
+
+enum class CodecSampleRate {
+  CODEC_SAMPLE_RATE_NONE   = 0x0,
+  CODEC_SAMPLE_RATE_44100  = 0x1 << 0,
+  CODEC_SAMPLE_RATE_48000  = 0x1 << 1,
+  CODEC_SAMPLE_RATE_88200  = 0x1 << 2,
+  CODEC_SAMPLE_RATE_96000  = 0x1 << 3,
+  CODEC_SAMPLE_RATE_176400 = 0x1 << 4,
+  CODEC_SAMPLE_RATE_192000 = 0x1 << 5,
+  CODEC_SAMPLE_RATE_16000  = 0x1 << 6,
+  CODEC_SAMPLE_RATE_24000  = 0x1 << 7,
+  CODEC_SAMPLE_RATE_32000  = 0x1 << 8,
+  CODEC_SAMPLE_RATE_8000   = 0x1 << 9
+};
+
+enum class CodecBPS {
+  CODEC_BITS_PER_SAMPLE_NONE = 0x0,
+  CODEC_BITS_PER_SAMPLE_16   = 0x1 << 0,
+  CODEC_BITS_PER_SAMPLE_24   = 0x1 << 1,
+  CODEC_BITS_PER_SAMPLE_32   = 0x1 << 2
+};
+
+enum class CodecChannelMode {
+  CODEC_CHANNEL_MODE_NONE   = 0x0,
+  CODEC_CHANNEL_MODE_MONO   = 0x1 << 0,
+  CODEC_CHANNEL_MODE_STEREO = 0x1 << 1
+};
+
+struct CodecConfig {
+  CodecIndex codec_type;
+  CodecPriority codec_priority; // Codec selection priority
+                                // relative to other codecs: larger value
+                                // means higher priority. If 0, reset to
+                                // default.
+  CodecSampleRate sample_rate;
+  CodecBPS bits_per_sample;
+  CodecChannelMode channel_mode;
+  int64_t codec_specific_1;     // Codec-specific value 1
+  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
+};
+
+class PacsClientCallbacks {
+ public:
+  virtual ~PacsClientCallbacks() = default;
+
+  /** Callback for pacs server registration status */
+  virtual void OnInitialized(int status, int client_id) = 0;
+
+  /** Callback for pacs server connection state change */
+  virtual void OnConnectionState(const RawAddress& address,
+                                 ConnectionState state) = 0;
+
+  /** Callback for audio ( media or voice) being available */
+  virtual void OnAudioContextAvailable(const RawAddress& address,
+                                       uint32_t available_contexts) = 0;
+
+  /** Callback for pacs discovery results */
+  virtual void OnSearchComplete(int status,
+                 const RawAddress& address,
+                 std::vector<CodecConfig> sink_pac_records,
+                 std::vector<CodecConfig> src_pac_records,
+                 uint32_t sink_locations,
+                 uint32_t src_locations,
+                 uint32_t available_contexts,
+                 uint32_t supported_contexts) = 0;
+};
+
+class PacsClientInterface {
+ public:
+  virtual ~PacsClientInterface() = default;
+
+  /** Register the Pacs client callbacks */
+  virtual void Init(PacsClientCallbacks* callbacks) = 0;
+
+  /** Connect to pacs server */
+  virtual void Connect(uint16_t client_id, const RawAddress& address) = 0;
+
+  /** Disconnect pacs server */
+  virtual void Disconnect(uint16_t client_id, const RawAddress& address) = 0;
+
+  /** start pacs discovery */
+  virtual void StartDiscovery(uint16_t client_id,
+                                const RawAddress& address) = 0;
+
+  /** get available audio context */
+  virtual void GetAvailableAudioContexts(uint16_t client_id,
+                                         const RawAddress& address) = 0;
+  /** Closes the interface. */
+  virtual void Cleanup(uint16_t client_id) = 0;
+};
+
+}  // namespace pacs
+}  // namespace bap
+}  // namespace bluetooth
+
+#endif /* ANDROID_INCLUDE_BT_CLIENT_H */
diff --git a/le_audio/vhal/include/hardware/bt_vcp_controller.h b/le_audio/vhal/include/hardware/bt_vcp_controller.h
new file mode 100644
index 0000000..ea2e35a
--- /dev/null
+++ b/le_audio/vhal/include/hardware/bt_vcp_controller.h
@@ -0,0 +1,82 @@
+/*
+ *Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * 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 ANDROID_INCLUDE_BT_VCP_CONTROLLER_H
+#define ANDROID_INCLUDE_BT_VCP_CONTROLLER_H
+
+#include <hardware/bluetooth.h>
+
+#define BT_PROFILE_VOLUME_CONTROL_ID "volume_control"
+
+namespace bluetooth {
+namespace vcp_controller {
+
+enum class ConnectionState {
+  DISCONNECTED = 0,
+  CONNECTING,
+  CONNECTED,
+  DISCONNECTING
+};
+
+class VcpControllerCallbacks {
+ public:
+  virtual ~VcpControllerCallbacks() = default;
+
+  /** Callback for profile connection state change */
+  virtual void OnConnectionState(ConnectionState state,
+                                  const RawAddress& address) = 0;
+
+  virtual void OnVolumeStateChange(uint8_t volume, uint8_t mute,
+                                  const RawAddress& address) = 0;
+
+  virtual void OnVolumeFlagsChange(uint8_t flags,
+                                  const RawAddress& address) = 0;
+};
+
+class VcpControllerInterface {
+ public:
+  virtual ~VcpControllerInterface() = default;
+
+  /** Register the Volume Controller callbacks */
+  virtual void Init(VcpControllerCallbacks* callbacks) = 0;
+
+  /** Connect to Volume Renderer device */
+  virtual void Connect(const RawAddress& address, bool isDirect) = 0;
+
+  /** Disconnect from Volume Renderer device */
+  virtual void Disconnect(const RawAddress& address) = 0;
+
+  /** Set absolute volume */
+  virtual void SetAbsVolume(uint8_t volume, const RawAddress& address) = 0;
+
+  virtual void Mute(const RawAddress& address) = 0;
+
+  virtual void Unmute(const RawAddress& address) = 0;
+
+  /** Closes the interface. */
+  virtual void Cleanup(void) = 0;
+};
+
+}  // namespace vcp_controller
+}  // namespace bluetooth
+
+#endif /* ANDROID_INCLUDE_BT_VCP_CONTROLLER_H */
+
+
diff --git a/main/shim/acl.cc b/main/shim/acl.cc
index 533129d..5f1aa9f 100644
--- a/main/shim/acl.cc
+++ b/main/shim/acl.cc
@@ -61,6 +61,7 @@
 #include "stack/include/btm_status.h"
 #include "stack/include/sec_hci_link_interface.h"
 #include "stack/l2cap/l2c_int.h"
+#include "types/ble_address_with_type.h"
 #include "types/raw_address.h"
 
 extern tBTM_CB btm_cb;
@@ -128,6 +129,51 @@
   std::unordered_set<hci::AddressWithType> acceptlist_set_;
 };
 
+class ShadowAddressResolutionList {
+ public:
+  ShadowAddressResolutionList(uint8_t max_address_resolution_size)
+      : max_address_resolution_size_(max_address_resolution_size) {}
+
+  bool Add(const hci::AddressWithType& address_with_type) {
+    if (address_resolution_set_.size() == max_address_resolution_size_) {
+      LOG_ERROR("Address Resolution is full size:%zu",
+                address_resolution_set_.size());
+      return false;
+    }
+    if (!address_resolution_set_.insert(address_with_type).second) {
+      LOG_WARN("Attempted to add duplicate le address to address_resolution:%s",
+               PRIVATE_ADDRESS(address_with_type));
+    }
+    return true;
+  }
+
+  bool Remove(const hci::AddressWithType& address_with_type) {
+    auto iter = address_resolution_set_.find(address_with_type);
+    if (iter == address_resolution_set_.end()) {
+      LOG_WARN("Unknown device being removed from address_resolution:%s",
+               PRIVATE_ADDRESS(address_with_type));
+      return false;
+    }
+    address_resolution_set_.erase(iter);
+    return true;
+  }
+
+  std::unordered_set<hci::AddressWithType> GetCopy() const {
+    return address_resolution_set_;
+  }
+
+  bool IsFull() const {
+    return address_resolution_set_.size() ==
+           static_cast<size_t>(max_address_resolution_size_);
+  }
+
+  void Clear() { address_resolution_set_.clear(); }
+
+ private:
+  uint8_t max_address_resolution_size_{0};
+  std::unordered_set<hci::AddressWithType> address_resolution_set_;
+};
+
 struct ConnectionDescriptor {
   CreationTime creation_time_;
   TeardownTime teardown_time_;
@@ -676,8 +722,10 @@
 };
 
 struct shim::legacy::Acl::impl {
-  impl(uint8_t max_acceptlist_size)
-      : shadow_acceptlist_(ShadowAcceptlist(max_acceptlist_size)) {}
+  impl(uint8_t max_acceptlist_size, uint8_t max_address_resolution_size)
+      : shadow_acceptlist_(ShadowAcceptlist(max_acceptlist_size)),
+        shadow_address_resolution_list_(
+            ShadowAddressResolutionList(max_address_resolution_size)) {}
 
   std::map<HciHandle, std::unique_ptr<ClassicShimAclConnection>>
       handle_to_classic_connection_map_;
@@ -688,6 +736,7 @@
       FixedQueue<std::unique_ptr<ConnectionDescriptor>>(kConnectionHistorySize);
 
   ShadowAcceptlist shadow_acceptlist_;
+  ShadowAddressResolutionList shadow_address_resolution_list_;
 
   bool IsClassicAcl(HciHandle handle) {
     return handle_to_classic_connection_map_.find(handle) !=
@@ -858,6 +907,35 @@
     LOG_DEBUG("Cleared entire Le address acceptlist count:%zu", count);
   }
 
+  void AddToAddressResolution(const hci::AddressWithType& address_with_type,
+                              const std::array<uint8_t, 16>& peer_irk,
+                              const std::array<uint8_t, 16>& local_irk) {
+    if (shadow_address_resolution_list_.IsFull()) {
+      LOG_WARN("Le Address Resolution list is full");
+      return;
+    }
+    // TODO This should really be added upon successful completion
+    shadow_address_resolution_list_.Add(address_with_type);
+    GetAclManager()->AddDeviceToResolvingList(address_with_type, peer_irk,
+                                              local_irk);
+  }
+
+  void RemoveFromAddressResolution(
+      const hci::AddressWithType& address_with_type) {
+    // TODO This should really be removed upon successful removal
+    if (!shadow_address_resolution_list_.Remove(address_with_type)) {
+      LOG_WARN("Unable to remove from Le Address Resolution list device:%s",
+               PRIVATE_ADDRESS(address_with_type));
+    }
+    GetAclManager()->RemoveDeviceFromResolvingList(address_with_type);
+  }
+
+  void ClearResolvingList() {
+    GetAclManager()->ClearResolvingList();
+    // TODO This should really be cleared after successful clear status
+    shadow_address_resolution_list_.Clear();
+  }
+
   void DumpConnectionHistory() const {
     std::vector<std::string> history =
         connection_history_.ReadElementsAsString();
@@ -882,12 +960,23 @@
     }
     auto acceptlist = shadow_acceptlist_.GetCopy();
     LOG_DUMPSYS(fd,
-                "Shadow le accept list  size:%-3zu controller_max_size:%hhu",
+                "Shadow le accept list              size:%-3zu "
+                "controller_max_size:%hhu",
                 acceptlist.size(),
                 controller_get_interface()->get_ble_acceptlist_size());
     unsigned cnt = 0;
     for (auto& entry : acceptlist) {
-      LOG_DUMPSYS(fd, "%03u le acceptlist:%s", ++cnt, entry.ToString().c_str());
+      LOG_DUMPSYS(fd, "  %03u %s", ++cnt, entry.ToString().c_str());
+    }
+    auto address_resolution_list = shadow_address_resolution_list_.GetCopy();
+    LOG_DUMPSYS(fd,
+                "Shadow le address resolution list  size:%-3zu "
+                "controller_max_size:%hhu",
+                address_resolution_list.size(),
+                controller_get_interface()->get_ble_resolving_list_max_size());
+    cnt = 0;
+    for (auto& entry : address_resolution_list) {
+      LOG_DUMPSYS(fd, "  %03u %s", ++cnt, entry.ToString().c_str());
     }
   }
 #undef DUMPSYS_TAG
@@ -1046,11 +1135,13 @@
 
 shim::legacy::Acl::Acl(os::Handler* handler,
                        const acl_interface_t& acl_interface,
-                       uint8_t max_acceptlist_size)
+                       uint8_t max_acceptlist_size,
+                       uint8_t max_address_resolution_size)
     : handler_(handler), acl_interface_(acl_interface) {
   ASSERT(handler_ != nullptr);
   ValidateAclInterface(acl_interface_);
-  pimpl_ = std::make_unique<Acl::impl>(max_acceptlist_size);
+  pimpl_ = std::make_unique<Acl::impl>(max_acceptlist_size,
+                                       max_address_resolution_size);
   GetAclManager()->RegisterCallbacks(this, handler_);
   GetAclManager()->RegisterLeCallbacks(this, handler_);
   GetController()->RegisterCompletedMonitorAclPacketsCallback(
@@ -1451,3 +1542,21 @@
 void shim::legacy::Acl::ClearAcceptList() {
   handler_->CallOn(pimpl_.get(), &Acl::impl::clear_acceptlist);
 }
+
+void shim::legacy::Acl::AddToAddressResolution(
+    const hci::AddressWithType& address_with_type,
+    const std::array<uint8_t, 16>& peer_irk,
+    const std::array<uint8_t, 16>& local_irk) {
+  handler_->CallOn(pimpl_.get(), &Acl::impl::AddToAddressResolution,
+                   address_with_type, peer_irk, local_irk);
+}
+
+void shim::legacy::Acl::RemoveFromAddressResolution(
+    const hci::AddressWithType& address_with_type) {
+  handler_->CallOn(pimpl_.get(), &Acl::impl::RemoveFromAddressResolution,
+                   address_with_type);
+}
+
+void shim::legacy::Acl::ClearAddressResolution() {
+  handler_->CallOn(pimpl_.get(), &Acl::impl::ClearResolvingList);
+}
diff --git a/main/shim/acl.h b/main/shim/acl.h
index da50942..3fa9e41 100644
--- a/main/shim/acl.h
+++ b/main/shim/acl.h
@@ -42,7 +42,7 @@
             public LinkPolicyInterface {
  public:
   Acl(os::Handler* handler, const acl_interface_t& acl_interface,
-      uint8_t max_acceptlist_size);
+      uint8_t max_acceptlist_size, uint8_t max_address_resolution_size);
   ~Acl();
 
   // hci::acl_manager::ConnectionCallbacks
@@ -75,6 +75,14 @@
   void DisconnectClassic(uint16_t handle, tHCI_REASON reason) override;
   void DisconnectLe(uint16_t handle, tHCI_REASON reason) override;
 
+  // Address Resolution List
+  void AddToAddressResolution(const hci::AddressWithType& address_with_type,
+                              const std::array<uint8_t, 16>& peer_irk,
+                              const std::array<uint8_t, 16>& local_irk);
+  void RemoveFromAddressResolution(
+      const hci::AddressWithType& address_with_type);
+  void ClearAddressResolution();
+
   // LinkPolicyInterface
   bool HoldMode(uint16_t hci_handle, uint16_t max_interval,
                 uint16_t min_interval) override;
diff --git a/main/shim/btm_api.cc b/main/shim/btm_api.cc
index 6d79049..2e0a338 100644
--- a/main/shim/btm_api.cc
+++ b/main/shim/btm_api.cc
@@ -936,18 +936,6 @@
   return BTM_NO_RESOURCES;
 }
 
-uint8_t bluetooth::shim::BTM_GetEirUuidList(uint8_t* p_eir, size_t eir_len,
-                                            uint8_t uuid_size,
-                                            uint8_t* p_num_uuid,
-                                            uint8_t* p_uuid_list,
-                                            uint8_t max_num_uuid) {
-  LOG_INFO("UNIMPLEMENTED %s", __func__);
-  CHECK(p_eir != nullptr);
-  CHECK(p_num_uuid != nullptr);
-  CHECK(p_uuid_list != nullptr);
-  return 0;
-}
-
 void bluetooth::shim::BTM_SecAddBleDevice(const RawAddress& bd_addr,
                                           tBT_DEVICE_TYPE dev_type,
                                           tBLE_ADDR_TYPE addr_type) {
diff --git a/main/shim/btm_api.h b/main/shim/btm_api.h
index cd3d08a..8eb81d9 100644
--- a/main/shim/btm_api.h
+++ b/main/shim/btm_api.h
@@ -386,33 +386,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_GetEirUuidList
- *
- * Description      This function parses EIR and returns UUID list.
- *
- * Parameters       p_eir - EIR
- *                  eirl_len - EIR len
- *                  uuid_size - Uuid::kNumBytes16, Uuid::kNumBytes32,
- *                              Uuid::kNumBytes128
- *                  p_num_uuid - return number of UUID in found list
- *                  p_uuid_list - return UUID 16-bit list
- *                  max_num_uuid - maximum number of UUID to be returned
- *
- * Returns          0 - if not found
- *                  HCI_EIR_COMPLETE_16BITS_UUID_TYPE
- *                  HCI_EIR_MORE_16BITS_UUID_TYPE
- *                  HCI_EIR_COMPLETE_32BITS_UUID_TYPE
- *                  HCI_EIR_MORE_32BITS_UUID_TYPE
- *                  HCI_EIR_COMPLETE_128BITS_UUID_TYPE
- *                  HCI_EIR_MORE_128BITS_UUID_TYPE
- *
- ******************************************************************************/
-uint8_t BTM_GetEirUuidList(uint8_t* p_eir, size_t eir_len, uint8_t uuid_size,
-                           uint8_t* p_num_uuid, uint8_t* p_uuid_list,
-                           uint8_t max_num_uuid);
-
-/*******************************************************************************
- *
  * Function         BTM_SecAddBleDevice
  *
  * Description      Add/modify device.  This function will be normally called
diff --git a/main/shim/stack.cc b/main/shim/stack.cc
index 4a3d200..88fb826 100644
--- a/main/shim/stack.cc
+++ b/main/shim/stack.cc
@@ -179,7 +179,8 @@
     if (!common::init_flags::gd_core_is_enabled()) {
       acl_ = new legacy::Acl(
           stack_handler_, legacy::GetAclInterface(),
-          controller_get_interface()->get_ble_acceptlist_size());
+          controller_get_interface()->get_ble_acceptlist_size(),
+          controller_get_interface()->get_ble_resolving_list_max_size());
     }
   }
   if (!common::init_flags::gd_core_is_enabled()) {
diff --git a/main/test/main_shim_test.cc b/main/test/main_shim_test.cc
index 7f40241..6d206e1 100644
--- a/main/test/main_shim_test.cc
+++ b/main/test/main_shim_test.cc
@@ -61,6 +61,8 @@
 namespace test = bluetooth::hci::testing;
 
 const uint8_t kMaxLeAcceptlistSize = 16;
+const uint8_t kMaxAddressResolutionSize = kMaxLeAcceptlistSize;
+
 std::map<std::string, int> mock_function_count_map;
 tL2C_CB l2cb;
 tBTM_CB btm_cb;
@@ -304,7 +306,8 @@
                 UnregisterCompletedMonitorAclPacketsCallback)
         .Times(1);
     return std::make_unique<shim::legacy::Acl>(handler_, GetMockAclInterface(),
-                                               kMaxLeAcceptlistSize);
+                                               kMaxLeAcceptlistSize,
+                                               kMaxAddressResolutionSize);
   }
 };
 
diff --git a/stack/avdt/avdt_int.h b/stack/avdt/avdt_int.h
index d872573..1950ac5 100644
--- a/stack/avdt/avdt_int.h
+++ b/stack/avdt/avdt_int.h
@@ -418,6 +418,7 @@
         media_seq(0),
         allocated(false),
         in_use(false),
+        need_open(false),
         role(0),
         remove(false),
         state(0),
@@ -467,6 +468,7 @@
     media_seq = 0;
     allocated = false;
     in_use = false;
+    need_open = false;
     role = 0;
     remove = false;
     state = 0;
@@ -491,6 +493,7 @@
   uint16_t media_seq;                // Media packet sequence number
   bool allocated;                    // True if the SCB is allocated
   bool in_use;                       // True if used by peer
+  bool need_open;      // True if need open after receiving delay report (AVDT 1_3)
   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
diff --git a/stack/avdt/avdt_scb_act.cc b/stack/avdt/avdt_scb_act.cc
index 611abb5..bea47084 100644
--- a/stack/avdt/avdt_scb_act.cc
+++ b/stack/avdt/avdt_scb_act.cc
@@ -686,7 +686,6 @@
  *
  ******************************************************************************/
 void avdt_scb_hdl_setconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
-  tAVDT_EVT_HDR single;
 
   if (p_scb->p_ccb != NULL) {
     /* save configuration */
@@ -696,11 +695,19 @@
     // Delay reporting is sent before open request (i.e., in configured state).
     avdt_scb_snd_snk_delay_rpt_req(p_scb, p_data);
 
-    /* initiate open */
-    single.seid = p_scb->peer_seid;
-    tAVDT_SCB_EVT avdt_scb_evt;
-    avdt_scb_evt.msg.single = single;
-    avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, &avdt_scb_evt);
+    p_scb->need_open = false;
+    /* Check if both local and SEP support AVDT version 1.3*/
+    if (A2DP_GetAvdtpVersion() >= AVDT_VERSION_1_3 &&
+      (p_scb->stream_config.cfg.psc_mask & AVDT_PSC_DELAY_RPT)) {
+       p_scb->need_open = true;
+    } else {
+      /* Initiate open */
+      tAVDT_EVT_HDR single;
+      single.seid = p_scb->peer_seid;
+      tAVDT_SCB_EVT avdt_scb_evt;
+      avdt_scb_evt.msg.single = single;
+      avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, &avdt_scb_evt);
+    }
   }
 }
 
@@ -863,9 +870,20 @@
       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)
+  if (p_scb->p_ccb) {
     avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_DELAY_RPT, &p_data->msg);
+    /* Check if we need open stream after set_config */
+    if (p_scb->need_open) {
+      /* Initiate open */
+      tAVDT_EVT_HDR single;
+      single.seid = p_scb->peer_seid;
+      tAVDT_SCB_EVT avdt_scb_evt;
+      avdt_scb_evt.msg.single = single;
+      avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, &avdt_scb_evt);
+      /* Clear flag */
+      p_scb->need_open = false;
+    }
+  }
   else
     avdt_scb_rej_not_in_use(p_scb, p_data);
 }
diff --git a/stack/btm/btm_ble_bgconn.cc b/stack/btm/btm_ble_bgconn.cc
index 80ba1cd..8a70c20 100644
--- a/stack/btm/btm_ble_bgconn.cc
+++ b/stack/btm/btm_ble_bgconn.cc
@@ -364,7 +364,9 @@
   btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_INIT);
   if (btm_cb.ble_ctr_cb.rl_state != BTM_BLE_RL_IDLE &&
       controller_get_interface()->supports_ble_privacy()) {
+#if (BLE_LOCAL_PRIVACY_ENABLED == TRUE)
     own_addr_type |= BLE_ADDR_TYPE_ID_BIT;
+#endif
     peer_addr_type |= BLE_ADDR_TYPE_ID_BIT;
   }
 
diff --git a/stack/btm/btm_inq.cc b/stack/btm/btm_inq.cc
index 67ca22c..c54973c 100644
--- a/stack/btm/btm_inq.cc
+++ b/stack/btm/btm_inq.cc
@@ -145,8 +145,8 @@
 
 static uint8_t btm_convert_uuid_to_eir_service(uint16_t uuid16);
 void btm_set_eir_uuid(uint8_t* p_eir, tBTM_INQ_RESULTS* p_results);
-static const uint8_t* btm_eir_get_uuid_list(uint8_t* p_eir, size_t eir_len,
-                                            uint8_t uuid_size,
+static const uint8_t* btm_eir_get_uuid_list(const uint8_t* p_eir,
+                                            size_t eir_len, uint8_t uuid_size,
                                             uint8_t* p_num_uuid,
                                             uint8_t* p_uuid_list_type);
 
@@ -1683,9 +1683,9 @@
  *                  HCI_EIR_MORE_128BITS_UUID_TYPE
  *
  ******************************************************************************/
-uint8_t BTM_GetEirUuidList(uint8_t* p_eir, size_t eir_len, uint8_t uuid_size,
-                           uint8_t* p_num_uuid, uint8_t* p_uuid_list,
-                           uint8_t max_num_uuid) {
+uint8_t BTM_GetEirUuidList(const uint8_t* p_eir, size_t eir_len,
+                           uint8_t uuid_size, uint8_t* p_num_uuid,
+                           uint8_t* p_uuid_list, uint8_t max_num_uuid) {
   const uint8_t* p_uuid_data;
   uint8_t type;
   uint8_t yy, xx;
@@ -1747,8 +1747,8 @@
  *                  beginning of UUID list in EIR - otherwise
  *
  ******************************************************************************/
-static const uint8_t* btm_eir_get_uuid_list(uint8_t* p_eir, size_t eir_len,
-                                            uint8_t uuid_size,
+static const uint8_t* btm_eir_get_uuid_list(const uint8_t* p_eir,
+                                            size_t eir_len, uint8_t uuid_size,
                                             uint8_t* p_num_uuid,
                                             uint8_t* p_uuid_list_type) {
   const uint8_t* p_uuid_data;
diff --git a/stack/btm/btm_int_types.h b/stack/btm/btm_int_types.h
index 9800310..ce0d46b 100644
--- a/stack/btm/btm_int_types.h
+++ b/stack/btm/btm_int_types.h
@@ -118,9 +118,6 @@
   tBTM_BLE_SEC_ACT sec_act;
 } tBTM_SEC_QUEUE_ENTRY;
 
-// Bluetooth Quality Report - Report receiver
-typedef void(tBTM_BT_QUALITY_REPORT_RECEIVER)(uint8_t len, uint8_t* p_stream);
-
 /* Define a structure to hold all the BTM data
 */
 
diff --git a/stack/include/btm_api.h b/stack/include/btm_api.h
index 7005fbe..522e05c 100644
--- a/stack/include/btm_api.h
+++ b/stack/include/btm_api.h
@@ -922,9 +922,9 @@
  *                  HCI_EIR_MORE_128BITS_UUID_TYPE
  *
  ******************************************************************************/
-uint8_t BTM_GetEirUuidList(uint8_t* p_eir, size_t eir_len, uint8_t uuid_size,
-                           uint8_t* p_num_uuid, uint8_t* p_uuid_list,
-                           uint8_t max_num_uuid);
+uint8_t BTM_GetEirUuidList(const uint8_t* p_eir, size_t eir_len,
+                           uint8_t uuid_size, uint8_t* p_num_uuid,
+                           uint8_t* p_uuid_list, uint8_t max_num_uuid);
 
 /*******************************************************************************
  *
diff --git a/stack/include/btm_client_interface.h b/stack/include/btm_client_interface.h
index d4a3f28..2d9b96b 100644
--- a/stack/include/btm_client_interface.h
+++ b/stack/include/btm_client_interface.h
@@ -216,7 +216,7 @@
     uint8_t (*BTM_GetEirSupportedServices)(uint32_t* p_eir_uuid, uint8_t** p,
                                            uint8_t max_num_uuid16,
                                            uint8_t* p_num_uuid16);
-    uint8_t (*BTM_GetEirUuidList)(uint8_t* p_eir, size_t eir_len,
+    uint8_t (*BTM_GetEirUuidList)(const uint8_t* p_eir, size_t eir_len,
                                   uint8_t uuid_size, uint8_t* p_num_uuid,
                                   uint8_t* p_uuid_list, uint8_t max_num_uuid);
     void (*BTM_AddEirService)(uint32_t* p_eir_uuid, uint16_t uuid16);
diff --git a/test/Android.bp b/test/Android.bp
index ee93ae2..0a95be0 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -399,3 +399,10 @@
       "common/init_flags.cc",
   ],
 }
+
+filegroup {
+  name: "TestMockBluetoothInterface",
+  srcs: [
+      "mock/mock_bluetooth_interface.cc",
+  ],
+}
diff --git a/test/mock/mock_bluetooth_interface.cc b/test/mock/mock_bluetooth_interface.cc
new file mode 100644
index 0000000..bb09b05
--- /dev/null
+++ b/test/mock/mock_bluetooth_interface.cc
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2021 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 <cstdint>
+
+#include "btif/include/stack_manager.h"
+#include "device/include/interop.h"
+#include "hardware/bluetooth.h"
+#include "stack/include/bt_octets.h"
+#include "types/raw_address.h"
+
+void set_hal_cbacks(bt_callbacks_t* callbacks) {}
+
+static int init(bt_callbacks_t* callbacks, bool start_restricted,
+                bool is_common_criteria_mode, int config_compare_result,
+                const char** init_flags, bool is_atv) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int enable() { return BT_STATUS_SUCCESS; }
+
+static int disable(void) { return BT_STATUS_SUCCESS; }
+
+static void cleanup(void) {}
+
+bool is_restricted_mode() { return false; }
+bool is_common_criteria_mode() { return false; }
+
+int get_common_criteria_config_compare_result() { return BT_STATUS_SUCCESS; }
+
+bool is_atv_device() { return false; }
+
+static int get_adapter_properties(void) { return BT_STATUS_SUCCESS; }
+
+static int get_adapter_property(bt_property_type_t type) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int set_adapter_property(const bt_property_t* property) {
+  return BT_STATUS_SUCCESS;
+}
+
+int get_remote_device_properties(RawAddress* remote_addr) {
+  return BT_STATUS_SUCCESS;
+}
+
+int get_remote_device_property(RawAddress* remote_addr,
+                               bt_property_type_t type) {
+  return BT_STATUS_SUCCESS;
+}
+
+int set_remote_device_property(RawAddress* remote_addr,
+                               const bt_property_t* property) {
+  return BT_STATUS_SUCCESS;
+}
+
+int get_remote_services(RawAddress* remote_addr, int transport) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int start_discovery(void) { return BT_STATUS_SUCCESS; }
+
+static int cancel_discovery(void) { return BT_STATUS_SUCCESS; }
+
+static int create_bond(const RawAddress* bd_addr, int transport) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int create_bond_out_of_band(const RawAddress* bd_addr, int transport,
+                                   const bt_oob_data_t* p192_data,
+                                   const bt_oob_data_t* p256_data) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int generate_local_oob_data(tBT_TRANSPORT transport) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int cancel_bond(const RawAddress* bd_addr) { return BT_STATUS_SUCCESS; }
+
+static int remove_bond(const RawAddress* bd_addr) { return BT_STATUS_SUCCESS; }
+
+static int get_connection_state(const RawAddress* bd_addr) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int pin_reply(const RawAddress* bd_addr, uint8_t accept, uint8_t pin_len,
+                     bt_pin_code_t* pin_code) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int ssp_reply(const RawAddress* bd_addr, bt_ssp_variant_t variant,
+                     uint8_t accept, uint32_t passkey) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int read_energy_info() { return BT_STATUS_SUCCESS; }
+
+static void dump(int fd, const char** arguments) {}
+
+static void dumpMetrics(std::string* output) {}
+
+static const void* get_profile_interface(const char* profile_id) {
+  return nullptr;
+}
+
+int dut_mode_configure(uint8_t enable) { return BT_STATUS_SUCCESS; }
+
+int dut_mode_send(uint16_t opcode, uint8_t* buf, uint8_t len) {
+  return BT_STATUS_SUCCESS;
+}
+
+int le_test_mode(uint16_t opcode, uint8_t* buf, uint8_t len) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int set_os_callouts(bt_os_callouts_t* callouts) {
+  return BT_STATUS_SUCCESS;
+}
+
+static int config_clear(void) { return 0; }
+
+static bluetooth::avrcp::ServiceInterface* get_avrcp_service(void) {
+  return nullptr;
+}
+
+static std::string obfuscate_address(const RawAddress& address) {
+  return std::string("Test");
+}
+
+static int get_metric_id(const RawAddress& address) { return 0; }
+
+static int set_dynamic_audio_buffer_size(int codec, int size) { return 0; }
+
+EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
+    sizeof(bluetoothInterface),
+    init,
+    enable,
+    disable,
+    cleanup,
+    get_adapter_properties,
+    get_adapter_property,
+    set_adapter_property,
+    get_remote_device_properties,
+    get_remote_device_property,
+    set_remote_device_property,
+    nullptr,
+    get_remote_services,
+    start_discovery,
+    cancel_discovery,
+    create_bond,
+    create_bond_out_of_band,
+    remove_bond,
+    cancel_bond,
+    get_connection_state,
+    pin_reply,
+    ssp_reply,
+    get_profile_interface,
+    dut_mode_configure,
+    dut_mode_send,
+    le_test_mode,
+    set_os_callouts,
+    read_energy_info,
+    dump,
+    dumpMetrics,
+    config_clear,
+    interop_database_clear,
+    interop_database_add,
+    get_avrcp_service,
+    obfuscate_address,
+    get_metric_id,
+    set_dynamic_audio_buffer_size,
+    generate_local_oob_data};
+
+// callback reporting helpers
+
+bt_property_t* property_deep_copy_array(int num_properties,
+                                        bt_property_t* properties) {
+  return nullptr;
+}
+
+void invoke_adapter_state_changed_cb(bt_state_t state) {}
+
+void invoke_adapter_properties_cb(bt_status_t status, int num_properties,
+                                  bt_property_t* properties) {}
+
+void invoke_remote_device_properties_cb(bt_status_t status, RawAddress bd_addr,
+                                        int num_properties,
+                                        bt_property_t* properties) {}
+
+void invoke_device_found_cb(int num_properties, bt_property_t* properties) {}
+
+void invoke_discovery_state_changed_cb(bt_discovery_state_t state) {}
+
+void invoke_pin_request_cb(RawAddress bd_addr, bt_bdname_t bd_name,
+                           uint32_t cod, bool min_16_digit) {}
+
+void invoke_ssp_request_cb(RawAddress bd_addr, bt_bdname_t bd_name,
+                           uint32_t cod, bt_ssp_variant_t pairing_variant,
+                           uint32_t pass_key) {}
+
+void invoke_oob_data_request_cb(tBT_TRANSPORT t, bool valid, Octet16 c,
+                                Octet16 r, RawAddress raw_address,
+                                uint8_t address_type) {}
+
+void invoke_bond_state_changed_cb(bt_status_t status, RawAddress bd_addr,
+                                  bt_bond_state_t state, int fail_reason) {}
+
+void invoke_acl_state_changed_cb(bt_status_t status, RawAddress bd_addr,
+                                 bt_acl_state_t state, int transport_link_type,
+                                 bt_hci_error_code_t hci_reason) {}
+
+void invoke_thread_evt_cb(bt_cb_thread_evt event) {}
+
+void invoke_le_test_mode_cb(bt_status_t status, uint16_t count) {}
+
+// takes ownership of |uid_data|
+void invoke_energy_info_cb(bt_activity_energy_info energy_info,
+                           bt_uid_traffic_t* uid_data) {}
+
+void invoke_link_quality_report_cb(uint64_t timestamp, int report_id, int rssi,
+                                   int snr, int retransmission_count,
+                                   int packets_not_receive_count,
+                                   int negative_acknowledgement_count) {}
diff --git a/test/mock/mock_bta_dm_api.cc b/test/mock/mock_bta_dm_api.cc
index a7fb587..4b17208 100644
--- a/test/mock/mock_bta_dm_api.cc
+++ b/test/mock/mock_bta_dm_api.cc
@@ -223,7 +223,7 @@
   mock_function_count_map[__func__]++;
   test::mock::bta_dm_api::BTA_EnableTestMode();
 }
-void BTA_GetEirService(uint8_t* p_eir, size_t eir_len,
+void BTA_GetEirService(const uint8_t* p_eir, size_t eir_len,
                        tBTA_SERVICE_MASK* p_services) {
   mock_function_count_map[__func__]++;
   test::mock::bta_dm_api::BTA_GetEirService(p_eir, eir_len, p_services);
diff --git a/test/mock/mock_bta_dm_api.h b/test/mock/mock_bta_dm_api.h
index a715090..b4413d5 100644
--- a/test/mock/mock_bta_dm_api.h
+++ b/test/mock/mock_bta_dm_api.h
@@ -433,11 +433,11 @@
 // Params: uint8_t* p_eir, size_t eir_len, tBTA_SERVICE_MASK* p_services
 // Return: void
 struct BTA_GetEirService {
-  std::function<void(uint8_t* p_eir, size_t eir_len,
+  std::function<void(const uint8_t* p_eir, size_t eir_len,
                      tBTA_SERVICE_MASK* p_services)>
-      body{
-          [](uint8_t* p_eir, size_t eir_len, tBTA_SERVICE_MASK* p_services) {}};
-  void operator()(uint8_t* p_eir, size_t eir_len,
+      body{[](const uint8_t* p_eir, size_t eir_len,
+              tBTA_SERVICE_MASK* p_services) {}};
+  void operator()(const uint8_t* p_eir, size_t eir_len,
                   tBTA_SERVICE_MASK* p_services) {
     body(p_eir, eir_len, p_services);
   };
diff --git a/test/mock/mock_main_shim_btm_api.cc b/test/mock/mock_main_shim_btm_api.cc
index c02ce8c..83aeb03 100644
--- a/test/mock/mock_main_shim_btm_api.cc
+++ b/test/mock/mock_main_shim_btm_api.cc
@@ -251,14 +251,6 @@
   mock_function_count_map[__func__]++;
   return 0;
 }
-uint8_t bluetooth::shim::BTM_GetEirUuidList(uint8_t* p_eir, size_t eir_len,
-                                            uint8_t uuid_size,
-                                            uint8_t* p_num_uuid,
-                                            uint8_t* p_uuid_list,
-                                            uint8_t max_num_uuid) {
-  mock_function_count_map[__func__]++;
-  return 0;
-}
 void bluetooth::shim::BTM_AddEirService(uint32_t* p_eir_uuid, uint16_t uuid16) {
   mock_function_count_map[__func__]++;
 }
diff --git a/test/mock/mock_stack_btm_inq.cc b/test/mock/mock_stack_btm_inq.cc
index 011754d..ef8d092 100644
--- a/test/mock/mock_stack_btm_inq.cc
+++ b/test/mock/mock_stack_btm_inq.cc
@@ -137,9 +137,9 @@
   mock_function_count_map[__func__]++;
   return 0;
 }
-uint8_t BTM_GetEirUuidList(uint8_t* p_eir, size_t eir_len, uint8_t uuid_size,
-                           uint8_t* p_num_uuid, uint8_t* p_uuid_list,
-                           uint8_t max_num_uuid) {
+uint8_t BTM_GetEirUuidList(const uint8_t* p_eir, size_t eir_len,
+                           uint8_t uuid_size, uint8_t* p_num_uuid,
+                           uint8_t* p_uuid_list, uint8_t max_num_uuid) {
   mock_function_count_map[__func__]++;
   return 0;
 }
diff --git a/types/raw_address.h b/types/raw_address.h
index 8f5b8dc..b3e7530 100644
--- a/types/raw_address.h
+++ b/types/raw_address.h
@@ -88,6 +88,12 @@
     *(p)++ = (uint8_t)(a.address)[BD_ADDR_LEN - 1 - ijk];
 }
 
+inline void STREAM_TO_BDADDR(RawAddress& a, const uint8_t*& p) {
+  uint8_t* pbda = (uint8_t*)(a.address) + BD_ADDR_LEN - 1;
+  for (int ijk = 0; ijk < BD_ADDR_LEN; ijk++) *pbda-- = *(p)++;
+}
+
+// DEPRECATED
 inline void STREAM_TO_BDADDR(RawAddress& a, uint8_t*& p) {
   uint8_t* pbda = (uint8_t*)(a.address) + BD_ADDR_LEN - 1;
   for (int ijk = 0; ijk < BD_ADDR_LEN; ijk++) *pbda-- = *(p)++;
diff --git a/vendor_libs/test_vendor_lib/Android.bp b/vendor_libs/test_vendor_lib/Android.bp
index 62d0392..7a122c4 100644
--- a/vendor_libs/test_vendor_lib/Android.bp
+++ b/vendor_libs/test_vendor_lib/Android.bp
@@ -13,6 +13,7 @@
     defaults: [
         "gd_defaults",
         "gd_clang_tidy",
+        "gd_clang_tidy_ignore_android",
     ],
     host_supported: true,
     proprietary: true,