Merge "Pass the written value for gatt characteristic and descriptor write callbacks to the java layer"
diff --git a/bta/av/bta_av_aact.cc b/bta/av/bta_av_aact.cc
index 585c552..2ef03fd 100644
--- a/bta/av/bta_av_aact.cc
+++ b/bta/av/bta_av_aact.cc
@@ -852,7 +852,6 @@
   /* if de-registering shut everything down */
   msg.hdr.layer_specific = p_scb->hndl;
   p_scb->started = false;
-  p_scb->offload_started = false;
   p_scb->use_rtp_header_marker_bit = false;
   p_scb->cong = false;
   p_scb->role = role;
@@ -875,8 +874,6 @@
     }
   */
 
-  p_scb->offload_start_pending = false;
-
   if (p_scb->deregistering) {
     /* remove stream */
     for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
@@ -1171,7 +1168,6 @@
   bta_av_conn_chg((tBTA_AV_DATA*)&msg);
   /* set the congestion flag, so AV would not send media packets by accident */
   p_scb->cong = true;
-  p_scb->offload_start_pending = false;
   // Don't use AVDTP SUSPEND for restrict listed devices
   btif_storage_get_stored_remote_name(p_scb->PeerAddress(), remote_name);
   if (interop_match_name(INTEROP_DISABLE_AVDTP_SUSPEND, remote_name) ||
@@ -1895,9 +1891,15 @@
   BTM_unblock_role_switch_and_sniff_mode_for(p_scb->PeerAddress());
 
   if (p_scb->co_started) {
-    if (p_scb->offload_started) {
+    uint16_t handle = get_btm_client_interface().lifecycle.BTM_GetHCIConnHandle(
+        p_scb->PeerAddress(), BT_TRANSPORT_BR_EDR);
+    if (bta_av_cb.offload_started_acl_hdl == handle) {
       bta_av_vendor_offload_stop();
-      p_scb->offload_started = false;
+      bta_av_cb.offload_started_acl_hdl = HCI_INVALID_HANDLE;
+    } else if (bta_av_cb.offload_start_pending_acl_hdl == handle) {
+      APPL_TRACE_WARNING("%s: Stop pending offload start command", __func__);
+      bta_av_vendor_offload_stop();
+      bta_av_cb.offload_start_pending_acl_hdl = HCI_INVALID_HANDLE;
     }
 
     bta_av_stream_chg(p_scb, false);
@@ -2519,9 +2521,15 @@
 
   /* in case that we received suspend_ind, we may need to call co_stop here */
   if (p_scb->co_started) {
-    if (p_scb->offload_started) {
+    uint16_t handle = get_btm_client_interface().lifecycle.BTM_GetHCIConnHandle(
+        p_scb->PeerAddress(), BT_TRANSPORT_BR_EDR);
+    if (bta_av_cb.offload_started_acl_hdl == handle) {
       bta_av_vendor_offload_stop();
-      p_scb->offload_started = false;
+      bta_av_cb.offload_started_acl_hdl = HCI_INVALID_HANDLE;
+    } else if (bta_av_cb.offload_start_pending_acl_hdl == handle) {
+      APPL_TRACE_WARNING("%s: Stop pending offload start command", __func__);
+      bta_av_vendor_offload_stop();
+      bta_av_cb.offload_start_pending_acl_hdl = HCI_INVALID_HANDLE;
     }
     bta_av_stream_chg(p_scb, false);
 
@@ -3012,6 +3020,13 @@
         APPL_TRACE_DEBUG("%s: VS_HCI_STOP_A2DP_MEDIA successful", __func__);
         break;
       case VS_HCI_A2DP_OFFLOAD_START:
+        if (bta_av_cb.offload_start_pending_acl_hdl != HCI_INVALID_HANDLE) {
+          bta_av_cb.offload_started_acl_hdl =
+              bta_av_cb.offload_start_pending_acl_hdl;
+          bta_av_cb.offload_start_pending_acl_hdl = HCI_INVALID_HANDLE;
+        } else {
+          LOG_INFO("%s: No pending start command due to AVDTP suspend immediately", __func__);
+        }
         (*bta_av_cb.p_cback)(BTA_AV_OFFLOAD_START_RSP_EVT, &value);
         break;
       default:
@@ -3020,8 +3035,10 @@
   } else {
     APPL_TRACE_DEBUG("%s: Offload failed for subopcode= %d", __func__,
                      sub_opcode);
-    if (param->opcode != VS_HCI_A2DP_OFFLOAD_STOP)
+    if (param->opcode != VS_HCI_A2DP_OFFLOAD_STOP) {
+      bta_av_cb.offload_start_pending_acl_hdl = HCI_INVALID_HANDLE;
       (*bta_av_cb.p_cback)(BTA_AV_OFFLOAD_START_RSP_EVT, &value);
+    }
   }
 }
 
@@ -3046,7 +3063,7 @@
   UINT16_TO_STREAM(p_param, offload_start->mtu);
   ARRAY_TO_STREAM(p_param, offload_start->codec_info,
                   (int8_t)sizeof(offload_start->codec_info));
-  p_scb->offload_started = true;
+  bta_av_cb.offload_start_pending_acl_hdl = offload_start->acl_hdl;
   LOG_INFO(
       "codec: %#x, sample rate: %#x, bit depth: %#x, channel: %#x, bitrate: "
       "%#x, ACL: %#x, L2CAP: %#x, MTU: %#x",
@@ -3086,6 +3103,10 @@
   /* Support offload if only one audio source stream is open. */
   if (p_scb->started != true) {
     status = BTA_AV_FAIL_STREAM;
+  } else if (bta_av_cb.offload_start_pending_acl_hdl != HCI_INVALID_HANDLE ||
+             bta_av_cb.offload_started_acl_hdl != HCI_INVALID_HANDLE) {
+    APPL_TRACE_WARNING("%s: offload already started, ignore request", __func__);
+    return;
   } else {
     bta_av_offload_codec_builder(p_scb, &offload_start);
     bta_av_vendor_offload_start(p_scb, &offload_start);
@@ -3155,7 +3176,7 @@
     status = BTA_AV_FAIL_STREAM;
   }
 
-  p_scb->offload_start_pending = false;
+  bta_av_cb.offload_start_pending_acl_hdl = HCI_INVALID_HANDLE;
   tBTA_AV bta_av_data;
   bta_av_data.status = status;
   (*bta_av_cb.p_cback)(BTA_AV_OFFLOAD_START_RSP_EVT, &bta_av_data);
diff --git a/bta/av/bta_av_int.h b/bta/av/bta_av_int.h
index e97a24b..9a65ec5 100644
--- a/bta/av/bta_av_int.h
+++ b/bta/av/bta_av_int.h
@@ -539,8 +539,6 @@
   uint8_t q_tag; /* identify the associated q_info union member */
   bool no_rtp_header; /* true if add no RTP header */
   uint16_t uuid_int; /*intended UUID of Initiator to connect to */
-  bool offload_start_pending;
-  bool offload_started;
 
   /**
    * Called to setup the state when connected to a peer.
@@ -639,6 +637,8 @@
   uint8_t rc_acp_idx; /* (index + 1) to RCB */
   uint8_t rs_idx;    /* (index + 1) to SCB for the one waiting for RS on open */
   bool sco_occupied; /* true if SCO is being used or call is in progress */
+  uint16_t offload_start_pending_acl_hdl;
+  uint16_t offload_started_acl_hdl;
 } tBTA_AV_CB;
 
 // total attempts are half seconds
diff --git a/bta/av/bta_av_main.cc b/bta/av/bta_av_main.cc
index 83f802c..257770c 100644
--- a/bta/av/bta_av_main.cc
+++ b/bta/av/bta_av_main.cc
@@ -40,6 +40,7 @@
 #include "osi/include/properties.h"
 #include "stack/include/acl_api.h"
 #include "stack/include/bt_hdr.h"
+#include "stack/include/btm_api.h"
 #include "types/hci_role.h"
 #include "types/raw_address.h"
 
@@ -165,6 +166,8 @@
   /* store parameters */
   bta_av_cb.p_cback = p_data->api_enable.p_cback;
   bta_av_cb.features = p_data->api_enable.features;
+  bta_av_cb.offload_start_pending_acl_hdl = HCI_INVALID_HANDLE;
+  bta_av_cb.offload_started_acl_hdl = HCI_INVALID_HANDLE;
 
   tBTA_AV_ENABLE enable;
   enable.features = bta_av_cb.features;
@@ -337,6 +340,16 @@
     evt = BTA_AV_SIG_CHG_EVT;
     if (event == AVDT_DISCONNECT_IND_EVT) {
       p_scb = bta_av_addr_to_scb(bd_addr);
+      if (p_scb) {
+        uint16_t handle =
+            BTM_GetHCIConnHandle(p_scb->PeerAddress(), BT_TRANSPORT_BR_EDR);
+        if (bta_av_cb.offload_started_acl_hdl == handle ||
+            bta_av_cb.offload_start_pending_acl_hdl == handle) {
+          LOG_INFO("%s: Cleanup offload related flag", __func__);
+          bta_av_cb.offload_started_acl_hdl = HCI_INVALID_HANDLE;
+          bta_av_cb.offload_start_pending_acl_hdl = HCI_INVALID_HANDLE;
+        }
+      }
     } else if (event == AVDT_CONNECT_IND_EVT) {
       APPL_TRACE_DEBUG("%s: CONN_IND is ACP:%d", __func__,
                        p_data->hdr.err_param);
@@ -1406,6 +1419,10 @@
   dprintf(fd, "  Connected audio channels mask: 0x%x\n", bta_av_cb.conn_audio);
   dprintf(fd, "  Registered audio channels mask: 0x%x\n", bta_av_cb.reg_audio);
   dprintf(fd, "  Connected LCBs mask: 0x%x\n", bta_av_cb.conn_lcb);
+  dprintf(fd, "  Offload start pending handle: %d\n",
+          bta_av_cb.offload_start_pending_acl_hdl);
+  dprintf(fd, "  Offload started handle: %d\n",
+          bta_av_cb.offload_started_acl_hdl);
 
   for (size_t i = 0; i < sizeof(bta_av_cb.lcb) / sizeof(bta_av_cb.lcb[0]);
        i++) {
diff --git a/bta/dm/bta_dm_act.cc b/bta/dm/bta_dm_act.cc
index 38f1f0e..42fcdc7 100644
--- a/bta/dm/bta_dm_act.cc
+++ b/bta/dm/bta_dm_act.cc
@@ -2909,8 +2909,8 @@
     num_uuid = p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len / Uuid::kNumBytes16;
 #else   // BTA_EIR_CANNED_UUID_LIST
     max_num_uuid = (free_eir_length - 2) / Uuid::kNumBytes16;
-    data_type = BTM_GetEirSupportedServices(bta_dm_cb.eir_uuid, &p,
-                                            max_num_uuid, &num_uuid);
+    data_type = get_btm_client_interface().eir.BTM_GetEirSupportedServices(
+        bta_dm_cb.eir_uuid, &p, max_num_uuid, &num_uuid);
     p = (uint8_t*)p_buf + BTM_HCI_EIR_OFFSET; /* reset p */
 #endif  // BTA_EIR_CANNED_UUID_LIST
 
@@ -2967,8 +2967,8 @@
     num_uuid = 0;
 
     max_num_uuid = (free_eir_length - 2) / Uuid::kNumBytes16;
-    data_type = BTM_GetEirSupportedServices(bta_dm_cb.eir_uuid, &p,
-                                            max_num_uuid, &num_uuid);
+    data_type = get_btm_client_interface().eir.BTM_GetEirSupportedServices(
+        bta_dm_cb.eir_uuid, &p, max_num_uuid, &num_uuid);
 
     if (data_type == HCI_EIR_MORE_16BITS_UUID_TYPE) {
       APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated");
diff --git a/bta/include/bta_api.h b/bta/include/bta_api.h
index 7335609..8ac2635 100644
--- a/bta/include/bta_api.h
+++ b/bta/include/bta_api.h
@@ -65,7 +65,11 @@
 #define BTA_BIP_SERVICE_ID 13        /* Basic Imaging profile */
 #define BTA_A2DP_SINK_SERVICE_ID 18  /* A2DP Sink */
 #define BTA_HID_SERVICE_ID 20        /* HID */
+#define BTA_PBAP_SERVICE_ID 22       /* PhoneBook Access Server*/
 #define BTA_HFP_HS_SERVICE_ID 24     /* HSP HS role */
+#define BTA_MAP_SERVICE_ID 25        /* Message Access Profile */
+#define BTA_MN_SERVICE_ID 26         /* Message Notification Service */
+#define BTA_PCE_SERVICE_ID 28        /* PhoneBook Access Client */
 #define BTA_SDP_SERVICE_ID 29        /* SDP Search */
 #define BTA_HIDD_SERVICE_ID 30       /* HID Device */
 
diff --git a/bta/le_audio/client.cc b/bta/le_audio/client.cc
index dd15408..8dc6e3f 100644
--- a/bta/le_audio/client.cc
+++ b/bta/le_audio/client.cc
@@ -414,6 +414,12 @@
                                    group_id);
   }
 
+  void remove_group_if_possible(LeAudioDeviceGroup* group) {
+    if (group && group->IsEmpty() && !group->cig_created_) {
+      aseGroups_.Remove(group->group_id_);
+    }
+  }
+
   void group_remove_node(LeAudioDeviceGroup* group, const RawAddress& address,
                          bool update_group_module = false) {
     int group_id = group->group_id_;
@@ -431,8 +437,7 @@
 
     /* Remove group if this was the last leAudioDevice in this group */
     if (group->IsEmpty()) {
-      aseGroups_.Remove(group_id);
-
+      remove_group_if_possible(group);
       return;
     }
 
@@ -657,6 +662,12 @@
       return;
     }
 
+    if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID) {
+      Disconnect(address);
+      leAudioDevice->removing_device_ = true;
+      return;
+    }
+
     /* Remove the group assignment if not yet removed. It might happen that the
      * group module has already called the appropriate callback and we have
      * already removed the group assignment.
@@ -666,12 +677,6 @@
       group_remove_node(group, address, true);
     }
 
-    if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID) {
-      Disconnect(address);
-      leAudioDevice->removing_device_ = true;
-      return;
-    }
-
     leAudioDevices_.Remove(address);
   }
 
@@ -1181,7 +1186,13 @@
     leAudioDevice->conn_id_ = GATT_INVALID_CONN_ID;
     leAudioDevice->encrypted_ = false;
 
-    if (leAudioDevice->removing_device_) leAudioDevices_.Remove(address);
+    if (leAudioDevice->removing_device_) {
+      if (leAudioDevice->group_id_ != bluetooth::groups::kGroupUnknown) {
+        auto group = aseGroups_.FindById(leAudioDevice->group_id_);
+        group_remove_node(group, address, true);
+      }
+      leAudioDevices_.Remove(address);
+    }
   }
 
   bool subscribe_for_indications(uint16_t conn_id, const RawAddress& address,
@@ -2525,6 +2536,7 @@
         auto* evt = static_cast<cig_remove_cmpl_evt*>(data);
         LeAudioDeviceGroup* group = aseGroups_.FindById(evt->cig_id);
         groupStateMachine_->ProcessHciNotifOnCigRemove(evt->status, group);
+        remove_group_if_possible(group);
       } break;
       default:
         LOG(ERROR) << __func__ << " Invalid event " << int{event_type};
diff --git a/bta/le_audio/hal_verifier.cc b/bta/le_audio/hal_verifier.cc
index 6fd2c66..9210714 100644
--- a/bta/le_audio/hal_verifier.cc
+++ b/bta/le_audio/hal_verifier.cc
@@ -18,6 +18,6 @@
 #include "bta_le_audio_api.h"
 
 bool LeAudioHalVerifier::SupportsLeAudio() {
-  return bluetooth::audio::HalVersionManager::GetHalVersion() ==
+  return bluetooth::audio::HalVersionManager::GetHalVersion() >=
          bluetooth::audio::BluetoothAudioHalVersion::VERSION_2_1;
 }
diff --git a/bta/le_audio/le_audio_client_test.cc b/bta/le_audio/le_audio_client_test.cc
index 3af1ed1..13fe4c8 100644
--- a/bta/le_audio/le_audio_client_test.cc
+++ b/bta/le_audio/le_audio_client_test.cc
@@ -531,6 +531,10 @@
           state_machine_callbacks_->StatusReportCb(
               group->group_id_, GroupStreamStatus::STREAMING);
           streaming_groups[group->group_id_] = group;
+
+          /* Assume CIG is created */
+          group->cig_created_ = true;
+
           return true;
         });
 
@@ -557,8 +561,8 @@
         });
 
     ON_CALL(mock_state_machine_, ProcessHciNotifAclDisconnected(_, _))
-        .WillByDefault([](LeAudioDeviceGroup* group,
-                          LeAudioDevice* leAudioDevice) {
+        .WillByDefault([this](LeAudioDeviceGroup* group,
+                              LeAudioDevice* leAudioDevice) {
           if (!group) return;
           auto* stream_conf = &group->stream_conf;
           if (stream_conf->valid) {
@@ -587,6 +591,11 @@
               stream_conf->valid = false;
             }
           }
+
+          if (group->IsEmpty()) {
+            group->cig_created_ = false;
+            InjectCigRemoved(group->group_id_);
+          }
         });
 
     ON_CALL(mock_state_machine_, ProcessHciNotifCisDisconnected(_, _, _))
@@ -1551,6 +1560,16 @@
         bluetooth::hci::iso_manager::kIsoEventCisDisconnected, &cis_evt);
   }
 
+  void InjectCigRemoved(uint8_t cig_id) {
+    bluetooth::hci::iso_manager::cig_remove_cmpl_evt evt;
+    evt.status = 0;
+    evt.cig_id = cig_id;
+
+    ASSERT_NE(cig_callbacks_, nullptr);
+    cig_callbacks_->OnCisEvent(
+        bluetooth::hci::iso_manager::kIsoEventCigOnRemoveCmpl, &evt);
+  }
+
   MockLeAudioClientCallbacks mock_client_callbacks_;
   MockLeAudioClientAudioSource mock_audio_source_;
   MockLeAudioClientAudioSink mock_audio_sink_;
@@ -2230,6 +2249,69 @@
   Mock::VerifyAndClearExpectations(&mock_btif_storage_);
 }
 
+TEST_F(UnicastTest, RemoveWhileStreaming) {
+  const RawAddress test_address0 = GetTestAddress(0);
+  int group_id = bluetooth::groups::kGroupUnknown;
+
+  SetSampleDatabaseEarbudsValid(
+      1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
+      codec_spec_conf::kLeAudioLocationStereo, false /*add_csis*/,
+      true /*add_cas*/, true /*add_pacs*/, true /*add_ascs*/, 1 /*set_size*/,
+      0 /*rank*/);
+  EXPECT_CALL(mock_client_callbacks_,
+              OnConnectionState(ConnectionState::CONNECTED, test_address0))
+      .Times(1);
+  EXPECT_CALL(mock_client_callbacks_,
+              OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED))
+      .WillOnce(DoAll(SaveArg<1>(&group_id)));
+
+  ConnectLeAudio(test_address0);
+  ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);
+
+  // Start streaming
+  uint8_t cis_count_out = 1;
+  uint8_t cis_count_in = 0;
+
+  EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+  LeAudioClient::Get()->GroupSetActive(group_id);
+
+  EXPECT_CALL(mock_state_machine_, StartStream(_, _)).Times(1);
+
+  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
+
+  SyncOnMainLoop();
+  Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
+  Mock::VerifyAndClearExpectations(&mock_audio_source_);
+  Mock::VerifyAndClearExpectations(&mock_state_machine_);
+  SyncOnMainLoop();
+
+  // Verify Data transfer on one audio source cis
+  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
+
+  EXPECT_CALL(mock_groups_module_, RemoveDevice(test_address0, group_id))
+      .Times(1);
+
+  LeAudioDeviceGroup* group = nullptr;
+  EXPECT_CALL(mock_state_machine_, ProcessHciNotifAclDisconnected(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&group)));
+  EXPECT_CALL(
+      mock_client_callbacks_,
+      OnGroupNodeStatus(test_address0, group_id, GroupNodeStatus::REMOVED));
+
+  EXPECT_CALL(mock_client_callbacks_,
+              OnConnectionState(ConnectionState::DISCONNECTED, test_address0))
+      .Times(1);
+
+  LeAudioClient::Get()->RemoveDevice(test_address0);
+
+  SyncOnMainLoop();
+  Mock::VerifyAndClearExpectations(&mock_groups_module_);
+  Mock::VerifyAndClearExpectations(&mock_state_machine_);
+  Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
+
+  ASSERT_NE(group, nullptr);
+}
+
 TEST_F(UnicastTest, SpeakerStreaming) {
   const RawAddress test_address0 = GetTestAddress(0);
   int group_id = bluetooth::groups::kGroupUnknown;
diff --git a/btcore/Android.bp b/btcore/Android.bp
index f96877f..ea71c79 100644
--- a/btcore/Android.bp
+++ b/btcore/Android.bp
@@ -8,8 +8,8 @@
     default_applicable_licenses: ["system_bt_license"],
 }
 
-cc_library_static {
-    name: "libbtcore",
+cc_defaults {
+    name: "libbtcore_defaults",
     defaults: ["fluoride_defaults"],
     local_include_dirs: ["include"],
     include_dirs: [
@@ -35,6 +35,17 @@
     },
 }
 
+cc_library_static {
+    name: "libbtcore",
+    defaults: ["libbtcore_defaults"],
+}
+
+cc_library_static {
+    name: "libbtcore-static",
+    defaults: ["libbtcore_defaults"],
+    cflags: ["-DSTATIC_LIBBLUETOOTH"],
+}
+
 cc_library_headers {
     name: "libbtcore_headers",
     defaults: ["libchrome_support_defaults"],
diff --git a/btif/Android.bp b/btif/Android.bp
index 238f964..bbdcebd 100644
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -88,8 +88,8 @@
 }
 
 // libbtif static library for target
-cc_library_static {
-    name: "libbtif",
+cc_defaults {
+    name: "libbtif_defaults",
     defaults: ["fluoride_defaults"],
     include_dirs: btifCommonIncludes,
     srcs: [
@@ -195,6 +195,17 @@
     host_supported: true,
 }
 
+cc_library_static {
+    name: "libbtif",
+    defaults: ["libbtif_defaults"],
+}
+
+cc_library_static {
+    name: "libbtif-static",
+    defaults: ["libbtif_defaults"],
+    cflags: ["-DSTATIC_LIBBLUETOOTH"],
+}
+
 // btif unit tests for target
 cc_test {
     name: "net_test_btif",
diff --git a/btif/BUILD.gn b/btif/BUILD.gn
index 0100bb0..26be562 100644
--- a/btif/BUILD.gn
+++ b/btif/BUILD.gn
@@ -102,6 +102,7 @@
     "//bt/device/include",
     "//bt/embdrv/sbc/encoder/include",
     "//bt/embdrv/sbc/decoder/include",
+    "//bt/gd",
     "//bt/hci/include",
     "//bt/stack/a2dp",
     "//bt/stack/btm",
diff --git a/btif/src/btif_dm.cc b/btif/src/btif_dm.cc
index 4f0097f..3fd506c 100644
--- a/btif/src/btif_dm.cc
+++ b/btif/src/btif_dm.cc
@@ -307,6 +307,22 @@
     case BTA_HIDD_SERVICE_ID: {
       btif_hd_execute_service(b_enable);
     } break;
+    case BTA_PBAP_SERVICE_ID:
+      FALLTHROUGH_INTENDED; /* FALLTHROUGH */
+    case BTA_PCE_SERVICE_ID:
+      FALLTHROUGH_INTENDED; /* FALLTHROUGH */
+    case BTA_MAP_SERVICE_ID:
+      FALLTHROUGH_INTENDED; /* FALLTHROUGH */
+    case BTA_MN_SERVICE_ID: {
+      /**
+       * Do nothing; these services were started elsewhere. However, we need to flow through this
+       * codepath in order to properly report back the local UUIDs back to adapter properties in
+       * Java. To achieve this, we need to catch these service IDs in order for {@link
+       * btif_in_execute_service_request} to return {@code BT_STATUS_SUCCESS}, so that in {@link
+       * btif_dm_enable_service} the check passes and the UUIDs are allowed to be passed up into
+       * the Java layer.
+       */
+    } break;
     default:
       BTIF_TRACE_ERROR("%s: Unknown service %d being %s", __func__, service_id,
                        (b_enable) ? "enabled" : "disabled");
diff --git a/btif/src/btif_sdp_server.cc b/btif/src/btif_sdp_server.cc
index cab4520..ba166b6 100644
--- a/btif/src/btif_sdp_server.cc
+++ b/btif/src/btif_sdp_server.cc
@@ -288,6 +288,38 @@
 bt_status_t remove_sdp_record(int record_id) {
   int handle;
 
+  bluetooth_sdp_record* record;
+  bluetooth_sdp_types sdp_type = SDP_TYPE_RAW;
+  {
+    std::unique_lock<std::recursive_mutex> lock(sdp_lock);
+    record = sdp_slots[record_id].record_data;
+    if (record != NULL) {
+      sdp_type = record->hdr.type;
+    }
+  }
+  tBTA_SERVICE_ID service_id = -1;
+  switch (sdp_type) {
+    case SDP_TYPE_MAP_MAS:
+      service_id = BTA_MAP_SERVICE_ID;
+      break;
+    case SDP_TYPE_MAP_MNS:
+      service_id = BTA_MN_SERVICE_ID;
+      break;
+    case SDP_TYPE_PBAP_PSE:
+      service_id = BTA_PBAP_SERVICE_ID;
+      break;
+    case SDP_TYPE_PBAP_PCE:
+      service_id = BTA_PCE_SERVICE_ID;
+      break;
+    default:
+      /* other enumeration values were not enabled in {@link on_create_record_event} */
+      break;
+  }
+  if (service_id > 0) {
+    // {@link btif_disable_service} sets the mask {@link btif_enabled_services}.
+    btif_disable_service(service_id);
+  }
+
   /* Get the Record handle, and free the slot */
   handle = free_sdp_slot(record_id);
   BTIF_TRACE_DEBUG("Sdp Server %s id=%d to handle=0x%08x", __func__, record_id,
@@ -317,6 +349,7 @@
    * */
   BTIF_TRACE_DEBUG("Sdp Server %s", __func__);
   const sdp_slot_t* sdp_slot = start_create_sdp(id);
+  tBTA_SERVICE_ID service_id = -1;
   /* In the case we are shutting down, sdp_slot is NULL */
   if (sdp_slot != NULL) {
     bluetooth_sdp_record* record = sdp_slot->record_data;
@@ -324,12 +357,15 @@
     switch (record->hdr.type) {
       case SDP_TYPE_MAP_MAS:
         handle = add_maps_sdp(&record->mas);
+        service_id = BTA_MAP_SERVICE_ID;
         break;
       case SDP_TYPE_MAP_MNS:
         handle = add_mapc_sdp(&record->mns);
+        service_id = BTA_MN_SERVICE_ID;
         break;
       case SDP_TYPE_PBAP_PSE:
         handle = add_pbaps_sdp(&record->pse);
+        service_id = BTA_PBAP_SERVICE_ID;
         break;
       case SDP_TYPE_OPP_SERVER:
         handle = add_opps_sdp(&record->ops);
@@ -339,6 +375,7 @@
         break;
       case SDP_TYPE_PBAP_PCE:
         handle = add_pbapc_sdp(&record->pce);
+        service_id = BTA_PCE_SERVICE_ID;
         break;
       default:
         BTIF_TRACE_DEBUG("Record type %d is not supported", record->hdr.type);
@@ -346,6 +383,18 @@
     }
     if (handle != -1) {
       set_sdp_handle(id, handle);
+      if (service_id > 0) {
+        /**
+         * {@link btif_enable_service} calls {@link btif_dm_enable_service}, which calls {@link
+         * btif_in_execute_service_request}.
+         *     - {@link btif_enable_service} sets the mask {@link btif_enabled_services}.
+         *     - {@link btif_dm_enable_service} invokes the java callback to return uuids based
+         *       on the enabled services mask.
+         *     - {@link btif_in_execute_service_request} gates the java callback in {@link
+         *       btif_dm_enable_service}.
+         */
+        btif_enable_service(service_id);
+      }
     }
   }
 }
diff --git a/btif/src/btif_storage.cc b/btif/src/btif_storage.cc
index 38640e2..af84a22 100644
--- a/btif/src/btif_storage.cc
+++ b/btif/src/btif_storage.cc
@@ -698,11 +698,27 @@
             *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_AUDIO_SINK);
             num_uuids++;
           } break;
+          case BTA_PBAP_SERVICE_ID: {
+            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_PBAP_PSE);
+            num_uuids++;
+          } break;
           case BTA_HFP_HS_SERVICE_ID: {
             *(p_uuid + num_uuids) =
                 Uuid::From16Bit(UUID_SERVCLASS_HF_HANDSFREE);
             num_uuids++;
           } break;
+          case BTA_MAP_SERVICE_ID: {
+            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_MESSAGE_ACCESS);
+            num_uuids++;
+          } break;
+          case BTA_MN_SERVICE_ID: {
+            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_MESSAGE_NOTIFICATION);
+            num_uuids++;
+          } break;
+          case BTA_PCE_SERVICE_ID: {
+            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_PBAP_PCE);
+            num_uuids++;
+          } break;
         }
       }
     }
diff --git a/gd/btaa/android/activity_attribution.cc b/gd/btaa/android/activity_attribution.cc
index 34d4a36..19c0df1 100644
--- a/gd/btaa/android/activity_attribution.cc
+++ b/gd/btaa/android/activity_attribution.cc
@@ -46,41 +46,62 @@
 static const std::string kBtWakeupReason("hs_uart_wakeup");
 static const size_t kHciAclHeaderSize = 4;
 
+static std::mutex g_module_mutex;
+static ActivityAttribution* g_module = nullptr;
+static bool is_wakeup_callback_registered = false;
+static bool is_wakelock_callback_registered = false;
+
 struct wakelock_callback : public BnWakelockCallback {
-  wakelock_callback(ActivityAttribution* module) : module_(module) {}
+  wakelock_callback() {}
 
   Status notifyAcquired() override {
-    module_->OnWakelockAcquired();
+    std::lock_guard<std::mutex> guard(g_module_mutex);
+    if (g_module != nullptr) {
+      g_module->OnWakelockAcquired();
+    }
     return Status::ok();
   }
   Status notifyReleased() override {
-    module_->OnWakelockReleased();
+    std::lock_guard<std::mutex> guard(g_module_mutex);
+    if (g_module != nullptr) {
+      g_module->OnWakelockReleased();
+    }
     return Status::ok();
   }
-
-  ActivityAttribution* module_;
 };
 
+static std::shared_ptr<wakelock_callback> g_wakelock_callback = nullptr;
+
 struct wakeup_callback : public BnSuspendCallback {
-  wakeup_callback(ActivityAttribution* module) : module_(module) {}
+  wakeup_callback() {}
 
   Status notifyWakeup(bool success, const std::vector<std::string>& wakeup_reasons) override {
     for (auto& wakeup_reason : wakeup_reasons) {
       if (wakeup_reason.find(kBtWakeupReason) != std::string::npos) {
-        module_->OnWakeup();
+        std::lock_guard<std::mutex> guard(g_module_mutex);
+        if (g_module != nullptr) {
+          g_module->OnWakeup();
+        }
         break;
       }
     }
     return Status::ok();
   }
-
-  ActivityAttribution* module_;
 };
 
+static std::shared_ptr<wakeup_callback> g_wakeup_callback = nullptr;
+
 struct ActivityAttribution::impl {
   impl(ActivityAttribution* module) {
-    bool is_registered = false;
+    std::lock_guard<std::mutex> guard(g_module_mutex);
+    g_module = module;
+    if (is_wakeup_callback_registered && is_wakelock_callback_registered) {
+      LOG_ERROR("Wakeup and wakelock callbacks are already registered");
+      return;
+    }
 
+    Status register_callback_status;
+    bool is_register_successful = false;
     auto control_service =
         ISuspendControlService::fromBinder(SpAIBinder(AServiceManager_getService("suspend_control")));
     if (!control_service) {
@@ -88,21 +109,33 @@
       return;
     }
 
-    Status register_callback_status =
-        control_service->registerCallback(SharedRefBase::make<wakeup_callback>(module), &is_registered);
-    if (!is_registered || !register_callback_status.isOk()) {
-      LOG_ERROR("Fail to register wakeup callback");
-      return;
+    if (!is_wakeup_callback_registered) {
+      g_wakeup_callback = SharedRefBase::make<wakeup_callback>();
+      register_callback_status = control_service->registerCallback(g_wakeup_callback, &is_register_successful);
+      if (!is_register_successful || !register_callback_status.isOk()) {
+        LOG_ERROR("Fail to register wakeup callback");
+        return;
+      }
+      is_wakeup_callback_registered = true;
     }
 
-    register_callback_status = control_service->registerWakelockCallback(
-        SharedRefBase::make<wakelock_callback>(module), kBtWakelockName, &is_registered);
-    if (!is_registered || !register_callback_status.isOk()) {
-      LOG_ERROR("Fail to register wakelock callback");
-      return;
+    if (!is_wakelock_callback_registered) {
+      g_wakelock_callback = SharedRefBase::make<wakelock_callback>();
+      register_callback_status =
+          control_service->registerWakelockCallback(g_wakelock_callback, kBtWakelockName, &is_register_successful);
+      if (!is_register_successful || !register_callback_status.isOk()) {
+        LOG_ERROR("Fail to register wakelock callback");
+        return;
+      }
+      is_wakelock_callback_registered = true;
     }
   }
 
+  ~impl() {
+    std::lock_guard<std::mutex> guard(g_module_mutex);
+    g_module = nullptr;
+  }
+
   void on_hci_packet(hal::HciPacket packet, hal::SnoopLogger::PacketType type, uint16_t length) {
     attribution_processor_.OnBtaaPackets(std::move(hci_processor_.OnHciPacket(std::move(packet), type, length)));
   }
diff --git a/gd/rust/linux/mgmt/src/bin/btmanagerd/config_util.rs b/gd/rust/linux/mgmt/src/bin/btmanagerd/config_util.rs
index d29e782..26c997b 100644
--- a/gd/rust/linux/mgmt/src/bin/btmanagerd/config_util.rs
+++ b/gd/rust/linux/mgmt/src/bin/btmanagerd/config_util.rs
@@ -5,7 +5,7 @@
 pub const HCI_DEVICES_DIR: &str = "/sys/class/bluetooth";
 
 // File to store the Bluetooth daemon to use (bluez or floss)
-const BLUETOOTH_DAEMON_CURRENT: &str = "/var/lib/misc/bluetooth-daemon.current";
+const BLUETOOTH_DAEMON_CURRENT: &str = "/var/lib/bluetooth/bluetooth-daemon.current";
 
 // File to store the config for BluetoothManager
 const BTMANAGERD_CONF: &str = "/var/lib/bluetooth/btmanagerd.json";
diff --git a/gd/rust/shim/Android.bp b/gd/rust/shim/Android.bp
index 6db3bfd..b5b6042 100644
--- a/gd/rust/shim/Android.bp
+++ b/gd/rust/shim/Android.bp
@@ -26,8 +26,8 @@
     },
 }
 
-rust_ffi_static {
-    name: "libbt_shim_ffi",
+rust_defaults {
+    name: "libbt_shim_defaults",
     defaults: ["gd_rust_defaults"],
     crate_name: "bt_shim",
     srcs: ["src/lib.rs"],
@@ -51,8 +51,18 @@
     ],
 }
 
+rust_library_rlib {
+    name: "libbt_shim",
+    defaults: ["libbt_shim_defaults"],
+}
+
+rust_ffi_static {
+    name: "libbt_shim_ffi",
+    defaults: ["libbt_shim_defaults"],
+}
+
 cc_library_static {
-    name: "libbluetooth_rust_interop",
+    name: "libbt_shim_bridge",
     defaults: ["gd_ffi_defaults"],
     generated_headers: [
         "libbt_init_flags_bridge_header",
@@ -87,9 +97,16 @@
     shared_libs: [
         "libchrome",
     ],
+}
+
+cc_library_static {
+    name: "libbluetooth_rust_interop",
+    defaults: ["gd_ffi_defaults"],
     whole_static_libs: [
+        "libbt_shim_bridge",
         "libbt_shim_ffi",
     ],
+    host_supported: true,
 }
 
 cc_library_static {
diff --git a/gd/rust/topshim/Android.bp b/gd/rust/topshim/Android.bp
index 95326a6..1e9677e 100644
--- a/gd/rust/topshim/Android.bp
+++ b/gd/rust/topshim/Android.bp
@@ -51,6 +51,7 @@
         "btif/btif_shim.cc",
         "gatt/gatt_shim.cc",
         "hfp/hfp_shim.cc",
+        "controller/controller_shim.cc",
     ],
     generated_headers: ["libbt_topshim_bridge_header", "cxx-bridge-header"],
     generated_sources: ["libbt_topshim_bridge_code"],
@@ -76,6 +77,7 @@
         "src/profiles/avrcp.rs",
         "src/profiles/hfp.rs",
         "src/profiles/gatt.rs",
+        "src/controller.rs",
     ],
     output_extension: "rs.h",
     export_include_dirs: ["."],
@@ -91,6 +93,7 @@
         "src/profiles/avrcp.rs",
         "src/profiles/hfp.rs",
         "src/profiles/gatt.rs",
+        "src/controller.rs",
     ],
     output_extension: "cc",
     export_include_dirs: ["."],
diff --git a/gd/rust/topshim/BUILD.gn b/gd/rust/topshim/BUILD.gn
index d6e2252..bf986cb 100644
--- a/gd/rust/topshim/BUILD.gn
+++ b/gd/rust/topshim/BUILD.gn
@@ -29,6 +29,7 @@
     "src/profiles/avrcp.rs",
     "src/profiles/hfp.rs",
     "src/profiles/gatt.rs",
+    "src/controller.rs",
   ]
   all_dependent_configs = [ ":rust_topshim_config" ]
   deps = [":cxxlibheader"]
@@ -41,8 +42,9 @@
     "src/profiles/avrcp.rs",
     "src/profiles/hfp.rs",
     "src/profiles/gatt.rs",
+    "src/controller.rs",
   ]
-  deps = [":btif_bridge_header"]
+  deps = [":btif_bridge_header", "//bt/gd:BluetoothGeneratedPackets_h"]
   configs = [ "//bt/gd:gd_defaults" ]
 }
 
@@ -53,9 +55,10 @@
     "btav_sink/btav_sink_shim.cc",
     "hfp/hfp_shim.cc",
     "gatt/gatt_shim.cc",
+    "controller/controller_shim.cc",
   ]
 
-  deps = [":btif_bridge_header"]
+  deps = [":btif_bridge_header", "//bt/gd:BluetoothGeneratedPackets_h"]
   configs += ["//bt/gd:gd_defaults"]
 }
 
diff --git a/gd/rust/topshim/btav_sink/btav_sink_shim.cc b/gd/rust/topshim/btav_sink/btav_sink_shim.cc
index f0816df..53d943a 100644
--- a/gd/rust/topshim/btav_sink/btav_sink_shim.cc
+++ b/gd/rust/topshim/btav_sink/btav_sink_shim.cc
@@ -19,6 +19,8 @@
 #include <memory>
 
 #include "include/hardware/bluetooth.h"
+#include "rust/cxx.h"
+#include "src/profiles/a2dp.rs.h"
 #include "types/raw_address.h"
 
 namespace bluetooth {
@@ -37,6 +39,12 @@
     audio_state_cb,
     audio_config_cb,
 };
+
+static RawAddress from_rust_address(const RustRawAddress& raddr) {
+  RawAddress addr;
+  addr.FromOctets(raddr.address.data());
+  return addr;
+}
 }  // namespace internal
 
 A2dpSinkIntf::~A2dpSinkIntf() {
@@ -54,11 +62,23 @@
   return a2dp_sink;
 }
 
-int A2dpSinkIntf::init() {
+int A2dpSinkIntf::init() const {
   return intf_->init(&internal::g_a2dp_sink_callbacks, 1);
 }
 
-void A2dpSinkIntf::cleanup() {
+int A2dpSinkIntf::connect(RustRawAddress bt_addr) const {
+  return intf_->connect(internal::from_rust_address(bt_addr));
+}
+
+int A2dpSinkIntf::disconnect(RustRawAddress bt_addr) const {
+  return intf_->disconnect(internal::from_rust_address(bt_addr));
+}
+
+int A2dpSinkIntf::set_active_device(RustRawAddress bt_addr) const {
+  return intf_->set_active_device(internal::from_rust_address(bt_addr));
+}
+
+void A2dpSinkIntf::cleanup() const {
   // TODO: Implement.
 }
 
diff --git a/gd/rust/topshim/btav_sink/btav_sink_shim.h b/gd/rust/topshim/btav_sink/btav_sink_shim.h
index 2306ee1..785c150 100644
--- a/gd/rust/topshim/btav_sink/btav_sink_shim.h
+++ b/gd/rust/topshim/btav_sink/btav_sink_shim.h
@@ -18,20 +18,28 @@
 
 #include <memory>
 
+#include "gd/rust/topshim/btav_sink/btav_sink_shim.h"
 #include "include/hardware/bt_av.h"
+#include "rust/cxx.h"
+#include "types/raw_address.h"
 
 namespace bluetooth {
 namespace topshim {
 namespace rust {
 
+struct RustRawAddress;
+
 class A2dpSinkIntf {
  public:
   A2dpSinkIntf(const btav_sink_interface_t* intf) : intf_(intf){};
   ~A2dpSinkIntf();
 
   // interface for Settings
-  int init();
-  void cleanup();
+  int init() const;
+  int connect(RustRawAddress bt_addr) const;
+  int disconnect(RustRawAddress bt_addr) const;
+  int set_active_device(RustRawAddress bt_addr) const;
+  void cleanup() const;
 
  private:
   const btav_sink_interface_t* intf_;
diff --git a/gd/rust/topshim/controller/controller_shim.cc b/gd/rust/topshim/controller/controller_shim.cc
new file mode 100644
index 0000000..cd04f4f
--- /dev/null
+++ b/gd/rust/topshim/controller/controller_shim.cc
@@ -0,0 +1,54 @@
+/*
+ * 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 "gd/rust/topshim/controller/controller_shim.h"
+
+#include <memory>
+
+#include "rust/cxx.h"
+#include "src/controller.rs.h"
+#include "types/raw_address.h"
+
+namespace bluetooth {
+namespace topshim {
+namespace rust {
+namespace internal {
+static ControllerIntf* g_controller_intf;
+
+static RustRawAddress to_rust_address(const RawAddress& address) {
+  RustRawAddress raddr;
+  std::copy(std::begin(address.address), std::end(address.address), std::begin(raddr.address));
+  return raddr;
+}
+}  // namespace internal
+
+ControllerIntf::~ControllerIntf() {}
+
+std::unique_ptr<ControllerIntf> GetControllerInterface() {
+  if (internal::g_controller_intf) std::abort();
+  auto controller_intf = std::make_unique<ControllerIntf>();
+  internal::g_controller_intf = controller_intf.get();
+  return controller_intf;
+}
+
+RustRawAddress ControllerIntf::read_local_addr() const {
+  if (!controller_) std::abort();
+  return internal::to_rust_address(*controller_->get_address());
+}
+
+}  // namespace rust
+}  // namespace topshim
+}  // namespace bluetooth
diff --git a/gd/rust/topshim/controller/controller_shim.h b/gd/rust/topshim/controller/controller_shim.h
new file mode 100644
index 0000000..b817ee9
--- /dev/null
+++ b/gd/rust/topshim/controller/controller_shim.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 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 GD_RUST_TOPSHIM_CONTROLLER_SHIM
+#define GD_RUST_TOPSHIM_CONTROLLER_SHIM
+
+#include <memory>
+
+#include "main/shim/controller.h"
+#include "rust/cxx.h"
+#include "types/raw_address.h"
+
+namespace bluetooth {
+namespace topshim {
+namespace rust {
+
+struct RustRawAddress;
+
+class ControllerIntf {
+ public:
+  ControllerIntf() : controller_(controller_get_interface()) {}
+  ~ControllerIntf();
+
+  RustRawAddress read_local_addr() const;
+
+ private:
+  const controller_t* controller_;
+};
+
+// ControllerIntf* GetControllerInterface();
+std::unique_ptr<ControllerIntf> GetControllerInterface();
+
+}  // namespace rust
+}  // namespace topshim
+}  // namespace bluetooth
+
+#endif  // GD_RUST_TOPSHIM_CONTROLLER_SHIM
\ No newline at end of file
diff --git a/gd/rust/topshim/facade/Android.bp b/gd/rust/topshim/facade/Android.bp
index f0e185a..a92671b 100644
--- a/gd/rust/topshim/facade/Android.bp
+++ b/gd/rust/topshim/facade/Android.bp
@@ -12,6 +12,7 @@
     defaults: ["gd_rust_defaults"],
     crate_name: "bt_topshim_facade",
     srcs: ["src/main.rs"],
+    ld_flags: ["-fsanitize=undefined", "-fsanitize-minimal-runtime"],
     rustlibs: [
         "libbluetooth_rs",
         "libbt_common",
@@ -25,17 +26,18 @@
         "libbt_facade_helpers",
         "libbt_topshim",
         "libbt_topshim_facade_protobuf",
+        "libbt_shim",
     ],
     static_libs: [
         "libbt_topshim_cxx",
         "libbt-bta",
         "libbt-common",
         "libbtdevice",
-        "libbtif",
+        "libbtif-static",
         "libbt-hci",
         "libbt-stack",
         "libbt-utils",
-        "libbtcore",
+        "libbtcore-static",
         "libosi",
         "libbt-protos-lite",
         "libbte",
@@ -46,18 +48,19 @@
         "liblc3codec",
         "libudrv-uipc",
         "libbluetooth_gd", // Gabeldorsche
-        "libbluetooth_rust_interop",
         "libbluetooth-dumpsys",
+        "libbluetooth-types",
         "libflatbuffers-cpp",
+        "libbt_shim_bridge",
     ],
     shared_libs: [
         "libcrypto",
-        "libbluetooth",
         "libchrome",
+        "liblog",
+        "libcutils",
+        "libgrpc++",
+        "libgrpc_wrap"
     ],
-    sanitize: {
-        never: true,
-    },
     proc_macros: [
         "libpaste",
     ],
diff --git a/gd/rust/topshim/facade/src/main.rs b/gd/rust/topshim/facade/src/main.rs
index 0736933..44d27aa 100644
--- a/gd/rust/topshim/facade/src/main.rs
+++ b/gd/rust/topshim/facade/src/main.rs
@@ -21,6 +21,18 @@
 mod adapter_service;
 mod media_service;
 
+// This is needed for linking, libbt_shim_bridge needs symbols defined by
+// bt_shim, however bt_shim depends on rust crates (future, tokio) that
+// we use too, if we build and link them separately we ends with duplicate
+// symbols. To solve that we build bt_shim with bt_topshim_facade so the rust
+// compiler share the transitive dependencies.
+//
+// The `::*` is here to circuvent the single_component_path_imports from
+// clippy that is denied on the rust command line so we can't just allow it.
+// This is fine for now since bt_shim doesn't export anything
+#[allow(unused)]
+use bt_shim::*;
+
 fn main() {
     let sigint = install_sigint();
     bt_common::init_logging();
diff --git a/gd/rust/topshim/facade/src/media_service.rs b/gd/rust/topshim/facade/src/media_service.rs
index a5f37d5..1998af4 100644
--- a/gd/rust/topshim/facade/src/media_service.rs
+++ b/gd/rust/topshim/facade/src/media_service.rs
@@ -1,7 +1,9 @@
 //! Media service facade
 
 use bt_topshim::btif::BluetoothInterface;
-use bt_topshim::profiles::a2dp::{A2dp, A2dpCallbacksDispatcher, A2dpSink};
+use bt_topshim::profiles::a2dp::{
+    A2dp, A2dpCallbacksDispatcher, A2dpSink, A2dpSinkCallbacksDispatcher,
+};
 use bt_topshim::profiles::avrcp::{Avrcp, AvrcpCallbacksDispatcher};
 use bt_topshim_facade_protobuf::facade::{
     A2dpSourceConnectRequest, A2dpSourceConnectResponse, StartA2dpRequest, StartA2dpResponse,
@@ -17,6 +19,10 @@
     A2dpCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) }
 }
 
+fn get_a2dp_sink_dispatcher() -> A2dpSinkCallbacksDispatcher {
+    A2dpSinkCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) }
+}
+
 fn get_avrcp_dispatcher() -> AvrcpCallbacksDispatcher {
     AvrcpCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) }
 }
@@ -60,7 +66,7 @@
                 sink.success(StartA2dpResponse::default()).await.unwrap();
             })
         } else if req.start_a2dp_sink {
-            self.btif_a2dp_sink.lock().unwrap().initialize();
+            self.btif_a2dp_sink.lock().unwrap().initialize(get_a2dp_sink_dispatcher());
             ctx.spawn(async move {
                 sink.success(StartA2dpResponse::default()).await.unwrap();
             })
diff --git a/gd/rust/topshim/facade/utils.proto b/gd/rust/topshim/facade/utils.proto
new file mode 100644
index 0000000..9f0fd5c
--- /dev/null
+++ b/gd/rust/topshim/facade/utils.proto
@@ -0,0 +1,13 @@
+syntax = "proto3";
+
+package blueberry;
+
+message Empty {}
+
+message BluetoothAddress {
+  bytes address = 1;
+}
+
+service ReadOnlyProperty {
+  rpc ReadLocalAddress(Empty) returns (BluetoothAddress) {}
+}
\ No newline at end of file
diff --git a/gd/rust/topshim/hfp/hfp_shim.h b/gd/rust/topshim/hfp/hfp_shim.h
index e8a9622..3ce8db0 100644
--- a/gd/rust/topshim/hfp/hfp_shim.h
+++ b/gd/rust/topshim/hfp/hfp_shim.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <memory>
+
 #include "btif/include/btif_hf.h"
 #include "include/hardware/bluetooth_headset_callbacks.h"
 #include "types/raw_address.h"
diff --git a/gd/rust/topshim/src/controller.rs b/gd/rust/topshim/src/controller.rs
new file mode 100644
index 0000000..1984928
--- /dev/null
+++ b/gd/rust/topshim/src/controller.rs
@@ -0,0 +1,32 @@
+#[cxx::bridge(namespace = bluetooth::topshim::rust)]
+mod ffi {
+    pub struct RustRawAddress {
+        address: [u8; 6],
+    }
+
+    unsafe extern "C++" {
+        include!("controller/controller_shim.h");
+
+        type ControllerIntf;
+
+        fn GetControllerInterface() -> UniquePtr<ControllerIntf>;
+        fn read_local_addr(self: &ControllerIntf) -> RustRawAddress;
+    }
+}
+
+pub struct Controller {
+    internal: cxx::UniquePtr<ffi::ControllerIntf>,
+}
+
+unsafe impl Send for Controller {}
+
+impl Controller {
+    pub fn new() -> Controller {
+        let intf = ffi::GetControllerInterface();
+        Controller { internal: intf }
+    }
+
+    pub fn read_local_addr(&mut self) -> [u8; 6] {
+        self.internal.read_local_addr().address
+    }
+}
diff --git a/gd/rust/topshim/src/lib.rs b/gd/rust/topshim/src/lib.rs
index a28f2aa..c240ba8 100644
--- a/gd/rust/topshim/src/lib.rs
+++ b/gd/rust/topshim/src/lib.rs
@@ -8,5 +8,6 @@
 
 pub mod bindings;
 pub mod btif;
+pub mod controller;
 pub mod profiles;
 pub mod topstack;
diff --git a/gd/rust/topshim/src/profiles/a2dp.rs b/gd/rust/topshim/src/profiles/a2dp.rs
index ec285c4..380ebdd 100644
--- a/gd/rust/topshim/src/profiles/a2dp.rs
+++ b/gd/rust/topshim/src/profiles/a2dp.rs
@@ -182,8 +182,11 @@
 
         unsafe fn GetA2dpSinkProfile(btif: *const u8) -> UniquePtr<A2dpSinkIntf>;
 
-        fn init(self: Pin<&mut A2dpSinkIntf>) -> i32;
-        fn cleanup(self: Pin<&mut A2dpSinkIntf>);
+        fn init(self: &A2dpSinkIntf) -> i32;
+        fn connect(self: &A2dpSinkIntf, bt_addr: RustRawAddress) -> i32;
+        fn disconnect(self: &A2dpSinkIntf, bt_addr: RustRawAddress) -> i32;
+        fn set_active_device(self: &A2dpSinkIntf, bt_addr: RustRawAddress) -> i32;
+        fn cleanup(self: &A2dpSinkIntf);
     }
     extern "Rust" {
         fn connection_state_callback(addr: RustRawAddress, state: u32);
@@ -335,6 +338,17 @@
     }
 }
 
+#[derive(Debug)]
+pub enum A2dpSinkCallbacks {
+    ConnectionState(RawAddress, BtavConnectionState),
+}
+
+pub struct A2dpSinkCallbacksDispatcher {
+    pub dispatch: Box<dyn Fn(A2dpSinkCallbacks) + Send>,
+}
+
+type A2dpSinkCb = Arc<Mutex<A2dpSinkCallbacksDispatcher>>;
+
 pub struct A2dpSink {
     internal: cxx::UniquePtr<ffi::A2dpSinkIntf>,
     _is_init: bool,
@@ -353,11 +367,26 @@
         A2dpSink { internal: a2dp_sink, _is_init: false }
     }
 
-    pub fn initialize(&mut self) -> bool {
-        self.internal.pin_mut().init();
+    pub fn initialize(&mut self, callbacks: A2dpSinkCallbacksDispatcher) -> bool {
+        if get_dispatchers().lock().unwrap().set::<A2dpSinkCb>(Arc::new(Mutex::new(callbacks))) {
+            panic!("Tried to set dispatcher for A2dp Sink Callbacks while it already exists");
+        }
+        self.internal.init();
         true
     }
 
+    pub fn connect(&mut self, bt_addr: RawAddress) {
+        self.internal.connect(bt_addr.into());
+    }
+
+    pub fn disconnect(&mut self, bt_addr: RawAddress) {
+        self.internal.disconnect(bt_addr.into());
+    }
+
+    pub fn set_active_device(&mut self, bt_addr: RawAddress) {
+        self.internal.set_active_device(bt_addr.into());
+    }
+
     pub fn cleanup(&mut self) {}
 }
 
diff --git a/main/shim/btm_api.cc b/main/shim/btm_api.cc
index 2e0a338..d26dd6c 100644
--- a/main/shim/btm_api.cc
+++ b/main/shim/btm_api.cc
@@ -924,18 +924,6 @@
   CHECK(p_eir_uuid != nullptr);
 }
 
-uint8_t bluetooth::shim::BTM_GetEirSupportedServices(uint32_t* p_eir_uuid,
-                                                     uint8_t** p,
-                                                     uint8_t max_num_uuid16,
-                                                     uint8_t* p_num_uuid16) {
-  LOG_INFO("UNIMPLEMENTED %s", __func__);
-  CHECK(p_eir_uuid != nullptr);
-  CHECK(p != nullptr);
-  CHECK(*p != nullptr);
-  CHECK(p_num_uuid16 != nullptr);
-  return BTM_NO_RESOURCES;
-}
-
 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 8eb81d9..c276d01 100644
--- a/main/shim/btm_api.h
+++ b/main/shim/btm_api.h
@@ -366,26 +366,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_GetEirSupportedServices
- *
- * Description      This function is called to get UUID list from bit map UUID
- *                  list.
- *
- * Parameters       p_eir_uuid - bit mask of UUID list for EIR
- *                  p - reference of current pointer of EIR
- *                  max_num_uuid16 - max number of UUID can be written in EIR
- *                  num_uuid16 - number of UUID have been written in EIR
- *
- * Returns          HCI_EIR_MORE_16BITS_UUID_TYPE, if it has more than max
- *                  HCI_EIR_COMPLETE_16BITS_UUID_TYPE, otherwise
- *
- ******************************************************************************/
-uint8_t BTM_GetEirSupportedServices(uint32_t* p_eir_uuid, uint8_t** p,
-                                    uint8_t max_num_uuid16,
-                                    uint8_t* p_num_uuid16);
-
-/*******************************************************************************
- *
  * Function         BTM_SecAddBleDevice
  *
  * Description      Add/modify device.  This function will be normally called
diff --git a/stack/avrc/avrc_bld_tg.cc b/stack/avrc/avrc_bld_tg.cc
index 93ae296..92dbc5e 100644
--- a/stack/avrc/avrc_bld_tg.cc
+++ b/stack/avrc/avrc_bld_tg.cc
@@ -963,27 +963,31 @@
     p_item_len = p_data;
     p_data += 2;
     item_len = 0;
-    len_left -= 3; /* item_type(1) + item len(2) */
+    const uint16_t item_header_len = 3; /* item_type(1) + item len(2) */
+    uint16_t item_len_left = len_left - item_header_len;
     switch (p_item_list[xx].item_type) {
       case AVRC_ITEM_PLAYER:
         /* min len required: 2 + 1 + 4 + 1 + 16 + 2 + 2 = 30 + str_len */
         p_player = &p_item_list[xx].u.player;
         item_len = AVRC_FEATURE_MASK_SIZE + p_player->name.str_len + 12;
 
-        if ((len_left <= item_len) || !AVRC_ITEM_PLAYER_IS_VALID(p_player)) {
+        if ((item_len_left < item_len) ||
+            !AVRC_ITEM_PLAYER_IS_VALID(p_player)) {
+          if (item_len_left < item_len && item_count > 0) {
+            multi_items_add_fail = true;
+          }
           p_data = p_item_start;
-        } else {
-          UINT16_TO_BE_STREAM(p_data, p_player->player_id);
-          UINT8_TO_BE_STREAM(p_data, p_player->major_type);
-          UINT32_TO_BE_STREAM(p_data, p_player->sub_type);
-          UINT8_TO_BE_STREAM(p_data, p_player->play_status);
-          ARRAY_TO_BE_STREAM(p_data, p_player->features,
-                             AVRC_FEATURE_MASK_SIZE);
-          UINT16_TO_BE_STREAM(p_data, p_player->name.charset_id);
-          UINT16_TO_BE_STREAM(p_data, p_player->name.str_len);
-          ARRAY_TO_BE_STREAM(p_data, p_player->name.p_str,
-                             p_player->name.str_len);
+          break;
         }
+        UINT16_TO_BE_STREAM(p_data, p_player->player_id);
+        UINT8_TO_BE_STREAM(p_data, p_player->major_type);
+        UINT32_TO_BE_STREAM(p_data, p_player->sub_type);
+        UINT8_TO_BE_STREAM(p_data, p_player->play_status);
+        ARRAY_TO_BE_STREAM(p_data, p_player->features, AVRC_FEATURE_MASK_SIZE);
+        UINT16_TO_BE_STREAM(p_data, p_player->name.charset_id);
+        UINT16_TO_BE_STREAM(p_data, p_player->name.str_len);
+        ARRAY_TO_BE_STREAM(p_data, p_player->name.p_str,
+                           p_player->name.str_len);
         break;
 
       case AVRC_ITEM_FOLDER:
@@ -991,18 +995,21 @@
         p_folder = &p_item_list[xx].u.folder;
         item_len = AVRC_UID_SIZE + p_folder->name.str_len + 6;
 
-        if ((len_left > item_len) && p_folder->name.p_str &&
-            p_folder->type <= AVRC_FOLDER_TYPE_YEARS) {
-          ARRAY_TO_BE_STREAM(p_data, p_folder->uid, AVRC_UID_SIZE);
-          UINT8_TO_BE_STREAM(p_data, p_folder->type);
-          UINT8_TO_BE_STREAM(p_data, p_folder->playable);
-          UINT16_TO_BE_STREAM(p_data, p_folder->name.charset_id);
-          UINT16_TO_BE_STREAM(p_data, p_folder->name.str_len);
-          ARRAY_TO_BE_STREAM(p_data, p_folder->name.p_str,
-                             p_folder->name.str_len);
-        } else {
+        if ((item_len_left < item_len) || !p_folder->name.p_str ||
+            p_folder->type > AVRC_FOLDER_TYPE_YEARS) {
+          if (item_len_left < item_len && item_count > 0) {
+            multi_items_add_fail = true;
+          }
           p_data = p_item_start;
+          break;
         }
+        ARRAY_TO_BE_STREAM(p_data, p_folder->uid, AVRC_UID_SIZE);
+        UINT8_TO_BE_STREAM(p_data, p_folder->type);
+        UINT8_TO_BE_STREAM(p_data, p_folder->playable);
+        UINT16_TO_BE_STREAM(p_data, p_folder->name.charset_id);
+        UINT16_TO_BE_STREAM(p_data, p_folder->name.str_len);
+        ARRAY_TO_BE_STREAM(p_data, p_folder->name.p_str,
+                           p_folder->name.str_len);
         break;
 
       case AVRC_ITEM_MEDIA:
@@ -1010,43 +1017,43 @@
         p_media = &p_item_list[xx].u.media;
         item_len = AVRC_UID_SIZE + p_media->name.str_len + 6;
 
-        if ((len_left >= item_len) && p_media->name.p_str &&
-            p_media->type <= AVRC_MEDIA_TYPE_VIDEO) {
-          ARRAY_TO_BE_STREAM(p_data, p_media->uid, AVRC_UID_SIZE);
-          UINT8_TO_BE_STREAM(p_data, p_media->type);
-          UINT16_TO_BE_STREAM(p_data, p_media->name.charset_id);
-          UINT16_TO_BE_STREAM(p_data, p_media->name.str_len);
-          ARRAY_TO_BE_STREAM(p_data, p_media->name.p_str,
-                             p_media->name.str_len);
-          p_attr_count = p_data++;
-          *p_attr_count = 0;
-          len_left -= item_len;
-          if (p_media->attr_count > 0) {
-            p_attr = p_media->p_attr_list;
-            for (yy = 0; yy < p_media->attr_count; yy++) {
-              if (p_attr[yy].name.p_str &&
-                  AVRC_IS_VALID_MEDIA_ATTRIBUTE(p_attr[yy].attr_id) &&
-                  (len_left >= (p_attr[yy].name.str_len + 8))) {
-                (*p_attr_count)++;
-                UINT32_TO_BE_STREAM(p_data, p_attr[yy].attr_id);
-                UINT16_TO_BE_STREAM(p_data, p_attr[yy].name.charset_id);
-                UINT16_TO_BE_STREAM(p_data, p_attr[yy].name.str_len);
-                ARRAY_TO_BE_STREAM(p_data, p_attr[yy].name.p_str,
-                                   p_attr[yy].name.str_len);
-                item_len += (p_attr[yy].name.str_len + 8);
-                len_left -= (p_attr[yy].name.str_len + 8);
-              } else if ((len_left < (p_attr[yy].name.str_len + 8)) &&
-                         item_count > 0) {
-                p_data = p_item_start;
-                multi_items_add_fail = TRUE;
-                break;
-              }
-            }
+        if ((item_len_left < item_len) || !p_media->name.p_str ||
+            p_media->type > AVRC_MEDIA_TYPE_VIDEO) {
+          if (item_len_left < item_len && item_count > 0) {
+            multi_items_add_fail = true;
           }
-        } else {
-          if (len_left < item_len && item_count > 0)
-            multi_items_add_fail = TRUE;
           p_data = p_item_start;
+          break;
+        }
+        ARRAY_TO_BE_STREAM(p_data, p_media->uid, AVRC_UID_SIZE);
+        UINT8_TO_BE_STREAM(p_data, p_media->type);
+        UINT16_TO_BE_STREAM(p_data, p_media->name.charset_id);
+        UINT16_TO_BE_STREAM(p_data, p_media->name.str_len);
+        ARRAY_TO_BE_STREAM(p_data, p_media->name.p_str, p_media->name.str_len);
+        p_attr_count = p_data++;
+        *p_attr_count = 0;
+        uint16_t attribute_len_left = item_len_left - item_len;
+        p_attr = p_media->p_attr_list;
+        for (yy = 0; yy < p_media->attr_count; yy++) {
+          /* len required: 4 + 2 + 2 + str_len */
+          const uint16_t attribute_len = p_attr[yy].name.str_len + 8;
+          if (item_len_left < attribute_len || !p_attr[yy].name.p_str ||
+              AVRC_IS_VALID_MEDIA_ATTRIBUTE(p_attr[yy].attr_id)) {
+            if (attribute_len_left < attribute_len && item_count > 0) {
+              multi_items_add_fail = true;
+              p_data = p_item_start;
+              break;
+            }
+            continue;
+          }
+          (*p_attr_count)++;
+          UINT32_TO_BE_STREAM(p_data, p_attr[yy].attr_id);
+          UINT16_TO_BE_STREAM(p_data, p_attr[yy].name.charset_id);
+          UINT16_TO_BE_STREAM(p_data, p_attr[yy].name.str_len);
+          ARRAY_TO_BE_STREAM(p_data, p_attr[yy].name.p_str,
+                             p_attr[yy].name.str_len);
+          item_len += attribute_len;
+          attribute_len_left -= attribute_len;
         }
         break;
     } /* switch item_type */
@@ -1056,18 +1063,15 @@
       item_count++;
       /* fill in variable item lenth */
       UINT16_TO_BE_STREAM(p_item_len, item_len);
-    } else {
-      if (!multi_items_add_fail) {
-        /* some item is not added properly - set an error status */
-        if (len_left < item_len)
-          status = AVRC_STS_INTERNAL_ERR;
-        else
-          status = AVRC_STS_BAD_PARAM;
-      }
-    }
-    if (!multi_items_add_fail) {
-      len += item_len;
-      len += 3; /* the item_type(1) and item_len(2) */
+      len_left -= item_len + item_header_len;
+      len += item_len + item_header_len;
+    } else if (!multi_items_add_fail) {
+      /* some item is not added properly - set an error status */
+      if (item_len_left < item_len)
+        status = AVRC_STS_INTERNAL_ERR;
+      else
+        status = AVRC_STS_BAD_PARAM;
+      break;
     }
     AVRC_TRACE_DEBUG("len:%d, len_left:%d, num:%d, item_len:%d", len, len_left,
                      item_count, item_len);
diff --git a/stack/btm/btm_ble.cc b/stack/btm/btm_ble.cc
index 805a1e2..3d0228d 100644
--- a/stack/btm/btm_ble.cc
+++ b/stack/btm/btm_ble.cc
@@ -485,16 +485,21 @@
       p_dev_rec->device_type = p_inq_info->results.device_type;
       p_dev_rec->ble.ble_addr_type = p_inq_info->results.ble_addr_type;
     }
-    if (p_dev_rec->bd_addr == remote_bda && p_dev_rec->device_type != 0) {
+
+    if (p_dev_rec->bd_addr == remote_bda &&
+        p_dev_rec->ble.pseudo_addr == remote_bda) {
       *p_dev_type = p_dev_rec->device_type;
       *p_addr_type = p_dev_rec->ble.ble_addr_type;
     } else if (p_dev_rec->ble.pseudo_addr == remote_bda) {
       *p_dev_type = BT_DEVICE_TYPE_BLE;
       *p_addr_type = p_dev_rec->ble.ble_addr_type;
-    } else /* matching static adddress only */
-    {
-      LOG(ERROR) << __func__ << " device_type not set; assuming BR/EDR";
-      *p_dev_type = BT_DEVICE_TYPE_BREDR;
+    } else /* matching static adddress only */ {
+      if (p_dev_rec->device_type != BT_DEVICE_TYPE_UNKNOWN) {
+        *p_dev_type = p_dev_rec->device_type;
+      } else {
+        LOG_WARN("device_type not set; assuming BR/EDR");
+        *p_dev_type = BT_DEVICE_TYPE_BREDR;
+      }
       *p_addr_type = BLE_ADDR_PUBLIC;
     }
   }
diff --git a/stack/gatt/gatt_cl.cc b/stack/gatt/gatt_cl.cc
index 831d031..d8896b3 100644
--- a/stack/gatt/gatt_cl.cc
+++ b/stack/gatt/gatt_cl.cc
@@ -615,6 +615,7 @@
     gatt_end_operation(p_clcb, p_clcb->status, &value);
   }
 }
+
 /*******************************************************************************
  *
  * Function         gatt_process_notification
@@ -626,10 +627,10 @@
  ******************************************************************************/
 void gatt_process_notification(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code,
                                uint16_t len, uint8_t* p_data) {
-  tGATT_VALUE value;
+  tGATT_VALUE value = {};
   tGATT_REG* p_reg;
   uint16_t conn_id;
-  tGATT_STATUS encrypt_status;
+  tGATT_STATUS encrypt_status = {};
   uint8_t* p = p_data;
   uint8_t i;
   tGATTC_OPTYPE event = (op_code == GATT_HANDLE_VALUE_IND)
@@ -638,27 +639,16 @@
 
   VLOG(1) << __func__;
 
+  // Ensure our packet has enough data (2 bytes)
   if (len < GATT_NOTIFICATION_MIN_LEN) {
     LOG(ERROR) << "illegal notification PDU length, discard";
     return;
   }
 
-  memset(&value, 0, sizeof(value));
+  // Get 2 byte handle
   STREAM_TO_UINT16(value.handle, p);
 
-  if (op_code == GATT_HANDLE_MULTI_VALUE_NOTIF) {
-    STREAM_TO_UINT16(value.len, p);
-  } else {
-    value.len = len - 2;
-  }
-
-  if (value.len > GATT_MAX_ATTR_LEN) {
-    LOG(ERROR) << "value.len larger than GATT_MAX_ATTR_LEN, discard";
-    return;
-  }
-
-  STREAM_TO_ARRAY(value.value, p, value.len);
-
+  // Fail early if the GATT handle is not valid
   if (!GATT_HANDLE_IS_VALID(value.handle)) {
     /* illegal handle, send ack now */
     if (op_code == GATT_HANDLE_VALUE_IND)
@@ -666,6 +656,35 @@
     return;
   }
 
+  // Calculate value length based on opcode
+  if (op_code == GATT_HANDLE_MULTI_VALUE_NOTIF) {
+    // Ensure our packet has enough data; MIN + 2 more bytes for len value
+    if (len < GATT_NOTIFICATION_MIN_LEN + 2) {
+      LOG(ERROR) << "illegal notification PDU length, discard";
+      return;
+    }
+
+    // Allow multi value opcode to set value len from the packet
+    STREAM_TO_UINT16(value.len, p);
+
+    if (value.len > len - 4) {
+      LOG(ERROR) << "value.len (" << value.len << ") greater than length ("
+                 << (len - 4);
+      return;
+    }
+
+  } else {
+    // For single value, just use the passed in len minus opcode length (2)
+    value.len = len - 2;
+  }
+
+  // Verify the new calculated length
+  if (value.len > GATT_MAX_ATTR_LEN) {
+    LOG(ERROR) << "value.len larger than GATT_MAX_ATTR_LEN, discard";
+    return;
+  }
+
+  // Handle indications differently
   if (event == GATTC_OPTYPE_INDICATION) {
     if (tcb.ind_count) {
       /* this is an error case that receiving an indication but we
@@ -676,34 +695,67 @@
       LOG(ERROR) << __func__ << " rcv Ind. but ind_count=" << tcb.ind_count
                  << " (will reset ind_count)";
     }
+
+    // Zero out the ind_count
     tcb.ind_count = 0;
-  }
 
-  /* should notify all registered client with the handle value
-     notificaion/indication
-     Note: need to do the indication count and start timer first then do
-     callback
-   */
+    // Notify all registered clients with the handle value
+    // notification/indication
+    // Note: need to do the indication count and start timer first then do
+    // callback
+    for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) {
+      if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) tcb.ind_count++;
+    }
 
-  for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) {
-    if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb &&
-        (event == GATTC_OPTYPE_INDICATION))
-      tcb.ind_count++;
-  }
-
-  if (event == GATTC_OPTYPE_INDICATION) {
     /* start a timer for app confirmation */
-    if (tcb.ind_count > 0)
+    if (tcb.ind_count > 0) {
       gatt_start_ind_ack_timer(tcb, cid);
-    else /* no app to indicate, or invalid handle */
+    } else { /* no app to indicate, or invalid handle */
       attp_send_cl_confirmation_msg(tcb, cid);
+    }
   }
 
   encrypt_status = gatt_get_link_encrypt_status(tcb);
 
-  uint16_t rem_len = len;
-  while (rem_len) {
-    tGATT_CL_COMPLETE gatt_cl_complete;
+  STREAM_TO_ARRAY(value.value, p, value.len);
+
+  tGATT_CL_COMPLETE gatt_cl_complete;
+  gatt_cl_complete.att_value = value;
+  gatt_cl_complete.cid = cid;
+
+  for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) {
+    if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) {
+      conn_id = GATT_CREATE_CONN_ID(tcb.tcb_idx, p_reg->gatt_if);
+      (*p_reg->app_cb.p_cmpl_cb)(conn_id, event, encrypt_status,
+                                 &gatt_cl_complete);
+    }
+  }
+
+  // If this is single value, then nothing is left to do
+  if (op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) return;
+
+  // Need a signed type to check if the value is below 0
+  // as uint16_t doesn't have negatives so the negatives register as a number
+  // thus anything less than zero won't trigger the conditional and it is not
+  // always 0
+  // when done looping as value.len is arbitrary.
+  int16_t rem_len = (int16_t)len - (4 /* octets */ + value.len);
+
+  // Already streamed the first value and sent it, lets send the rest
+  while (rem_len > 4 /* octets */) {
+    // 2
+    STREAM_TO_UINT16(value.handle, p);
+    // + 2 = 4
+    STREAM_TO_UINT16(value.len, p);
+    // Accounting
+    rem_len -= 4;
+    // Make sure we don't read past the remaining data even if the length says
+    // we can Also need to watch comparing the int16_t with the uint16_t
+    value.len = std::min(rem_len, (int16_t)value.len);
+    STREAM_TO_ARRAY(value.value, p, value.len);
+    // Accounting
+    rem_len -= value.len;
+
     gatt_cl_complete.att_value = value;
     gatt_cl_complete.cid = cid;
 
@@ -714,16 +766,6 @@
                                    &gatt_cl_complete);
       }
     }
-
-    if (op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) return;
-
-    /* 4 stands for 2 octects for handle and 2 octecs for len */
-    rem_len -= (4 + value.len);
-    if (rem_len) {
-      STREAM_TO_UINT16(value.handle, p);
-      STREAM_TO_UINT16(value.len, p);
-      STREAM_TO_ARRAY(value.value, p, value.len);
-    }
   }
 }
 
diff --git a/test/mock/mock_main_shim_btm_api.cc b/test/mock/mock_main_shim_btm_api.cc
index 83aeb03..33f9672 100644
--- a/test/mock/mock_main_shim_btm_api.cc
+++ b/test/mock/mock_main_shim_btm_api.cc
@@ -244,13 +244,6 @@
   mock_function_count_map[__func__]++;
   return 0;
 }
-uint8_t bluetooth::shim::BTM_GetEirSupportedServices(uint32_t* p_eir_uuid,
-                                                     uint8_t** p,
-                                                     uint8_t max_num_uuid16,
-                                                     uint8_t* p_num_uuid16) {
-  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.cc b/test/mock/mock_stack_btm.cc
index d14af15..0dd5c65 100644
--- a/test/mock/mock_stack_btm.cc
+++ b/test/mock/mock_stack_btm.cc
@@ -18,6 +18,7 @@
  * Generated mock file from original source file
  */
 
+#include "stack/include/btm_api.h"
 #include "stack/include/btm_ble_api_types.h"
 #include "stack/include/btm_client_interface.h"
 #include "types/raw_address.h"
@@ -29,7 +30,12 @@
 void BTM_BleReadControllerFeatures(tBTM_BLE_CTRL_FEATURES_CBACK* p_vsc_cback) {}
 uint8_t BTM_GetAcceptlistSize() { return 0; }
 
-struct btm_client_interface_s btm_client_interface = {};
+struct btm_client_interface_s btm_client_interface = {
+    .eir =
+        {
+            .BTM_GetEirSupportedServices = BTM_GetEirSupportedServices,
+        },
+};
 
 struct btm_client_interface_s& get_btm_client_interface() {
   return btm_client_interface;
diff --git a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
index 654aeac..6cd3fc5 100644
--- a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
@@ -1611,12 +1611,16 @@
 }
 
 void DualModeController::LeSetAddressResolutionEnable(CommandView command) {
-    // NOP
-    auto payload =
-      std::make_unique<bluetooth::packet::RawBuilder>(std::vector<uint8_t>(
-          {static_cast<uint8_t>(bluetooth::hci::ErrorCode::SUCCESS)}));
-  send_event_(bluetooth::hci::CommandCompleteBuilder::Create(
-      kNumCommandPackets, command.GetOpCode(), std::move(payload)));
+  auto command_view = gd_hci::LeSetAddressResolutionEnableView::Create(
+      gd_hci::LeSecurityCommandView::Create(
+          gd_hci::SecurityCommandView::Create(command)));
+  ASSERT(command_view.IsValid());
+  auto status = link_layer_controller_.LeSetAddressResolutionEnable(
+      command_view.GetAddressResolutionEnable() ==
+      bluetooth::hci::Enable::ENABLED);
+  send_event_(
+      bluetooth::hci::LeSetAddressResolutionEnableCompleteBuilder::Create(
+          kNumCommandPackets, status));
 }
 
 void DualModeController::LeSetResovalablePrivateAddressTimeout(CommandView command) {
diff --git a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
index dc1ffbb..091b700 100644
--- a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
@@ -1566,8 +1566,6 @@
   auto adv_type = scan_response.GetAdvertisementType();
   auto address_type =
       static_cast<LeAdvertisement::AddressType>(scan_response.GetAddressType());
-  LOG_INFO("Scan response with %s",
-           bluetooth::hci::OpCodeText(le_scan_enable_).c_str());
   if (le_scan_enable_ == bluetooth::hci::OpCode::LE_SET_SCAN_ENABLE) {
     if (adv_type != model::packets::AdvertisementType::SCAN_RESPONSE) {
       return;
@@ -2878,6 +2876,15 @@
   return ErrorCode::SUCCESS;
 }
 
+ErrorCode LinkLayerController::LeSetAddressResolutionEnable(bool enable) {
+  if (ResolvingListBusy()) {
+    return ErrorCode::COMMAND_DISALLOWED;
+  }
+
+  le_resolving_list_enabled_ = enable;
+  return ErrorCode::SUCCESS;
+}
+
 ErrorCode LinkLayerController::LeResolvingListClear() {
   if (ResolvingListBusy()) {
     return ErrorCode::COMMAND_DISALLOWED;
diff --git a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
index 8693add..f3ab298 100644
--- a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
+++ b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
@@ -181,6 +181,7 @@
   bool LeConnectListContainsDevice(Address addr, uint8_t addr_type);
   bool LeConnectListFull();
   bool ResolvingListBusy();
+  ErrorCode LeSetAddressResolutionEnable(bool enable);
   ErrorCode LeResolvingListClear();
   ErrorCode LeResolvingListAddDevice(Address addr, uint8_t addr_type,
                                      std::array<uint8_t, kIrkSize> peerIrk,
@@ -463,6 +464,7 @@
     std::array<uint8_t, kIrkSize> local_irk;
   };
   std::vector<ResolvingListEntry> le_resolving_list_;
+  bool le_resolving_list_enabled_{false};
 
   std::array<LeAdvertiser, 7> advertisers_;