Merge "Add broadcast feature supported API"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index b396ce2..3f1b70e 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,11 +7,14 @@
 
 [Builtin Hooks Options]
 rustfmt = --config-path=rustfmt.toml
+# Only turn on clang-format check for C and C++ headers and sources
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp,hpp
 
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
                   -fw android/app/src/com/android/bluetooth/
                       android/app/lib/mapapi/com/android/bluetooth/mapapi/
                       android/app/tests/src/com/android/bluetooth/
+                      framework/
 aosp_first = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
 yapf_hook = ./system/tools/scripts/yapf_checker.py
diff --git a/android/app/AndroidManifest.xml b/android/app/AndroidManifest.xml
index b84bdf9..aa40cab 100644
--- a/android/app/AndroidManifest.xml
+++ b/android/app/AndroidManifest.xml
@@ -422,6 +422,14 @@
                 <action android:name="android.bluetooth.IBluetoothHearingAid"/>
             </intent-filter>
         </service>
+        <service android:process="@string/process"
+             android:name=".hap.HapClientService"
+             android:enabled="@bool/profile_supported_hap_client"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.bluetooth.IBluetoothHapClient"/>
+            </intent-filter>
+        </service>
         <service
             android:process="@string/process"
             android:name=".vc.VolumeControlService"
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index bbf46bb..5e3a297 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -1714,6 +1714,22 @@
   return sBluetoothInterface->get_metric_id(addr_obj);
 }
 
+static jboolean allowLowLatencyAudioNative(JNIEnv* env, jobject obj,
+                                           jboolean allowed,
+                                           jbyteArray address) {
+  ALOGV("%s", __func__);
+  if (!sBluetoothInterface) return false;
+  jbyte* addr = env->GetByteArrayElements(address, nullptr);
+  if (addr == nullptr) {
+    jniThrowIOException(env, EINVAL);
+    return false;
+  }
+  RawAddress addr_obj = {};
+  addr_obj.FromOctets((uint8_t*)addr);
+  sBluetoothInterface->allow_low_latency_audio(allowed, addr_obj);
+  return true;
+}
+
 static JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
     {"classInitNative", "()V", (void*)classInitNative},
@@ -1753,7 +1769,9 @@
     {"createSocketChannelNative", "(ILjava/lang/String;[BIII)I",
      (void*)createSocketChannelNative},
     {"requestMaximumTxDataLengthNative", "([B)V",
-     (void*)requestMaximumTxDataLengthNative}};
+     (void*)requestMaximumTxDataLengthNative},
+    {"allowLowLatencyAudioNative", "(Z[B)Z", (void*)allowLowLatencyAudioNative},
+};
 
 int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
   return jniRegisterNativeMethods(
diff --git a/android/app/jni/com_android_bluetooth_gatt.cpp b/android/app/jni/com_android_bluetooth_gatt.cpp
index fd933f4..9242de4 100644
--- a/android/app/jni/com_android_bluetooth_gatt.cpp
+++ b/android/app/jni/com_android_bluetooth_gatt.cpp
@@ -210,7 +210,8 @@
                             uint8_t secondary_phy, uint8_t advertising_sid,
                             int8_t tx_power, int8_t rssi,
                             uint16_t periodic_adv_int,
-                            std::vector<uint8_t> adv_data) {
+                            std::vector<uint8_t> adv_data,
+                            RawAddress* original_bda) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -221,10 +222,13 @@
   sCallbackEnv->SetByteArrayRegion(jb.get(), 0, adv_data.size(),
                                    (jbyte*)adv_data.data());
 
-  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult, event_type,
-                               addr_type, address.get(), primary_phy,
-                               secondary_phy, advertising_sid, tx_power, rssi,
-                               periodic_adv_int, jb.get());
+  ScopedLocalRef<jstring> original_address(
+      sCallbackEnv.get(), bdaddr2newjstr(sCallbackEnv.get(), original_bda));
+
+  sCallbackEnv->CallVoidMethod(
+      mCallbacksObj, method_onScanResult, event_type, addr_type, address.get(),
+      primary_phy, secondary_phy, advertising_sid, tx_power, rssi,
+      periodic_adv_int, jb.get(), original_address.get());
 }
 
 void btgattc_open_cb(int conn_id, int status, int clientIf,
@@ -916,10 +920,18 @@
     sCallbackEnv->SetByteArrayRegion(jb.get(), 0, adv_data.size(),
                                      (jbyte*)adv_data.data());
 
-    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult, event_type,
-                                 addr_type, address.get(), primary_phy,
-                                 secondary_phy, advertising_sid, tx_power, rssi,
-                                 periodic_adv_int, jb.get());
+    // TODO(optedoblivion): Figure out original address for here, use empty
+    // for now
+
+    // length of data + '\0'
+    char empty_address[18] = "00:00:00:00:00:00";
+    ScopedLocalRef<jstring> fake_address(
+        sCallbackEnv.get(), sCallbackEnv->NewStringUTF(empty_address));
+
+    sCallbackEnv->CallVoidMethod(
+        mCallbacksObj, method_onScanResult, event_type, addr_type,
+        address.get(), primary_phy, secondary_phy, advertising_sid, tx_power,
+        rssi, periodic_adv_int, jb.get(), fake_address.get());
   }
 
   void OnTrackAdvFoundLost(AdvertisingTrackInfo track_info) {
@@ -994,8 +1006,9 @@
       env->GetMethodID(clazz, "onClientRegistered", "(IIJJ)V");
   method_onScannerRegistered =
       env->GetMethodID(clazz, "onScannerRegistered", "(IIJJ)V");
-  method_onScanResult = env->GetMethodID(clazz, "onScanResult",
-                                         "(IILjava/lang/String;IIIIII[B)V");
+  method_onScanResult =
+      env->GetMethodID(clazz, "onScanResult",
+                       "(IILjava/lang/String;IIIIII[BLjava/lang/String;)V");
   method_onConnected =
       env->GetMethodID(clazz, "onConnected", "(IIILjava/lang/String;)V");
   method_onDisconnected =
diff --git a/android/app/res/values/config.xml b/android/app/res/values/config.xml
index a34113d..4653c91 100644
--- a/android/app/res/values/config.xml
+++ b/android/app/res/values/config.xml
@@ -37,6 +37,7 @@
     <bool name="profile_supported_mcp_server">true</bool>
     <bool name="profile_supported_csip_set_coordinator">true</bool>
     <bool name="profile_supported_le_call_control">true</bool>
+    <bool name="profile_supported_hap_client">true</bool>
 
     <!-- If true, we will require location to be enabled on the device to
          fire Bluetooth LE scan result callbacks in addition to having one
@@ -106,6 +107,7 @@
     <integer name="a2dp_source_codec_priority_aptx">3001</integer>
     <integer name="a2dp_source_codec_priority_aptx_hd">4001</integer>
     <integer name="a2dp_source_codec_priority_ldac">5001</integer>
+    <integer name="a2dp_source_codec_priority_lc3">6001</integer>
 
     <!-- For enabling the AVRCP Target Cover Artowrk feature-->
     <bool name="avrcp_target_enable_cover_art">true</bool>
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java b/android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
index c5c3e56..28c8523 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
@@ -51,6 +51,8 @@
             BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
     private @CodecPriority int mA2dpSourceCodecPriorityLdac =
             BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
+    private @CodecPriority int mA2dpSourceCodecPriorityLc3 =
+            BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
 
     private BluetoothCodecConfig[] mCodecConfigOffloading = new BluetoothCodecConfig[0];
 
@@ -231,6 +233,16 @@
             mA2dpSourceCodecPriorityLdac = value;
         }
 
+        try {
+            value = resources.getInteger(R.integer.a2dp_source_codec_priority_lc3);
+        } catch (NotFoundException e) {
+            value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
+        }
+        if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) && (value
+                < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) {
+            mA2dpSourceCodecPriorityLc3 = value;
+        }
+
         BluetoothCodecConfig codecConfig;
         BluetoothCodecConfig[] codecConfigArray =
                 new BluetoothCodecConfig[BluetoothCodecConfig.getMaxCodecType()];
@@ -259,6 +271,11 @@
                 .setCodecPriority(mA2dpSourceCodecPriorityLdac)
                 .build();
         codecConfigArray[4] = codecConfig;
+        codecConfig = new BluetoothCodecConfig.Builder()
+                .setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3)
+                .setCodecPriority(mA2dpSourceCodecPriorityLc3)
+                .build();
+        codecConfigArray[5] = codecConfig;
 
         return codecConfigArray;
     }
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 32b35ea..0780ee9 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -2647,6 +2647,16 @@
             service.dump(fd, writer, args);
             writer.close();
         }
+
+        @Override
+        public boolean allowLowLatencyAudio(boolean allowed, BluetoothDevice device) {
+            AdapterService service = getService();
+            if (service == null) {
+                return false;
+            }
+            enforceBluetoothPrivilegedPermission(service);
+            return service.allowLowLatencyAudio(allowed, device);
+        }
     }
 
     // ----API Methods--------
@@ -3771,6 +3781,11 @@
         return getResources().getInteger(R.integer.config_bluetooth_operating_voltage_mv) / 1000.0;
     }
 
+    @VisibleForTesting
+    protected RemoteDevices getRemoteDevices() {
+        return mRemoteDevices;
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         if (args.length == 0) {
@@ -4147,6 +4162,17 @@
         return getMetricIdNative(Utils.getByteAddress(device));
     }
 
+    /**
+     *  Allow audio low latency
+     *
+     *  @param allowed true if audio low latency is being allowed
+     *  @param device device whose audio low latency will be allowed or disallowed
+     *  @return boolean true if audio low latency is successfully allowed or disallowed
+     */
+    public boolean allowLowLatencyAudio(boolean allowed, BluetoothDevice device) {
+        return allowLowLatencyAudioNative(allowed, Utils.getByteAddress(device));
+    }
+
     static native void classInitNative();
 
     native boolean initNative(boolean startRestricted, boolean isCommonCriteriaMode,
@@ -4239,6 +4265,8 @@
 
     /*package*/ native void requestMaximumTxDataLengthNative(byte[] address);
 
+    private native boolean allowLowLatencyAudioNative(boolean allowed, byte[] address);
+
     // Returns if this is a mock object. This is currently used in testing so that we may not call
     // System.exit() while finalizing the object. Otherwise GC of mock objects unfortunately ends up
     // calling finalize() which in turn calls System.exit() and the process crashes.
diff --git a/android/app/src/com/android/bluetooth/btservice/Config.java b/android/app/src/com/android/bluetooth/btservice/Config.java
index 4c1eec3..abbb9f4 100644
--- a/android/app/src/com/android/bluetooth/btservice/Config.java
+++ b/android/app/src/com/android/bluetooth/btservice/Config.java
@@ -34,6 +34,7 @@
 import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
 import com.android.bluetooth.csip.CsipSetCoordinatorService;
 import com.android.bluetooth.gatt.GattService;
+import com.android.bluetooth.hap.HapClientService;
 import com.android.bluetooth.hearingaid.HearingAidService;
 import com.android.bluetooth.hfp.HeadsetService;
 import com.android.bluetooth.hfpclient.HeadsetClientService;
@@ -132,6 +133,8 @@
             new ProfileConfig(CsipSetCoordinatorService.class,
                     R.bool.profile_supported_csip_set_coordinator,
                     (1 << BluetoothProfile.CSIP_SET_COORDINATOR)),
+            new ProfileConfig(HapClientService.class, R.bool.profile_supported_hap_client,
+                    (1 << BluetoothProfile.HAP_CLIENT)),
     };
 
     private static Class[] sSupportedProfiles = new Class[0];
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index 992c6af..1b9fcfb 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -111,6 +111,28 @@
     private static final int TRUNCATED_RESULT_SIZE = 11;
     private static final int TIME_STAMP_LENGTH = 2;
 
+    private enum MatchOrigin {
+        PSEUDO_ADDRESS,
+        ORIGINAL_ADDRESS
+    }
+
+    private static class MatchResult {
+        private final boolean mMatches;
+        private final MatchOrigin mOrigin;
+        private MatchResult(boolean matches, MatchOrigin origin) {
+            this.mMatches = matches;
+            this.mOrigin = origin;
+        }
+
+        public boolean getMatches() {
+            return mMatches;
+        }
+
+        public MatchOrigin getMatchOrigin() {
+            return mOrigin;
+        }
+    }
+
     /**
      * The default floor value for LE batch scan report delays greater than 0
      */
@@ -356,7 +378,8 @@
                             }
                             for (String test : TEST_MODE_BEACONS) {
                                 onScanResultInternal(0x1b, 0x1, "DD:34:02:05:5C:4D", 1, 0, 0xff,
-                                        127, -54, 0x0, HexEncoding.decode(test));
+                                        127, -54, 0x0, HexEncoding.decode(test),
+                                        "DD:34:02:05:5C:4E");
                             }
                             sendEmptyMessageDelayed(0, DateUtils.SECOND_IN_MILLIS);
                         }
@@ -1160,23 +1183,24 @@
 
     void onScanResult(int eventType, int addressType, String address, int primaryPhy,
             int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt,
-            byte[] advData) {
+            byte[] advData, String originalAddress) {
         // When in testing mode, ignore all real-world events
         if (isTestModeEnabled()) return;
 
         onScanResultInternal(eventType, addressType, address, primaryPhy, secondaryPhy,
-                advertisingSid, txPower, rssi, periodicAdvInt, advData);
+                advertisingSid, txPower, rssi, periodicAdvInt, advData, originalAddress);
     }
 
     void onScanResultInternal(int eventType, int addressType, String address, int primaryPhy,
             int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt,
-            byte[] advData) {
+            byte[] advData, String originalAddress) {
         if (VDBG) {
             Log.d(TAG, "onScanResult() - eventType=0x" + Integer.toHexString(eventType)
                     + ", addressType=" + addressType + ", address=" + address + ", primaryPhy="
                     + primaryPhy + ", secondaryPhy=" + secondaryPhy + ", advertisingSid=0x"
                     + Integer.toHexString(advertisingSid) + ", txPower=" + txPower + ", rssi="
-                    + rssi + ", periodicAdvInt=0x" + Integer.toHexString(periodicAdvInt));
+                    + rssi + ", periodicAdvInt=0x" + Integer.toHexString(periodicAdvInt)
+                    + ", originalAddress=" + originalAddress);
         }
 
         byte[] legacyAdvData = Arrays.copyOfRange(advData, 0, 62);
@@ -1184,6 +1208,7 @@
         for (ScanClient client : mScanManager.getRegularScanQueue()) {
             ScannerMap.App app = mScannerMap.getById(client.scannerId);
             if (app == null) {
+                Log.i(TAG, "App is null; skip.");
                 continue;
             }
 
@@ -1195,6 +1220,7 @@
             if (settings.getLegacy()) {
                 if ((eventType & ET_LEGACY_MASK) == 0) {
                     // If this is legacy scan, but nonlegacy result - skip.
+                    Log.i(TAG, "Legacy scan, non legacy result; skip.");
                     continue;
                 } else {
                     // Some apps are used to fixed-size advertise data.
@@ -1212,6 +1238,7 @@
 
             if (client.hasDisavowedLocation) {
                 if (mLocationDenylistPredicate.test(result)) {
+                    Log.i(TAG, "Skipping client for location deny list");
                     continue;
                 }
             }
@@ -1232,11 +1259,22 @@
                     result = sanitized;
                 }
             }
-            if (!hasPermission || !matchesFilters(client, result)) {
+            MatchResult matchResult = matchesFilters(client, result, originalAddress);
+            if (!hasPermission || !matchResult.getMatches()) {
+                Log.i(TAG, "Skipping client: permission="
+                        + hasPermission + " matches=" + matchResult.getMatches());
                 continue;
             }
 
+            if (matchResult.getMatchOrigin() == MatchOrigin.ORIGINAL_ADDRESS) {
+                result = new ScanResult(BluetoothAdapter.getDefaultAdapter()
+                        .getRemoteDevice(originalAddress), eventType, primaryPhy, secondaryPhy,
+                        advertisingSid, txPower, rssi, periodicAdvInt, scanRecord,
+                        SystemClock.elapsedRealtimeNanos());
+            }
+
             if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_ALL_MATCHES) == 0) {
+                Log.i(TAG, "Skipping client: CALLBACK_TYPE_ALL_MATCHES");
                 continue;
             }
 
@@ -1335,16 +1373,28 @@
     }
 
     // Check if a scan record matches a specific filters.
-    private boolean matchesFilters(ScanClient client, ScanResult scanResult) {
+    private MatchResult matchesFilters(ScanClient client, ScanResult scanResult) {
+        return matchesFilters(client, scanResult, null);
+    }
+
+    // Check if a scan record matches a specific filters or original address
+    private MatchResult matchesFilters(ScanClient client, ScanResult scanResult,
+            String originalAddress) {
         if (client.filters == null || client.filters.isEmpty()) {
-            return true;
+            // TODO: Do we really wanna return true here?
+            return new MatchResult(true, MatchOrigin.PSEUDO_ADDRESS);
         }
         for (ScanFilter filter : client.filters) {
+            // Need to check the filter matches, and the original address without changing the API
             if (filter.matches(scanResult)) {
-                return true;
+                return new MatchResult(true, MatchOrigin.PSEUDO_ADDRESS);
+            }
+            if (originalAddress != null
+                    && originalAddress.equalsIgnoreCase(filter.getDeviceAddress())) {
+                return new MatchResult(true, MatchOrigin.ORIGINAL_ADDRESS);
             }
         }
-        return false;
+        return new MatchResult(false, MatchOrigin.PSEUDO_ADDRESS);
     }
 
     void onClientRegistered(int status, int clientIf, long uuidLsb, long uuidMsb)
@@ -1956,7 +2006,7 @@
         // Reconstruct the scan results.
         ArrayList<ScanResult> results = new ArrayList<ScanResult>();
         for (ScanResult scanResult : permittedResults) {
-            if (matchesFilters(client, scanResult)) {
+            if (matchesFilters(client, scanResult).getMatches()) {
                 results.add(scanResult);
             }
         }
diff --git a/android/app/src/com/android/bluetooth/gatt/ScanFilterQueue.java b/android/app/src/com/android/bluetooth/gatt/ScanFilterQueue.java
index 4572c89..8231519 100644
--- a/android/app/src/com/android/bluetooth/gatt/ScanFilterQueue.java
+++ b/android/app/src/com/android/bluetooth/gatt/ScanFilterQueue.java
@@ -181,13 +181,13 @@
             addName(filter.getDeviceName());
         }
         if (filter.getDeviceAddress() != null) {
-            byte addressType = (byte) filter.getAddressType();
-            // If addressType == iADDRESS_TYPE_PUBLIC (0) then this is the original
-            // setDeviceAddress(address) API path which provided DEVICE_TYPE_ALL (2) which might map
-            // to the stack value for address type of BTM_BLE_STATIC (2)
-            // Additionally, we shouldn't confuse device type with address type.
-            addDeviceAddress(filter.getDeviceAddress(),
-                    ((addressType == 0) ? DEVICE_TYPE_ALL : addressType), filter.getIrk());
+            /*
+             * Pass the addres type here.  This address type will be used for the resolving address,
+             * however, the host stack will force the type to 0x02 for the APCF filter in
+             * btm_ble_adv_filter.cc#BTM_LE_PF_addr_filter(...)
+             */
+            addDeviceAddress(filter.getDeviceAddress(), (byte) filter.getAddressType(),
+                    filter.getIrk());
         }
         if (filter.getServiceUuid() != null) {
             if (filter.getServiceUuidMask() == null) {
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
new file mode 100644
index 0000000..e6e5f0c
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.hap;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.IBluetoothHapClient;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.SynchronousResultReceiver;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides Bluetooth Hearing Access profile, as a service.
+ * @hide
+ */
+public class HapClientService extends ProfileService {
+    private static final boolean DBG = true;
+    private static final String TAG = "HapClientService";
+
+    private static HapClientService sHapClient;
+
+    private static synchronized void setHapClient(HapClientService instance) {
+        if (DBG) {
+            Log.d(TAG, "setHapClient(): set to: " + instance);
+        }
+        sHapClient = instance;
+    }
+
+    /**
+     * Get the HapClientService instance
+     * @return HapClientService instance
+     */
+    public static synchronized HapClientService getHapClientService() {
+        if (sHapClient == null) {
+            Log.w(TAG, "getHapClientService(): service is NULL");
+            return null;
+        }
+
+        if (!sHapClient.isAvailable()) {
+            Log.w(TAG, "getHapClientService(): service is not available");
+            return null;
+        }
+        return sHapClient;
+    }
+
+    @Override
+    protected void create() {
+        if (DBG) {
+            Log.d(TAG, "create()");
+        }
+    }
+
+    @Override
+    protected void cleanup() {
+        if (DBG) {
+            Log.d(TAG, "cleanup()");
+        }
+    }
+
+    @Override
+    protected IProfileServiceBinder initBinder() {
+        return new BluetoothHapClientBinder(this);
+    }
+
+    @Override
+    protected boolean start() {
+        if (DBG) {
+            Log.d(TAG, "start()");
+        }
+
+        if (sHapClient != null) {
+            throw new IllegalStateException("start() called twice");
+        }
+
+        // Mark service as started
+        setHapClient(this);
+
+        return true;
+    }
+
+    @Override
+    protected boolean stop() {
+        if (DBG) {
+            Log.d(TAG, "stop()");
+        }
+        if (sHapClient == null) {
+            Log.w(TAG, "stop() called before start()");
+            return true;
+        }
+
+        // Marks service as stopped
+        setHapClient(null);
+
+        return true;
+    }
+
+    @Override
+    public void dump(StringBuilder sb) {
+        super.dump(sb);
+    }
+
+    /**
+     * Binder object: must be a static class or memory leak may occur
+     */
+    @VisibleForTesting
+    static class BluetoothHapClientBinder extends IBluetoothHapClient.Stub
+            implements IProfileServiceBinder {
+        private HapClientService mService;
+
+        BluetoothHapClientBinder(HapClientService svc) {
+            mService = svc;
+        }
+
+        private HapClientService getService(AttributionSource source) {
+            if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
+                    || !Utils.checkServiceAvailable(mService, TAG)
+                    || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
+                Log.w(TAG, "Hearing Access call not allowed for non-active user");
+                return null;
+            }
+
+            if (mService != null && mService.isAvailable()) {
+                return mService;
+            }
+            return null;
+        }
+
+        @Override
+        public void cleanup() {
+            mService = null;
+        }
+
+        @Override
+        public void connect(BluetoothDevice device, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void disconnect(BluetoothDevice device, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void getConnectedDevices(AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                List<BluetoothDevice> defaultValue = new ArrayList<>();
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void getDevicesMatchingConnectionStates(int[] states,
+                AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                List<BluetoothDevice> defaultValue = new ArrayList<>();
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void getConnectionState(BluetoothDevice device, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy,
+                AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void getConnectionPolicy(BluetoothDevice device, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                int defaultValue = BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void getActivePresetIndex(BluetoothDevice device, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void getHapGroup(BluetoothDevice device, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                int defaultValue = BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void selectActivePreset(BluetoothDevice device, int presetIndex,
+                AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void groupSelectActivePreset(int groupId, int presetIndex,
+                AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void nextActivePreset(BluetoothDevice device, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void groupNextActivePreset(int groupId, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void previousActivePreset(BluetoothDevice device, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void groupPreviousActivePreset(int groupId, AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void getPresetInfo(BluetoothDevice device, int presetIndex,
+                AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void getAllPresetsInfo(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void getFeatures(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void setPresetName(BluetoothDevice device, int presetIndex, String name,
+                AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
+        public void groupSetPresetName(int groupId, int presetIndex, String name,
+                AttributionSource source, SynchronousResultReceiver receiver) {
+            try {
+                boolean defaultValue = false;
+                receiver.send(defaultValue);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pan/PanService.java b/android/app/src/com/android/bluetooth/pan/PanService.java
index 08d1a08..6ab5289 100644
--- a/android/app/src/com/android/bluetooth/pan/PanService.java
+++ b/android/app/src/com/android/bluetooth/pan/PanService.java
@@ -20,7 +20,6 @@
 import static android.Manifest.permission.TETHER_PRIVILEGED;
 
 import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothPan.LocalPanRole;
@@ -28,19 +27,15 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.IBluetoothPan;
 import android.content.AttributionSource;
-import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources.NotFoundException;
-import android.net.ConnectivityManager;
-import android.net.InetAddresses;
-import android.net.InterfaceConfiguration;
-import android.net.LinkAddress;
+import android.net.ITetheredInterfaceCallback;
+import android.net.TetheringInterface;
 import android.net.TetheringManager;
 import android.os.Handler;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
+import android.os.HandlerExecutor;
 import android.os.Message;
-import android.os.ServiceManager;
+import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.Log;
 
@@ -51,12 +46,13 @@
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
 import com.android.modules.utils.SynchronousResultReceiver;
 
-import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -74,12 +70,13 @@
     private static final int BLUETOOTH_PREFIX_LENGTH = 24;
 
     private HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
-    private ArrayList<String> mBluetoothIfaceAddresses;
     private int mMaxPanDevices;
     private String mPanIfName;
-    private String mNapIfaceAddr;
+    private boolean mIsTethering = false;
     private boolean mNativeAvailable;
+    private List<ITetheredInterfaceCallback> mBluetoothTetheringCallbacks;
 
+    private TetheringManager mTetheringManager;
     private DatabaseManager mDatabaseManager;
     @VisibleForTesting
     UserManager mUserManager;
@@ -94,6 +91,24 @@
 
     private AdapterService mAdapterService;
 
+    TetheringManager.TetheringEventCallback mTetheringCallback =
+            new TetheringManager.TetheringEventCallback() {
+                @Override
+                public void onError(TetheringInterface iface, int error) {
+                    if (mIsTethering
+                            && iface.getType() == TetheringManager.TETHERING_BLUETOOTH) {
+                        // tethering is fail because of @TetheringIfaceError error.
+                        Log.e(TAG, "Error setting up tether interface: " + error);
+                        for (Map.Entry device : mPanDevices.entrySet()) {
+                            disconnectPanNative(Utils.getByteAddress(
+                                    (BluetoothDevice) device.getKey()));
+                        }
+                        mPanDevices.clear();
+                        mIsTethering = false;
+                    }
+                }
+            };
+
     static {
         classInitNative();
     }
@@ -129,8 +144,8 @@
         mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(),
                 "DatabaseManager cannot be null when PanService starts");
 
+        mBluetoothTetheringCallbacks = new ArrayList<>();
         mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
-        mBluetoothIfaceAddresses = new ArrayList<String>();
         try {
             mMaxPanDevices = getResources().getInteger(
                     com.android.internal.R.integer.config_max_pan_devices);
@@ -142,6 +157,9 @@
 
         mUserManager = getSystemService(UserManager.class);
 
+        mTetheringManager = getSystemService(TetheringManager.class);
+        mTetheringManager.registerTetheringEventCallback(
+                new HandlerExecutor(BackgroundThread.getHandler()), mTetheringCallback);
         setPanService(this);
         mStarted = true;
 
@@ -151,6 +169,7 @@
     @Override
     protected boolean stop() {
         mAdapterService = null;
+        mTetheringManager.unregisterTetheringEventCallback(mTetheringCallback);
         mHandler.removeCallbacksAndMessages(null);
         return true;
     }
@@ -337,7 +356,8 @@
         }
 
         @Override
-        public void setBluetoothTethering(boolean value, AttributionSource source,
+        public void setBluetoothTethering(ITetheredInterfaceCallback callback,
+                boolean value, AttributionSource source,
                 SynchronousResultReceiver receiver) {
             try {
                 PanService service = getService(source);
@@ -345,7 +365,7 @@
                     Log.d(TAG, "setBluetoothTethering: " + value
                             + ", pkgName: " + source.getPackageName()
                             + ", mTetherOn: " + service.mTetherOn);
-                    service.setBluetoothTethering(value, source.getPackageName(),
+                    service.setBluetoothTethering(callback, value, source.getPackageName(),
                             source.getAttributionTag());
                 }
                 receiver.send(null);
@@ -426,8 +446,8 @@
             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
             android.Manifest.permission.TETHER_PRIVILEGED,
     })
-    void setBluetoothTethering(boolean value, final String pkgName,
-            final String callingAttributionTag) {
+    void setBluetoothTethering(ITetheredInterfaceCallback callback, boolean value,
+            final String pkgName, final String callingAttributionTag) {
         if (DBG) {
             Log.d(TAG, "setBluetoothTethering: " + value + ", mTetherOn: " + mTetherOn);
         }
@@ -440,6 +460,16 @@
         if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING) && value) {
             throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
         }
+        if (callback != null) {
+            if (value) {
+                mBluetoothTetheringCallbacks.add(callback);
+            } else {
+                mBluetoothTetheringCallbacks.remove(callback);
+            }
+        } else if (mBluetoothTetheringCallbacks.isEmpty()) {
+            Log.e(TAG, "setBluetoothTethering: " + value + ", Error: no callbacks registered.");
+            return;
+        }
         if (mTetherOn != value) {
             //drop any existing panu or pan-nap connection when changing the tethering state
             mTetherOn = value;
@@ -635,22 +665,29 @@
                     return;
                 }
                 Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE");
-                if (mNapIfaceAddr == null) {
-                    mNapIfaceAddr = startTethering(iface);
-                    if (mNapIfaceAddr == null) {
-                        Log.e(TAG, "Error seting up tether interface");
-                        mPanDevices.remove(device);
-                        disconnectPanNative(Utils.getByteAddress(device));
-                        return;
+                if (!mIsTethering) {
+                    mIsTethering = true;
+                    try {
+                        for (ITetheredInterfaceCallback cb : mBluetoothTetheringCallbacks) {
+                            cb.onAvailable(iface);
+                        }
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
                     }
                 }
             } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
                 mPanDevices.remove(device);
                 Log.i(TAG, "remote(PANU) is disconnected, Remaining connected PANU devices: "
                         + mPanDevices.size());
-                if (mNapIfaceAddr != null && mPanDevices.size() == 0) {
-                    stopTethering(iface);
-                    mNapIfaceAddr = null;
+                if (mIsTethering && mPanDevices.size() == 0) {
+                    try {
+                        for (ITetheredInterfaceCallback cb : mBluetoothTetheringCallbacks) {
+                            cb.onUnavailable();
+                        }
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                    mIsTethering = false;
                 }
             }
         } else if (mStarted) {
@@ -686,83 +723,6 @@
         sendBroadcast(intent, BLUETOOTH_CONNECT);
     }
 
-    private String startTethering(String iface) {
-        return configureBtIface(true, iface);
-    }
-
-    private String stopTethering(String iface) {
-        return configureBtIface(false, iface);
-    }
-
-    private String configureBtIface(boolean enable, String iface) {
-        Log.i(TAG, "configureBtIface: " + iface + " enable: " + enable);
-
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-        TetheringManager tm = getBaseContext().getSystemService(TetheringManager.class);
-        String[] bluetoothRegexs = tm.getTetherableBluetoothRegexs();
-
-        // bring toggle the interfaces
-        String[] currentIfaces = new String[0];
-        try {
-            currentIfaces = service.listInterfaces();
-        } catch (Exception e) {
-            Log.e(TAG, "Error listing Interfaces :" + e);
-            return null;
-        }
-
-        boolean found = false;
-        for (String currIface : currentIfaces) {
-            if (currIface.equals(iface)) {
-                found = true;
-                break;
-            }
-        }
-
-        if (!found) {
-            return null;
-        }
-
-        InterfaceConfiguration ifcg = null;
-        String address = null;
-        try {
-            ifcg = service.getInterfaceConfig(iface);
-            if (ifcg != null) {
-                InetAddress addr = null;
-                LinkAddress linkAddr = ifcg.getLinkAddress();
-                if (linkAddr == null || (addr = linkAddr.getAddress()) == null || addr.equals(
-                        InetAddresses.parseNumericAddress("0.0.0.0")) || addr.equals(
-                        InetAddresses.parseNumericAddress("::0"))) {
-                    address = BLUETOOTH_IFACE_ADDR_START;
-                    addr = InetAddresses.parseNumericAddress(address);
-                }
-
-                ifcg.setLinkAddress(new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH));
-                if (enable) {
-                    ifcg.setInterfaceUp();
-                } else {
-                    ifcg.setInterfaceDown();
-                }
-                service.setInterfaceConfig(iface, ifcg);
-
-                if (enable) {
-                    int tetherStatus = tm.tether(iface);
-                    if (tetherStatus != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
-                        Log.e(TAG, "Error tethering " + iface + " tetherStatus: " + tetherStatus);
-                        return null;
-                    }
-                } else {
-                    int untetherStatus = tm.untether(iface);
-                    Log.i(TAG, "Untethered: " + iface + " untetherStatus: " + untetherStatus);
-                }
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
-            return null;
-        }
-        return address;
-    }
-
     private List<BluetoothDevice> getConnectedPanDevices() {
         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
 
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
index e3845c8..5ad083e 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
@@ -24,6 +24,8 @@
 import android.bluetooth.BluetoothGattServerCallback;
 import android.bluetooth.BluetoothGattService;
 import android.bluetooth.BluetoothLeCall;
+import android.os.Handler;
+import android.os.Looper;
 import android.content.Context;
 import android.util.Log;
 
@@ -133,6 +135,7 @@
     private final GattCharacteristic mIncomingCallCharacteristic;
     private final GattCharacteristic mCallFriendlyNameCharacteristic;
     private BluetoothGattServerProxy mBluetoothGattServer;
+    private Handler mHandler;
     private Callback mCallback;
 
     public static abstract class Callback {
@@ -210,6 +213,7 @@
         setCallControlPointOptionalOpcodes(isLocalHoldOpcodeSupported, isJoinOpcodeSupported);
         mStatusFlagsCharacteristic.setValue(0, BluetoothGattCharacteristic.FORMAT_UINT16, 0);
         mCallback = callback;
+        mHandler = new Handler(Looper.getMainLooper());
 
         if (mBluetoothGattServer == null) {
             mBluetoothGattServer = new BluetoothGattServerProxy(mContext);
@@ -355,6 +359,10 @@
             return success;
         }
 
+        public boolean setValueNoNotify(byte[] value) {
+            return super.setValue(value);
+        }
+
         public boolean clearValue(boolean notify) {
             boolean success = super.setValue(new byte[0]);
             if (success && notify && isNotifiable()) {
@@ -409,10 +417,10 @@
             value[1] = (byte) (callIndex);
             value[2] = (byte) (requestResult);
 
-            super.setValue(value);
+            super.setValueNoNotify(value);
 
             // to avoid sending control point notification before write response
-            mContext.getMainThreadHandler().post(() -> mNotifier.notify(device, this));
+            mHandler.post(() -> mNotifier.notify(device, this));
         }
     }
 
@@ -493,12 +501,24 @@
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
         for (Map.Entry<Integer, TbsCall> entry : callsList.entrySet()) {
             TbsCall call = entry.getValue();
-            int listItemLength = Math.min(listItemLengthMax, 3 + call.getUri().getBytes().length);
+            if (call == null) {
+                Log.w(TAG, "setBearerListCurrentCalls: call is null");
+                continue;
+            }
+
+            int uri_len = 0;
+            if (call.getUri() != null) {
+                uri_len =  call.getUri().getBytes().length;
+            }
+
+            int listItemLength = Math.min(listItemLengthMax, 3 + uri_len);
             stream.write((byte) (listItemLength & 0xff));
             stream.write((byte) (entry.getKey() & 0xff));
             stream.write((byte) (call.getState() & 0xff));
             stream.write((byte) (call.getFlags() & 0xff));
-            stream.write(call.getUri().getBytes(), 0, listItemLength - 3);
+            if (uri_len > 0) {
+                stream.write(call.getUri().getBytes(), 0, listItemLength - 3);
+            }
         }
 
         return mBearerListCurrentCallsCharacteristic.setValue(stream.toByteArray());
@@ -576,9 +596,17 @@
         if (DBG) {
             Log.d(TAG, "setIncomingCall: callIndex=" + callIndex + " uri=" + uri);
         }
-        byte[] value = new byte[uri.length() + 1];
+        int uri_len = 0;
+        if (uri != null) {
+            uri_len = uri.length();
+        }
+
+        byte[] value = new byte[uri_len + 1];
         value[0] = (byte) (callIndex & 0xff);
-        System.arraycopy(uri.getBytes(), 0, value, 1, uri.length());
+
+        if (uri_len > 0) {
+            System.arraycopy(uri.getBytes(), 0, value, 1, uri_len);
+        }
 
         return mIncomingCallCharacteristic.setValue(value);
     }
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
index c3a07ba..a590a51 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
@@ -444,6 +444,11 @@
         }
 
         Integer callIndex = bearer.callIdIndexMap.remove(callId);
+        if (callIndex == null) {
+            Log.e(TAG, "callIndex: is null for callId" + callId);
+            return;
+        }
+
         TbsCall tbsCall = mCurrentCallsList.remove(callIndex);
         if (tbsCall == null) {
             Log.e(TAG, "callRemoved: no such call");
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
index 89eead8..2ecb730 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
@@ -56,7 +56,8 @@
             BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
             BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
             BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-            BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC
+            BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+            BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3
     };
 
     // Not use the default value to make sure it reads from config
@@ -65,6 +66,7 @@
     private static final int APTX_PRIORITY_DEFAULT = 5001;
     private static final int APTX_HD_PRIORITY_DEFAULT = 7001;
     private static final int LDAC_PRIORITY_DEFAULT = 9001;
+    private static final int LC3_PRIORITY_DEFAULT = 11001;
     private static final int PRIORITY_HIGH = 1000000;
 
     private static final BluetoothCodecConfig[] sCodecCapabilities = new BluetoothCodecConfig[] {
@@ -105,6 +107,14 @@
                                      | BluetoothCodecConfig.BITS_PER_SAMPLE_24
                                      | BluetoothCodecConfig.BITS_PER_SAMPLE_32,
                                      BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+                                     LC3_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100
+                                     | BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_MONO
+                                     | BluetoothCodecConfig.CHANNEL_MODE_STEREO,
                                      0, 0, 0, 0)        // Codec-specific fields
     };
 
@@ -138,6 +148,12 @@
                                      BluetoothCodecConfig.SAMPLE_RATE_96000,
                                      BluetoothCodecConfig.BITS_PER_SAMPLE_32,
                                      BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+                                     LC3_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
                                      0, 0, 0, 0)        // Codec-specific fields
     };
 
@@ -158,6 +174,8 @@
                 .thenReturn(APTX_HD_PRIORITY_DEFAULT);
         when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_ldac))
                 .thenReturn(LDAC_PRIORITY_DEFAULT);
+        when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_lc3))
+                .thenReturn(LC3_PRIORITY_DEFAULT);
 
         mA2dpCodecConfig = new A2dpCodecConfig(mMockContext, mA2dpNativeInterface);
         mTestDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
@@ -191,6 +209,9 @@
                 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
                     Assert.assertEquals(config.getCodecPriority(), LDAC_PRIORITY_DEFAULT);
                     break;
+                case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
+                    Assert.assertEquals(config.getCodecPriority(), LC3_PRIORITY_DEFAULT);
+                    break;
             }
         }
     }
@@ -219,6 +240,10 @@
         testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
+                true);
+        testCodecPriorityChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, PRIORITY_HIGH,
                 false);
     }
 
@@ -230,23 +255,27 @@
     public void testSetCodecPreference_priorityDefaultToRaiseHigh() {
         testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
-                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
                 true);
         testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
-                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
                 true);
         testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
-                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
                 true);
         testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
-                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
                 true);
         testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
-                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
+                true);
+        testCodecPriorityChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, PRIORITY_HIGH,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
                 false);
     }
 
@@ -272,6 +301,10 @@
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
                 true);
+        testCodecPriorityChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, PRIORITY_HIGH,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
+                true);
     }
 
     @Test
@@ -296,6 +329,10 @@
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
                 true);
+        testCodecPriorityChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, PRIORITY_HIGH,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+                true);
     }
 
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
index 510a3fb..94c8ee1 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
@@ -81,6 +81,8 @@
 @RunWith(AndroidJUnit4.class)
 public class AdapterServiceTest {
     private static final String TAG = AdapterServiceTest.class.getSimpleName();
+    private static final String TEST_BT_ADDR_1 = "00:11:22:33:44:55";
+    private static final String TEST_BT_ADDR_2 = "00:11:22:33:44:66";
 
     private AdapterService mAdapterService;
     private AdapterService.AdapterServiceBinder mServiceBinder;
@@ -754,6 +756,23 @@
                 obfuscatedAddress1));
     }
 
+    @Test
+    public void testAddressConsolidation() {
+        // Create device properties
+        RemoteDevices remoteDevices = mAdapterService.getRemoteDevices();
+        remoteDevices.addDeviceProperties(Utils.getBytesFromAddress((TEST_BT_ADDR_1)));
+        String identityAddress = mAdapterService.getIdentityAddress(TEST_BT_ADDR_1);
+        Assert.assertEquals(identityAddress, TEST_BT_ADDR_1);
+
+        // Trigger address consolidate callback
+        remoteDevices.addressConsolidateCallback(Utils.getBytesFromAddress(TEST_BT_ADDR_1),
+                Utils.getBytesFromAddress(TEST_BT_ADDR_2));
+
+        // Verify we can get correct identity address
+        identityAddress = mAdapterService.getIdentityAddress(TEST_BT_ADDR_1);
+        Assert.assertEquals(identityAddress, TEST_BT_ADDR_2);
+    }
+
     private static byte[] getMetricsSalt(HashMap<String, HashMap<String, String>> adapterConfig) {
         HashMap<String, String> metricsSection = adapterConfig.get("Metrics");
         if (metricsSection == null) {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java
index f2181a1..7985e44 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java
@@ -465,8 +465,8 @@
         Assert.assertTrue(Arrays.equals(characteristic.getValue(),
                 new byte[] {(byte) (requestedOpcode & 0xff), (byte) (callIndex & 0xff),
                         (byte) (result & 0xff)}));
-        verify(mMockGattServer).notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic),
-                eq(false));
+        verify(mMockGattServer, after(2000)).notifyCharacteristicChanged(eq(mCurrentDevice),
+                eq(characteristic), eq(false));
         reset(mMockGattServer);
 
         callIndex = 0x02;
@@ -477,7 +477,8 @@
         Assert.assertTrue(Arrays.equals(characteristic.getValue(),
                 new byte[] {(byte) (requestedOpcode & 0xff), (byte) (callIndex & 0xff),
                         (byte) (result & 0xff)}));
-        verify(mMockGattServer, times(0)).notifyCharacteristicChanged(any(), any(), anyBoolean());
+        verify(mMockGattServer, after(2000).times(0)).notifyCharacteristicChanged(any(), any(),
+                anyBoolean());
     }
 
     @Test
diff --git a/framework/.clang-format b/framework/.clang-format
new file mode 100644
index 0000000..03af56d
--- /dev/null
+++ b/framework/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+
+AccessModifierOffset: -4
+AlignOperands: false
+AllowShortFunctionsOnASingleLine: Inline
+AlwaysBreakBeforeMultilineStrings: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerIndentWidth: 6
+ContinuationIndentWidth: 8
+IndentWidth: 4
+PenaltyBreakBeforeFirstCallParameter: 100000
+SpacesBeforeTrailingComments: 1
diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java
index 9a4151a..2ee171b 100644
--- a/framework/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java
@@ -38,14 +38,10 @@
  */
 public final class BluetoothCodecConfig implements Parcelable {
     /** @hide */
-    @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = {
-            SOURCE_CODEC_TYPE_SBC,
-            SOURCE_CODEC_TYPE_AAC,
-            SOURCE_CODEC_TYPE_APTX,
-            SOURCE_CODEC_TYPE_APTX_HD,
-            SOURCE_CODEC_TYPE_LDAC,
-            SOURCE_CODEC_TYPE_INVALID
-    })
+    @IntDef(prefix = "SOURCE_CODEC_TYPE_",
+        value = {SOURCE_CODEC_TYPE_SBC, SOURCE_CODEC_TYPE_AAC, SOURCE_CODEC_TYPE_APTX,
+            SOURCE_CODEC_TYPE_APTX_HD, SOURCE_CODEC_TYPE_LDAC, SOURCE_CODEC_TYPE_LC3,
+            SOURCE_CODEC_TYPE_INVALID})
     @Retention(RetentionPolicy.SOURCE)
     public @interface SourceCodecType {}
 
@@ -76,6 +72,11 @@
     public static final int SOURCE_CODEC_TYPE_LDAC = 4;
 
     /**
+     * Source codec type LC3.
+     */
+    public static final int SOURCE_CODEC_TYPE_LC3 = 5;
+
+    /**
      * Source codec type invalid. This is the default value used for codec
      * type.
      */
@@ -85,7 +86,7 @@
      * Represents the count of valid source codec types. Can be accessed via
      * {@link #getMaxCodecType}.
      */
-    private static final int SOURCE_CODEC_TYPE_MAX = 5;
+    private static final int SOURCE_CODEC_TYPE_MAX = 6;
 
     /** @hide */
     @IntDef(prefix = "CODEC_PRIORITY_", value = {
@@ -460,6 +461,8 @@
                 return "aptX HD";
             case SOURCE_CODEC_TYPE_LDAC:
                 return "LDAC";
+            case SOURCE_CODEC_TYPE_LC3:
+              return "LC3";
             case SOURCE_CODEC_TYPE_INVALID:
                 return "INVALID CODEC";
             default:
@@ -664,9 +667,10 @@
         switch (mCodecType) {
             case SOURCE_CODEC_TYPE_AAC:
             case SOURCE_CODEC_TYPE_LDAC:
-                if (mCodecSpecific1 != other.mCodecSpecific1) {
-                    return false;
-                }
+            case SOURCE_CODEC_TYPE_LC3:
+              if (mCodecSpecific1 != other.mCodecSpecific1) {
+                return false;
+              }
             default:
                 return true;
         }
diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java
index 1edf5cc..70971a0 100644
--- a/framework/java/android/bluetooth/BluetoothDevice.java
+++ b/framework/java/android/bluetooth/BluetoothDevice.java
@@ -2828,4 +2828,34 @@
     public static @MetadataKey int getMaxMetadataKey() {
         return METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD;
     }
+
+    /**
+     * Enable or disable audio low latency for this {@link BluetoothDevice}.
+     *
+     * @param allowed true if low latency is allowed, false if low latency is disallowed.
+     * @return true if the value is successfully set,
+     * false if there is a error when setting the value.
+     * @hide
+     */
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+    })
+    public boolean setLowLatencyAudioAllowed(boolean allowed) {
+        final IBluetooth service = sService;
+        Log.i(TAG, "Allowing bluetooth audio low latency: " + allowed);
+        if (service == null) {
+            Log.e(TAG, "Bluetooth is not enabled. Cannot allow low latency");
+            return false;
+        }
+        try {
+            service.allowLowLatencyAudio(allowed, this);
+        } catch (RemoteException e) {
+            Log.e(TAG, "allowLowLatencyAudio fail ", e);
+            e.rethrowFromSystemServer();
+        }
+        return true;
+    }
 }
diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java
index 15db686..02df543 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudio.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudio.java
@@ -25,6 +25,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
 import android.content.AttributionSource;
@@ -243,6 +244,13 @@
     public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
 
     /**
+     * This represents an invalid audio location.
+     *
+     * @hide
+     */
+    public static final int AUDIO_LOCATION_INVALID = -1;
+
+    /**
      * Contains group id.
      * @hide
      */
@@ -723,6 +731,39 @@
     }
 
     /**
+     * Get the audio location for the device. The return value is a bit field. The bit definition
+     * is included in Bluetooth SIG Assigned Numbers - Generic Audio - Audio Location Definitions.
+     * ex. Front Left: 0x00000001
+     *     Front Right: 0x00000002
+     *     Front Left | Front Right: 0x00000003
+     *
+     * @param device the bluetooth device
+     * @return The bit field of audio location for the device, if bluetooth is off, return
+     * AUDIO_LOCATION_INVALID.
+     *
+     * @hide
+     */
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED
+    })
+    @SystemApi
+    public int getAudioLocation(@NonNull BluetoothDevice device) {
+        if (VDBG) log("getAudioLocation()");
+        final IBluetoothLeAudio service = getService();
+        final int defaultLocation = AUDIO_LOCATION_INVALID;
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        } else if (mAdapter.isEnabled()) {
+            //TODO: add the implementation.
+            if (VDBG) log("getAudioLocation() from LE audio service");
+        }
+        return defaultLocation;
+    }
+
+    /**
      * Set connection policy of the profile
      *
      * <p> The device should already be paired.
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
index b866cce..c6d161e 100644
--- a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
@@ -77,12 +77,28 @@
     public static final int BASS_STATUS_NO_EMPTY_SLOT = 0x09;
     public static final int BASS_STATUS_INVALID_GROUP_OP = 0x10;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            BluetoothProfile.STATE_CONNECTED,
+            BluetoothProfile.STATE_CONNECTING,
+            BluetoothProfile.STATE_DISCONNECTED,
+            BluetoothProfile.STATE_DISCONNECTING
+    })
+    public @interface ConnectionStateValues {}
+
+    /**
+     * Callback invoked when the connection state for an LE Audio Broadcast Sink changes
+     */
+    public void onConnectionStateChange(@ConnectionStateValues int prevState,
+            @ConnectionStateValues int newState) {}
+
     /**
      * Callback invoked when a new LE Audio Broadcast Source is found.
      *
      * @param result {@link ScanResult} scan result representing a Broadcast Source
      */
-    public void onBluetoothLeBroadcastSourceFound(@NonNull ScanResult result) {}
+    public void onSourceFound(@NonNull ScanResult result) {}
 
     /**
      * Callback invoked when the Broadcast Assistant synchronizes with Periodic Advertisements (PAs)
@@ -90,7 +106,7 @@
      *
      * @param source the selected Broadcast Source
      */
-    public void onBluetoothLeBroadcastSourceSelected(
+    public void onSourceSelected(
             @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
 
     /**
@@ -99,7 +115,7 @@
      *
      * @param source the Broadcast Source with which synchronization was lost
      */
-    public void onBluetoothLeBroadcastSourceLost(
+    public void onSourceLost(
             @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
 
     /**
@@ -109,7 +125,7 @@
      * @param sink Scan Delegator device on which a new Broadcast Source has been added
      * @param source the added Broadcast Source
      */
-    public void onBluetoothLeBroadcastSourceAdded(
+    public void onSourceAdded(
             @NonNull BluetoothDevice sink,
             @NonNull BluetoothLeBroadcastSourceInfo source,
             @BassStatus int status) {}
@@ -121,7 +137,7 @@
      * @param sink Scan Delegator device on which a Broadcast Source has been updated
      * @param source the updated Broadcast Source
      */
-    public void onBluetoothLeBroadcastSourceUpdated(
+    public void onSourceUpdated(
             @NonNull BluetoothDevice sink,
             @NonNull BluetoothLeBroadcastSourceInfo source,
             @BassStatus int status) {}
@@ -133,7 +149,7 @@
      * @param sink Scan Delegator device from which a Broadcast Source has been removed
      * @param source the removed Broadcast Source
      */
-    public void onBluetoothLeBroadcastSourceRemoved(
+    public void onSourceRemoved(
             @NonNull BluetoothDevice sink,
             @NonNull BluetoothLeBroadcastSourceInfo source,
             @BassStatus int status) {}
diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java
index d4ad4ef..4641024 100644
--- a/framework/java/android/bluetooth/BluetoothPan.java
+++ b/framework/java/android/bluetooth/BluetoothPan.java
@@ -16,10 +16,12 @@
 
 package android.bluetooth;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 import static android.bluetooth.BluetoothUtils.getSyncTimeout;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -30,6 +32,9 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.AttributionSource;
 import android.content.Context;
+import android.net.ITetheredInterfaceCallback;
+import android.net.TetheringManager.TetheredInterfaceCallback;
+import android.net.TetheringManager.TetheredInterfaceRequest;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -41,6 +46,8 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -183,6 +190,49 @@
      */
     public static final int PAN_OPERATION_SUCCESS = 1004;
 
+    /**
+     * Request class used by Tethering to notify that the interface is closed.
+     *
+     * @see #requestTetheredInterface
+     * @hide
+     */
+    public class BluetoothTetheredInterfaceRequest implements TetheredInterfaceRequest {
+        private IBluetoothPan mService;
+        private ITetheredInterfaceCallback mCb;
+
+        private BluetoothTetheredInterfaceRequest(@NonNull IBluetoothPan service,
+                @NonNull ITetheredInterfaceCallback cb) {
+            this.mService = service;
+            this.mCb = cb;
+        }
+
+        /**
+         * Called when the Tethering interface has been released.
+         */
+        @RequiresPermission(allOf = {
+                android.Manifest.permission.BLUETOOTH_CONNECT,
+                android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+                android.Manifest.permission.TETHER_PRIVILEGED,
+        })
+        @Override
+        public void release() {
+            if (mService == null || mCb == null) {
+                throw new IllegalStateException(
+                        "The tethered interface has already been released.");
+            }
+            try {
+                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+                mService.setBluetoothTethering(mCb, false, mAttributionSource, recv);
+                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+            } catch (RemoteException | TimeoutException e) {
+                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+            } finally {
+                mService = null;
+                mCb = null;
+            }
+        }
+    }
+
     private final Context mContext;
 
     private final BluetoothAdapter mAdapter;
@@ -450,9 +500,12 @@
     }
 
     /**
-     * Turns on/off bluetooth tethering
+     * Turns on/off bluetooth tethering.
      *
      * @param value is whether to enable or disable bluetooth tethering
+     *
+     * @deprecated Use {@link #requestTetheredInterface} with
+     *             {@link TetheredInterfaceCallback} instead.
      * @hide
      */
     @SystemApi
@@ -462,6 +515,7 @@
             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
             android.Manifest.permission.TETHER_PRIVILEGED,
     })
+    @Deprecated
     public void setBluetoothTethering(boolean value) {
         String pkgName = mContext.getOpPackageName();
         if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
@@ -472,7 +526,7 @@
         } else if (isEnabled()) {
             try {
                 final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setBluetoothTethering(value, mAttributionSource, recv);
+                service.setBluetoothTethering(null, value, mAttributionSource, recv);
                 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
             } catch (RemoteException | TimeoutException e) {
                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
@@ -481,6 +535,66 @@
     }
 
     /**
+     * Turns on Bluetooth tethering.
+     *
+     * <p>When one or more devices are connected, the PAN service will trigger
+     * {@link TetheredInterfaceCallback#onAvailable} to inform the caller that
+     * it is ready to tether. On the contrary, when all devices have been disconnected,
+     * the PAN service will trigger {@link TetheredInterfaceCallback#onUnavailable}.
+     * <p>To turn off Bluetooth tethering, the caller must use
+     * {@link TetheredInterfaceRequest#release} method.
+     *
+     * @param executor thread to execute callback methods
+     * @param callback is the tethering callback to indicate PAN service is ready
+     *                 or not to tether to one or more devices
+     *
+     * @return new instance of {@link TetheredInterfaceRequest} which can be
+     *         used to turn off Bluetooth tethering or {@code null} if service
+     *         is not enabled
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+            android.Manifest.permission.TETHER_PRIVILEGED,
+    })
+    @Nullable
+    public TetheredInterfaceRequest requestTetheredInterface(
+            @NonNull final Executor executor,
+            @NonNull final TetheredInterfaceCallback callback) {
+        Objects.requireNonNull(callback, "Callback must be non-null");
+        Objects.requireNonNull(executor, "Executor must be non-null");
+        final IBluetoothPan service = getService();
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        } else if (isEnabled()) {
+            final ITetheredInterfaceCallback cbInternal = new ITetheredInterfaceCallback.Stub() {
+                @Override
+                public void onAvailable(String iface) {
+                    executor.execute(() -> callback.onAvailable(iface));
+                }
+
+                @Override
+                public void onUnavailable() {
+                    executor.execute(() -> callback.onUnavailable());
+                }
+            };
+            try {
+                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+                service.setBluetoothTethering(cbInternal, true, mAttributionSource, recv);
+                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+                return new BluetoothTetheredInterfaceRequest(service, cbInternal);
+            } catch (RemoteException | TimeoutException e) {
+                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+            }
+        }
+        return null;
+    }
+
+    /**
      * Determines whether tethering is enabled
      *
      * @return true if tethering is on, false if not or some error occurred
diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java
index d0f74e9..1fa2bcf 100644
--- a/framework/java/android/bluetooth/BluetoothProfile.java
+++ b/framework/java/android/bluetooth/BluetoothProfile.java
@@ -246,13 +246,19 @@
      */
     int LE_CALL_CONTROL = 27;
 
+    /*
+     * Hearing Access Profile Client
+     *
+     */
+    int HAP_CLIENT = 28;
+
     /**
      * Max profile ID. This value should be updated whenever a new profile is added to match
      * the largest value assigned to a profile.
      *
      * @hide
      */
-    int MAX_PROFILE_ID = 27;
+    int MAX_PROFILE_ID = 28;
 
     /**
      * Default priority for devices that we try to auto-connect to and
@@ -452,6 +458,8 @@
                 return "HEARING_AID";
             case LE_AUDIO:
                 return "LE_AUDIO";
+            case HAP_CLIENT:
+                return "HAP_CLIENT";
             default:
                 return "UNKNOWN_PROFILE";
         }
diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java
index 2a8ff51..5eee498 100644
--- a/framework/java/android/bluetooth/BluetoothUuid.java
+++ b/framework/java/android/bluetooth/BluetoothUuid.java
@@ -158,6 +158,12 @@
     /** @hide */
     @NonNull
     @SystemApi
+    /* FIXME: Not known yet, using a placeholder instead. */
+    public static final ParcelUuid HAS =
+            ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid LE_AUDIO =
             ParcelUuid.fromString("0000184E-0000-1000-8000-00805F9B34FB");
     /** @hide */
diff --git a/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java
index bd55426..824ac53 100644
--- a/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java
+++ b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java
@@ -27,56 +27,77 @@
  * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
  */
 public class BluetoothCodecConfigTest extends TestCase {
-    private static final int[] kCodecTypeArray = new int[] {
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID,
-    };
-    private static final int[] kCodecPriorityArray = new int[] {
-        BluetoothCodecConfig.CODEC_PRIORITY_DISABLED,
-        BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-        BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
-    };
-    private static final int[] kSampleRateArray = new int[] {
-        BluetoothCodecConfig.SAMPLE_RATE_NONE,
-        BluetoothCodecConfig.SAMPLE_RATE_44100,
-        BluetoothCodecConfig.SAMPLE_RATE_48000,
-        BluetoothCodecConfig.SAMPLE_RATE_88200,
-        BluetoothCodecConfig.SAMPLE_RATE_96000,
-        BluetoothCodecConfig.SAMPLE_RATE_176400,
-        BluetoothCodecConfig.SAMPLE_RATE_192000,
-    };
-    private static final int[] kBitsPerSampleArray = new int[] {
-        BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
-        BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-        BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-        BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-    };
-    private static final int[] kChannelModeArray = new int[] {
-        BluetoothCodecConfig.CHANNEL_MODE_NONE,
-        BluetoothCodecConfig.CHANNEL_MODE_MONO,
-        BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-    };
-    private static final long[] kCodecSpecific1Array = new long[] { 1000, 1001, 1002, 1003, };
-    private static final long[] kCodecSpecific2Array = new long[] { 2000, 2001, 2002, 2003, };
-    private static final long[] kCodecSpecific3Array = new long[] { 3000, 3001, 3002, 3003, };
-    private static final long[] kCodecSpecific4Array = new long[] { 4000, 4001, 4002, 4003, };
+  private static final int[] kCodecTypeArray = new int[] {
+      BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+      BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+      BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+      BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+      BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+      BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+      BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID,
+  };
+  private static final int[] kCodecPriorityArray = new int[] {
+      BluetoothCodecConfig.CODEC_PRIORITY_DISABLED,
+      BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+      BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
+  };
+  private static final int[] kSampleRateArray = new int[] {
+      BluetoothCodecConfig.SAMPLE_RATE_NONE,
+      BluetoothCodecConfig.SAMPLE_RATE_44100,
+      BluetoothCodecConfig.SAMPLE_RATE_48000,
+      BluetoothCodecConfig.SAMPLE_RATE_88200,
+      BluetoothCodecConfig.SAMPLE_RATE_96000,
+      BluetoothCodecConfig.SAMPLE_RATE_176400,
+      BluetoothCodecConfig.SAMPLE_RATE_192000,
+  };
+  private static final int[] kBitsPerSampleArray = new int[] {
+      BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+      BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+      BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+      BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+  };
+  private static final int[] kChannelModeArray = new int[] {
+      BluetoothCodecConfig.CHANNEL_MODE_NONE,
+      BluetoothCodecConfig.CHANNEL_MODE_MONO,
+      BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+  };
+  private static final long[] kCodecSpecific1Array = new long[] {
+      1000,
+      1001,
+      1002,
+      1003,
+  };
+  private static final long[] kCodecSpecific2Array = new long[] {
+      2000,
+      2001,
+      2002,
+      2003,
+  };
+  private static final long[] kCodecSpecific3Array = new long[] {
+      3000,
+      3001,
+      3002,
+      3003,
+  };
+  private static final long[] kCodecSpecific4Array = new long[] {
+      4000,
+      4001,
+      4002,
+      4003,
+  };
 
-    private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length *
-        kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length *
-        kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length *
-        kCodecSpecific4Array.length;
+  private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length
+      * kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length
+      * kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length
+      * kCodecSpecific4Array.length;
 
-    private int selectCodecType(int configId) {
-        int left = kCodecTypeArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecTypeArray.length;
-        return kCodecTypeArray[index];
-    }
+  private int selectCodecType(int configId) {
+    int left = kCodecTypeArray.length;
+    int right = kTotalConfigs / left;
+    int index = configId / right;
+    index = index % kCodecTypeArray.length;
+    return kCodecTypeArray[index];
+  }
 
     private int selectCodecPriority(int configId) {
         int left = kCodecTypeArray.length * kCodecPriorityArray.length;
@@ -193,6 +214,9 @@
             if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) {
                 assertEquals("LDAC", bcc.getCodecName());
             }
+            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) {
+              assertEquals("LC3", bcc.getCodecName());
+            }
             if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
                 assertEquals("INVALID CODEC", bcc.getCodecName());
             }
diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java
index 409025b..6b7a75a 100644
--- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java
@@ -23,6 +23,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioManager;
+import android.net.TetheringManager;
+import android.net.TetheringManager.TetheredInterfaceCallback;
+import android.net.TetheringManager.TetheredInterfaceRequest;
 import android.os.Environment;
 import android.util.Log;
 
@@ -407,6 +410,8 @@
     private BluetoothPan mPan = null;
     private BluetoothMapClient mMce = null;
     private String mMsgHandle = null;
+    private TetheredInterfaceCallback mPanCallback = null;
+    private TetheredInterfaceRequest mBluetoothIfaceRequest;
 
     /**
      * Creates a utility instance for testing Bluetooth.
@@ -740,7 +745,17 @@
         assertNotNull(mPan);
 
         long start = System.currentTimeMillis();
-        mPan.setBluetoothTethering(true);
+        mPanCallback = new TetheringManager.TetheredInterfaceCallback() {
+                    @Override
+                    public void onAvailable(String iface) {
+                    }
+
+                    @Override
+                    public void onUnavailable() {
+                    }
+                };
+        mBluetoothIfaceRequest = mPan.requestTetheredInterface(mContext.getMainExecutor(),
+                mPanCallback);
         long stop = System.currentTimeMillis();
         assertTrue(mPan.isTetheringOn());
 
@@ -758,7 +773,10 @@
         assertNotNull(mPan);
 
         long start = System.currentTimeMillis();
-        mPan.setBluetoothTethering(false);
+        if (mBluetoothIfaceRequest != null) {
+            mBluetoothIfaceRequest.release();
+            mBluetoothIfaceRequest = null;
+        }
         long stop = System.currentTimeMillis();
         assertFalse(mPan.isTetheringOn());
 
diff --git a/service/.clang-format b/service/.clang-format
new file mode 100644
index 0000000..9f6d7a9
--- /dev/null
+++ b/service/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+
+AccessModifierOffset: -4
+AlignOperands: false
+AllowShortFunctionsOnASingleLine: Inline
+AlwaysBreakBeforeMultilineStrings: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerIndentWidth: 6
+ContinuationIndentWidth: 8
+IndentWidth: 4
+PenaltyBreakBeforeFirstCallParameter: 100000
+SpacesBeforeTrailingComments: 1#
diff --git a/system/binder/Android.bp b/system/binder/Android.bp
index 1391206..a5b5cda 100644
--- a/system/binder/Android.bp
+++ b/system/binder/Android.bp
@@ -24,6 +24,7 @@
         "android/bluetooth/IBluetoothProfileServiceConnection.aidl",
         "android/bluetooth/IBluetoothHeadset.aidl",
         "android/bluetooth/IBluetoothHearingAid.aidl",
+        "android/bluetooth/IBluetoothHapClient.aidl",
         "android/bluetooth/IBluetoothVolumeControl.aidl",
         "android/bluetooth/IBluetoothHidHost.aidl",
         "android/bluetooth/IBluetoothLeAudio.aidl",
diff --git a/system/binder/android/bluetooth/BluetoothHapPresetInfo.aidl b/system/binder/android/bluetooth/BluetoothHapPresetInfo.aidl
new file mode 100644
index 0000000..06f205a
--- /dev/null
+++ b/system/binder/android/bluetooth/BluetoothHapPresetInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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 BluetoothHapPresetInfo;
diff --git a/system/binder/android/bluetooth/IBluetooth.aidl b/system/binder/android/bluetooth/IBluetooth.aidl
index a21fc07..97e2c59 100644
--- a/system/binder/android/bluetooth/IBluetooth.aidl
+++ b/system/binder/android/bluetooth/IBluetooth.aidl
@@ -255,4 +255,7 @@
     boolean canBondWithoutDialog(in BluetoothDevice device, in AttributionSource attributionSource);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     void generateLocalOobData(in int transport, IBluetoothOobDataCallback callback, in AttributionSource attributionSource);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    boolean allowLowLatencyAudio(in boolean allowed, in BluetoothDevice device);
 }
diff --git a/system/binder/android/bluetooth/IBluetoothHapClient.aidl b/system/binder/android/bluetooth/IBluetoothHapClient.aidl
new file mode 100644
index 0000000..672ab40
--- /dev/null
+++ b/system/binder/android/bluetooth/IBluetoothHapClient.aidl
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.content.AttributionSource;
+
+import com.android.modules.utils.SynchronousResultReceiver;
+
+/**
+ * APIs for Bluetooth Hearing Access Profile client
+ *
+ * @hide
+ */
+oneway interface IBluetoothHapClient {
+    // Public API
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void connect(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void disconnect(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getConnectedDevices(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getDevicesMatchingConnectionStates(in int[] states, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getConnectionState(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void setConnectionPolicy(in BluetoothDevice device, int connectionPolicy, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getConnectionPolicy(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getHapGroup(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getActivePresetIndex(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void selectActivePreset(in BluetoothDevice device, int presetIndex, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void groupSelectActivePreset(int groupId, int presetIndex, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void nextActivePreset(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void groupNextActivePreset(int groupId, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void previousActivePreset(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void groupPreviousActivePreset(int groupId, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getPresetInfo(in BluetoothDevice device, int presetIndex, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getAllPresetsInfo(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getFeatures(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void setPresetName(in BluetoothDevice device, int presetIndex, in String name, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void groupSetPresetName(int groupId, int presetIndex, in String name, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+
+    const int PRESET_INDEX_UNAVAILABLE = 0;
+    const int GROUP_ID_UNAVAILABLE = -1;
+
+    const int STATUS_SET_NAME_NOT_ALLOWED = 1;
+    const int STATUS_OPERATION_NOT_SUPPORTED = 2;
+    const int STATUS_OPERATION_NOT_POSSIBLE = 3;
+    const int STATUS_INVALID_PRESET_NAME_LENGTH = 4;
+    const int STATUS_INVALID_PRESET_INDEX = 5;
+    const int STATUS_GROUP_OPERATION_NOT_SUPPORTED = 6;
+    const int STATUS_PROCEDURE_ALREADY_IN_PROGRESS = 7;
+
+    const int FEATURE_BIT_NUM_TYPE_MONAURAL = 0;
+    const int FEATURE_BIT_NUM_TYPE_BANDED = 1;
+    const int FEATURE_BIT_NUM_SYNCHRONIZATED_PRESETS = 2;
+    const int FEATURE_BIT_NUM_INDEPENDENT_PRESETS = 3;
+    const int FEATURE_BIT_NUM_DYNAMIC_PRESETS = 4;
+    const int FEATURE_BIT_NUM_WRITABLE_PRESETS = 5;
+
+    const int PRESET_INFO_REASON_ALL_PRESET_INFO = 0;
+    const int PRESET_INFO_REASON_PRESET_INFO_UPDATE = 1;
+    const int PRESET_INFO_REASON_PRESET_DELETED = 2;
+    const int PRESET_INFO_REASON_PRESET_AVAILABILITY_CHANGED = 3;
+    const int PRESET_INFO_REASON_PRESET_INFO_REQUEST_RESPONSE = 4;
+}
diff --git a/system/binder/android/bluetooth/IBluetoothPan.aidl b/system/binder/android/bluetooth/IBluetoothPan.aidl
index 1ba88ee..31dbebd 100644
--- a/system/binder/android/bluetooth/IBluetoothPan.aidl
+++ b/system/binder/android/bluetooth/IBluetoothPan.aidl
@@ -18,6 +18,7 @@
 
 import android.bluetooth.BluetoothDevice;
 import android.content.AttributionSource;
+import android.net.ITetheredInterfaceCallback;
 
 import com.android.modules.utils.SynchronousResultReceiver;
 
@@ -31,7 +32,7 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
     void isTetheringOn(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED,android.Manifest.permission.TETHER_PRIVILEGED})")
-    void setBluetoothTethering(boolean value, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    void setBluetoothTethering(ITetheredInterfaceCallback callback, boolean value, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     void connect(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
diff --git a/system/blueberry/tests/gd/cert/gd_base_test.py b/system/blueberry/tests/gd/cert/gd_base_test.py
index c3a36d7..da5b943 100644
--- a/system/blueberry/tests/gd/cert/gd_base_test.py
+++ b/system/blueberry/tests/gd/cert/gd_base_test.py
@@ -39,8 +39,26 @@
 from mobly import base_test
 
 
+class Timeout:
+
+    def __init__(self, seconds=1, error_message='Timeout'):
+        self.seconds = seconds
+        self.error_message = error_message
+
+    def handle_timeout(self, signum, frame):
+        raise TimeoutError(self.error_message)
+
+    def __enter__(self):
+        signal.signal(signal.SIGALRM, self.handle_timeout)
+        signal.alarm(self.seconds)
+
+    def __exit__(self, type, value, traceback):
+        signal.alarm(0)
+
+
 class GdBaseTestClass(base_test.BaseTestClass):
 
+    FUNCTION_CALL_TIMEOUT_SECONDS = 5
     SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10
 
     def setup_class(self, dut_module, cert_module):
@@ -117,12 +135,15 @@
     def teardown_test(self):
         stack = ""
         try:
-            stack = "CERT"
-            self.cert.rootservice.StopStack(facade_rootservice.StopStackRequest())
-            stack = "DUT"
-            self.dut.rootservice.StopStack(facade_rootservice.StopStackRequest())
+            with Timeout(seconds=self.FUNCTION_CALL_TIMEOUT_SECONDS):
+                stack = "CERT"
+                self.cert.rootservice.StopStack(facade_rootservice.StopStackRequest())
+                stack = "DUT"
+                self.dut.rootservice.StopStack(facade_rootservice.StopStackRequest())
         except RpcError as rpc_error:
             asserts.fail("Failed to stop {} stack, RpcError={!r}".format(stack, rpc_error))
+        except TimeoutError:
+            logging.error("Failed to stop {} stack in {} s".format(stack, self.FUNCTION_CALL_TIMEOUT_SECONDS))
         finally:
             # Destroy GD device objects
             self._controller_manager.unregister_controllers()
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 3058155..9b791bf 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -486,6 +486,7 @@
         "packages/modules/Bluetooth/system/stack/include",
     ],
     srcs : [
+        ":TestStubOsi",
         ":TestMockBtaLeAudioHalVerifier",
         "gatt/database.cc",
         "gatt/database_builder.cc",
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index e0d40c5..fceaed7 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -25,6 +25,8 @@
 
 #define LOG_TAG "bt_bta_dm"
 
+#include <base/logging.h>
+
 #include <cstdint>
 
 #include "bta/dm/bta_dm_int.h"
@@ -44,6 +46,7 @@
 #include "osi/include/fixed_queue.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
+#include "stack/btm/btm_ble_int.h"
 #include "stack/btm/btm_sec.h"
 #include "stack/btm/neighbor_inquiry.h"
 #include "stack/gatt/connection_manager.h"
@@ -56,8 +59,6 @@
 #include "types/bluetooth/uuid.h"
 #include "types/raw_address.h"
 
-#include <base/logging.h>
-
 #if (GAP_INCLUDED == TRUE)
 #include "gap_api.h"
 #endif
@@ -1809,6 +1810,10 @@
   uint16_t service_class;
 
   result.inq_res.bd_addr = p_inq->remote_bd_addr;
+
+  // Pass the original address to GattService#onScanResult
+  result.inq_res.original_bda = p_inq->original_bda;
+
   memcpy(result.inq_res.dev_class, p_inq->dev_class, DEV_CLASS_LEN);
   BTM_COD_SERVICE_CLASS(service_class, p_inq->dev_class);
   result.inq_res.is_limited =
@@ -3312,6 +3317,7 @@
   APPL_TRACE_DEBUG("bta_dm_observe_results_cb");
 
   result.inq_res.bd_addr = p_inq->remote_bd_addr;
+  result.inq_res.original_bda = p_inq->original_bda;
   result.inq_res.rssi = p_inq->rssi;
   result.inq_res.ble_addr_type = p_inq->ble_addr_type;
   result.inq_res.inq_result_type = p_inq->inq_result_type;
diff --git a/system/bta/hearing_aid/hearing_aid.cc b/system/bta/hearing_aid/hearing_aid.cc
index 738cda3..9a0b3fd 100644
--- a/system/bta/hearing_aid/hearing_aid.cc
+++ b/system/bta/hearing_aid/hearing_aid.cc
@@ -384,7 +384,6 @@
       return;
     }
 
-    hearingDevice->connecting_actively = false;
     hearingDevice->conn_id = conn_id;
 
     /* We must update connection parameters one at a time, otherwise anchor
@@ -962,6 +961,7 @@
       send_state_change_to_other_side(hearingDevice, inform_conn_state);
     }
 
+    hearingDevice->connecting_actively = false;
     hearingDevice->accepting_audio = true;
     LOG(INFO) << __func__ << ": address=" << address
               << ", hi_sync_id=" << loghex(hearingDevice->hi_sync_id)
@@ -1327,15 +1327,12 @@
         break;
       }
 
-      // TODO: handle properly!
       case GAP_EVT_CONN_CLOSED:
         LOG(INFO) << __func__
                   << ": GAP_EVT_CONN_CLOSED: " << hearingDevice->address
                   << ", playback_started=" << hearingDevice->playback_started;
-        hearingDevice->accepting_audio = false;
-        hearingDevice->gap_handle = 0;
-        hearingDevice->playback_started = false;
-        hearingDevice->command_acked = false;
+        /* Disconnect profile when data channel is not available */
+        Disconnect(hearingDevice->address);
         break;
       case GAP_EVT_CONN_DATA_AVAIL: {
         DVLOG(2) << "GAP_EVT_CONN_DATA_AVAIL";
@@ -1460,6 +1457,7 @@
     VLOG(2) << __func__ << ": " << address;
 
     bool connected = hearingDevice->accepting_audio;
+    bool connecting_by_user = hearingDevice->connecting_actively;
 
     LOG(INFO) << "GAP_EVT_CONN_CLOSED: " << hearingDevice->address
               << ", playback_started=" << hearingDevice->playback_started
@@ -1483,6 +1481,10 @@
     hearingDevices.Remove(address);
 
     if (!connected) {
+      /* In case user wanted to connect, sent DISCONNECTED state */
+      if (connecting_by_user)
+        callbacks->OnConnectionState(ConnectionState::DISCONNECTED, address);
+
       return;
     }
 
diff --git a/system/bta/include/bta_api.h b/system/bta/include/bta_api.h
index da6cefe..b7f0f54 100644
--- a/system/bta/include/bta_api.h
+++ b/system/bta/include/bta_api.h
@@ -434,6 +434,8 @@
   tBT_DEVICE_TYPE device_type;
   uint8_t flag;
   bool include_rsi; /* true, if ADV contains RSI data */
+  RawAddress original_bda; /* original address to pass up to
+                              GattService#onScanResult */
 } tBTA_DM_INQ_RES;
 
 /* Structure associated with BTA_DM_INQ_CMPL_EVT */
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index fd72d5b..3aa605e 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -167,7 +167,6 @@
         callbacks_(callbacks_),
         active_group_id_(bluetooth::groups::kGroupUnknown),
         current_context_type_(LeAudioContextType::MEDIA),
-        upcoming_context_type_(LeAudioContextType::MEDIA),
         stream_setup_start_timestamp_(0),
         stream_setup_end_timestamp_(0),
         audio_receiver_state_(AudioState::IDLE),
@@ -530,6 +529,7 @@
     LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
     auto final_context_type = context_type;
 
+    DLOG(INFO) << __func__;
     if (context_type >= static_cast<uint16_t>(LeAudioContextType::RFU)) {
       LOG(ERROR) << __func__ << ", stream context type is not supported: "
                  << loghex(context_type);
@@ -654,6 +654,8 @@
       if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);
 
       StopAudio();
+      ClientAudioIntefraceRelease();
+
       GroupStop(active_group_id_);
       callbacks_->OnGroupStatus(active_group_id_, GroupStatus::INACTIVE);
       active_group_id_ = group_id;
@@ -691,8 +693,11 @@
       }
     }
 
-    /* Configure audio HAL sessions with most frequent context */
-    UpdateCurrentHalSessions(group_id, current_context_type_);
+    /* Configure audio HAL sessions with most frequent context.
+     * If reconfiguration is not needed it means, context type is not supported
+     */
+    UpdateConfigAndCheckIfReconfigurationIsNeeded(group_id,
+                                                  LeAudioContextType::MEDIA);
     if (current_source_codec_config.IsInvalid() &&
         current_sink_codec_config.IsInvalid()) {
       LOG(WARNING) << __func__ << ", unsupported device configurations";
@@ -700,6 +705,18 @@
       return;
     }
 
+    /* Expose audio sessions */
+    audio_framework_source_config.data_interval_us =
+        current_source_codec_config.data_interval_us;
+    LeAudioClientAudioSource::Start(audio_framework_source_config,
+                                    audioSinkReceiver);
+
+    audio_framework_sink_config.data_interval_us =
+          current_source_codec_config.data_interval_us;
+
+    LeAudioClientAudioSink::Start(audio_framework_sink_config,
+                                  audioSourceReceiver);
+
     active_group_id_ = group_id;
     callbacks_->OnGroupStatus(active_group_id_, GroupStatus::ACTIVE);
   }
@@ -1710,12 +1727,21 @@
     if (audio_sender_state_ == AudioState::IDLE &&
         audio_receiver_state_ == AudioState::IDLE) {
       DLOG(INFO) << __func__
-                 << " Device not streaming but active. Lets update audio "
-                    "session to match needed channel number";
-      UpdateCurrentHalSessions(active_group_id_, current_context_type_);
+                 << " Device not streaming but active - nothing to do";
       return;
     }
 
+    auto num_of_devices =
+        get_num_of_devices_in_configuration(stream_conf->conf);
+
+    if (num_of_devices < group->NumOfConnected()) {
+      /* Second device got just paired. We need to reconfigure CIG */
+      stream_conf->reconfiguration_ongoing = true;
+      groupStateMachine_->StopStream(group);
+      return;
+    }
+
+    /* Second device got reconnect. Try to get it to the stream seamlessly */
     le_audio::types::AudioLocations sink_group_audio_locations = 0;
     uint8_t sink_num_of_active_ases = 0;
 
@@ -1785,7 +1811,7 @@
   }
 
   void get_mono_stream(const std::vector<uint8_t>& data,
-                       std::vector<int16_t>& chan_mono) {
+                       std::vector<int16_t>& chan_mono, int pitch = 1) {
     uint16_t num_of_frames_per_ch;
 
     int dt_us = current_source_codec_config.data_interval_us;
@@ -1793,7 +1819,7 @@
     num_of_frames_per_ch = lc3_frame_samples(dt_us, sr_hz);
 
     chan_mono.reserve(num_of_frames_per_ch);
-    for (int i = 0; i < num_of_frames_per_ch; i++) {
+    for (int i = 0; i < pitch * num_of_frames_per_ch; i += pitch) {
       const uint8_t* sample = data.data() + i * 4;
 
       int16_t left = (int16_t)((*(sample + 1) << 8) + *sample) >> 1;
@@ -1807,37 +1833,6 @@
     }
   }
 
-  void get_left_and_right_stream(const std::vector<uint8_t>& data,
-                                 std::vector<int16_t>& chan_left,
-                                 std::vector<int16_t>& chan_right,
-                                 bool prepare_mono = false) {
-    uint16_t num_of_frames_per_ch;
-
-    int dt_us = current_source_codec_config.data_interval_us;
-    int sr_hz = current_source_codec_config.sample_rate;
-    num_of_frames_per_ch = lc3_frame_samples(dt_us, sr_hz);
-
-    chan_left.reserve(num_of_frames_per_ch);
-    chan_right.reserve(num_of_frames_per_ch);
-    for (int i = 0; i < num_of_frames_per_ch; i++) {
-      const uint8_t* sample = data.data() + i * 4;
-
-      int16_t left = (int16_t)((*(sample + 1) << 8) + *sample) >> 1;
-
-      sample += 2;
-      int16_t right = (int16_t)((*(sample + 1) << 8) + *sample) >> 1;
-
-      if (prepare_mono) {
-        uint16_t mono_data = (int16_t)(((uint32_t)left + (uint32_t)right) >> 1);
-        left = mono_data;
-        right = mono_data;
-      }
-
-      chan_left.push_back(left);
-      chan_right.push_back(right);
-    }
-  }
-
   void PrepareAndSendToTwoDevices(
       const std::vector<uint8_t>& data,
       struct le_audio::stream_configuration* stream_conf) {
@@ -1869,27 +1864,37 @@
     std::vector<uint8_t> chan_right_enc(byte_count, 0);
 
     bool mono = (left_cis_handle == 0) || (right_cis_handle == 0);
+
+    int af_hz = audio_framework_source_config.sample_rate;
+    LOG_ASSERT(af_hz >= sr_hz) << __func__ << " sample freq issue";
+
+    int pitch = af_hz / sr_hz;
+
+    LOG(INFO) << __func__ << " pitch " << pitch
+              << " data size: " << (int)data.size()
+              << " byte count: " << byte_count << " mono: " << mono;
     if (!mono) {
-      lc3_encode(lc3_encoder_left, (const int16_t*)data.data(), 2,
+      lc3_encode(lc3_encoder_left, (const int16_t*)data.data(), 2 * pitch,
                  chan_left_enc.size(), chan_left_enc.data());
-      lc3_encode(lc3_encoder_right, ((const int16_t*)data.data()) + 1, 2,
-                 chan_right_enc.size(), chan_right_enc.data());
+      lc3_encode(lc3_encoder_right, ((const int16_t*)data.data()) + 1,
+                 2 * pitch, chan_right_enc.size(), chan_right_enc.data());
     } else {
-      std::vector<int16_t> chan_left;
-      std::vector<int16_t> chan_right;
-      get_left_and_right_stream(data, chan_left, chan_right, mono);
+      std::vector<int16_t> chan_mono;
+      get_mono_stream(data, chan_mono, pitch);
 
       if (left_cis_handle) {
-        lc3_encode(lc3_encoder_left, (const int16_t*)chan_left.data(), 1,
+        lc3_encode(lc3_encoder_left, (const int16_t*)chan_mono.data(), 1,
                    chan_left_enc.size(), chan_left_enc.data());
       }
 
       if (right_cis_handle) {
-        lc3_encode(lc3_encoder_right, (const int16_t*)chan_right.data(), 1,
+        lc3_encode(lc3_encoder_right, (const int16_t*)chan_mono.data(), 1,
                    chan_right_enc.size(), chan_right_enc.data());
       }
     }
 
+    DLOG(INFO) << __func__ << " left_cis_handle: " << +left_cis_handle
+               << " right_cis_handle: " << right_cis_handle;
     /* Send data to the controller */
     if (left_cis_handle)
       IsoManager::GetInstance()->SendIsoData(
@@ -1919,23 +1924,24 @@
     }
     std::vector<uint8_t> chan_encoded(num_channels * byte_count, 0);
 
+    int af_hz = audio_framework_source_config.sample_rate;
+    LOG_ASSERT(af_hz >= sr_hz) << __func__ << " sample freq issue";
+
+    int pitch = af_hz / sr_hz;
+
     if (num_channels == 1) {
       /* Since we always get two channels from framework, lets make it mono here
        */
       std::vector<int16_t> chan_mono;
-      get_mono_stream(data, chan_mono);
+      get_mono_stream(data, chan_mono, pitch);
 
       lc3_encode(lc3_encoder_left, (const int16_t*)chan_mono.data(), 1,
                  byte_count, chan_encoded.data());
 
     } else {
-      std::vector<int16_t> chan_left;
-      std::vector<int16_t> chan_right;
-      get_left_and_right_stream(data, chan_left, chan_right, false);
-
-      lc3_encode(lc3_encoder_left, (const int16_t*)chan_left.data(), 1,
+      lc3_encode(lc3_encoder_left, (const int16_t*)data.data(), 2 * pitch,
                  byte_count, chan_encoded.data());
-      lc3_encode(lc3_encoder_right, (const int16_t*)chan_right.data(), 1,
+      lc3_encode(lc3_encoder_right, (const int16_t*)data.data() + 1, 2 * pitch,
                  byte_count, chan_encoded.data() + byte_count);
     }
 
@@ -2083,7 +2089,8 @@
     /* TODO: What to do if not all data sinked ? */
     if (written != to_write) LOG(ERROR) << __func__ << ", not all data sinked";
 
-    LOG(INFO) << __func__;
+    DLOG(INFO) << __func__
+               << " num of frames: " << (int)lc3_decoder->lc3Config.NF;
   }
 
   static inline Lc3Config::FrameDuration Lc3ConfigFrameDuration(
@@ -2236,10 +2243,7 @@
     }
   }
 
-  void StopAudio(void) {
-    SuspendAudio();
-    ClientAudioIntefraceRelease();
-  }
+  void StopAudio(void) { SuspendAudio(); }
 
   void printSingleConfiguration(int fd, LeAudioCodecConfiguration* conf,
                                 bool print_audio_state, bool sender = false) {
@@ -2261,14 +2265,26 @@
   }
 
   void printCurrentStreamConfiguration(int fd) {
-    auto conf = &current_source_codec_config;
-    dprintf(fd, " Speaker codec config \n");
+    auto conf = &audio_framework_source_config;
+    dprintf(fd, " Speaker codec config (audio framework) \n");
     if (conf) {
       printSingleConfiguration(fd, conf, false);
     }
 
-    dprintf(fd, " Microphone codec config \n");
+    dprintf(fd, " Microphone codec config (audio framework) \n");
+    conf = &audio_framework_sink_config;
+    if (conf) {
+      printSingleConfiguration(fd, conf, false);
+    }
+
+    conf = &current_source_codec_config;
+    dprintf(fd, " Speaker codec config (Bluetooth)\n");
+    if (conf) {
+      printSingleConfiguration(fd, conf, true, true);
+    }
+
     conf = &current_sink_codec_config;
+    dprintf(fd, " Microphone codec config (Bluetooth)\n");
     if (conf) {
       printSingleConfiguration(fd, conf, true, false);
     }
@@ -2277,7 +2293,6 @@
   void Dump(int fd) {
     dprintf(fd, "  Active group: %d\n", active_group_id_);
     dprintf(fd, "    current content type: 0x%08hx\n", current_context_type_);
-    dprintf(fd, "    upcoming content type: 0x%08hx\n", upcoming_context_type_);
     dprintf(
         fd, "    stream setup time if started: %d ms\n",
         (int)((stream_setup_end_timestamp_ - stream_setup_start_timestamp_) /
@@ -2298,20 +2313,16 @@
     if (gatt_if_) BTA_GATTC_AppDeregister(gatt_if_);
   }
 
-  void UpdateCurrentHalSessions(int group_id, LeAudioContextType context_type) {
-    if (group_id == bluetooth::groups::kGroupUnknown) {
-      LOG(WARNING) << ", cannot start straming if no active group set";
-      return;
-    }
-
+  bool UpdateConfigAndCheckIfReconfigurationIsNeeded(
+      int group_id, LeAudioContextType context_type) {
+    bool reconfiguration_needed = false;
     auto group = aseGroups_.FindById(group_id);
     if (!group) {
       LOG(ERROR) << __func__
                  << ", Invalid group: " << static_cast<int>(group_id);
-      return;
+      return reconfiguration_needed;
     }
 
-    bool send_active = false;
     std::optional<LeAudioCodecConfiguration> source_configuration =
         group->GetCodecConfigurationByDirection(
             context_type, le_audio::types::kLeAudioDirectionSink);
@@ -2319,42 +2330,15 @@
         group->GetCodecConfigurationByDirection(
             context_type, le_audio::types::kLeAudioDirectionSource);
 
-    if (!sink_configuration) {
-      /* Let's check if le_audio group supports conversational, if so,
-       * expose DECODED session to the system
-       */
-      sink_configuration = group->GetCodecConfigurationByDirection(
-          LeAudioContextType::CONVERSATIONAL,
-          le_audio::types::kLeAudioDirectionSource);
-      if (sink_configuration) {
-        LOG(INFO) << __func__
-                  << " exposing DECODED session to the system even context: "
-                  << static_cast<int>(context_type) << " does not use it";
-      }
-    }
-
     if (source_configuration) {
-      /* Stream configuration differs from previous one */
-      if (!current_source_codec_config.IsInvalid() &&
-          (*source_configuration != current_source_codec_config)) {
-        callbacks_->OnGroupStatus(group_id, GroupStatus::INACTIVE);
-        send_active = true;
-        LeAudioClientAudioSource::Stop();
+      if (*source_configuration != current_source_codec_config) {
+        current_source_codec_config = *source_configuration;
+        reconfiguration_needed = true;
       }
-
-      current_source_codec_config = *source_configuration;
-
-      /*Let's always request 2 channels from the framework */
-      auto audio_framework_configuration = current_source_codec_config;
-      audio_framework_configuration.num_channels = 2;
-
-      LeAudioClientAudioSource::Start(audio_framework_configuration,
-                                      audioSinkReceiver);
-
     } else {
       if (!current_source_codec_config.IsInvalid()) {
-        LeAudioClientAudioSource::Stop();
         current_source_codec_config = {0, 0, 0, 0};
+        reconfiguration_needed = true;
       }
 
       LOG(INFO) << __func__
@@ -2364,24 +2348,14 @@
     }
 
     if (sink_configuration) {
-      /* Stream configuration differs from previous one */
-      if (!current_sink_codec_config.IsInvalid() &&
-          (*sink_configuration != current_sink_codec_config)) {
-        if (send_active == false) {
-          callbacks_->OnGroupStatus(group_id, GroupStatus::INACTIVE);
-          send_active = true;
-        }
-        LeAudioClientAudioSink::Stop();
+      if (*sink_configuration != current_sink_codec_config) {
+        current_sink_codec_config = *sink_configuration;
+        reconfiguration_needed = true;
       }
-
-      current_sink_codec_config = *sink_configuration;
-
-      LeAudioClientAudioSink::Start(current_sink_codec_config,
-                                    audioSourceReceiver);
     } else {
       if (!current_sink_codec_config.IsInvalid()) {
-        LeAudioClientAudioSink::Stop();
         current_sink_codec_config = {0, 0, 0, 0};
+        reconfiguration_needed = true;
       }
 
       LOG(INFO) << __func__
@@ -2390,24 +2364,20 @@
                 << static_cast<int>(context_type);
     }
 
-    if (send_active) {
-      callbacks_->OnGroupStatus(group_id, GroupStatus::ACTIVE);
+    if (reconfiguration_needed) {
+      LOG(INFO) << __func__
+                << " Session reconfiguration needed group: " << group->group_id_
+                << " for context type: " << static_cast<int>(context_type);
     }
-    current_context_type_ = upcoming_context_type_;
+
+    current_context_type_ = context_type;
+    return reconfiguration_needed;
   }
 
   bool OnAudioResume(LeAudioDeviceGroup* group) {
-    if (upcoming_context_type_ != current_context_type_) {
-      return false;
+    if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+      return true;
     }
-
-    /* Even context type is same, we might need more audio channels e.g. because
-     * new device got connected */
-    if (ReconfigureHalSessionIfNeeded(group, current_context_type_)) {
-      return false;
-    }
-
-    /* TODO check if group already started streaming */
     return InternalGroupStream(active_group_id_,
                                static_cast<uint16_t>(current_context_type_));
   }
@@ -2498,8 +2468,6 @@
                << " audio_sender_state: " << audio_sender_state_ << "\n"
                << " current_context_type_: "
                << static_cast<int>(current_context_type_) << "\n"
-               << " upcoming_context_type_: "
-               << static_cast<int>(upcoming_context_type_) << "\n"
                << " group " << (group ? " exist " : " does not exist ") << "\n";
 
     switch (audio_sender_state_) {
@@ -2530,8 +2498,16 @@
             break;
           case AudioState::RELEASING:
           case AudioState::READY_TO_RELEASE:
+            /* If group is reconfiguring, reassing state and wait for
+             * the stream to be established
+             */
+            if (group->stream_conf.reconfiguration_ongoing) {
+              audio_sender_state_ = audio_receiver_state_;
+              return;
+            }
+            FALLTHROUGH;
           default:
-            LeAudioClientAudioSink::CancelStreamingRequest();
+            LeAudioClientAudioSource::CancelStreamingRequest();
             break;
         }
 
@@ -2628,8 +2604,6 @@
                << " audio_sender_state: " << audio_sender_state_ << "\n"
                << " current_context_type_: "
                << static_cast<int>(current_context_type_) << "\n"
-               << " upcoming_context_type_: "
-               << static_cast<int>(upcoming_context_type_) << "\n"
                << " group " << (group ? " exist " : " does not exist ") << "\n";
 
     switch (audio_receiver_state_) {
@@ -2658,6 +2632,14 @@
             break;
           case AudioState::RELEASING:
           case AudioState::READY_TO_RELEASE:
+            /* If group is reconfiguring, reassing state and wait for
+             * the stream to be established
+             */
+            if (group->stream_conf.reconfiguration_ongoing) {
+              audio_receiver_state_ = audio_sender_state_;
+              return;
+            }
+            FALLTHROUGH;
           default:
             LeAudioClientAudioSink::CancelStreamingRequest();
             break;
@@ -2767,55 +2749,26 @@
     return available_contents[0];
   }
 
-  bool ReconfigureHalSessionIfNeeded(LeAudioDeviceGroup* group,
-                                     LeAudioContextType new_context_type) {
-    std::optional<LeAudioCodecConfiguration> source_configuration =
-        group->GetCodecConfigurationByDirection(
-            new_context_type, le_audio::types::kLeAudioDirectionSink);
-
-    std::optional<LeAudioCodecConfiguration> sink_configuration =
-        group->GetCodecConfigurationByDirection(
-            new_context_type, le_audio::types::kLeAudioDirectionSource);
-
-    if (!sink_configuration) {
-      /* Let's check if le_audio group supports conversational, if so,
-       * expose DECODED session to the system
-       */
-      sink_configuration = group->GetCodecConfigurationByDirection(
-          LeAudioContextType::CONVERSATIONAL,
-          le_audio::types::kLeAudioDirectionSource);
-      if (sink_configuration) {
-        DLOG(INFO) << __func__
-                   << " exposing DECODED session to the system even context: "
-                   << static_cast<int>(new_context_type) << " does not use it";
-      }
+  bool StopStreamIfNeeded(LeAudioDeviceGroup* group,
+                          LeAudioContextType new_context_type) {
+    DLOG(INFO) << __func__ << " context type " << int(new_context_type);
+    if (!UpdateConfigAndCheckIfReconfigurationIsNeeded(group->group_id_,
+                                                       new_context_type)) {
+      DLOG(INFO) << __func__ << " reconfiguration not needed";
+      return false;
     }
 
-    if ((source_configuration &&
-         (*source_configuration != current_source_codec_config)) ||
-        (sink_configuration &&
-         (*sink_configuration != current_sink_codec_config))) {
-      LOG(INFO) << __func__
-                << " Session reconfiguration needed group: " << group->group_id_
-                << "for context type: " << static_cast<int>(new_context_type);
-
-      if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
-        if (alarm_is_scheduled(suspend_timeout_))
-          alarm_cancel(suspend_timeout_);
-
-        GroupStop(group->group_id_);
-      }
-
-      upcoming_context_type_ = new_context_type;
-      /* Schedule HAL Session update */
-      do_in_main_thread(FROM_HERE,
-                        base::Bind(&LeAudioClientImpl::UpdateCurrentHalSessions,
-                                   base::Unretained(instance), group->group_id_,
-                                   upcoming_context_type_));
-      return true;
+    if (group->GetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+      DLOG(INFO) << __func__ << " Group is not streaming ";
+      return false;
     }
 
-    return false;
+    if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);
+
+    /* Need to reconfigure stream */
+    group->stream_conf.reconfiguration_ongoing = true;
+    GroupStop(group->group_id_);
+    return true;
   }
 
   void OnAudioMetadataUpdate(const source_metadata_t& source_metadata) {
@@ -2869,14 +2822,17 @@
       return;
     }
 
-    if (ReconfigureHalSessionIfNeeded(group, new_context)) {
+    current_context_type_ = new_context;
+    if (StopStreamIfNeeded(group, new_context)) {
       return;
     }
 
-    /* Configuration is the same for new context, just will do update
-     * metadata of stream
-     */
-    GroupStream(active_group_id_, static_cast<uint16_t>(new_context));
+    if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+      /* Configuration is the same for new context, just will do update
+       * metadata of stream
+       */
+      GroupStream(active_group_id_, static_cast<uint16_t>(new_context));
+    }
   }
 
   static void OnGattReadRspStatic(uint16_t conn_id, tGATT_STATUS status,
@@ -3021,7 +2977,39 @@
         rxUnreceivedPackets, duplicatePackets);
   }
 
+  bool RestartStreamingAfterReconfiguration(int group_id) {
+    if (group_id != active_group_id_) return false;
+
+    DLOG(INFO) << __func__ << " audio_sender_state_: " << audio_sender_state_
+               << " audio_receiver_state_: " << audio_receiver_state_;
+
+    auto group = aseGroups_.FindById(group_id);
+    if (!group) return false;
+
+    auto stream_conf = &group->stream_conf;
+    DLOG(INFO) << __func__ << " stream_conf->reconfiguration_ongoing "
+               << stream_conf->reconfiguration_ongoing;
+
+    if (!stream_conf->reconfiguration_ongoing) return false;
+
+    if (!groupStateMachine_->StartStream(
+            group, static_cast<LeAudioContextType>(current_context_type_)))
+      return false;
+
+    if (audio_sender_state_ == AudioState::RELEASING)
+      audio_sender_state_ = AudioState::READY_TO_START;
+
+    if (audio_receiver_state_ == AudioState::RELEASING)
+      audio_receiver_state_ = AudioState::READY_TO_START;
+
+    stream_conf->reconfiguration_ongoing = false;
+    return true;
+  }
+
   void StatusReportCb(int group_id, GroupStreamStatus status) {
+    DLOG(INFO) << __func__ << "status: " << static_cast<int>(status)
+               << " audio_sender_state_: " << audio_sender_state_
+               << " audio_receiver_state_: " << audio_receiver_state_;
     switch (status) {
       case GroupStreamStatus::STREAMING:
         if (audio_sender_state_ == AudioState::READY_TO_START)
@@ -3036,15 +3024,23 @@
         /** Stop Audio but don't release all the Audio resources */
         SuspendAudio();
         break;
-      case GroupStreamStatus::IDLE:
+      case GroupStreamStatus::IDLE: {
         stream_setup_end_timestamp_ = 0;
         stream_setup_start_timestamp_ = 0;
-        CancelStreamingRequest();
+
+        if (!RestartStreamingAfterReconfiguration(group_id))
+          CancelStreamingRequest();
+
         break;
+      }
       case GroupStreamStatus::RELEASING:
       case GroupStreamStatus::SUSPENDING:
-        audio_sender_state_ = AudioState::RELEASING;
-        audio_receiver_state_ = AudioState::RELEASING;
+        if (audio_sender_state_ != AudioState::IDLE)
+          audio_sender_state_ = AudioState::RELEASING;
+
+        if (audio_receiver_state_ != AudioState::IDLE)
+          audio_receiver_state_ = AudioState::RELEASING;
+
         break;
       default:
         break;
@@ -3059,7 +3055,6 @@
   LeAudioGroupStateMachine* groupStateMachine_;
   int active_group_id_;
   LeAudioContextType current_context_type_;
-  LeAudioContextType upcoming_context_type_;
   uint64_t stream_setup_start_timestamp_;
   uint64_t stream_setup_end_timestamp_;
 
@@ -3068,9 +3063,27 @@
   /* Speaker(s) */
   AudioState audio_sender_state_;
 
+  /* Current stream configuration */
   LeAudioCodecConfiguration current_source_codec_config;
   LeAudioCodecConfiguration current_sink_codec_config;
 
+  /* Static Audio Framework session configuration.
+   *  Resampling will be done inside the bt stack
+   */
+  LeAudioCodecConfiguration audio_framework_source_config = {
+      .num_channels = 2,
+      .sample_rate = bluetooth::audio::le_audio::kSampleRate48000,
+      .bits_per_sample = bluetooth::audio::le_audio::kBitsPerSample16,
+      .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us,
+  };
+
+  LeAudioCodecConfiguration audio_framework_sink_config = {
+      .num_channels = 1,
+      .sample_rate = bluetooth::audio::le_audio::kSampleRate16000,
+      .bits_per_sample = bluetooth::audio::le_audio::kBitsPerSample16,
+      .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us,
+  };
+
   void* lc3_encoder_left_mem;
   void* lc3_encoder_right_mem;
 
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index 314cbea..40588f6 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -1187,6 +1187,8 @@
          << "      active stream configuration name: "
          << (active_conf ? active_conf->name : " not set") << "\n"
          << "    Last used stream configuration: \n"
+         << "      reconfiguration_ongoing: "
+         << stream_conf.reconfiguration_ongoing << "\n"
          << "      codec id : " << +(stream_conf.id.coding_format) << "\n"
          << "      name: "
          << (stream_conf.conf != nullptr ? stream_conf.conf->name : " null ")
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index b9f4d07..13f8067 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -30,6 +30,7 @@
 #include "btm_iso_api.h"
 #include "common/message_loop_thread.h"
 #include "device/include/controller.h"
+#include "fake_osi.h"
 #include "gatt/database_builder.h"
 #include "hardware/bt_gatt_types.h"
 #include "le_audio_types.h"
@@ -59,6 +60,8 @@
 
 using namespace bluetooth::le_audio;
 
+extern struct fake_osi_alarm_set_on_mloop fake_osi_alarm_set_on_mloop_;
+
 namespace bluetooth {
 namespace audio {
 namespace le_audio {
@@ -538,13 +541,21 @@
           group->SetTargetState(
               types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
           group->SetState(group->GetTargetState());
-          state_machine_callbacks_->StatusReportCb(
-              group->group_id_, GroupStreamStatus::STREAMING);
           streaming_groups[group->group_id_] = group;
 
           /* Assume CIG is created */
           group->cig_created_ = true;
 
+          do_in_main_thread(
+              FROM_HERE, base::BindOnce(
+                             [](int group_id,
+                                le_audio::LeAudioGroupStateMachine::Callbacks*
+                                    state_machine_callbacks) {
+                               state_machine_callbacks->StatusReportCb(
+                                   group_id, GroupStreamStatus::STREAMING);
+                             },
+                             group->group_id_,
+                             base::Unretained(this->state_machine_callbacks_)));
           return true;
         });
 
@@ -670,6 +681,8 @@
           // Inject the state
           group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
           group->SetState(group->GetTargetState());
+          state_machine_callbacks_->StatusReportCb(
+              group->group_id_, GroupStreamStatus::RELEASING);
           state_machine_callbacks_->StatusReportCb(group->group_id_,
                                                    GroupStreamStatus::IDLE);
         });
@@ -939,7 +952,8 @@
     ConnectLeAudio(addr);
   }
 
-  void UpdateMetadata(audio_usage_t usage, audio_content_type_t content_type) {
+  void UpdateMetadata(audio_usage_t usage, audio_content_type_t content_type,
+                      bool reconfigure_existing_stream = false) {
     std::promise<void> do_metadata_update_promise;
 
     struct playback_track_metadata tracks_[2] = {
@@ -952,6 +966,10 @@
     tracks_[0].usage = usage;
     tracks_[0].content_type = content_type;
 
+    if (reconfigure_existing_stream) {
+      EXPECT_CALL(mock_audio_source_, ConfirmStreamingRequest()).Times(1);
+    }
+
     auto do_metadata_update_future = do_metadata_update_promise.get_future();
     audio_sink_receiver_->OnAudioMetadataUpdate(
         std::move(do_metadata_update_promise), source_metadata);
@@ -959,25 +977,33 @@
   }
 
   void StartStreaming(audio_usage_t usage, audio_content_type_t content_type,
-                      int group_id, bool reconfigured_sink = false) {
+                      int group_id, bool reconfigure_existing_stream = false) {
     ASSERT_NE(audio_sink_receiver_, nullptr);
 
-    UpdateMetadata(usage, content_type);
+    UpdateMetadata(usage, content_type, reconfigure_existing_stream);
 
-    if (reconfigured_sink) {
-      EXPECT_CALL(mock_audio_source_, CancelStreamingRequest()).Times(1);
-      audio_sink_receiver_->OnAudioResume();
-    }
-    SyncOnMainLoop();
-    audio_sink_receiver_->OnAudioResume();
+    /* Stream has been automatically restarted on UpdateMetadata */
+    if (reconfigure_existing_stream) return;
 
     EXPECT_CALL(mock_audio_source_, ConfirmStreamingRequest()).Times(1);
-    state_machine_callbacks_->StatusReportCb(group_id,
-                                             GroupStreamStatus::STREAMING);
+    do_in_main_thread(FROM_HERE,
+                      base::BindOnce(
+                          [](LeAudioClientAudioSinkReceiver* sink_receiver) {
+                            sink_receiver->OnAudioResume();
+                          },
+                          audio_sink_receiver_));
+
+    SyncOnMainLoop();
+    Mock::VerifyAndClearExpectations(&mock_audio_source_);
 
     if (usage == AUDIO_USAGE_VOICE_COMMUNICATION) {
       ASSERT_NE(audio_source_receiver_, nullptr);
-      audio_source_receiver_->OnAudioResume();
+      do_in_main_thread(
+          FROM_HERE, base::BindOnce(
+                         [](LeAudioClientAudioSourceReceiver* source_receiver) {
+                           source_receiver->OnAudioResume();
+                         },
+                         audio_source_receiver_));
     }
   }
 
@@ -2256,7 +2282,9 @@
   uint8_t cis_count_out = 1;
   uint8_t cis_count_in = 0;
 
+  // Audio sessions are started only when device gets active
   EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
 
   EXPECT_CALL(mock_state_machine_, StartStream(_, _)).Times(1);
@@ -2319,7 +2347,9 @@
   uint8_t cis_count_out = 1;
   uint8_t cis_count_in = 0;
 
+  // Audio sessions are started only when device gets active
   EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2349,6 +2379,7 @@
   // Release
   EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
   EXPECT_CALL(mock_audio_source_, Release(_)).Times(1);
+  EXPECT_CALL(mock_audio_sink_, Release(_)).Times(1);
   LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
   Mock::VerifyAndClearExpectations(&mock_audio_source_);
 }
@@ -2374,6 +2405,7 @@
 
   // Start streaming
   EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2429,17 +2461,14 @@
                     codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                     group_id, 2 /* rank*/, true /*connect_through_csis*/);
 
+  // Start streaming
   EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
   Mock::VerifyAndClearExpectations(&mock_audio_source_);
 
-  // Start streaming with reconfiguration from default media stream setup
-  EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
-  EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
-  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
-
   StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
-                 group_id, true /* reconfigure */);
+                 group_id);
 
   Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
   Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2449,17 +2478,11 @@
   // Verify Data transfer on two peer sinks and one source
   uint8_t cis_count_out = 2;
   uint8_t cis_count_in = 1;
-  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 640);
+  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
 
   // Suspend
-  EXPECT_CALL(mock_audio_source_, Release(_)).Times(0);
-  EXPECT_CALL(mock_audio_sink_, Release(_)).Times(0);
-  EXPECT_CALL(mock_audio_source_, Stop()).Times(0);
-  EXPECT_CALL(mock_audio_sink_, Stop()).Times(0);
   LeAudioClient::Get()->GroupSuspend(group_id);
   SyncOnMainLoop();
-  Mock::VerifyAndClearExpectations(&mock_audio_source_);
-  Mock::VerifyAndClearExpectations(&mock_audio_sink_);
 
   // Resume
   StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
@@ -2469,7 +2492,7 @@
   Mock::VerifyAndClearExpectations(&mock_audio_sink_);
 
   // Verify Data transfer still works
-  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 640);
+  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
 
   // Stop
   StopStreaming(group_id, true);
@@ -2511,7 +2534,9 @@
                     codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                     group_id, 2 /* rank*/, true /*connect_through_csis*/);
 
+  // Start streaming
   EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
   Mock::VerifyAndClearExpectations(&mock_audio_source_);
 
@@ -2520,11 +2545,9 @@
       mock_state_machine_,
       StartStream(_, le_audio::types::LeAudioContextType::NOTIFICATIONS))
       .Times(1);
-  EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
-  EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
 
-  StartStreaming(AUDIO_USAGE_NOTIFICATION, AUDIO_CONTENT_TYPE_UNKNOWN, group_id,
-                 true /* reconfigure */);
+  StartStreaming(AUDIO_USAGE_NOTIFICATION, AUDIO_CONTENT_TYPE_UNKNOWN,
+                 group_id);
 
   Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
   Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2582,6 +2605,7 @@
 
   // Start streaming MEDIA
   EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2597,16 +2621,12 @@
 
   // Stop
   StopStreaming(group_id);
+  // simulate suspend timeout passed, alarm executing
+  fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
   Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
 
-  // Start streaming
-  EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
-  EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
-  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
-  LeAudioClient::Get()->GroupSetActive(group_id);
-
   StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
-                 group_id, true /* reconfigure */);
+                 group_id);
 
   Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
   Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2616,7 +2636,7 @@
   // Verify Data transfer on two peer sinks and one source
   cis_count_out = 2;
   cis_count_in = 1;
-  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 640);
+  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
 }
 
 TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) {
@@ -2636,6 +2656,7 @@
 
   // Start streaming
   EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2656,11 +2677,7 @@
                     codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                     group_id, 2 /* rank*/, true /*connect_through_csis*/);
 
-  /* We should expect two iso channels to be fed with data, but for now, when
-   * second device is connected later, we just continue stream to one device.
-   * TODO: improve it.
-   */
-  cis_count_out = 1;
+  cis_count_out = 2;
   cis_count_in = 0;
   TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
 }
@@ -2687,8 +2704,9 @@
                     codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                     group_id, 2 /* rank*/, true /*connect_through_csis*/);
 
-  // Start streaming
+  // Audio sessions are started only when device gets active
   EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index 111504f..c74e8ec 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -88,6 +88,11 @@
   return group_size >= min_req_devices_cnt(audio_set_conf);
 }
 
+uint8_t get_num_of_devices_in_configuration(
+    const AudioSetConfiguration* audio_set_conf) {
+  return min_req_devices_cnt(audio_set_conf);
+}
+
 static bool IsCodecConfigurationSupported(const types::LeAudioLtvMap& pacs,
                                           const LeAudioLc3Config& lc3_config) {
   const auto& reqs = lc3_config.GetAsLtvMap();
diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h
index 6371696..48b6901 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -952,9 +952,12 @@
     const types::acs_ac_record& pac_record,
     const CodecCapabilitySetting& codec_capability_setting);
 const AudioSetConfigurations* get_confs_by_type(types::LeAudioContextType type);
+uint8_t get_num_of_devices_in_configuration(
+    const AudioSetConfiguration* audio_set_configuration);
 }  // namespace set_configurations
 
 struct stream_configuration {
+  bool reconfiguration_ongoing;
 
   types::LeAudioCodecId id;
 
diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index 7427eb4..2d6bedb 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -591,6 +591,12 @@
   return btif_set_dynamic_audio_buffer_size(codec, size);
 }
 
+static bool allow_low_latency_audio(bool allowed, const RawAddress& address) {
+  LOG_INFO("%s %s", __func__, allowed ? "true" : "false");
+  // Call HAL here
+  return true;
+}
+
 EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
     sizeof(bluetoothInterface),
     init,
@@ -629,7 +635,8 @@
     obfuscate_address,
     get_metric_id,
     set_dynamic_audio_buffer_size,
-    generate_local_oob_data};
+    generate_local_oob_data,
+    allow_low_latency_audio};
 
 // callback reporting helpers
 
diff --git a/system/btif/src/btif_ble_scanner.cc b/system/btif/src/btif_ble_scanner.cc
index fce9354..4d6c2f9 100644
--- a/system/btif/src/btif_ble_scanner.cc
+++ b/system/btif/src/btif_ble_scanner.cc
@@ -110,7 +110,7 @@
                               uint8_t ble_secondary_phy,
                               uint8_t ble_advertising_sid, int8_t ble_tx_power,
                               uint16_t ble_periodic_adv_int,
-                              vector<uint8_t> value) {
+                              vector<uint8_t> value, RawAddress original_bda) {
   uint8_t remote_name_len;
   bt_device_type_t dev_type;
   bt_property_t properties;
@@ -156,7 +156,8 @@
   btif_storage_set_remote_addr_type(&bd_addr, addr_type);
   HAL_CBACK(bt_gatt_callbacks, scanner->scan_result_cb, ble_evt_type, addr_type,
             &bd_addr, ble_primary_phy, ble_secondary_phy, ble_advertising_sid,
-            ble_tx_power, rssi, ble_periodic_adv_int, std::move(value));
+            ble_tx_power, rssi, ble_periodic_adv_int, std::move(value),
+            &original_bda);
 }
 
 void bta_scan_results_cb(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* p_data) {
@@ -185,11 +186,11 @@
   }
 
   tBTA_DM_INQ_RES* r = &p_data->inq_res;
-  do_in_jni_thread(Bind(bta_scan_results_cb_impl, r->bd_addr, r->device_type,
-                        r->rssi, r->ble_addr_type, r->ble_evt_type,
-                        r->ble_primary_phy, r->ble_secondary_phy,
-                        r->ble_advertising_sid, r->ble_tx_power,
-                        r->ble_periodic_adv_int, std::move(value)));
+  do_in_jni_thread(
+      Bind(bta_scan_results_cb_impl, r->bd_addr, r->device_type, r->rssi,
+           r->ble_addr_type, r->ble_evt_type, r->ble_primary_phy,
+           r->ble_secondary_phy, r->ble_advertising_sid, r->ble_tx_power,
+           r->ble_periodic_adv_int, std::move(value), r->original_bda));
 }
 
 void bta_track_adv_event_cb(tBTM_BLE_TRACK_ADV_DATA* p_track_adv_data) {
diff --git a/system/gd/hci/acl_manager.cc b/system/gd/hci/acl_manager.cc
index 269a0cd..2a806cb 100644
--- a/system/gd/hci/acl_manager.cc
+++ b/system/gd/hci/acl_manager.cc
@@ -91,20 +91,14 @@
       return;
     }
     uint16_t handle = packet->GetHandle();
-    if (handle == kQualcommDebugHandle) {
+    if (handle == kQualcommDebugHandle) return;
+    if (classic_impl_->send_packet_upward(
+            handle, [&packet](struct acl_manager::assembler* assembler) { assembler->on_incoming_packet(*packet); }))
       return;
-    }
-    auto connection_pair = classic_impl_->acl_connections_.find(handle);
-    if (connection_pair != classic_impl_->acl_connections_.end()) {
-      connection_pair->second.assembler_.on_incoming_packet(*packet);
-    } else {
-      auto le_connection_pair = le_impl_->le_acl_connections_.find(handle);
-      if (le_connection_pair == le_impl_->le_acl_connections_.end()) {
-        LOG_INFO("Dropping packet of size %zu to unknown connection 0x%0hx", packet->size(), handle);
-        return;
-      }
-      le_connection_pair->second.assembler_.on_incoming_packet(*packet);
-    }
+    if (le_impl_->send_packet_upward(
+            handle, [&packet](struct acl_manager::assembler* assembler) { assembler->on_incoming_packet(*packet); }))
+      return;
+    LOG_INFO("Dropping packet of size %zu to unknown connection 0x%0hx", packet->size(), packet->GetHandle());
   }
 
   void Dump(
diff --git a/system/gd/hci/acl_manager/classic_acl_connection.cc b/system/gd/hci/acl_manager/classic_acl_connection.cc
index 4410828..5fd5eaf 100644
--- a/system/gd/hci/acl_manager/classic_acl_connection.cc
+++ b/system/gd/hci/acl_manager/classic_acl_connection.cc
@@ -327,16 +327,19 @@
       const Address& address,
       uint16_t connection_handle)
       : tracker(acl_connection_interface, address, connection_handle), queue_(std::move(queue)) {}
-  ConnectionManagementCallbacks* GetEventCallbacks() {
-    ASSERT(!callbacks_given_);
-    callbacks_given_ = true;
+  ConnectionManagementCallbacks* GetEventCallbacks(std::function<void(uint16_t)> invalidate_callbacks) {
+    ASSERT_LOG(!invalidate_callbacks_, "Already returned event callbacks for this connection");
+    invalidate_callbacks_ = std::move(invalidate_callbacks);
     return &tracker;
   }
+  void PutEventCallbacks() {
+    if (invalidate_callbacks_) invalidate_callbacks_(tracker.connection_handle_);
+    invalidate_callbacks_ = {};
+  }
 
-  bool callbacks_given_{false};
   AclConnectionTracker tracker;
   std::shared_ptr<Queue> queue_;
-  std::shared_ptr<std::atomic<bool>> is_callback_valid_;
+  std::function<void(uint16_t)> invalidate_callbacks_;
 };
 
 ClassicAclConnection::ClassicAclConnection()
@@ -350,14 +353,13 @@
 }
 
 ClassicAclConnection::~ClassicAclConnection() {
-  if (pimpl_->is_callback_valid_) *pimpl_->is_callback_valid_ = false;
+  pimpl_->PutEventCallbacks();
   delete pimpl_;
 }
 
 ConnectionManagementCallbacks* ClassicAclConnection::GetEventCallbacks(
-    std::shared_ptr<std::atomic<bool>> is_callback_valid) {
-  pimpl_->is_callback_valid_ = is_callback_valid;
-  return pimpl_->GetEventCallbacks();
+    std::function<void(uint16_t)> invalidate_callbacks) {
+  return pimpl_->GetEventCallbacks(std::move(invalidate_callbacks));
 }
 
 void ClassicAclConnection::RegisterCallbacks(ConnectionManagementCallbacks* callbacks, os::Handler* handler) {
diff --git a/system/gd/hci/acl_manager/classic_acl_connection.h b/system/gd/hci/acl_manager/classic_acl_connection.h
index a72f2df..328cbb1 100644
--- a/system/gd/hci/acl_manager/classic_acl_connection.h
+++ b/system/gd/hci/acl_manager/classic_acl_connection.h
@@ -75,7 +75,7 @@
   virtual bool ReadRemoteExtendedFeatures(uint8_t page_number);
 
   // Called once before passing the connection to the client
-  virtual ConnectionManagementCallbacks* GetEventCallbacks(std::shared_ptr<std::atomic<bool>> is_callback_valid);
+  virtual ConnectionManagementCallbacks* GetEventCallbacks(std::function<void(uint16_t)> invalidate_callbacks);
 
  private:
   AclConnectionInterface* acl_connection_interface_;
diff --git a/system/gd/hci/acl_manager/classic_impl.h b/system/gd/hci/acl_manager/classic_impl.h
index d38ece7..3229227 100644
--- a/system/gd/hci/acl_manager/classic_impl.h
+++ b/system/gd/hci/acl_manager/classic_impl.h
@@ -33,12 +33,14 @@
 
 struct acl_connection {
   acl_connection(AddressWithType address_with_type, AclConnection::QueueDownEnd* queue_down_end, os::Handler* handler)
-      : assembler_(address_with_type, queue_down_end, handler), address_with_type_(address_with_type) {}
-  ~acl_connection() = default;
-  struct acl_manager::assembler assembler_;
+      : address_with_type_(address_with_type),
+        assembler_(new acl_manager::assembler(address_with_type, queue_down_end, handler)) {}
+  ~acl_connection() {
+    delete assembler_;
+  }
   AddressWithType address_with_type_;
+  struct acl_manager::assembler* assembler_;
   ConnectionManagementCallbacks* connection_management_callbacks_ = nullptr;
-  std::shared_ptr<std::atomic<bool>> is_callback_valid_ = std::make_shared<std::atomic<bool>>(true);
 };
 
 struct classic_impl : public security::ISecurityManagerListener {
@@ -48,13 +50,11 @@
       os::Handler* handler,
       RoundRobinScheduler* round_robin_scheduler,
       bool crash_on_unknown_handle)
-      : hci_layer_(hci_layer),
-        controller_(controller),
-        round_robin_scheduler_(round_robin_scheduler),
-        crash_on_unknown_handle_(crash_on_unknown_handle) {
+      : hci_layer_(hci_layer), controller_(controller), round_robin_scheduler_(round_robin_scheduler) {
     hci_layer_ = hci_layer;
     controller_ = controller;
     handler_ = handler;
+    connections.crash_on_unknown_handle_ = crash_on_unknown_handle;
     should_accept_connection_ = common::Bind([](Address, ClassOfDevice) { return true; });
     acl_connection_interface_ = hci_layer_->GetAclConnectionInterface(
         handler_->BindOn(this, &classic_impl::on_classic_event),
@@ -64,18 +64,10 @@
 
   ~classic_impl() {
     hci_layer_->PutAclConnectionInterface();
-    acl_connections_.clear();
+    connections.reset();
     security_manager_.reset();
   }
 
-  ConnectionManagementCallbacks* get_callbacks(uint16_t handle) {
-    auto connection = acl_connections_.find(handle);
-    if (connection == acl_connections_.end()) {
-      return nullptr;
-    }
-    return (connection->second.is_callback_valid_) ? connection->second.connection_management_callbacks_ : nullptr;
-  }
-
   void on_classic_event(EventView event_packet) {
     EventCode event_code = event_packet.GetEventCode();
     switch (event_code) {
@@ -129,32 +121,107 @@
     }
   }
 
-  void on_classic_disconnect(uint16_t handle, ErrorCode reason) {
-    auto callbacks = get_callbacks(handle);
-    if (callbacks != nullptr) {
-      round_robin_scheduler_->Unregister(handle);
-      callbacks->OnDisconnection(reason);
-      acl_connections_.erase(handle);
-    } else {
-      // This handle is probably for SCO, so we use the callback workaround.
-      if (non_acl_disconnect_callback_ != nullptr) {
-        non_acl_disconnect_callback_(handle, static_cast<uint8_t>(reason));
+ private:
+  static constexpr uint16_t kIllegalConnectionHandle = 0xffff;
+  struct {
+   private:
+    std::map<uint16_t, acl_connection> acl_connections_;
+    mutable std::mutex acl_connections_guard_;
+    ConnectionManagementCallbacks* find_callbacks(uint16_t handle) {
+      auto connection = acl_connections_.find(handle);
+      if (connection == acl_connections_.end()) return nullptr;
+      return connection->second.connection_management_callbacks_;
+    }
+    ConnectionManagementCallbacks* find_callbacks(const Address& address) {
+      for (auto& connection_pair : acl_connections_) {
+        if (connection_pair.second.address_with_type_.GetAddress() == address) {
+          return connection_pair.second.connection_management_callbacks_;
+        }
+      }
+      return nullptr;
+    }
+    void remove(uint16_t handle) {
+      auto connection = acl_connections_.find(handle);
+      if (connection != acl_connections_.end()) {
+        connection->second.connection_management_callbacks_ = nullptr;
+        acl_connections_.erase(handle);
       }
     }
-  }
 
-  void handle_register_callbacks(ConnectionCallbacks* callbacks, os::Handler* handler) {
-    ASSERT(client_callbacks_ == nullptr);
-    ASSERT(client_handler_ == nullptr);
-    client_callbacks_ = callbacks;
-    client_handler_ = handler;
-  }
+   public:
+    bool crash_on_unknown_handle_ = false;
+    bool is_empty() const {
+      std::unique_lock<std::mutex> lock(acl_connections_guard_);
+      return acl_connections_.empty();
+    }
+    void reset() {
+      std::unique_lock<std::mutex> lock(acl_connections_guard_);
+      acl_connections_.clear();
+    }
+    void invalidate(uint16_t handle) {
+      std::unique_lock<std::mutex> lock(acl_connections_guard_);
+      remove(handle);
+    }
+    void execute(
+        uint16_t handle,
+        std::function<void(ConnectionManagementCallbacks* callbacks)> execute,
+        bool remove_afterwards = false) {
+      std::unique_lock<std::mutex> lock(acl_connections_guard_);
+      auto callbacks = find_callbacks(handle);
+      if (callbacks != nullptr)
+        execute(callbacks);
+      else
+        ASSERT_LOG(!crash_on_unknown_handle_, "Received command for unknown handle:0x%x", handle);
+      if (remove_afterwards) remove(handle);
+    }
+    void execute(const Address& address, std::function<void(ConnectionManagementCallbacks* callbacks)> execute) {
+      std::unique_lock<std::mutex> lock(acl_connections_guard_);
+      auto callbacks = find_callbacks(address);
+      if (callbacks != nullptr) execute(callbacks);
+    }
+    bool send_packet_upward(uint16_t handle, std::function<void(struct acl_manager::assembler* assembler)> cb) {
+      std::unique_lock<std::mutex> lock(acl_connections_guard_);
+      auto connection = acl_connections_.find(handle);
+      if (connection != acl_connections_.end()) cb(connection->second.assembler_);
+      return connection != acl_connections_.end();
+    }
+    void add(
+        uint16_t handle,
+        const AddressWithType& remote_address,
+        AclConnection::QueueDownEnd* queue_end,
+        os::Handler* handler,
+        ConnectionManagementCallbacks* connection_management_callbacks) {
+      std::unique_lock<std::mutex> lock(acl_connections_guard_);
+      auto emplace_pair = acl_connections_.emplace(
+          std::piecewise_construct,
+          std::forward_as_tuple(handle),
+          std::forward_as_tuple(remote_address, queue_end, handler));
+      ASSERT(emplace_pair.second);  // Make sure the connection is unique
+      emplace_pair.first->second.connection_management_callbacks_ = connection_management_callbacks;
+    }
+    uint16_t HACK_get_handle(const Address& address) const {
+      std::unique_lock<std::mutex> lock(acl_connections_guard_);
+      for (auto it = acl_connections_.begin(); it != acl_connections_.end(); it++) {
+        if (it->second.address_with_type_.GetAddress() == address) {
+          return it->first;
+        }
+      }
+      return kIllegalConnectionHandle;
+    }
+    bool is_classic_link_already_connected(const Address& address) const {
+      std::unique_lock<std::mutex> lock(acl_connections_guard_);
+      for (const auto& connection : acl_connections_) {
+        if (connection.second.address_with_type_.GetAddress() == address) {
+          return true;
+        }
+      }
+      return false;
+    }
+  } connections;
 
-  void handle_unregister_callbacks(ConnectionCallbacks* callbacks, std::promise<void> promise) {
-    ASSERT_LOG(client_callbacks_ == callbacks, "Registered callback entity is different then unregister request");
-    client_callbacks_ = nullptr;
-    client_handler_ = nullptr;
-    promise.set_value();
+ public:
+  bool send_packet_upward(uint16_t handle, std::function<void(struct acl_manager::assembler* assembler)> cb) {
+    return connections.send_packet_upward(handle, cb);
   }
 
   void on_incoming_connection(EventView packet) {
@@ -200,12 +267,7 @@
   }
 
   bool is_classic_link_already_connected(Address address) {
-    for (const auto& connection : acl_connections_) {
-      if (connection.second.address_with_type_.GetAddress() == address) {
-        return true;
-      }
-    }
-    return false;
+    return connections.is_classic_link_already_connected(address);
   }
 
   void create_connection(Address address) {
@@ -266,29 +328,26 @@
     }
     uint16_t handle = connection_complete.GetConnectionHandle();
     auto queue = std::make_shared<AclConnection::Queue>(10);
-    auto conn_pair = acl_connections_.emplace(
-        std::piecewise_construct,
-        std::forward_as_tuple(handle),
-        std::forward_as_tuple(
-            AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS}, queue->GetDownEnd(), handler_));
-    ASSERT(conn_pair.second);  // Make sure it's not a duplicate
+    auto queue_down_end = queue->GetDownEnd();
     round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue);
     std::unique_ptr<ClassicAclConnection> connection(
         new ClassicAclConnection(std::move(queue), acl_connection_interface_, handle, address));
     connection->locally_initiated_ = locally_initiated;
-    auto& connection_proxy = conn_pair.first->second;
-    connection_proxy.connection_management_callbacks_ =
-        connection->GetEventCallbacks(connection_proxy.is_callback_valid_);
-    if (delayed_role_change_ != nullptr) {
-      if (delayed_role_change_->GetBdAddr() == address) {
+    connections.add(
+        handle,
+        AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS},
+        queue_down_end,
+        handler_,
+        connection->GetEventCallbacks([this](uint16_t handle) { this->connections.invalidate(handle); }));
+    connections.execute(address, [=](ConnectionManagementCallbacks* callbacks) {
+      if (delayed_role_change_ == nullptr) {
+        callbacks->OnRoleChange(hci::ErrorCode::SUCCESS, current_role);
+      } else if (delayed_role_change_->GetBdAddr() == address) {
         LOG_INFO("Sending delayed role change for %s", delayed_role_change_->GetBdAddr().ToString().c_str());
-        connection_proxy.connection_management_callbacks_->OnRoleChange(
-            delayed_role_change_->GetStatus(), delayed_role_change_->GetNewRole());
+        callbacks->OnRoleChange(delayed_role_change_->GetStatus(), delayed_role_change_->GetNewRole());
+        delayed_role_change_.reset();
       }
-      delayed_role_change_ = nullptr;
-    } else {
-      connection_proxy.connection_management_callbacks_->OnRoleChange(hci::ErrorCode::SUCCESS, current_role);
-    }
+    });
     client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectSuccess,
                                            common::Unretained(client_callbacks_), std::move(connection)));
     while (!pending_outgoing_connections_.empty()) {
@@ -306,6 +365,34 @@
     }
   }
 
+  void cancel_connect(Address address) {
+    if (outgoing_connecting_address_ == address) {
+      LOG_INFO("Cannot cancel non-existent connection to %s", address.ToString().c_str());
+      return;
+    }
+    std::unique_ptr<CreateConnectionCancelBuilder> packet = CreateConnectionCancelBuilder::Create(address);
+    acl_connection_interface_->EnqueueCommand(
+        std::move(packet), handler_->BindOnce(&check_command_complete<CreateConnectionCancelCompleteView>));
+  }
+
+  static constexpr bool kRemoveConnectionAfterwards = true;
+  void on_classic_disconnect(uint16_t handle, ErrorCode reason) {
+    bool event_also_routes_to_other_receivers = connections.crash_on_unknown_handle_;
+    connections.crash_on_unknown_handle_ = false;
+    connections.execute(
+        handle,
+        [=](ConnectionManagementCallbacks* callbacks) {
+          round_robin_scheduler_->Unregister(handle);
+          callbacks->OnDisconnection(reason);
+        },
+        kRemoveConnectionAfterwards);
+    // This handle is probably for SCO, so we use the callback workaround.
+    if (non_acl_disconnect_callback_ != nullptr) {
+      non_acl_disconnect_callback_(handle, static_cast<uint8_t>(reason));
+    }
+    connections.crash_on_unknown_handle_ = event_also_routes_to_other_receivers;
+  }
+
   void on_connection_packet_type_changed(EventView packet) {
     ConnectionPacketTypeChangedView packet_type_changed = ConnectionPacketTypeChangedView::Create(packet);
     if (!packet_type_changed.IsValid()) {
@@ -318,13 +405,9 @@
       return;
     }
     uint16_t handle = packet_type_changed.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    // We don't handle this event; we didn't do this in legacy stack either.
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      // We don't handle this event; we didn't do this in legacy stack either.
+    });
   }
 
   void on_central_link_key_complete(EventView packet) {
@@ -339,14 +422,10 @@
       return;
     }
     uint16_t handle = complete_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    KeyFlag key_flag = complete_view.GetKeyFlag();
-    callbacks->OnCentralLinkKeyComplete(key_flag);
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      KeyFlag key_flag = complete_view.GetKeyFlag();
+      callbacks->OnCentralLinkKeyComplete(key_flag);
+    });
   }
 
   void on_authentication_complete(EventView packet) {
@@ -356,49 +435,9 @@
       return;
     }
     uint16_t handle = authentication_complete.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnAuthenticationComplete(authentication_complete.GetStatus());
-  }
-
-  void cancel_connect(Address address) {
-    if (outgoing_connecting_address_ == address) {
-      LOG_INFO("Cannot cancel non-existent connection to %s", address.ToString().c_str());
-      return;
-    }
-    std::unique_ptr<CreateConnectionCancelBuilder> packet = CreateConnectionCancelBuilder::Create(address);
-    acl_connection_interface_->EnqueueCommand(
-        std::move(packet), handler_->BindOnce(&check_command_complete<CreateConnectionCancelCompleteView>));
-  }
-
-  void central_link_key(KeyFlag key_flag) {
-    std::unique_ptr<CentralLinkKeyBuilder> packet = CentralLinkKeyBuilder::Create(key_flag);
-    acl_connection_interface_->EnqueueCommand(
-        std::move(packet), handler_->BindOnce(&check_command_status<CentralLinkKeyStatusView>));
-  }
-
-  void switch_role(Address address, Role role) {
-    std::unique_ptr<SwitchRoleBuilder> packet = SwitchRoleBuilder::Create(address, role);
-    acl_connection_interface_->EnqueueCommand(std::move(packet),
-                                              handler_->BindOnce(&check_command_status<SwitchRoleStatusView>));
-  }
-
-  void write_default_link_policy_settings(uint16_t default_link_policy_settings) {
-    std::unique_ptr<WriteDefaultLinkPolicySettingsBuilder> packet =
-        WriteDefaultLinkPolicySettingsBuilder::Create(default_link_policy_settings);
-    acl_connection_interface_->EnqueueCommand(
-        std::move(packet), handler_->BindOnce(&check_command_complete<WriteDefaultLinkPolicySettingsCompleteView>));
-  }
-
-  void accept_connection(Address address) {
-    auto role = AcceptConnectionRequestRole::BECOME_CENTRAL;  // We prefer to be central
-    acl_connection_interface_->EnqueueCommand(
-        AcceptConnectionRequestBuilder::Create(address, role),
-        handler_->BindOnceOn(this, &classic_impl::on_accept_connection_status, address));
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      callbacks->OnAuthenticationComplete(authentication_complete.GetStatus());
+    });
   }
 
   void on_change_connection_link_key_complete(EventView packet) {
@@ -413,13 +452,8 @@
       return;
     }
     uint16_t handle = complete_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnChangeConnectionLinkKeyComplete();
+    connections.execute(
+        handle, [=](ConnectionManagementCallbacks* callbacks) { callbacks->OnChangeConnectionLinkKeyComplete(); });
   }
 
   void on_read_clock_offset_complete(EventView packet) {
@@ -434,14 +468,10 @@
       return;
     }
     uint16_t handle = complete_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    uint16_t clock_offset = complete_view.GetClockOffset();
-    callbacks->OnReadClockOffsetComplete(clock_offset);
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      uint16_t clock_offset = complete_view.GetClockOffset();
+      callbacks->OnReadClockOffsetComplete(clock_offset);
+    });
   }
 
   void on_mode_change(EventView packet) {
@@ -451,14 +481,10 @@
       return;
     }
     uint16_t handle = mode_change_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnModeChange(
-        mode_change_view.GetStatus(), mode_change_view.GetCurrentMode(), mode_change_view.GetInterval());
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      callbacks->OnModeChange(
+          mode_change_view.GetStatus(), mode_change_view.GetCurrentMode(), mode_change_view.GetInterval());
+    });
   }
 
   void on_sniff_subrating(EventView packet) {
@@ -468,18 +494,14 @@
       return;
     }
     uint16_t handle = sniff_subrating_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnSniffSubrating(
-        sniff_subrating_view.GetStatus(),
-        sniff_subrating_view.GetMaximumTransmitLatency(),
-        sniff_subrating_view.GetMaximumReceiveLatency(),
-        sniff_subrating_view.GetMinimumRemoteTimeout(),
-        sniff_subrating_view.GetMinimumLocalTimeout());
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      callbacks->OnSniffSubrating(
+          sniff_subrating_view.GetStatus(),
+          sniff_subrating_view.GetMaximumTransmitLatency(),
+          sniff_subrating_view.GetMaximumReceiveLatency(),
+          sniff_subrating_view.GetMinimumRemoteTimeout(),
+          sniff_subrating_view.GetMinimumLocalTimeout());
+    });
   }
 
   void on_qos_setup_complete(EventView packet) {
@@ -494,46 +516,14 @@
       return;
     }
     uint16_t handle = complete_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    ServiceType service_type = complete_view.GetServiceType();
-    uint32_t token_rate = complete_view.GetTokenRate();
-    uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
-    uint32_t latency = complete_view.GetLatency();
-    uint32_t delay_variation = complete_view.GetDelayVariation();
-    callbacks->OnQosSetupComplete(service_type, token_rate, peak_bandwidth, latency, delay_variation);
-  }
-
-  void on_role_change(EventView packet) {
-    RoleChangeView role_change_view = RoleChangeView::Create(packet);
-    if (!role_change_view.IsValid()) {
-      LOG_ERROR("Received on_role_change with invalid packet");
-      return;
-    }
-    bool sent = false;
-    auto hci_status = role_change_view.GetStatus();
-    Address bd_addr = role_change_view.GetBdAddr();
-    Role new_role = role_change_view.GetNewRole();
-    for (auto& connection_pair : acl_connections_) {
-      if (connection_pair.second.address_with_type_.GetAddress() == bd_addr) {
-        connection_pair.second.connection_management_callbacks_->OnRoleChange(hci_status, new_role);
-        sent = true;
-      }
-    }
-    if (!sent) {
-      if (delayed_role_change_ != nullptr) {
-        LOG_WARN("Second delayed role change (@%s dropped)", delayed_role_change_->GetBdAddr().ToString().c_str());
-      }
-      LOG_INFO(
-          "Role change for %s with no matching connection (new role: %s)",
-          role_change_view.GetBdAddr().ToString().c_str(),
-          RoleText(role_change_view.GetNewRole()).c_str());
-      delayed_role_change_ = std::make_unique<RoleChangeView>(role_change_view);
-    }
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      ServiceType service_type = complete_view.GetServiceType();
+      uint32_t token_rate = complete_view.GetTokenRate();
+      uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
+      uint32_t latency = complete_view.GetLatency();
+      uint32_t delay_variation = complete_view.GetDelayVariation();
+      callbacks->OnQosSetupComplete(service_type, token_rate, peak_bandwidth, latency, delay_variation);
+    });
   }
 
   void on_flow_specification_complete(EventView packet) {
@@ -548,20 +538,16 @@
       return;
     }
     uint16_t handle = complete_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    FlowDirection flow_direction = complete_view.GetFlowDirection();
-    ServiceType service_type = complete_view.GetServiceType();
-    uint32_t token_rate = complete_view.GetTokenRate();
-    uint32_t token_bucket_size = complete_view.GetTokenBucketSize();
-    uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
-    uint32_t access_latency = complete_view.GetAccessLatency();
-    callbacks->OnFlowSpecificationComplete(
-        flow_direction, service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      FlowDirection flow_direction = complete_view.GetFlowDirection();
+      ServiceType service_type = complete_view.GetServiceType();
+      uint32_t token_rate = complete_view.GetTokenRate();
+      uint32_t token_bucket_size = complete_view.GetTokenBucketSize();
+      uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
+      uint32_t access_latency = complete_view.GetAccessLatency();
+      callbacks->OnFlowSpecificationComplete(
+          flow_direction, service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
+    });
   }
 
   void on_flush_occurred(EventView packet) {
@@ -571,51 +557,78 @@
       return;
     }
     uint16_t handle = flush_occurred_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnFlushOccurred();
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) { callbacks->OnFlushOccurred(); });
   }
 
   void on_read_remote_version_information(
       hci::ErrorCode hci_status, uint16_t handle, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version) {
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnReadRemoteVersionInformationComplete(hci_status, version, manufacturer_name, sub_version);
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      callbacks->OnReadRemoteVersionInformationComplete(hci_status, version, manufacturer_name, sub_version);
+    });
   }
 
   void on_read_remote_supported_features_complete(EventView packet) {
     auto view = ReadRemoteSupportedFeaturesCompleteView::Create(packet);
     ASSERT_LOG(view.IsValid(), "Read remote supported features packet invalid");
     uint16_t handle = view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnReadRemoteSupportedFeaturesComplete(view.GetLmpFeatures());
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      callbacks->OnReadRemoteSupportedFeaturesComplete(view.GetLmpFeatures());
+    });
   }
 
   void on_read_remote_extended_features_complete(EventView packet) {
     auto view = ReadRemoteExtendedFeaturesCompleteView::Create(packet);
     ASSERT_LOG(view.IsValid(), "Read remote extended features packet invalid");
     uint16_t handle = view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      callbacks->OnReadRemoteExtendedFeaturesComplete(
+          view.GetPageNumber(), view.GetMaximumPageNumber(), view.GetExtendedLmpFeatures());
+    });
+  }
+
+  void OnEncryptionStateChanged(EncryptionChangeView encryption_change_view) override {
+    if (!encryption_change_view.IsValid()) {
+      LOG_ERROR("Invalid packet");
+      return;
+    } else if (encryption_change_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = encryption_change_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("error_code %s", error_code.c_str());
       return;
     }
-    callbacks->OnReadRemoteExtendedFeaturesComplete(
-        view.GetPageNumber(), view.GetMaximumPageNumber(), view.GetExtendedLmpFeatures());
+    uint16_t handle = encryption_change_view.GetConnectionHandle();
+    connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+      EncryptionEnabled enabled = encryption_change_view.GetEncryptionEnabled();
+      callbacks->OnEncryptionChange(enabled);
+    });
+  }
+
+  void on_role_change(EventView packet) {
+    RoleChangeView role_change_view = RoleChangeView::Create(packet);
+    if (!role_change_view.IsValid()) {
+      LOG_ERROR("Received on_role_change with invalid packet");
+      return;
+    }
+    auto hci_status = role_change_view.GetStatus();
+    Address bd_addr = role_change_view.GetBdAddr();
+    Role new_role = role_change_view.GetNewRole();
+    bool sent = false;
+    connections.execute(bd_addr, [=, &sent](ConnectionManagementCallbacks* callbacks) {
+      if (callbacks != nullptr) {
+        callbacks->OnRoleChange(hci_status, new_role);
+        sent = true;
+      }
+    });
+    if (!sent) {
+      if (delayed_role_change_ != nullptr) {
+        LOG_WARN("Second delayed role change (@%s dropped)", delayed_role_change_->GetBdAddr().ToString().c_str());
+      }
+      LOG_INFO(
+          "Role change for %s with no matching connection (new role: %s)",
+          role_change_view.GetBdAddr().ToString().c_str(),
+          RoleText(role_change_view.GetNewRole()).c_str());
+      delayed_role_change_ = std::make_unique<RoleChangeView>(role_change_view);
+    }
   }
 
   void on_link_supervision_timeout_changed(EventView packet) {
@@ -632,6 +645,32 @@
     }
   }
 
+  void central_link_key(KeyFlag key_flag) {
+    std::unique_ptr<CentralLinkKeyBuilder> packet = CentralLinkKeyBuilder::Create(key_flag);
+    acl_connection_interface_->EnqueueCommand(
+        std::move(packet), handler_->BindOnce(&check_command_status<CentralLinkKeyStatusView>));
+  }
+
+  void switch_role(Address address, Role role) {
+    std::unique_ptr<SwitchRoleBuilder> packet = SwitchRoleBuilder::Create(address, role);
+    acl_connection_interface_->EnqueueCommand(
+        std::move(packet), handler_->BindOnce(&check_command_status<SwitchRoleStatusView>));
+  }
+
+  void write_default_link_policy_settings(uint16_t default_link_policy_settings) {
+    std::unique_ptr<WriteDefaultLinkPolicySettingsBuilder> packet =
+        WriteDefaultLinkPolicySettingsBuilder::Create(default_link_policy_settings);
+    acl_connection_interface_->EnqueueCommand(
+        std::move(packet), handler_->BindOnce(&check_command_complete<WriteDefaultLinkPolicySettingsCompleteView>));
+  }
+
+  void accept_connection(Address address) {
+    auto role = AcceptConnectionRequestRole::BECOME_CENTRAL;  // We prefer to be central
+    acl_connection_interface_->EnqueueCommand(
+        AcceptConnectionRequestBuilder::Create(address, role),
+        handler_->BindOnceOn(this, &classic_impl::on_accept_connection_status, address));
+  }
+
   void reject_connection(std::unique_ptr<RejectConnectionRequestBuilder> builder) {
     acl_connection_interface_->EnqueueCommand(
         std::move(builder), handler_->BindOnce(&check_command_status<RejectConnectionRequestStatusView>));
@@ -641,45 +680,33 @@
   void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) override {}
   void OnDeviceBondFailed(bluetooth::hci::AddressWithType device, security::PairingFailure status) override {}
 
-  void OnEncryptionStateChanged(EncryptionChangeView encryption_change_view) override {
-    if (!encryption_change_view.IsValid()) {
-      LOG_ERROR("Invalid packet");
-      return;
-    } else if (encryption_change_view.GetStatus() != ErrorCode::SUCCESS) {
-      auto status = encryption_change_view.GetStatus();
-      std::string error_code = ErrorCodeText(status);
-      LOG_ERROR("error_code %s", error_code.c_str());
-      return;
-    }
-    uint16_t handle = encryption_change_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Unknown connection handle 0x%04hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    EncryptionEnabled enabled = encryption_change_view.GetEncryptionEnabled();
-    callbacks->OnEncryptionChange(enabled);
-  }
-
   void set_security_module(security::SecurityModule* security_module) {
     security_manager_ = security_module->GetSecurityManager();
     security_manager_->RegisterCallbackListener(this, handler_);
   }
 
   uint16_t HACK_get_handle(Address address) {
-    for (auto it = acl_connections_.begin(); it != acl_connections_.end(); it++) {
-      if (it->second.address_with_type_.GetAddress() == address) {
-        return it->first;
-      }
-    }
-    return 0xFFFF;
+    return connections.HACK_get_handle(address);
   }
 
   void HACK_SetNonAclDisconnectCallback(std::function<void(uint16_t, uint8_t)> callback) {
     non_acl_disconnect_callback_ = callback;
   }
 
+  void handle_register_callbacks(ConnectionCallbacks* callbacks, os::Handler* handler) {
+    ASSERT(client_callbacks_ == nullptr);
+    ASSERT(client_handler_ == nullptr);
+    client_callbacks_ = callbacks;
+    client_handler_ = handler;
+  }
+
+  void handle_unregister_callbacks(ConnectionCallbacks* callbacks, std::promise<void> promise) {
+    ASSERT_LOG(client_callbacks_ == callbacks, "Registered callback entity is different then unregister request");
+    client_callbacks_ = nullptr;
+    client_handler_ = nullptr;
+    promise.set_value();
+  }
+
   HciLayer* hci_layer_ = nullptr;
   Controller* controller_ = nullptr;
   RoundRobinScheduler* round_robin_scheduler_ = nullptr;
@@ -687,7 +714,6 @@
   os::Handler* handler_ = nullptr;
   ConnectionCallbacks* client_callbacks_ = nullptr;
   os::Handler* client_handler_ = nullptr;
-  std::map<uint16_t, acl_connection> acl_connections_;
   Address outgoing_connecting_address_{Address::kEmpty};
   Address incoming_connecting_address_{Address::kEmpty};
   common::Callback<bool(Address, ClassOfDevice)> should_accept_connection_;
@@ -695,7 +721,6 @@
   std::unique_ptr<RoleChangeView> delayed_role_change_ = nullptr;
 
   std::unique_ptr<security::SecurityManager> security_manager_;
-  bool crash_on_unknown_handle_ = false;
 
   std::function<void(uint16_t, uint8_t)> non_acl_disconnect_callback_;
 };
diff --git a/system/gd/hci/acl_manager/le_acl_connection.cc b/system/gd/hci/acl_manager/le_acl_connection.cc
index 400f2f6..d57aab2 100644
--- a/system/gd/hci/acl_manager/le_acl_connection.cc
+++ b/system/gd/hci/acl_manager/le_acl_connection.cc
@@ -85,16 +85,17 @@
 struct LeAclConnection::impl {
   impl(LeAclConnectionInterface* le_acl_connection_interface, std::shared_ptr<Queue> queue, uint16_t connection_handle)
       : queue_(std::move(queue)), tracker(le_acl_connection_interface, connection_handle) {}
-  LeConnectionManagementCallbacks* GetEventCallbacks() {
-    ASSERT(!callbacks_given_);
-    callbacks_given_ = true;
+  LeConnectionManagementCallbacks* GetEventCallbacks(std::function<void(uint16_t)> invalidate_callbacks) {
+    ASSERT_LOG(!invalidate_callbacks_, "Already returned event callbacks for this connection");
+    invalidate_callbacks_ = std::move(invalidate_callbacks);
     return &tracker;
   }
-
-  bool callbacks_given_{false};
+  void PutEventCallbacks() {
+    if (invalidate_callbacks_) invalidate_callbacks_(tracker.connection_handle_);
+  }
   std::shared_ptr<Queue> queue_;
   LeAclConnectionTracker tracker;
-  std::shared_ptr<std::atomic<bool>> is_callback_valid_;
+  std::function<void(uint16_t)> invalidate_callbacks_;
 };
 
 LeAclConnection::LeAclConnection()
@@ -116,7 +117,7 @@
 }
 
 LeAclConnection::~LeAclConnection() {
-  if (pimpl_->is_callback_valid_) *pimpl_->is_callback_valid_ = false;
+  pimpl_->PutEventCallbacks();
   delete pimpl_;
 }
 
@@ -140,9 +141,8 @@
 }
 
 LeConnectionManagementCallbacks* LeAclConnection::GetEventCallbacks(
-    std::shared_ptr<std::atomic<bool>> is_callback_valid) {
-  pimpl_->is_callback_valid_ = is_callback_valid;
-  return pimpl_->GetEventCallbacks();
+    std::function<void(uint16_t)> invalidate_callbacks) {
+  return pimpl_->GetEventCallbacks(std::move(invalidate_callbacks));
 }
 
 bool LeAclConnection::LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
diff --git a/system/gd/hci/acl_manager/le_acl_connection.h b/system/gd/hci/acl_manager/le_acl_connection.h
index 7dc3a86..fd81111 100644
--- a/system/gd/hci/acl_manager/le_acl_connection.h
+++ b/system/gd/hci/acl_manager/le_acl_connection.h
@@ -71,7 +71,7 @@
   // TODO implement LeRemoteConnectionParameterRequestReply, LeRemoteConnectionParameterRequestNegativeReply
 
   // Called once before passing the connection to the client
-  virtual LeConnectionManagementCallbacks* GetEventCallbacks(std::shared_ptr<std::atomic<bool>> is_callback_valid);
+  virtual LeConnectionManagementCallbacks* GetEventCallbacks(std::function<void(uint16_t)> invalidate_callbacks);
 
  private:
   virtual bool check_connection_parameters(
diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h
index 0da24b6..b666fbd 100644
--- a/system/gd/hci/acl_manager/le_impl.h
+++ b/system/gd/hci/acl_manager/le_impl.h
@@ -48,12 +48,14 @@
 
 struct le_acl_connection {
   le_acl_connection(AddressWithType remote_address, AclConnection::QueueDownEnd* queue_down_end, os::Handler* handler)
-      : assembler_(remote_address, queue_down_end, handler), remote_address_(remote_address) {}
-  ~le_acl_connection() = default;
-  struct acl_manager::assembler assembler_;
+      : remote_address_(remote_address),
+        assembler_(new acl_manager::assembler(remote_address, queue_down_end, handler)) {}
+  ~le_acl_connection() {
+    delete assembler_;
+  }
   AddressWithType remote_address_;
+  acl_manager::assembler* assembler_;
   LeConnectionManagementCallbacks* le_connection_management_callbacks_ = nullptr;
-  std::shared_ptr<std::atomic<bool>> is_callback_valid_ = std::make_shared<std::atomic<bool>>(true);
 };
 
 struct le_impl : public bluetooth::hci::LeAddressManagerCallback {
@@ -63,13 +65,11 @@
       os::Handler* handler,
       RoundRobinScheduler* round_robin_scheduler,
       bool crash_on_unknown_handle)
-      : hci_layer_(hci_layer),
-        controller_(controller),
-        round_robin_scheduler_(round_robin_scheduler),
-        crash_on_unknown_handle_(crash_on_unknown_handle) {
+      : hci_layer_(hci_layer), controller_(controller), round_robin_scheduler_(round_robin_scheduler) {
     hci_layer_ = hci_layer;
     controller_ = controller;
     handler_ = handler;
+    connections.crash_on_unknown_handle_ = crash_on_unknown_handle;
     le_acl_connection_interface_ = hci_layer_->GetLeAclConnectionInterface(
         handler_->BindOn(this, &le_impl::on_le_event),
         handler_->BindOn(this, &le_impl::on_le_disconnect),
@@ -88,7 +88,7 @@
     }
     delete le_address_manager_;
     hci_layer_->PutLeAclConnectionInterface();
-    le_acl_connections_.clear();
+    connections.reset();
   }
 
   void on_le_event(LeMetaEventView event_packet) {
@@ -117,22 +117,91 @@
     }
   }
 
-  LeConnectionManagementCallbacks* get_callbacks(uint16_t handle) {
-    auto connection = le_acl_connections_.find(handle);
-    if (connection == le_acl_connections_.end()) {
-      return nullptr;
+ private:
+  static constexpr uint16_t kIllegalConnectionHandle = 0xffff;
+  struct {
+   private:
+    std::map<uint16_t, le_acl_connection> le_acl_connections_;
+    mutable std::mutex le_acl_connections_guard_;
+    LeConnectionManagementCallbacks* find_callbacks(uint16_t handle) {
+      auto connection = le_acl_connections_.find(handle);
+      if (connection == le_acl_connections_.end()) return nullptr;
+      return connection->second.le_connection_management_callbacks_;
     }
-    return (connection->second.is_callback_valid_) ? connection->second.le_connection_management_callbacks_ : nullptr;
+    void remove(uint16_t handle) {
+      auto connection = le_acl_connections_.find(handle);
+      if (connection != le_acl_connections_.end()) {
+        connection->second.le_connection_management_callbacks_ = nullptr;
+        le_acl_connections_.erase(handle);
+      }
+    }
+
+   public:
+    bool crash_on_unknown_handle_ = false;
+    bool is_empty() const {
+      std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+      return le_acl_connections_.empty();
+    }
+    void reset() {
+      std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+      le_acl_connections_.clear();
+    }
+    void invalidate(uint16_t handle) {
+      std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+      remove(handle);
+    }
+    void execute(
+        uint16_t handle,
+        std::function<void(LeConnectionManagementCallbacks* callbacks)> execute,
+        bool remove_afterwards = false) {
+      std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+      auto callbacks = find_callbacks(handle);
+      if (callbacks != nullptr)
+        execute(callbacks);
+      else
+        ASSERT_LOG(!crash_on_unknown_handle_, "Received command for unknown handle:0x%x", handle);
+      if (remove_afterwards) remove(handle);
+    }
+    bool send_packet_upward(uint16_t handle, std::function<void(struct acl_manager::assembler* assembler)> cb) {
+      std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+      auto connection = le_acl_connections_.find(handle);
+      if (connection != le_acl_connections_.end()) cb(connection->second.assembler_);
+      return connection != le_acl_connections_.end();
+    }
+    void add(
+        uint16_t handle,
+        const AddressWithType& remote_address,
+        AclConnection::QueueDownEnd* queue_end,
+        os::Handler* handler,
+        LeConnectionManagementCallbacks* le_connection_management_callbacks) {
+      std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+      auto emplace_pair = le_acl_connections_.emplace(
+          std::piecewise_construct,
+          std::forward_as_tuple(handle),
+          std::forward_as_tuple(remote_address, queue_end, handler));
+      ASSERT(emplace_pair.second);  // Make sure the connection is unique
+      emplace_pair.first->second.le_connection_management_callbacks_ = le_connection_management_callbacks;
+    }
+    uint16_t HACK_get_handle(Address address) const {
+      std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+      for (auto it = le_acl_connections_.begin(); it != le_acl_connections_.end(); it++) {
+        if (it->second.remote_address_.GetAddress() == address) {
+          return it->first;
+        }
+      }
+      return kIllegalConnectionHandle;
+    }
+  } connections;
+
+ public:
+  void enqueue_command(std::unique_ptr<CommandBuilder> command_packet) {
+    hci_layer_->EnqueueCommand(
+        std::move(command_packet),
+        handler_->BindOnce(&LeAddressManager::OnCommandComplete, common::Unretained(le_address_manager_)));
   }
 
-  void on_le_disconnect(uint16_t handle, ErrorCode reason) {
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      return;
-    }
-    round_robin_scheduler_->Unregister(handle);
-    callbacks->OnDisconnection(reason);
-    le_acl_connections_.erase(handle);
+  bool send_packet_upward(uint16_t handle, std::function<void(struct acl_manager::assembler* assembler)> cb) {
+    return connections.send_packet_upward(handle, cb);
   }
 
   void on_common_le_connection_complete(AddressWithType address_with_type) {
@@ -194,18 +263,15 @@
     auto role = connection_complete.GetRole();
     uint16_t handle = connection_complete.GetConnectionHandle();
     auto queue = std::make_shared<AclConnection::Queue>(10);
-    auto emplace_pair = le_acl_connections_.emplace(
-        std::piecewise_construct,
-        std::forward_as_tuple(handle),
-        std::forward_as_tuple(remote_address, queue->GetDownEnd(), handler_));
-    ASSERT(emplace_pair.second);  // Make sure the connection is unique
-    auto& connection_proxy = emplace_pair.first->second;
+    auto queue_down_end = queue->GetDownEnd();
     round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, handle, queue);
     std::unique_ptr<LeAclConnection> connection(new LeAclConnection(
         std::move(queue), le_acl_connection_interface_, handle, local_address, remote_address, role));
     connection->peer_address_with_type_ = AddressWithType(address, peer_address_type);
-    connection_proxy.le_connection_management_callbacks_ =
-        connection->GetEventCallbacks(connection_proxy.is_callback_valid_);
+    connections.add(
+        handle, remote_address, queue_down_end, handler_, connection->GetEventCallbacks([this](uint16_t handle) {
+          this->connections.invalidate(handle);
+        }));
     le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
                                               common::Unretained(le_client_callbacks_), remote_address,
                                               std::move(connection)));
@@ -267,23 +333,34 @@
     }
     uint16_t handle = connection_complete.GetConnectionHandle();
     auto queue = std::make_shared<AclConnection::Queue>(10);
-    auto emplace_pair = le_acl_connections_.emplace(
-        std::piecewise_construct,
-        std::forward_as_tuple(handle),
-        std::forward_as_tuple(remote_address, queue->GetDownEnd(), handler_));
-    ASSERT(emplace_pair.second);  // Make sure it's not a duplicate
-    auto& connection_proxy = emplace_pair.first->second;
+    auto queue_down_end = queue->GetDownEnd();
     round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, handle, queue);
     std::unique_ptr<LeAclConnection> connection(new LeAclConnection(
         std::move(queue), le_acl_connection_interface_, handle, local_address, remote_address, role));
     connection->peer_address_with_type_ = AddressWithType(address, peer_address_type);
-    connection_proxy.le_connection_management_callbacks_ =
-        connection->GetEventCallbacks(connection_proxy.is_callback_valid_);
+    connections.add(
+        handle, remote_address, queue_down_end, handler_, connection->GetEventCallbacks([this](uint16_t handle) {
+          this->connections.invalidate(handle);
+        }));
     le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
                                               common::Unretained(le_client_callbacks_), remote_address,
                                               std::move(connection)));
   }
 
+  static constexpr bool kRemoveConnectionAfterwards = true;
+  void on_le_disconnect(uint16_t handle, ErrorCode reason) {
+    bool event_also_routes_to_other_receivers = connections.crash_on_unknown_handle_;
+    connections.crash_on_unknown_handle_ = false;
+    connections.execute(
+        handle,
+        [=](LeConnectionManagementCallbacks* callbacks) {
+          round_robin_scheduler_->Unregister(handle);
+          callbacks->OnDisconnection(reason);
+        },
+        kRemoveConnectionAfterwards);
+    connections.crash_on_unknown_handle_ = event_also_routes_to_other_receivers;
+  }
+
   void on_le_connection_update_complete(LeMetaEventView view) {
     auto complete_view = LeConnectionUpdateCompleteView::Create(view);
     if (!complete_view.IsValid()) {
@@ -291,17 +368,13 @@
       return;
     }
     auto handle = complete_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Can't find connection 0x%hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnConnectionUpdate(
-        complete_view.GetStatus(),
-        complete_view.GetConnInterval(),
-        complete_view.GetConnLatency(),
-        complete_view.GetSupervisionTimeout());
+    connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+      callbacks->OnConnectionUpdate(
+          complete_view.GetStatus(),
+          complete_view.GetConnInterval(),
+          complete_view.GetConnLatency(),
+          complete_view.GetSupervisionTimeout());
+    });
   }
 
   void on_le_phy_update_complete(LeMetaEventView view) {
@@ -311,29 +384,16 @@
       return;
     }
     auto handle = complete_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Can't find connection 0x%hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnPhyUpdate(complete_view.GetStatus(), complete_view.GetTxPhy(), complete_view.GetRxPhy());
+    connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+      callbacks->OnPhyUpdate(complete_view.GetStatus(), complete_view.GetTxPhy(), complete_view.GetRxPhy());
+    });
   }
 
   void on_le_read_remote_version_information(
       hci::ErrorCode hci_status, uint16_t handle, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version) {
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_INFO("No le connection registered for 0x%hx", handle);
-      return;
-    }
-    callbacks->OnReadRemoteVersionInformationComplete(hci_status, version, manufacturer_name, sub_version);
-  }
-
-  void enqueue_command(std::unique_ptr<CommandBuilder> command_packet) {
-    hci_layer_->EnqueueCommand(
-        std::move(command_packet),
-        handler_->BindOnce(&LeAddressManager::OnCommandComplete, common::Unretained(le_address_manager_)));
+    connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+      callbacks->OnReadRemoteVersionInformationComplete(hci_status, version, manufacturer_name, sub_version);
+    });
   }
 
   void on_data_length_change(LeMetaEventView view) {
@@ -343,17 +403,13 @@
       return;
     }
     auto handle = data_length_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Can't find connection 0x%hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnDataLengthChange(
-        data_length_view.GetMaxTxOctets(),
-        data_length_view.GetMaxTxTime(),
-        data_length_view.GetMaxRxOctets(),
-        data_length_view.GetMaxRxTime());
+    connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+      callbacks->OnDataLengthChange(
+          data_length_view.GetMaxTxOctets(),
+          data_length_view.GetMaxTxTime(),
+          data_length_view.GetMaxRxOctets(),
+          data_length_view.GetMaxRxTime());
+    });
   }
 
   void on_remote_connection_parameter_request(LeMetaEventView view) {
@@ -364,24 +420,30 @@
     }
 
     auto handle = request_view.GetConnectionHandle();
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Can't find connection 0x%hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    // TODO: this is blindly accepting any parameters, just so we don't hang connection
-    // have proper parameter negotiation
-    le_acl_connection_interface_->EnqueueCommand(
-        LeRemoteConnectionParameterRequestReplyBuilder::Create(
-            handle,
-            request_view.GetIntervalMin(),
-            request_view.GetIntervalMax(),
-            request_view.GetLatency(),
-            request_view.GetTimeout(),
-            0,
-            0),
-        handler_->BindOnce([](CommandCompleteView status) {}));
+    connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+      // TODO: this is blindly accepting any parameters, just so we don't hang connection
+      // have proper parameter negotiation
+      le_acl_connection_interface_->EnqueueCommand(
+          LeRemoteConnectionParameterRequestReplyBuilder::Create(
+              handle,
+              request_view.GetIntervalMin(),
+              request_view.GetIntervalMax(),
+              request_view.GetLatency(),
+              request_view.GetTimeout(),
+              0,
+              0),
+          handler_->BindOnce([](CommandCompleteView status) {}));
+    });
+  }
+
+  uint16_t HACK_get_handle(Address address) {
+    return connections.HACK_get_handle(address);
+  }
+
+  void UpdateLocalAddress(uint16_t handle, hci::AddressWithType address_with_type) {
+    connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+      callbacks->OnLocalAddressUpdate(address_with_type);
+    });
   }
 
   void add_device_to_connect_list(AddressWithType address_with_type) {
@@ -705,25 +767,6 @@
     }
   }
 
-  uint16_t HACK_get_handle(Address address) {
-    for (auto it = le_acl_connections_.begin(); it != le_acl_connections_.end(); it++) {
-      if (it->second.remote_address_.GetAddress() == address) {
-        return it->first;
-      }
-    }
-    return 0xFFFF;
-  }
-
-  void UpdateLocalAddress(uint16_t handle, hci::AddressWithType address_with_type) {
-    auto callbacks = get_callbacks(handle);
-    if (callbacks == nullptr) {
-      LOG_WARN("Can't find connection 0x%hx", handle);
-      ASSERT(!crash_on_unknown_handle_);
-      return;
-    }
-    callbacks->OnLocalAddressUpdate(address_with_type);
-  }
-
   void register_with_address_manager() {
     if (!address_manager_registered) {
       le_address_manager_->Register(this);
@@ -733,7 +776,7 @@
   }
 
   void check_for_unregister() {
-    if (le_acl_connections_.empty() && connecting_le_.empty() && address_manager_registered && ready_to_unregister) {
+    if (connections.is_empty() && connecting_le_.empty() && address_manager_registered && ready_to_unregister) {
       le_address_manager_->Unregister(this);
       address_manager_registered = false;
       pause_connection = false;
@@ -751,7 +794,6 @@
   LeAclConnectionInterface* le_acl_connection_interface_ = nullptr;
   LeConnectionCallbacks* le_client_callbacks_ = nullptr;
   os::Handler* le_client_handler_ = nullptr;
-  std::map<uint16_t, le_acl_connection> le_acl_connections_;
   std::set<AddressWithType> connecting_le_;
   std::set<AddressWithType> canceled_connections_;
   std::set<AddressWithType> direct_connections_;
@@ -760,7 +802,6 @@
   bool address_manager_registered = false;
   bool ready_to_unregister = false;
   bool pause_connection = false;
-  bool crash_on_unknown_handle_ = false;
   std::map<AddressWithType, os::Alarm> create_connection_timeout_alarms_;
 };
 
diff --git a/system/gd/hci/hci_layer.cc b/system/gd/hci/hci_layer.cc
index 37d00f3..7e73e7b 100644
--- a/system/gd/hci/hci_layer.cc
+++ b/system/gd/hci/hci_layer.cc
@@ -487,6 +487,7 @@
 }
 
 void HciLayer::Disconnect(uint16_t handle, ErrorCode reason) {
+  std::unique_lock<std::mutex> lock(callback_handlers_guard_);
   for (auto callback : disconnect_handlers_) {
     callback.Invoke(handle, reason);
   }
@@ -505,6 +506,7 @@
 
 void HciLayer::ReadRemoteVersion(
     hci::ErrorCode hci_status, uint16_t handle, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version) {
+  std::unique_lock<std::mutex> lock(callback_handlers_guard_);
   for (auto callback : read_remote_version_handlers_) {
     callback.Invoke(hci_status, handle, version, manufacturer_name, sub_version);
   }
@@ -516,8 +518,11 @@
     ContextualCallback<
         void(hci::ErrorCode hci_status, uint16_t, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version)>
         on_read_remote_version) {
-  disconnect_handlers_.push_back(on_disconnect);
-  read_remote_version_handlers_.push_back(on_read_remote_version);
+  {
+    std::unique_lock<std::mutex> lock(callback_handlers_guard_);
+    disconnect_handlers_.push_back(on_disconnect);
+    read_remote_version_handlers_.push_back(on_read_remote_version);
+  }
   for (const auto event : AclConnectionEvents) {
     RegisterEventHandler(event, event_handler);
   }
@@ -528,8 +533,11 @@
   for (const auto event : AclConnectionEvents) {
     UnregisterEventHandler(event);
   }
-  disconnect_handlers_.clear();
-  read_remote_version_handlers_.clear();
+  {
+    std::unique_lock<std::mutex> lock(callback_handlers_guard_);
+    disconnect_handlers_.clear();
+    read_remote_version_handlers_.clear();
+  }
 }
 
 LeAclConnectionInterface* HciLayer::GetLeAclConnectionInterface(
@@ -538,8 +546,11 @@
     ContextualCallback<
         void(hci::ErrorCode hci_status, uint16_t, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version)>
         on_read_remote_version) {
-  disconnect_handlers_.push_back(on_disconnect);
-  read_remote_version_handlers_.push_back(on_read_remote_version);
+  {
+    std::unique_lock<std::mutex> lock(callback_handlers_guard_);
+    disconnect_handlers_.push_back(on_disconnect);
+    read_remote_version_handlers_.push_back(on_read_remote_version);
+  }
   for (const auto event : LeConnectionManagementEvents) {
     RegisterLeEventHandler(event, event_handler);
   }
@@ -550,8 +561,11 @@
   for (const auto event : LeConnectionManagementEvents) {
     UnregisterLeEventHandler(event);
   }
-  disconnect_handlers_.clear();
-  read_remote_version_handlers_.clear();
+  {
+    std::unique_lock<std::mutex> lock(callback_handlers_guard_);
+    disconnect_handlers_.clear();
+    read_remote_version_handlers_.clear();
+  }
 }
 
 SecurityInterface* HciLayer::GetSecurityInterface(ContextualCallback<void(EventView)> event_handler) {
diff --git a/system/gd/hci/hci_layer.h b/system/gd/hci/hci_layer.h
index 3f4511d..8ff74d3 100644
--- a/system/gd/hci/hci_layer.h
+++ b/system/gd/hci/hci_layer.h
@@ -143,6 +143,7 @@
   std::list<common::ContextualCallback<void(uint16_t, ErrorCode)>> disconnect_handlers_;
   std::list<common::ContextualCallback<void(hci::ErrorCode, uint16_t, uint8_t, uint16_t, uint16_t)>>
       read_remote_version_handlers_;
+  std::mutex callback_handlers_guard_;
   void on_disconnection_complete(EventView event_view);
   void on_read_remote_version_complete(EventView event_view);
 
diff --git a/system/gd/packet/parser/Android.bp b/system/gd/packet/parser/Android.bp
index 41901de..aaa0805 100644
--- a/system/gd/packet/parser/Android.bp
+++ b/system/gd/packet/parser/Android.bp
@@ -33,10 +33,10 @@
         "fields/variable_length_struct_field.cc",
         "checksum_def.cc",
         "custom_field_def.cc",
-        "packet_dependency.cc",
         "enum_def.cc",
         "enum_gen.cc",
         "packet_def.cc",
+        "packet_dependency.cc",
         "parent_def.cc",
         "struct_def.cc",
         "struct_parser_generator.cc",
diff --git a/system/gd/packet/parser/BUILD.gn b/system/gd/packet/parser/BUILD.gn
index 3964551..c9ac6c3 100644
--- a/system/gd/packet/parser/BUILD.gn
+++ b/system/gd/packet/parser/BUILD.gn
@@ -62,6 +62,7 @@
     "gen_rust.cc",
     "main.cc",
     "packet_def.cc",
+    "packet_dependency.cc",
     "parent_def.cc",
     "struct_def.cc",
     "struct_parser_generator.cc",
diff --git a/system/gd/packet/parser/packet_def.cc b/system/gd/packet/parser/packet_def.cc
index 82ebda6..0229360 100644
--- a/system/gd/packet/parser/packet_def.cc
+++ b/system/gd/packet/parser/packet_def.cc
@@ -21,6 +21,7 @@
 #include <set>
 
 #include "fields/all_fields.h"
+#include "packet_dependency.h"
 #include "util.h"
 
 PacketDef::PacketDef(std::string name, FieldList fields) : ParentDef(name, fields, nullptr) {}
@@ -883,8 +884,9 @@
 }
 
 void PacketDef::GenRustStructImpls(std::ostream& s) const {
-  s << "impl " << name_ << "Data {";
+  auto packet_dep = PacketDependency(GetRootDef());
 
+  s << "impl " << name_ << "Data {";
   // conforms function
   s << "fn conforms(bytes: &[u8]) -> bool {";
   GenRustConformanceCheck(s);
@@ -904,15 +906,15 @@
   s << " true";
   s << "}";
 
-  // parse function
-  if (parent_constraints_.empty() && children_.size() > 1 && parent_ != nullptr) {
-    auto constraint = FindConstraintField();
-    auto constraint_field = GetParamList().GetField(constraint);
+  auto parse_params = packet_dep.GetDependencies(name_);
+  s << "fn parse(bytes: &[u8]";
+  for (auto field_name : parse_params) {
+    auto constraint_field = GetParamList().GetField(field_name);
     auto constraint_type = constraint_field->GetRustDataType();
-    s << "fn parse(bytes: &[u8], " << constraint << ": " << constraint_type << ") -> Result<Self> {";
-  } else {
-    s << "fn parse(bytes: &[u8]) -> Result<Self> {";
+    s << ", " << field_name << ": " << constraint_type;
   }
+  s << ") -> Result<Self> {";
+
   fields = fields_.GetFieldsWithoutTypes({
       BodyField::kFieldType,
   });
@@ -940,51 +942,123 @@
     payload_offset = GetOffsetForField(payload_field[0]->GetName(), false);
   }
 
-  auto constraint_name = FindConstraintField();
-  auto constrained_descendants = FindDescendantsWithConstraint(constraint_name);
-
   if (children_.size() > 1) {
-    s << "let child = match " << constraint_name << " {";
+    auto match_on_variables = packet_dep.GetChildrenDependencies(name_);
+    // If match_on_variables is empty, this means there are multiple abstract packets which will
+    // specialize to a child down the packet tree.
+    // In this case match variables will be the union of parent fields and parse params of children.
+    if (match_on_variables.empty()) {
+      for (auto& field : fields_) {
+        if (std::any_of(children_.begin(), children_.end(), [&](auto child) {
+              auto pass_me = packet_dep.GetDependencies(child->name_);
+              return std::find(pass_me.begin(), pass_me.end(), field->GetName()) != pass_me.end();
+            })) {
+          match_on_variables.push_back(field->GetName());
+        }
+      }
+    }
 
-    for (const auto& desc : constrained_descendants) {
-      auto desc_path = FindPathToDescendant(desc.first->name_);
-      std::reverse(desc_path.begin(), desc_path.end());
-      auto constraint_field = GetParamList().GetField(constraint_name);
+    s << "let child = match (";
+
+    for (auto var : match_on_variables) {
+      if (var == match_on_variables[match_on_variables.size() - 1]) {
+        s << var;
+      } else {
+        s << var << ", ";
+      }
+    }
+    s << ") {";
+
+    auto get_match_val = [&](
+        std::string& match_var,
+        std::variant<int64_t,
+        std::string> constraint) -> std::string {
+      auto constraint_field = GetParamList().GetField(match_var);
       auto constraint_type = constraint_field->GetFieldType();
 
       if (constraint_type == EnumField::kFieldType) {
-        auto type = std::get<std::string>(desc.second);
+        auto type = std::get<std::string>(constraint);
         auto variant_name = type.substr(type.find("::") + 2, type.length());
         auto enum_type = type.substr(0, type.find("::"));
-        auto enum_variant = enum_type + "::"
-            + util::UnderscoreToCamelCase(util::ToLowerCase(variant_name));
-        s << enum_variant;
-        s << " if " << desc_path[0]->name_ << "Data::conforms(&bytes[..])";
-        s << " => {";
-        s << name_ << "DataChild::";
-        s << desc_path[0]->name_ << "(Arc::new(";
-        if (desc_path[0]->parent_constraints_.empty()) {
-          s << desc_path[0]->name_ << "Data::parse(&bytes[..]";
-          s << ", " << enum_variant << ")?))";
-        } else {
-          s << desc_path[0]->name_ << "Data::parse(&bytes[..])?))";
-        }
-      } else if (constraint_type == ScalarField::kFieldType) {
-        s << std::get<int64_t>(desc.second) << " => {";
-        s << "unimplemented!();";
+        return enum_type + "::" + util::UnderscoreToCamelCase(util::ToLowerCase(variant_name));
       }
+      if (constraint_type == ScalarField::kFieldType) {
+        return std::to_string(std::get<int64_t>(constraint));
+      }
+      return "_";
+    };
+
+    for (auto& child : children_) {
+      s << "(";
+      for (auto var : match_on_variables) {
+        std::string match_val = "_";
+
+        if (child->parent_constraints_.find(var) != child->parent_constraints_.end()) {
+          match_val = get_match_val(var, child->parent_constraints_[var]);
+        } else {
+          auto dcs = child->FindDescendantsWithConstraint(var);
+          std::vector<std::string> all_match_vals;
+          for (auto& desc : dcs) {
+            all_match_vals.push_back(get_match_val(var, desc.second));
+          }
+          match_val = "";
+          for (std::size_t i = 0; i < all_match_vals.size(); ++i) {
+            match_val += all_match_vals[i];
+            if (i != all_match_vals.size() - 1) {
+              match_val += " | ";
+            }
+          }
+          match_val = (match_val == "") ? "_" : match_val;
+        }
+
+        if (var == match_on_variables[match_on_variables.size() - 1]) {
+          s << match_val << ")";
+        } else {
+          s << match_val << ", ";
+        }
+      }
+      s << " if " << child->name_ << "Data::conforms(&bytes[..])";
+      s << " => {";
+      s << name_ << "DataChild::";
+      s << child->name_ << "(Arc::new(";
+
+      auto child_parse_params = packet_dep.GetDependencies(child->name_);
+      if (child_parse_params.size() == 0) {
+        s << child->name_ << "Data::parse(&bytes[..]";
+      } else {
+        s << child->name_ << "Data::parse(&bytes[..], ";
+      }
+
+      for (auto var : child_parse_params) {
+        if (var == child_parse_params[child_parse_params.size() - 1]) {
+          s << var;
+        } else {
+          s << var << ", ";
+        }
+      }
+      s << ")?))";
       s << "}\n";
     }
 
-    if (!constrained_descendants.empty()) {
-      s << "v => return Err(Error::ConstraintOutOfBounds{field: \"" << constraint_name
-        << "\".to_string(), value: v as u64}),";
+    s << "(";
+    for (int i = 1; i <= match_on_variables.size(); i++) {
+      if (i == match_on_variables.size()) {
+        s << "_";
+      } else {
+        s << "_, ";
+      }
     }
-
+    s << ")";
+    s << " => return Err(Error::InvalidPacketError),";
     s << "};\n";
   } else if (children_.size() == 1) {
     auto child = children_.at(0);
-    s << "let child = match " << child->name_ << "Data::parse(&bytes[..]) {";
+    auto params = packet_dep.GetDependencies(child->name_);
+    s << "let child = match " << child->name_ << "Data::parse(&bytes[..]";
+    for (auto field_name : params) {
+      s << ", " << field_name;
+    }
+    s << ") {";
     s << " Ok(c) if " << child->name_ << "Data::conforms(&bytes[..]) => {";
     s << name_ << "DataChild::" << child->name_ << "(Arc::new(c))";
     s << " },";
diff --git a/system/include/hardware/ble_scanner.h b/system/include/hardware/ble_scanner.h
index 67e805b..eb966b2 100644
--- a/system/include/hardware/ble_scanner.h
+++ b/system/include/hardware/ble_scanner.h
@@ -46,7 +46,8 @@
                                      uint8_t secondary_phy,
                                      uint8_t advertising_sid, int8_t tx_power,
                                      int8_t rssi, uint16_t periodic_adv_int,
-                                     std::vector<uint8_t> adv_data);
+                                     std::vector<uint8_t> adv_data,
+                                     RawAddress* original_bda);
 
 typedef struct {
   scan_result_callback scan_result_cb;
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index 542dc2e..c174d91 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -756,6 +756,15 @@
    * Fetches the local Out of Band data.
    */
   int (*generate_local_oob_data)(tBT_TRANSPORT transport);
+
+  /**
+   * Allow or disallow audio low latency
+   *
+   * @param allowed true if allowing audio low latency
+   * @param address Bluetooth MAC address of Bluetooth device
+   * @return true if audio low latency is successfully allowed or disallowed
+   */
+  bool (*allow_low_latency_audio)(bool allowed, const RawAddress& address);
 } bt_interface_t;
 
 #define BLUETOOTH_INTERFACE_STRING "bluetoothInterface"
diff --git a/system/main/shim/btm.cc b/system/main/shim/btm.cc
index c72030c..b79292c 100644
--- a/system/main/shim/btm.cc
+++ b/system/main/shim/btm.cc
@@ -63,7 +63,7 @@
     uint16_t event_type, uint8_t address_type, const RawAddress& raw_address,
     uint8_t primary_phy, uint8_t secondary_phy, uint8_t advertising_sid,
     int8_t tx_power, int8_t rssi, uint16_t periodic_adv_int, uint8_t data_len,
-    const uint8_t* data);
+    const uint8_t* data, const RawAddress& original_bda);
 
 extern void btm_api_process_inquiry_result(const RawAddress& raw_address,
                                            uint8_t page_scan_rep_mode,
@@ -128,12 +128,14 @@
     btm_ble_process_adv_addr(raw_address, &ble_address_type);
   }
 
+  // Pass up to GattService#onScanResult
+  RawAddress original_bda = raw_address;
   btm_ble_process_adv_addr(raw_address, &ble_address_type);
-  btm_ble_process_adv_pkt_cont(extended_event_type, ble_address_type,
-                               raw_address, primary_phy, secondary_phy,
-                               advertising_sid, tx_power, rssi,
-                               periodic_advertising_interval,
-                               advertising_data.size(), &advertising_data[0]);
+  btm_ble_process_adv_pkt_cont(
+      extended_event_type, ble_address_type, raw_address, primary_phy,
+      secondary_phy, advertising_sid, tx_power, rssi,
+      periodic_advertising_interval, advertising_data.size(),
+      &advertising_data[0], original_bda);
 }
 
 void Btm::ScanningCallbacks::OnTrackAdvFoundLost(
diff --git a/system/main/shim/btm_api.cc b/system/main/shim/btm_api.cc
index d871f76..a4ad2f9 100644
--- a/system/main/shim/btm_api.cc
+++ b/system/main/shim/btm_api.cc
@@ -1227,7 +1227,8 @@
 }
 
 bool bluetooth::shim::BTM_SecAddDevice(const RawAddress& bd_addr,
-                                       DEV_CLASS dev_class, BD_NAME bd_name,
+                                       DEV_CLASS dev_class,
+                                       const BD_NAME& bd_name,
                                        uint8_t* features, LinkKey* link_key,
                                        uint8_t key_type, uint8_t pin_length) {
   // Check if GD has a security record for the device
diff --git a/system/main/shim/btm_api.h b/system/main/shim/btm_api.h
index c276d01..e6ac3fe 100644
--- a/system/main/shim/btm_api.h
+++ b/system/main/shim/btm_api.h
@@ -1403,8 +1403,8 @@
  *
  ******************************************************************************/
 bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
-                      BD_NAME bd_name, uint8_t* features, LinkKey* link_key,
-                      uint8_t key_type, uint8_t pin_length);
+                      const BD_NAME& bd_name, uint8_t* features,
+                      LinkKey* link_key, uint8_t key_type, uint8_t pin_length);
 
 /** Free resources associated with the device associated with |bd_addr| address.
  *
diff --git a/system/service/bluetooth_interface.cc b/system/service/bluetooth_interface.cc
index ee58ef3..155b13d 100644
--- a/system/service/bluetooth_interface.cc
+++ b/system/service/bluetooth_interface.cc
@@ -584,6 +584,12 @@
   return btif_set_dynamic_audio_buffer_size(codec, size);
 }
 
+static bool allow_low_latency_audio(bool allowed, const RawAddress& address) {
+  LOG_INFO("%s %s", __func__, allowed ? "true" : "false");
+  // Call HAL here
+  return true;
+}
+
 EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
     sizeof(bluetoothInterface),
     init,
@@ -622,7 +628,8 @@
     obfuscate_address,
     get_metric_id,
     set_dynamic_audio_buffer_size,
-    generate_local_oob_data};
+    generate_local_oob_data,
+    allow_low_latency_audio};
 
 // callback reporting helpers
 
diff --git a/system/service/gatt_server_old.cc b/system/service/gatt_server_old.cc
index 7e2b6ab..c4d3790 100644
--- a/system/service/gatt_server_old.cc
+++ b/system/service/gatt_server_old.cc
@@ -377,7 +377,8 @@
                         uint8_t ble_secondary_phy, uint8_t ble_advertising_sid,
                         int8_t ble_tx_power, int8_t rssi,
                         uint16_t ble_periodic_adv_int,
-                        std::vector<uint8_t> adv_data) {
+                        std::vector<uint8_t> adv_data,
+                        RawAddress* original_bda) {
   std::string addr(BtAddrString(bda));
   std::lock_guard<std::mutex> lock(g_internal->lock);
   g_internal->scan_results[addr] = rssi;
diff --git a/system/service/hal/bluetooth_gatt_interface.cc b/system/service/hal/bluetooth_gatt_interface.cc
index 2131d7b..c9d165b 100644
--- a/system/service/hal/bluetooth_gatt_interface.cc
+++ b/system/service/hal/bluetooth_gatt_interface.cc
@@ -93,12 +93,13 @@
       RegisterClientCallback(g_interface, status, client_if, app_uuid));
 }
 
-void ScanResultCallback(
-    uint16_t ble_evt_type, uint8_t addr_type, RawAddress* bda,
-    uint8_t ble_primary_phy, uint8_t ble_secondary_phy,
-    uint8_t ble_advertising_sid, int8_t ble_tx_power, int8_t rssi,
-    uint16_t ble_periodic_adv_int,
-    std::vector<uint8_t> adv_data) {  // NOLINT(pass-by-value)
+void ScanResultCallback(uint16_t ble_evt_type, uint8_t addr_type,
+                        RawAddress* bda, uint8_t ble_primary_phy,
+                        uint8_t ble_secondary_phy, uint8_t ble_advertising_sid,
+                        int8_t ble_tx_power, int8_t rssi,
+                        uint16_t ble_periodic_adv_int,
+                        std::vector<uint8_t> adv_data,
+                        RawAddress* original_bda) {  // NOLINT(pass-by-value)
   shared_lock<shared_mutex_impl> lock(g_instance_lock);
   VERIFY_INTERFACE_OR_RETURN();
   CHECK(bda);
diff --git a/system/service/hal/fake_bluetooth_interface.cc b/system/service/hal/fake_bluetooth_interface.cc
index e32c380..729778d 100644
--- a/system/service/hal/fake_bluetooth_interface.cc
+++ b/system/service/hal/fake_bluetooth_interface.cc
@@ -80,6 +80,7 @@
     nullptr, /* get_metric_id */
     nullptr, /* set_dynamic_audio_buffer_size */
     nullptr, /* generate_local_oob_data */
+    nullptr, /* allow_low_latency_audio */
 };
 
 }  // namespace
diff --git a/system/stack/btm/btm_ble_adv_filter.cc b/system/stack/btm/btm_ble_adv_filter.cc
index cb9d3ef..93c898b 100644
--- a/system/stack/btm/btm_ble_adv_filter.cc
+++ b/system/stack/btm/btm_ble_adv_filter.cc
@@ -478,19 +478,60 @@
   UINT8_TO_STREAM(p, filt_index);
 
   if (action != BTM_BLE_SCAN_COND_CLEAR) {
-    if (addr.type == 2 /* DEVICE_TYPE_ALL in ScanFilterQueue.java */) {
-      LOG(INFO) << __func__ << " Filter address " << addr.bda
-                << " has DEVICE_TYPE_ALL, try to get identity address";
-      /* If no matching identity address is found for the input address,
-       * this call will have no effect. */
-      uint8_t dummy_addr_type;
-      btm_random_pseudo_to_identity_addr(&addr.bda, &dummy_addr_type);
-    }
+    LOG(INFO) << __func__ << " Filter address " << addr.bda
+              << " has DEVICE_TYPE_ALL, try to get identity address";
+    /*
+     * Always do the pseudo to id address check!
+     *
+     * In the happy path case this should be checking only random types.
+     *
+     * However, the Java layer only knows PUBLIC and RANDOM which leaves us with
+     * 0 and 1 respectively.
+     *
+     * In the native host stack we have 4 values.
+     *     -  Public = 0
+     *     -  Random = 1
+     *     -  Public ID = 2
+     *     -  Random ID = 3
+     *
+     * So we should really only need to do it for Random = 1.
+     *
+     * The app layer won't know the ID address since it didn't see it when it
+     * was scanning.
+     *
+     * Two possible CUJ:
+     *  1. app scans, finds RPA, bonds.  App will only know RPA (pseudo address)
+     *  2. app knows the (preshared) ID address (e.g. DCK+OOB+IRK)
+     *
+     * We cannot lock it to RANDOM here otherwise we break CUJ #1.
+     *
+     * Thus, we must always try to do the conversion.
+     */
+    btm_random_pseudo_to_identity_addr(&addr.bda, &addr.type);
 
     LOG(INFO) << __func__
               << " Adding scan filter with peer address: " << addr.bda;
 
     BDADDR_TO_STREAM(p, addr.bda);
+    /*
+     * DANGER: Thar be dragons!
+     *
+     * The vendor command (APCF Filtering 0x0157) takes Public (0) or Random (1)
+     * or Any (2).
+     *
+     * Advertising results have four types:
+     *     -  Public = 0
+     *     -  Random = 1
+     *     -  Public ID = 2
+     *     -  Random ID = 3
+     *
+     * e.g. specifying PUBLIC (0) will only return results with a public
+     * address. It will ignore resolved addresses, since they return PUBLIC
+     * IDENTITY (2). For this, Any (0x02) must be specified.  This should also
+     * cover if the RPA is derived from RANDOM STATIC.
+     */
+    /* ALWAYS FORCE 2 for this vendor command! */
+    addr.type = 0x02;  // Really, you will break scanning if you change this.
     UINT8_TO_STREAM(p, addr.type);
   }
 
diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc
index 694bcde..bf7439e 100644
--- a/system/stack/btm/btm_ble_gap.cc
+++ b/system/stack/btm/btm_ble_gap.cc
@@ -176,7 +176,8 @@
                                   uint8_t secondary_phy,
                                   uint8_t advertising_sid, int8_t tx_power,
                                   int8_t rssi, uint16_t periodic_adv_int,
-                                  uint8_t data_len, const uint8_t* data);
+                                  uint8_t data_len, const uint8_t* data,
+                                  const RawAddress& original_bda);
 static uint8_t btm_set_conn_mode_adv_init_addr(RawAddress& p_peer_addr_ptr,
                                                tBLE_ADDR_TYPE* p_peer_addr_type,
                                                tBLE_ADDR_TYPE* p_own_addr_type);
@@ -1847,13 +1848,17 @@
                       rssi);
     }
 
+    // Store this to pass up the callback chain to GattService#onScanResult for
+    // the check in ScanFilter#matches
+    RawAddress original_bda = bda;
+
     if (addr_type != BLE_ADDR_ANONYMOUS) {
       btm_ble_process_adv_addr(bda, &addr_type);
     }
 
-    btm_ble_process_adv_pkt_cont(event_type, addr_type, bda, primary_phy,
-                                 secondary_phy, advertising_sid, tx_power, rssi,
-                                 periodic_adv_int, pkt_data_len, pkt_data);
+    btm_ble_process_adv_pkt_cont(
+        event_type, addr_type, bda, primary_phy, secondary_phy, advertising_sid,
+        tx_power, rssi, periodic_adv_int, pkt_data_len, pkt_data, original_bda);
   }
 }
 
@@ -1902,6 +1907,10 @@
                       pkt_data_len, rssi);
     }
 
+    // Pass up the address to GattService#onScanResult to use in
+    // ScanFilter#matches
+    RawAddress original_bda = bda;
+
     btm_ble_process_adv_addr(bda, &addr_type);
 
     uint16_t event_type;
@@ -1933,7 +1942,7 @@
     btm_ble_process_adv_pkt_cont(
         event_type, addr_type, bda, PHY_LE_1M, PHY_LE_NO_PACKET, NO_ADI_PRESENT,
         TX_POWER_NOT_PRESENT, rssi, 0x00 /* no periodic adv */, pkt_data_len,
-        pkt_data);
+        pkt_data, original_bda);
   }
 }
 
@@ -1946,7 +1955,8 @@
                                   uint8_t secondary_phy,
                                   uint8_t advertising_sid, int8_t tx_power,
                                   int8_t rssi, uint16_t periodic_adv_int,
-                                  uint8_t data_len, const uint8_t* data) {
+                                  uint8_t data_len, const uint8_t* data,
+                                  const RawAddress& original_bda) {
   tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
   bool update = true;
 
@@ -2071,6 +2081,9 @@
                        const_cast<uint8_t*>(adv_data.data()), adv_data.size());
   }
 
+  // Pass address up to GattService#onScanResult
+  p_i->inq_info.results.original_bda = original_bda;
+
   tBTM_INQ_RESULTS_CB* p_obs_results_cb = btm_cb.ble_ctr_cb.p_obs_results_cb;
   if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT)) {
     (p_obs_results_cb)((tBTM_INQ_RESULTS*)&p_i->inq_info.results,
diff --git a/system/stack/btm/btm_dev.cc b/system/stack/btm/btm_dev.cc
index 95f43e4..18f05a2 100644
--- a/system/stack/btm/btm_dev.cc
+++ b/system/stack/btm/btm_dev.cc
@@ -62,8 +62,9 @@
  *
  ******************************************************************************/
 bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
-                      BD_NAME bd_name, uint8_t* features, LinkKey* p_link_key,
-                      uint8_t key_type, uint8_t pin_length) {
+                      const BD_NAME& bd_name, uint8_t* features,
+                      LinkKey* p_link_key, uint8_t key_type,
+                      uint8_t pin_length) {
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
   if (!p_dev_rec) {
     p_dev_rec = btm_sec_allocate_dev_rec();
diff --git a/system/stack/btm/btm_dev.h b/system/stack/btm/btm_dev.h
index fefa628..9da2e89 100644
--- a/system/stack/btm/btm_dev.h
+++ b/system/stack/btm/btm_dev.h
@@ -21,27 +21,6 @@
 #include "stack/include/bt_octets.h"
 #include "types/raw_address.h"
 
-/*******************************************************************************
- *
- * Function         BTM_SecAddDevice
- *
- * Description      Add/modify device.  This function will be normally called
- *                  during host startup to restore all required information
- *                  stored in the NVRAM.
- *
- * Parameters:      bd_addr          - BD address of the peer
- *                  dev_class        - Device Class
- *                  bd_name          - Name of the peer device. NULL if unknown.
- *                  features         - Remote device's features (up to 3 pages).
- *                                     NULL if not known
- *                  link_key         - Connection link key. NULL if unknown.
- *
- * Returns          true if added OK, else false
- *
- ******************************************************************************/
-bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
-                      BD_NAME bd_name, uint8_t* features, LinkKey* p_link_key,
-                      uint8_t key_type, uint8_t pin_length);
 void wipe_secrets_and_remove(tBTM_SEC_DEV_REC* p_dev_rec);
 
 /** Free resources associated with the device associated with |bd_addr| address.
diff --git a/system/stack/btm/btm_iso_impl.h b/system/stack/btm/btm_iso_impl.h
index 2d5fc41..623696a 100644
--- a/system/stack/btm/btm_iso_impl.h
+++ b/system/stack/btm/btm_iso_impl.h
@@ -60,8 +60,9 @@
   };
 
   struct iso_sync_info sync_info;
-  uint8_t state_flags;
+  std::atomic_uint8_t state_flags;
   uint32_t sdu_itv;
+  std::atomic_uint16_t used_credits;
 };
 
 typedef iso_base iso_cis;
@@ -121,11 +122,14 @@
         STREAM_TO_UINT16(conn_handle, stream);
 
         evt.conn_handles.push_back(conn_handle);
-        conn_hdl_to_cis_map_[conn_handle] = std::unique_ptr<iso_cis>(
-            new iso_cis({.sync_info = {.first_sync_ts = 0, .seq_nb = 0},
-                         .cig_id = cig_id,
-                         .state_flags = kStateFlagsNone,
-                         .sdu_itv = sdu_itv_mtos}));
+
+        auto cis = std::unique_ptr<iso_cis>(new iso_cis());
+        cis->cig_id = cig_id;
+        cis->sdu_itv = sdu_itv_mtos;
+        cis->sync_info = {.first_sync_ts = 0, .seq_nb = 0};
+        cis->used_credits = 0;
+        cis->state_flags = kStateFlagsNone;
+        conn_hdl_to_cis_map_[conn_handle] = std::move(cis);
       }
     }
 
@@ -434,6 +438,7 @@
     }
 
     iso_credits_--;
+    iso->used_credits++;
 
     BT_HDR* packet =
         prepare_ts_hci_packet(iso_handle, ts, iso->sync_info.seq_nb, data_len);
@@ -495,6 +500,11 @@
 
       cig_callbacks_->OnCisEvent(kIsoEventCisDisconnected, &evt);
       cis->state_flags &= ~kStateFlagIsConnected;
+
+      /* return used credits */
+      iso_credits_ += cis->used_credits;
+      cis->used_credits = 0;
+
       /* Data path is considered still valid, but can be reconfigured only once
        * CIS is reestablished.
        */
@@ -514,20 +524,35 @@
       STREAM_TO_UINT16(handle, p);
       STREAM_TO_UINT16(num_sent, p);
 
-      if ((conn_hdl_to_cis_map_.find(handle) == conn_hdl_to_cis_map_.end()) &&
-          (conn_hdl_to_bis_map_.find(handle) == conn_hdl_to_bis_map_.end()))
+      auto iter = conn_hdl_to_cis_map_.find(handle);
+      if (iter != conn_hdl_to_cis_map_.end()) {
+        iter->second->used_credits -= num_sent;
+        iso_credits_ += num_sent;
         continue;
+      }
 
-      iso_credits_ += num_sent;
+      iter = conn_hdl_to_bis_map_.find(handle);
+      if (iter != conn_hdl_to_bis_map_.end()) {
+        iter->second->used_credits -= num_sent;
+        iso_credits_ += num_sent;
+        continue;
+      }
     }
   }
 
   void handle_gd_num_completed_pkts(uint16_t handle, uint16_t credits) {
-    if ((conn_hdl_to_cis_map_.find(handle) == conn_hdl_to_cis_map_.end()) &&
-        (conn_hdl_to_bis_map_.find(handle) == conn_hdl_to_bis_map_.end()))
+    auto iter = conn_hdl_to_cis_map_.find(handle);
+    if (iter != conn_hdl_to_cis_map_.end()) {
+      iter->second->used_credits -= credits;
+      iso_credits_ += credits;
       return;
+    }
 
-    iso_credits_ += credits;
+    iter = conn_hdl_to_bis_map_.find(handle);
+    if (iter != conn_hdl_to_bis_map_.end()) {
+      iter->second->used_credits -= credits;
+      iso_credits_ += credits;
+    }
   }
 
   void process_create_big_cmpl_pkt(uint8_t len, uint8_t* data) {
@@ -563,11 +588,13 @@
       LOG_INFO(" received BIS conn_hdl %d", +conn_handle);
 
       if (evt.status == HCI_SUCCESS) {
-        conn_hdl_to_bis_map_[conn_handle] = std::unique_ptr<iso_bis>(
-            new iso_bis({.sync_info = {.first_sync_ts = ts, .seq_nb = 0},
-                         .big_handle = evt.big_id,
-                         .state_flags = kStateFlagIsBroadcast,
-                         .sdu_itv = last_big_create_req_sdu_itv_}));
+        auto bis = std::unique_ptr<iso_bis>(new iso_bis());
+        bis->big_handle = evt.big_id;
+        bis->sdu_itv = last_big_create_req_sdu_itv_;
+        bis->sync_info = {.first_sync_ts = ts, .seq_nb = 0};
+        bis->used_credits = 0;
+        bis->state_flags = kStateFlagIsBroadcast;
+        conn_hdl_to_bis_map_[conn_handle] = std::move(bis);
       }
     }
 
diff --git a/system/stack/btm/neighbor_inquiry.h b/system/stack/btm/neighbor_inquiry.h
index eb9a459..be8231f 100644
--- a/system/stack/btm/neighbor_inquiry.h
+++ b/system/stack/btm/neighbor_inquiry.h
@@ -118,6 +118,7 @@
   uint16_t ble_periodic_adv_int;
   uint8_t flag;
   bool include_rsi;
+  RawAddress original_bda;
 } tBTM_INQ_RESULTS;
 
 /****************************************
diff --git a/system/stack/btm/security_device_record.h b/system/stack/btm/security_device_record.h
index a9d5f01..06639c0 100644
--- a/system/stack/btm/security_device_record.h
+++ b/system/stack/btm/security_device_record.h
@@ -224,7 +224,7 @@
 
  private:
   friend bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
-                               BD_NAME bd_name, uint8_t* features,
+                               const BD_NAME& bd_name, uint8_t* features,
                                LinkKey* p_link_key, uint8_t key_type,
                                uint8_t pin_length);
   friend void BTM_PINCodeReply(const RawAddress& bd_addr, tBTM_STATUS res,
diff --git a/system/stack/include/btm_api.h b/system/stack/include/btm_api.h
index 522e05c..29b2679 100644
--- a/system/stack/include/btm_api.h
+++ b/system/stack/include/btm_api.h
@@ -613,8 +613,8 @@
  *
  ******************************************************************************/
 bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
-                      BD_NAME bd_name, uint8_t* features, LinkKey* link_key,
-                      uint8_t key_type, uint8_t pin_length);
+                      const BD_NAME& bd_name, uint8_t* features,
+                      LinkKey* link_key, uint8_t key_type, uint8_t pin_length);
 
 /** Free resources associated with the device associated with |bd_addr| address.
  *
diff --git a/system/stack/include/btm_client_interface.h b/system/stack/include/btm_client_interface.h
index cca01e4..dc41cc7 100644
--- a/system/stack/include/btm_client_interface.h
+++ b/system/stack/include/btm_client_interface.h
@@ -120,7 +120,7 @@
 
   struct {
     bool (*BTM_SecAddDevice)(const RawAddress& bd_addr, DEV_CLASS dev_class,
-                             BD_NAME bd_name, uint8_t* features,
+                             const BD_NAME& bd_name, uint8_t* features,
                              LinkKey* link_key, uint8_t key_type,
                              uint8_t pin_length);
     bool (*BTM_SecAddRmtNameNotifyCallback)(tBTM_RMT_NAME_CALLBACK* p_callback);
diff --git a/system/stack/test/btm_iso_test.cc b/system/stack/test/btm_iso_test.cc
index 0181387..94e77fc 100644
--- a/system/stack/test/btm_iso_test.cc
+++ b/system/stack/test/btm_iso_test.cc
@@ -2059,6 +2059,46 @@
   }
 }
 
+TEST_F(IsoManagerTest, SendIsoDataCreditsReturnedByDisconnection) {
+  uint8_t num_buffers = controller_interface_.GetIsoBufferCount();
+  std::vector<uint8_t> data_vec(108, 0);
+
+  // Check on CIG
+  IsoManager::GetInstance()->CreateCig(
+      volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+  bluetooth::hci::iso_manager::cis_establish_params params;
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    params.conn_pairs.push_back({handle, 1});
+  }
+  IsoManager::GetInstance()->EstablishCis(params);
+
+  for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+    IsoManager::GetInstance()->SetupIsoDataPath(handle,
+                                                kDefaultIsoDataPathParams);
+  }
+
+  /* Sending lot of ISO data to first ISO and getting all the credits */
+  EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers).RetiresOnSaturation();
+  for (uint8_t i = 0; i < num_buffers; i++) {
+    IsoManager::GetInstance()->SendIsoData(
+        volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(),
+        data_vec.size());
+  }
+
+  /* Return all credits by disconnecting CIS */
+  IsoManager::GetInstance()->HandleDisconnect(
+      volatile_test_cig_create_cmpl_evt_.conn_handles[0], 16);
+
+  /* Try to send ISO data on the second ISO. Expect credits being available.*/
+  EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers).RetiresOnSaturation();
+  for (uint8_t i = 0; i < num_buffers; i++) {
+    IsoManager::GetInstance()->SendIsoData(
+        volatile_test_cig_create_cmpl_evt_.conn_handles[1], data_vec.data(),
+        data_vec.size());
+  }
+}
+
 TEST_F(IsoManagerDeathTest, SendIsoDataWithNoDataPath) {
   std::vector<uint8_t> data_vec(108, 0);
 
diff --git a/system/test/mock/mock_bluetooth_interface.cc b/system/test/mock/mock_bluetooth_interface.cc
index c778c16..8f1423e 100644
--- a/system/test/mock/mock_bluetooth_interface.cc
+++ b/system/test/mock/mock_bluetooth_interface.cc
@@ -145,6 +145,10 @@
 
 static int set_dynamic_audio_buffer_size(int codec, int size) { return 0; }
 
+static bool allow_low_latency_audio(bool allowed, const RawAddress& address) {
+  return true;
+}
+
 EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
     sizeof(bluetoothInterface),
     init,
@@ -183,7 +187,8 @@
     obfuscate_address,
     get_metric_id,
     set_dynamic_audio_buffer_size,
-    generate_local_oob_data};
+    generate_local_oob_data,
+    allow_low_latency_audio};
 
 // callback reporting helpers
 
diff --git a/system/test/mock/mock_main_shim_btm_api.cc b/system/test/mock/mock_main_shim_btm_api.cc
index 33f9672..f0223d2 100644
--- a/system/test/mock/mock_main_shim_btm_api.cc
+++ b/system/test/mock/mock_main_shim_btm_api.cc
@@ -87,7 +87,8 @@
   return false;
 }
 bool bluetooth::shim::BTM_SecAddDevice(const RawAddress& bd_addr,
-                                       DEV_CLASS dev_class, BD_NAME bd_name,
+                                       DEV_CLASS dev_class,
+                                       const BD_NAME& bd_name,
                                        uint8_t* features, LinkKey* link_key,
                                        uint8_t key_type, uint8_t pin_length) {
   mock_function_count_map[__func__]++;
diff --git a/system/test/mock/mock_stack_btm_ble_gap.cc b/system/test/mock/mock_stack_btm_ble_gap.cc
index 9ea33b9..11f8507 100644
--- a/system/test/mock/mock_stack_btm_ble_gap.cc
+++ b/system/test/mock/mock_stack_btm_ble_gap.cc
@@ -156,7 +156,8 @@
                                   uint8_t secondary_phy,
                                   uint8_t advertising_sid, int8_t tx_power,
                                   int8_t rssi, uint16_t periodic_adv_int,
-                                  uint8_t data_len, const uint8_t* data) {
+                                  uint8_t data_len, const uint8_t* data,
+                                  const RawAddress& original_bda) {
   mock_function_count_map[__func__]++;
 }
 void btm_ble_process_adv_pkt_cont_for_inquiry(
diff --git a/system/test/mock/mock_stack_btm_dev.cc b/system/test/mock/mock_stack_btm_dev.cc
index 928e83a..a01217b 100644
--- a/system/test/mock/mock_stack_btm_dev.cc
+++ b/system/test/mock/mock_stack_btm_dev.cc
@@ -44,8 +44,9 @@
 #endif
 
 bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
-                      BD_NAME bd_name, uint8_t* features, LinkKey* p_link_key,
-                      uint8_t key_type, uint8_t pin_length) {
+                      const BD_NAME& bd_name, uint8_t* features,
+                      LinkKey* p_link_key, uint8_t key_type,
+                      uint8_t pin_length) {
   mock_function_count_map[__func__]++;
   return false;
 }