Merge "Import translations. DO NOT MERGE ANYWHERE" into tm-dev
diff --git a/android/app/jni/com_android_bluetooth_le_audio.cpp b/android/app/jni/com_android_bluetooth_le_audio.cpp
index 726385b..b97385f 100644
--- a/android/app/jni/com_android_bluetooth_le_audio.cpp
+++ b/android/app/jni/com_android_bluetooth_le_audio.cpp
@@ -39,6 +39,7 @@
 using bluetooth::le_audio::LeAudioClientInterface;
 
 namespace android {
+static jmethodID method_onInitialized;
 static jmethodID method_onConnectionStateChanged;
 static jmethodID method_onGroupStatus;
 static jmethodID method_onGroupNodeStatus;
@@ -125,6 +126,14 @@
  public:
   ~LeAudioClientCallbacksImpl() = default;
 
+  void OnInitialized(void) override {
+    LOG(INFO) << __func__;
+    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onInitialized);
+  }
+
   void OnConnectionState(ConnectionState state,
                          const RawAddress& bd_addr) override {
     LOG(INFO) << __func__ << ", state:" << int(state);
@@ -280,6 +289,7 @@
   method_onAudioConf = env->GetMethodID(clazz, "onAudioConf", "(IIIII)V");
   method_onSinkAudioLocationAvailable =
       env->GetMethodID(clazz, "onSinkAudioLocationAvailable", "([BI)V");
+  method_onInitialized = env->GetMethodID(clazz, "onInitialized", "()V");
   method_onConnectionStateChanged =
       env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
   method_onAudioLocalCodecCapabilities =
@@ -512,6 +522,17 @@
       group_id, input_codec_config, output_codec_config);
 }
 
+static void setCcidInformationNative(JNIEnv* env, jobject object, jint ccid,
+                                     jint contextType) {
+  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
+  if (!sLeAudioClientInterface) {
+    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth LeAudio Interface";
+    return;
+  }
+
+  sLeAudioClientInterface->SetCcidInformation(ccid, contextType);
+}
+
 static JNINativeMethod sMethods[] = {
     {"classInitNative", "()V", (void*)classInitNative},
     {"initNative", "([Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V",
@@ -526,6 +547,7 @@
      "(ILandroid/bluetooth/BluetoothLeAudioCodecConfig;Landroid/bluetooth/"
      "BluetoothLeAudioCodecConfig;)V",
      (void*)setCodecConfigPreferenceNative},
+    {"setCcidInformationNative", "(II)V", (void*)setCcidInformationNative},
 };
 
 /* Le Audio Broadcaster */
diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java
index 9e5ed27..07855ae 100644
--- a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java
+++ b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java
@@ -252,6 +252,14 @@
                     Log.d(TAG, "  └ Current queueItem: " + qitem);
                     Log.d(TAG, "  └ Current metadata : " + mdata);
                 }
+
+                // Some player do not provide full song info in queue item, allow case
+                // that only title and artist match.
+                if (Objects.equals(qitem.title, mdata.title)
+                        && Objects.equals(qitem.artist, mdata.artist)) {
+                    Log.d(TAG, mPackageName + " Only Title and Artist info sync for metadata");
+                    return true;
+                }
                 return false;
             }
         }
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java
index 7abd137..5945180 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java
@@ -57,7 +57,6 @@
     private Disconnecting mDisconnecting;
     private Connected mConnected;
     private int mLastConnectionState = -1;
-    private int mNumReceivers = 0;
 
     private CsipSetCoordinatorService mService;
     private CsipSetCoordinatorNativeInterface mNativeInterface;
@@ -513,14 +512,6 @@
         }
     }
 
-    public void setNumReceivers(int numReceivers) {
-        mNumReceivers = numReceivers;
-    }
-
-    public int getNumReceivers() {
-        return mNumReceivers;
-    }
-
     // This method does not check for error condition (newState == prevState)
     private void csipConnectionState(int newState, int prevState) {
         log("Connection state " + mDevice + ": " + profileStateToString(prevState) + "->"
diff --git a/android/app/src/com/android/bluetooth/le_audio/ContentControlIdKeeper.java b/android/app/src/com/android/bluetooth/le_audio/ContentControlIdKeeper.java
index 1673318..1d2770e 100644
--- a/android/app/src/com/android/bluetooth/le_audio/ContentControlIdKeeper.java
+++ b/android/app/src/com/android/bluetooth/le_audio/ContentControlIdKeeper.java
@@ -17,6 +17,15 @@
 
 package com.android.bluetooth.le_audio;
 
+import android.bluetooth.BluetoothLeAudio;
+import android.os.ParcelUuid;
+import android.util.Pair;
+
+import com.android.bluetooth.btservice.ServiceFactory;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
@@ -30,8 +39,19 @@
     public static final int CCID_MAX = 0xFF;
 
     private static SortedSet<Integer> sAssignedCcidList = new TreeSet();
+    private static HashMap<ParcelUuid, Pair<Integer, Integer>> sUserMap = new HashMap();
+    private static ServiceFactory sServiceFactory = null;
 
-    public static synchronized int acquireCcid() {
+    /**
+     * Functions is used to acquire Content Control ID (Ccid). Ccid is connected
+     * with a context type  and the user uuid. In most of cases user uuid is the GATT service
+     * UUID which makes use of Ccid
+     *
+     * @param userUuid user identifier (GATT service)
+     * @param contextType the context types as defined in {@link BluetoothLeAudio}
+     * @return ccid to be used in the Gatt service Ccid characteristic.
+    */
+    public static synchronized int acquireCcid(ParcelUuid userUuid, int contextType) {
         int ccid = CCID_INVALID;
 
         if (sAssignedCcidList.size() == 0) {
@@ -51,11 +71,38 @@
             }
         }
 
-        if (ccid != CCID_INVALID) sAssignedCcidList.add(ccid);
+        if (ccid != CCID_INVALID)  {
+            sAssignedCcidList.add(ccid);
+            sUserMap.put(userUuid, new Pair(ccid, contextType));
+
+            if (sServiceFactory == null) {
+                sServiceFactory = new ServiceFactory();
+            }
+            /* Notify LeAudioService about new ccid  */
+            LeAudioService service = sServiceFactory.getLeAudioService();
+            if (service != null) {
+                service.setCcidInformation(userUuid, ccid, contextType);
+            }
+        }
         return ccid;
     }
 
+    /**
+     * Release the acquired Ccid
+     *
+     * @param value Ccid value to release
+     */
     public static synchronized void releaseCcid(int value) {
         sAssignedCcidList.remove(value);
+        sUserMap.entrySet().removeIf(entry -> entry.getValue().first.equals(value));
+    }
+
+    /**
+     * Get Ccid information.
+     *
+     * @return Map of acquired ccids along with the user information.
+     */
+    public static synchronized Map<ParcelUuid, Pair<Integer, Integer>> getUserCcidMap() {
+        return Collections.unmodifiableMap(sUserMap);
     }
 }
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java
index a32b6c4..d696dc8 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java
@@ -90,6 +90,16 @@
     // Callbacks from the native stack back into the Java framework.
     // All callbacks are routed via the Service which will disambiguate which
     // state machine the message should be routed to.
+    private void onInitialized() {
+        LeAudioStackEvent event =
+                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
+
+        if (DBG) {
+            Log.d(TAG, "onInitialized: " + event);
+        }
+        sendMessageToService(event);
+    }
+
     private void onConnectionStateChanged(int state, byte[] address) {
         LeAudioStackEvent event =
                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
@@ -265,6 +275,18 @@
         setCodecConfigPreferenceNative(groupId, inputCodecConfig, outputCodecConfig);
     }
 
+    /**
+     * Set content control id (Ccid) along with context type.
+     * @param ccid content control id
+     * @param contextType assigned contextType
+     */
+    public void setCcidInformation(int ccid, int contextType) {
+        if (DBG) {
+            Log.d(TAG, "setCcidInformation ccid: " + ccid + " context type: " + contextType);
+        }
+        setCcidInformationNative(ccid, contextType);
+    }
+
     // Native methods that call into the JNI interface
     private static native void classInitNative();
     private native void initNative(BluetoothLeAudioCodecConfig[] codecConfigOffloading);
@@ -277,4 +299,5 @@
     private native void setCodecConfigPreferenceNative(int groupId,
             BluetoothLeAudioCodecConfig inputCodecConfig,
             BluetoothLeAudioCodecConfig outputCodecConfig);
+    private native void setCcidInformationNative(int ccid, int contextType);
 }
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
index edb3f17..42c4c5a 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -52,6 +52,7 @@
 import android.os.RemoteException;
 import android.sysprop.BluetoothProperties;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
@@ -59,6 +60,7 @@
 import com.android.bluetooth.btservice.ServiceFactory;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.mcp.McpService;
+import com.android.bluetooth.tbs.TbsGatt;
 import com.android.bluetooth.vc.VolumeControlService;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -119,6 +121,7 @@
     ServiceFactory mServiceFactory = new ServiceFactory();
 
     LeAudioNativeInterface mLeAudioNativeInterface;
+    boolean mLeAudioNativeIsInitialized = false;
     LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null;
     @VisibleForTesting
     AudioManager mAudioManager;
@@ -328,6 +331,7 @@
         // Cleanup native interfaces
         mLeAudioNativeInterface.cleanup();
         mLeAudioNativeInterface = null;
+        mLeAudioNativeIsInitialized = false;
 
         // Set the service and BLE devices as inactive
         setLeAudioService(null);
@@ -553,6 +557,10 @@
      * @return true on success, otherwise false
      */
     boolean groupAddNode(int groupId, BluetoothDevice device) {
+        if (!mLeAudioNativeIsInitialized) {
+            Log.e(TAG, "Le Audio not initialized properly.");
+            return false;
+        }
         return mLeAudioNativeInterface.groupAddNode(groupId, device);
     }
 
@@ -563,6 +571,10 @@
      * @return true on success, otherwise false
      */
     boolean groupRemoveNode(int groupId, BluetoothDevice device) {
+        if (!mLeAudioNativeIsInitialized) {
+            Log.e(TAG, "Le Audio not initialized properly.");
+            return false;
+        }
         return mLeAudioNativeInterface.groupRemoveNode(groupId, device);
     }
 
@@ -952,6 +964,10 @@
             return;
         }
 
+        if (!mLeAudioNativeIsInitialized) {
+            Log.e(TAG, "Le Audio not initialized properly.");
+            return;
+        }
         mLeAudioNativeInterface.groupSetActive(groupId);
     }
 
@@ -1346,6 +1362,14 @@
                 mBroadcastMetadataList.put(broadcastId, stackEvent.broadcastMetadata);
                 notifyBroadcastMetadataChanged(broadcastId, stackEvent.broadcastMetadata);
             }
+        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED) {
+            mLeAudioNativeIsInitialized = true;
+            for (Map.Entry<ParcelUuid, Pair<Integer, Integer>> entry :
+                    ContentControlIdKeeper.getUserCcidMap().entrySet()) {
+                ParcelUuid userUuid = entry.getKey();
+                Pair<Integer, Integer> ccidInformation = entry.getValue();
+                setCcidInformation(userUuid, ccidInformation.first, ccidInformation.second);
+            }
         }
 
         if (intent != null) {
@@ -1695,6 +1719,25 @@
     }
 
     /**
+     * Set the user application ccid along with used context type
+     * @param userUuid user uuid
+     * @param ccid content control id
+     * @param contextType context type
+     */
+    public void setCcidInformation(ParcelUuid userUuid, int ccid, int contextType) {
+        /* for the moment we care only for GMCS and GTBS */
+        if (userUuid != BluetoothUuid.GENERIC_MEDIA_CONTROL
+                && userUuid.getUuid() != TbsGatt.UUID_GTBS) {
+            return;
+        }
+        if (!mLeAudioNativeIsInitialized) {
+            Log.e(TAG, "Le Audio not initialized properly.");
+            return;
+        }
+        mLeAudioNativeInterface.setCcidInformation(ccid, contextType);
+    }
+
+    /**
      * Set volume for streaming devices
      * @param volume volume to set
      */
@@ -1987,6 +2030,11 @@
             return;
         }
 
+        if (!mLeAudioNativeIsInitialized) {
+            Log.e(TAG, "Le Audio not initialized properly.");
+            return;
+        }
+
         mLeAudioNativeInterface.setCodecConfigPreference(groupId,
                                 inputCodecConfig, outputCodecConfig);
     }
@@ -2225,6 +2273,27 @@
         }
 
         @Override
+        public void setCcidInformation(ParcelUuid userUuid, int ccid, int contextType,
+                AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                Objects.requireNonNull(userUuid, "userUuid cannot be null");
+                Objects.requireNonNull(source, "source cannot be null");
+                Objects.requireNonNull(receiver, "receiver cannot be null");
+
+                LeAudioService service = getService(source);
+                if (service == null) {
+                    throw new IllegalStateException("service is null");
+                }
+                enforceBluetoothPrivilegedPermission(service);
+                service.setCcidInformation(userUuid, ccid, contextType);
+                receiver.send(null);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+        @Override
         public void getGroupId(BluetoothDevice device, AttributionSource source,
                 SynchronousResultReceiver receiver) {
             try {
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java
index edfc9e9..6ec7483 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java
@@ -36,8 +36,9 @@
     public static final int EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE = 5;
     public static final int EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED = 6;
     public static final int EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED = 7;
+    public static final int EVENT_TYPE_NATIVE_INITIALIZED = 8;
         // -------- DO NOT PUT ANY NEW UNICAST EVENTS BELOW THIS LINE-------------
-    public static final int EVENT_TYPE_UNICAST_MAX = 8;
+    public static final int EVENT_TYPE_UNICAST_MAX = 9;
 
     // Broadcast related events
     public static final int EVENT_TYPE_BROADCAST_CREATED = EVENT_TYPE_UNICAST_MAX + 1;
@@ -136,6 +137,8 @@
                 return "EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED";
             case EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED:
                 return "EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED";
+            case EVENT_TYPE_NATIVE_INITIALIZED:
+                return "EVENT_TYPE_NATIVE_INITIALIZED";
             default:
                 return "EVENT_TYPE_UNKNOWN:" + type;
         }
diff --git a/android/app/src/com/android/bluetooth/mcp/MediaControlProfile.java b/android/app/src/com/android/bluetooth/mcp/MediaControlProfile.java
index f3e5fc0..827b949 100644
--- a/android/app/src/com/android/bluetooth/mcp/MediaControlProfile.java
+++ b/android/app/src/com/android/bluetooth/mcp/MediaControlProfile.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -675,7 +676,8 @@
             }
 
             // Instantiate a Service Instance and it's state machine
-            int ccid = ContentControlIdKeeper.acquireCcid();
+            int ccid = ContentControlIdKeeper.acquireCcid(BluetoothUuid.GENERIC_MEDIA_CONTROL,
+                    BluetoothLeAudio.CONTEXT_TYPE_MEDIA);
             if (ccid == ContentControlIdKeeper.CCID_INVALID) {
                 Log.e(TAG, "Unable to acquire valid CCID!");
                 return;
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
index 5ad083e..7c3f9fc 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
@@ -23,17 +23,14 @@
 import android.bluetooth.BluetoothGattDescriptor;
 import android.bluetooth.BluetoothGattServerCallback;
 import android.bluetooth.BluetoothGattService;
-import android.bluetooth.BluetoothLeCall;
+import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
-import android.content.Context;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.ByteArrayOutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -52,7 +49,7 @@
     @VisibleForTesting
     static final UUID UUID_TBS = makeUuid("184B");
     @VisibleForTesting
-    static final UUID UUID_GTBS = makeUuid("184C");
+    public static final UUID UUID_GTBS = makeUuid("184C");
     @VisibleForTesting
     static final UUID UUID_BEARER_PROVIDER_NAME = makeUuid("2BB3");
     @VisibleForTesting
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
index 2a281ab..b8dbbff 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
@@ -18,6 +18,7 @@
 package com.android.bluetooth.tbs;
 
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothLeCall;
 import android.bluetooth.BluetoothLeCallControl;
 import android.bluetooth.IBluetoothLeCallControlCallback;
@@ -151,7 +152,8 @@
         }
         mTbsGatt = tbsGatt;
 
-        int ccid = ContentControlIdKeeper.acquireCcid();
+        int ccid = ContentControlIdKeeper.acquireCcid(new ParcelUuid(TbsGatt.UUID_GTBS),
+                BluetoothLeAudio.CONTEXT_TYPE_COMMUNICATION);
         if (!isCcidValid(ccid)) {
             Log.e(TAG, " CCID is not valid");
             cleanup();
@@ -273,7 +275,8 @@
 
         // Acquire CCID for TbsObject. The CCID is released on remove()
         Bearer bearer = new Bearer(token, callback, uci, uriSchemes, capabilities, providerName,
-                technology, ContentControlIdKeeper.acquireCcid());
+                technology, ContentControlIdKeeper.acquireCcid(new ParcelUuid(UUID.randomUUID()),
+                        BluetoothLeAudio.CONTEXT_TYPE_COMMUNICATION));
         if (isCcidValid(bearer.ccid)) {
             mBearerList.add(bearer);
 
diff --git a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
index dd6431c..2313ff0 100644
--- a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
+++ b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
@@ -103,6 +103,7 @@
     private BluetoothCall mOldHeldCall = null;
     private boolean mHeadsetUpdatedRecently = false;
     private boolean mIsDisconnectedTonePlaying = false;
+    private boolean mIsTerminatedByClient = false;
 
     private static final Object LOCK = new Object();
     private BluetoothHeadsetProxy mBluetoothHeadset;
@@ -1287,6 +1288,10 @@
         case DisconnectCause.REJECTED:
             return BluetoothLeCallControl.TERMINATION_REASON_REMOTE_HANGUP;
         case DisconnectCause.LOCAL:
+            if (mIsTerminatedByClient) {
+                mIsTerminatedByClient = false;
+                return BluetoothLeCallControl.TERMINATION_REASON_CLIENT_HANGUP;
+            }
             return BluetoothLeCallControl.TERMINATION_REASON_SERVER_HANGUP;
         case DisconnectCause.ERROR:
             return BluetoothLeCallControl.TERMINATION_REASON_NETWORK_CONGESTION;
@@ -1413,6 +1418,7 @@
                 if (mCallInfo.isNullCall(call)) {
                     result = BluetoothLeCallControl.RESULT_ERROR_UNKNOWN_CALL_ID;
                 } else {
+                    mIsTerminatedByClient = true;
                     call.disconnect();
                 }
                 mBluetoothLeCallControl.requestResult(requestId, result);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
index a72010a..8a8901e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
@@ -174,6 +174,11 @@
         mService.mLeAudioNativeInterface = mNativeInterface;
         mService.mAudioManager = mAudioManager;
 
+        LeAudioStackEvent stackEvent =
+        new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
+        mService.messageFromNative(stackEvent);
+        assertThat(mService.mLeAudioNativeIsInitialized).isTrue();
+
         // Override the timeout value to speed up the test
         LeAudioStateMachine.sConnectTimeoutMs = TIMEOUT_MS;    // 1s
 
diff --git a/system/audio_a2dp_hw/Android.bp b/system/audio_a2dp_hw/Android.bp
index 55587a9..7d77480 100644
--- a/system/audio_a2dp_hw/Android.bp
+++ b/system/audio_a2dp_hw/Android.bp
@@ -52,7 +52,10 @@
 cc_test {
     name: "net_test_audio_a2dp_hw",
     test_suites: ["device-tests"],
-    defaults: ["audio_a2dp_hw_defaults"],
+    defaults: [
+        "audio_a2dp_hw_defaults",
+        "mts_defaults",
+    ],
     srcs: [
         "test/audio_a2dp_hw_test.cc",
     ],
diff --git a/system/audio_hearing_aid_hw/Android.bp b/system/audio_hearing_aid_hw/Android.bp
index 7ca723e..cb5f110 100644
--- a/system/audio_hearing_aid_hw/Android.bp
+++ b/system/audio_hearing_aid_hw/Android.bp
@@ -40,7 +40,10 @@
 cc_test {
     name: "net_test_audio_hearing_aid_hw",
     test_suites: ["device-tests"],
-    defaults: ["audio_hearing_aid_hw_defaults"],
+    defaults: [
+        "audio_hearing_aid_hw_defaults",
+        "mts_defaults",
+    ],
     srcs: [
         "test/audio_hearing_aid_hw_test.cc",
     ],
diff --git a/system/binder/android/bluetooth/IBluetoothLeAudio.aidl b/system/binder/android/bluetooth/IBluetoothLeAudio.aidl
index 7a3a05a..4e6e345 100644
--- a/system/binder/android/bluetooth/IBluetoothLeAudio.aidl
+++ b/system/binder/android/bluetooth/IBluetoothLeAudio.aidl
@@ -26,6 +26,8 @@
 
 import com.android.modules.utils.SynchronousResultReceiver;
 
+import android.os.ParcelUuid;
+
 /**
  * APIs for Bluetooth LE Audio service
  *
@@ -61,6 +63,8 @@
     void registerCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     void unregisterCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void setCcidInformation(in ParcelUuid userUuid, in int ccid, in int contextType, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
 
     /* Same value as bluetooth::groups::kGroupUnknown */
     const int LE_AUDIO_GROUP_ID_INVALID = -1;
diff --git a/system/blueberry/facade/hci/le_acl_manager_facade.proto b/system/blueberry/facade/hci/le_acl_manager_facade.proto
index 8eac08a..844f21a 100644
--- a/system/blueberry/facade/hci/le_acl_manager_facade.proto
+++ b/system/blueberry/facade/hci/le_acl_manager_facade.proto
@@ -6,15 +6,14 @@
 import "blueberry/facade/common.proto";
 
 service LeAclManagerFacade {
-  rpc CreateConnection(blueberry.facade.BluetoothAddressWithType) returns (stream LeConnectionEvent) {}
-  rpc CreateBackgroundAndDirectConnection(blueberry.facade.BluetoothAddressWithType)
-      returns (stream LeConnectionEvent) {}
+  rpc CreateConnection(CreateConnectionMsg) returns (stream LeConnectionEvent) {}
   rpc CancelConnection(blueberry.facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {}
   rpc Disconnect(LeHandleMsg) returns (google.protobuf.Empty) {}
   rpc ConnectionCommand(LeConnectionCommandMsg) returns (google.protobuf.Empty) {}
   rpc SendAclData(LeAclData) returns (google.protobuf.Empty) {}
   rpc FetchAclData(LeHandleMsg) returns (stream LeAclData) {}
   rpc FetchIncomingConnection(google.protobuf.Empty) returns (stream LeConnectionEvent) {}
+  rpc AddDeviceToResolvingList(IrkMsg) returns (google.protobuf.Empty) {}
 }
 
 message LeHandleMsg {
@@ -34,3 +33,14 @@
   bytes payload = 2;
 }
 
+message CreateConnectionMsg {
+  blueberry.facade.BluetoothAddressWithType peer_address = 1;
+  bool is_direct = 2;
+}
+
+message IrkMsg {
+  blueberry.facade.BluetoothAddressWithType peer = 1;
+  bytes peer_irk = 2;
+  bytes local_irk = 3;
+}
+
diff --git a/system/blueberry/tests/gd/cert/py_hci.py b/system/blueberry/tests/gd/cert/py_hci.py
index 682c528..f87ecff 100644
--- a/system/blueberry/tests/gd/cert/py_hci.py
+++ b/system/blueberry/tests/gd/cert/py_hci.py
@@ -13,6 +13,7 @@
 #   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.
+from datetime import timedelta
 
 from google.protobuf import empty_pb2 as empty_proto
 from blueberry.tests.gd.cert.event_stream import EventStream
@@ -70,6 +71,43 @@
         return self.our_acl_stream.get_event_queue()
 
 
+class PyHciLeAclConnection(IEventStream):
+
+    def __init__(self, handle, acl_stream, device, peer, peer_type, peer_resolvable, local_resolvable):
+        self.handle = int(handle)
+        self.device = device
+        self.peer = peer
+        self.peer_type = peer_type
+        self.peer_resolvable = peer_resolvable
+        self.local_resolvable = local_resolvable
+        # todo, handle we got is 0, so doesn't match - fix before enabling filtering
+        self.our_acl_stream = FilteringEventStream(acl_stream, None)
+
+    def send(self, pb_flag, b_flag, data):
+        acl = AclBuilder(self.handle, pb_flag, b_flag, RawBuilder(data))
+        self.device.hci.SendAcl(common.Data(payload=bytes(acl.Serialize())))
+
+    def send_first(self, data):
+        self.send(hci_packets.PacketBoundaryFlag.FIRST_AUTOMATICALLY_FLUSHABLE,
+                  hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(data))
+
+    def send_continuing(self, data):
+        self.send(hci_packets.PacketBoundaryFlag.CONTINUING_FRAGMENT, hci_packets.BroadcastFlag.POINT_TO_POINT,
+                  bytes(data))
+
+    def get_event_queue(self):
+        return self.our_acl_stream.get_event_queue()
+
+    def local_resolvable_address(self):
+        return self.local_resolvable
+
+    def peer_resolvable_address(self):
+        return self.peer_resolvable
+
+    def peer_address(self):
+        return self.peer
+
+
 class PyHciAdvertisement(object):
 
     def __init__(self, handle, py_hci):
@@ -79,7 +117,7 @@
     def set_data(self, complete_name):
         data = GapData()
         data.data_type = GapDataType.COMPLETE_LOCAL_NAME
-        data.data = list(bytes(complete_name))
+        data.data = list(complete_name)
         self.py_hci.send_command(
             LeSetExtendedAdvertisingDataBuilder(self.handle, Operation.COMPLETE_ADVERTISEMENT,
                                                 FragmentPreference.CONTROLLER_SHOULD_NOT, [data]))
@@ -87,7 +125,7 @@
     def set_scan_response(self, shortened_name):
         data = GapData()
         data.data_type = GapDataType.SHORTENED_LOCAL_NAME
-        data.data = list(bytes(shortened_name))
+        data.data = list(shortened_name)
         self.py_hci.send_command(
             LeSetExtendedAdvertisingScanResponseBuilder(self.handle, Operation.COMPLETE_ADVERTISEMENT,
                                                         FragmentPreference.CONTROLLER_SHOULD_NOT, [data]))
@@ -121,6 +159,7 @@
             self.register_for_events(hci_packets.EventCode.ROLE_CHANGE, hci_packets.EventCode.CONNECTION_REQUEST,
                                      hci_packets.EventCode.CONNECTION_COMPLETE,
                                      hci_packets.EventCode.CONNECTION_PACKET_TYPE_CHANGED)
+            self.register_for_le_events(hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE)
             self.acl_stream = EventStream(self.device.hci.StreamAcl(empty_proto.Empty()))
 
     def close(self):
@@ -187,6 +226,48 @@
             raise Exception("Please construct '%s' with acl_streaming=True!" % self.__class__.__name__)
         return PyHciAclConnection(handle, self.acl_stream, self.device)
 
+    def set_random_le_address(self, addr):
+        self.send_command(hci_packets.LeSetRandomAddressBuilder(addr))
+        assertThat(self.event_stream).emits(HciMatchers.CommandComplete(OpCode.LE_SET_RANDOM_ADDRESS))
+
+    def initiate_le_connection(self, remote_addr):
+        phy_scan_params = hci_packets.LeCreateConnPhyScanParameters()
+        phy_scan_params.scan_interval = 0x60
+        phy_scan_params.scan_window = 0x30
+        phy_scan_params.conn_interval_min = 0x18
+        phy_scan_params.conn_interval_max = 0x28
+        phy_scan_params.conn_latency = 0
+        phy_scan_params.supervision_timeout = 0x1f4
+        phy_scan_params.min_ce_length = 0
+        phy_scan_params.max_ce_length = 0
+        self.send_command(
+            hci_packets.LeExtendedCreateConnectionBuilder(
+                hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS, hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                hci_packets.AddressType.RANDOM_DEVICE_ADDRESS, remote_addr, 1, [phy_scan_params]))
+        assertThat(self.event_stream).emits(HciMatchers.CommandStatus(OpCode.LE_EXTENDED_CREATE_CONNECTION))
+
+    def incoming_le_connection(self):
+        connection_complete = HciCaptures.LeConnectionCompleteCapture()
+        assertThat(self.le_event_stream).emits(connection_complete)
+
+        handle = connection_complete.get().GetConnectionHandle()
+        peer = connection_complete.get().GetPeerAddress()
+        peer_type = connection_complete.get().GetPeerAddressType()
+        local_resolvable = connection_complete.get().GetLocalResolvablePrivateAddress()
+        peer_resolvable = connection_complete.get().GetPeerResolvablePrivateAddress()
+        if self.acl_stream is None:
+            raise Exception("Please construct '%s' with acl_streaming=True!" % self.__class__.__name__)
+        return PyHciLeAclConnection(handle, self.acl_stream, self.device, peer, peer_type, peer_resolvable,
+                                    local_resolvable)
+
+    def incoming_le_connection_fails(self):
+        connection_complete = HciCaptures.LeConnectionCompleteCapture()
+        assertThat(self.le_event_stream).emitsNone(connection_complete, timeout=timedelta(seconds=5))
+
+    def add_device_to_resolving_list(self, peer_address_type, peer_address, peer_irk, local_irk):
+        self.send_command(
+            hci_packets.LeAddDeviceToResolvingListBuilder(peer_address_type, peer_address, peer_irk, local_irk))
+
     def create_advertisement(self,
                              handle,
                              own_address,
diff --git a/system/blueberry/tests/gd/cert/py_le_acl_manager.py b/system/blueberry/tests/gd/cert/py_le_acl_manager.py
index 914ec84..1cb0cbe 100644
--- a/system/blueberry/tests/gd/cert/py_le_acl_manager.py
+++ b/system/blueberry/tests/gd/cert/py_le_acl_manager.py
@@ -29,12 +29,13 @@
 
 class PyLeAclManagerAclConnection(IEventStream, Closable):
 
-    def __init__(self, le_acl_manager, address, remote_addr, handle, event_stream):
+    def __init__(self, le_acl_manager, address, remote_addr, remote_addr_type, handle, event_stream):
         """
         An abstract representation for an LE ACL connection in GD certification test
         :param le_acl_manager: The LeAclManager from this GD device
         :param address: The local device address
         :param remote_addr: Remote device address
+        :param remote_addr_type: Remote device address type
         :param handle: Connection handle
         :param event_stream: The connection event stream for this connection
         """
@@ -46,6 +47,7 @@
         self.acl_stream = EventStream(
             self.le_acl_manager.FetchAclData(le_acl_manager_facade.LeHandleMsg(handle=self.handle)))
         self.remote_address = remote_addr
+        self.remote_address_type = remote_addr_type
         self.own_address = address
         self.disconnect_reason = None
 
@@ -113,18 +115,11 @@
         safeClose(pair[0])
         self.le_acl_manager.CancelConnection(pair[1])
 
-    def initiate_connection(self, remote_addr):
+    def initiate_connection(self, remote_addr, is_direct=True):
         assertThat(self.next_token in self.outgoing_connection_event_streams).isFalse()
+        create_connection_msg = le_acl_manager_facade.CreateConnectionMsg(peer_address=remote_addr, is_direct=is_direct)
         self.outgoing_connection_event_streams[self.next_token] = EventStream(
-            self.le_acl_manager.CreateConnection(remote_addr)), remote_addr
-        token = self.next_token
-        self.next_token += 1
-        return token
-
-    def initiate_background_and_direct_connection(self, remote_addr):
-        assertThat(self.next_token in self.outgoing_connection_event_streams).isFalse()
-        self.outgoing_connection_event_streams[self.next_token] = EventStream(
-            self.le_acl_manager.CreateBackgroundAndDirectConnection(remote_addr)), remote_addr
+            self.le_acl_manager.CreateConnection(create_connection_msg)), remote_addr
         token = self.next_token
         self.next_token += 1
         return token
@@ -135,11 +130,13 @@
         complete = connection_complete.get()
         handle = complete.GetConnectionHandle()
         remote = complete.GetPeerAddress()
+        remote_address_type = complete.GetPeerAddressType()
         if complete.GetSubeventCode() == hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE:
             address = complete.GetLocalResolvablePrivateAddress()
         else:
             address = None
-        connection = PyLeAclManagerAclConnection(self.le_acl_manager, address, remote, handle, event_stream)
+        connection = PyLeAclManagerAclConnection(self.le_acl_manager, address, remote, remote_address_type, handle,
+                                                 event_stream)
         self.active_connections.append(connection)
         return connection
 
diff --git a/system/blueberry/tests/gd/hci/direct_hci_test.py b/system/blueberry/tests/gd/hci/direct_hci_test.py
index 23f1b93..c0dbdc4 100644
--- a/system/blueberry/tests/gd/hci/direct_hci_test.py
+++ b/system/blueberry/tests/gd/hci/direct_hci_test.py
@@ -205,7 +205,6 @@
 
     def test_le_connection_dut_advertises(self):
         self.dut_hci.register_for_le_events(SubeventCode.CONNECTION_COMPLETE, SubeventCode.ADVERTISING_SET_TERMINATED,
-                                            SubeventCode.ENHANCED_CONNECTION_COMPLETE,
                                             SubeventCode.READ_REMOTE_FEATURES_COMPLETE)
         # Cert Connects
         self.cert_hal.unmask_event(EventCode.LE_META_EVENT)
@@ -239,7 +238,6 @@
             lambda packet: logging.debug(packet.payload) or b'SomeMoreAclData' in packet.payload)
 
     def test_le_filter_accept_list_connection_cert_advertises(self):
-        self.dut_hci.register_for_le_events(SubeventCode.CONNECTION_COMPLETE, SubeventCode.ENHANCED_CONNECTION_COMPLETE)
         # DUT Connects
         self.dut_hci.send_command(LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
         self.dut_hci.send_command(
diff --git a/system/blueberry/tests/gd/hci/le_acl_manager_test.py b/system/blueberry/tests/gd/hci/le_acl_manager_test.py
index 34ef228..5bdf758 100644
--- a/system/blueberry/tests/gd/hci/le_acl_manager_test.py
+++ b/system/blueberry/tests/gd/hci/le_acl_manager_test.py
@@ -16,16 +16,14 @@
 
 from blueberry.tests.gd.cert import gd_base_test
 from blueberry.tests.gd.cert.closable import safeClose
-from blueberry.tests.gd.cert.event_stream import EventStream
 from blueberry.tests.gd.cert.truth import assertThat
+from blueberry.tests.gd.cert.py_hci import PyHci, PyHciAdvertisement
 from blueberry.tests.gd.cert.py_le_acl_manager import PyLeAclManager
-from google.protobuf import empty_pb2 as empty_proto
 from blueberry.facade import common_pb2 as common
 from blueberry.facade.hci import le_acl_manager_facade_pb2 as le_acl_manager_facade
 from blueberry.facade.hci import le_advertising_manager_facade_pb2 as le_advertising_facade
 from blueberry.facade.hci import le_initiator_address_facade_pb2 as le_initiator_address_facade
 from blueberry.facade.hci import hci_facade_pb2 as hci_facade
-import bluetooth_packets_python3 as bt_packets
 from bluetooth_packets_python3 import hci_packets
 from bluetooth_packets_python3 import RawBuilder
 from mobly import test_runner
@@ -38,22 +36,24 @@
 
     def setup_test(self):
         gd_base_test.GdBaseTestClass.setup_test(self)
+        self.cert_hci = PyHci(self.cert, acl_streaming=True)
         self.dut_le_acl_manager = PyLeAclManager(self.dut)
-        self.cert_hci_le_event_stream = EventStream(self.cert.hci.StreamLeSubevents(empty_proto.Empty()))
-        self.cert_acl_data_stream = EventStream(self.cert.hci.StreamAcl(empty_proto.Empty()))
+        self.cert_public_address = self.cert_hci.read_own_address()
+        self.dut_public_address = self.dut.hci_controller.GetMacAddressSimple().decode("utf-8")
+        self.dut_random_address = 'd0:05:04:03:02:01'
+        self.cert_random_address = 'c0:05:04:03:02:01'
 
     def teardown_test(self):
-        safeClose(self.cert_hci_le_event_stream)
-        safeClose(self.cert_acl_data_stream)
         safeClose(self.dut_le_acl_manager)
+        self.cert_hci.close()
         gd_base_test.GdBaseTestClass.teardown_test(self)
 
     def set_privacy_policy_static(self):
-        self.dut_address = b'd0:05:04:03:02:01'
         private_policy = le_initiator_address_facade.PrivacyPolicy(
             address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
             address_with_type=common.BluetoothAddressWithType(
-                address=common.BluetoothAddress(address=bytes(self.dut_address)), type=common.RANDOM_DEVICE_ADDRESS))
+                address=common.BluetoothAddress(address=bytes(self.dut_random_address, "utf-8")),
+                type=common.RANDOM_DEVICE_ADDRESS))
         self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(private_policy)
 
     def register_for_event(self, event_code):
@@ -73,104 +73,91 @@
         acl = hci_packets.AclBuilder(handle, pb_flag, b_flag, RawBuilder(data))
         self.cert.hci.SendAcl(common.Data(payload=bytes(acl.Serialize())))
 
-    def dut_connects(self, check_address):
-        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
-        self.register_for_le_event(hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE)
+    def dut_connects(self):
+        # Cert Advertises
+        advertising_handle = 0
+        py_hci_adv = PyHciAdvertisement(advertising_handle, self.cert_hci)
+
+        self.cert_hci.create_advertisement(
+            advertising_handle,
+            self.cert_random_address,
+            hci_packets.LegacyAdvertisingProperties.ADV_IND,
+        )
+
+        py_hci_adv.set_data(b'Im_A_Cert')
+        py_hci_adv.set_scan_response(b'Im_A_C')
+        py_hci_adv.start()
+
+        dut_le_acl = self.dut_le_acl_manager.connect_to_remote(
+            remote_addr=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes(self.cert_random_address, 'utf8')),
+                type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)))
+
+        cert_le_acl = self.cert_hci.incoming_le_connection()
+        return dut_le_acl, cert_le_acl
+
+    def cert_advertises_resolvable(self):
+        self.cert_hci.add_device_to_resolving_list(hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                                                   self.dut_public_address,
+                                                   b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f',
+                                                   b'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f')
 
         # Cert Advertises
         advertising_handle = 0
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
-                advertising_handle,
-                hci_packets.LegacyAdvertisingProperties.ADV_IND,
-                400,
-                450,
-                7,
-                hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
-                hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
-                '00:00:00:00:00:00',
-                hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
-                0xF8,
-                1,  #SID
-                hci_packets.Enable.DISABLED  # Scan request notification
+        py_hci_adv = PyHciAdvertisement(advertising_handle, self.cert_hci)
+
+        self.cert_hci.create_advertisement(
+            advertising_handle,
+            self.cert_random_address,
+            hci_packets.LegacyAdvertisingProperties.ADV_IND,
+            own_address_type=hci_packets.OwnAddressType.RESOLVABLE_OR_PUBLIC_ADDRESS,
+            peer_address=self.dut_public_address,
+            peer_address_type=hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS)
+
+        py_hci_adv.set_data(b'Im_A_Cert')
+        py_hci_adv.set_scan_response(b'Im_A_C')
+        py_hci_adv.start()
+
+    def dut_connects_cert_resolvable(self):
+        self.dut.hci_le_acl_manager.AddDeviceToResolvingList(
+            le_acl_manager_facade.IrkMsg(
+                peer=common.BluetoothAddressWithType(
+                    address=common.BluetoothAddress(address=bytes(self.cert_public_address, "utf-8")),
+                    type=int(hci_packets.AddressType.PUBLIC_DEVICE_ADDRESS)),
+                peer_irk=b'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f',
+                local_irk=b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f',
             ))
 
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0C:05:04:03:02:01'))
-
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_A_Cert'))
-
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingDataBuilder(
-                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
-                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
-
-        gap_short_name = hci_packets.GapData()
-        gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
-        gap_short_name.data = list(bytes(b'Im_A_C'))
-
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
-                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
-                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name]))
-
-        enabled_set = hci_packets.EnabledSet()
-        enabled_set.advertising_handle = advertising_handle
-        enabled_set.duration = 0
-        enabled_set.max_extended_advertising_events = 0
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
-
-        self.dut_le_acl = self.dut_le_acl_manager.connect_to_remote(
+        dut_le_acl = self.dut_le_acl_manager.connect_to_remote(
             remote_addr=common.BluetoothAddressWithType(
-                address=common.BluetoothAddress(address=bytes('0C:05:04:03:02:01', 'utf8')),
-                type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)))
+                address=common.BluetoothAddress(address=bytes(self.cert_public_address, "utf-8")),
+                type=int(hci_packets.AddressType.PUBLIC_DEVICE_ADDRESS)))
 
-        # Cert gets ConnectionComplete with a handle and sends ACL data
-        handle = 0xfff
-        address = hci_packets.Address()
+        cert_le_acl = self.cert_hci.incoming_le_connection()
+        return dut_le_acl, cert_le_acl
 
-        def get_handle(packet):
-            packet_bytes = packet.payload
-            nonlocal handle
-            nonlocal address
-            if b'\x3e\x13\x01\x00' in packet_bytes:
-                cc_view = hci_packets.LeConnectionCompleteView(
-                    hci_packets.LeMetaEventView(
-                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
-                handle = cc_view.GetConnectionHandle()
-                address = cc_view.GetPeerAddress()
-                return True
-            if b'\x3e\x1f\x0A\x00' in packet_bytes:
-                cc_view = hci_packets.LeEnhancedConnectionCompleteView(
-                    hci_packets.LeMetaEventView(
-                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
-                handle = cc_view.GetConnectionHandle()
-                address = cc_view.GetPeerAddress()
-                return True
-            return False
-
-        self.cert_hci_le_event_stream.assert_event_occurs(get_handle)
-        self.cert_handle = handle
-        dut_address_from_complete = address
-        if check_address:
-            assertThat(dut_address_from_complete).isEqualTo(self.dut_address.decode())
-
-    def send_receive_and_check(self):
-        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+    def send_receive_and_check(self, dut_le_acl, cert_le_acl):
+        self.enqueue_acl_data(cert_le_acl.handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
                               hci_packets.BroadcastFlag.POINT_TO_POINT,
                               bytes(b'\x19\x00\x07\x00SomeAclData from the Cert'))
 
-        self.dut_le_acl.send(b'\x1C\x00\x07\x00SomeMoreAclData from the DUT')
-        self.cert_acl_data_stream.assert_event_occurs(lambda packet: b'SomeMoreAclData' in packet.payload)
-        assertThat(self.dut_le_acl).emits(lambda packet: b'SomeAclData' in packet.payload)
+        dut_le_acl.send(b'\x1C\x00\x07\x00SomeMoreAclData from the DUT')
+        assertThat(cert_le_acl.our_acl_stream).emits(lambda packet: b'SomeMoreAclData' in packet.payload)
+        assertThat(dut_le_acl).emits(lambda packet: b'SomeAclData' in packet.payload)
 
     def test_dut_connects(self):
         self.set_privacy_policy_static()
-        self.dut_connects(check_address=True)
-        self.send_receive_and_check()
+        dut_le_acl, cert_le_acl = self.dut_connects()
+
+        assertThat(cert_le_acl.handle).isNotNone()
+        assertThat(cert_le_acl.peer).isEqualTo(self.dut_random_address)
+        assertThat(cert_le_acl.peer_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        assertThat(dut_le_acl.handle).isNotNone()
+        assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address)
+        assertThat(dut_le_acl.remote_address_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        self.send_receive_and_check(dut_le_acl, cert_le_acl)
 
     def test_dut_connects_resolvable_address(self):
         privacy_policy = le_initiator_address_facade.PrivacyPolicy(
@@ -179,8 +166,39 @@
             minimum_rotation_time=7 * 60 * 1000,
             maximum_rotation_time=15 * 60 * 1000)
         self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
-        self.dut_connects(check_address=False)
-        self.send_receive_and_check()
+        dut_le_acl, cert_le_acl = self.dut_connects()
+
+        assertThat(cert_le_acl.handle).isNotNone()
+        assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_public_address)
+        assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_random_address)
+        assertThat(cert_le_acl.peer_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        assertThat(dut_le_acl.handle).isNotNone()
+        assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address)
+        assertThat(dut_le_acl.remote_address_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        self.send_receive_and_check(dut_le_acl, cert_le_acl)
+
+    def test_dut_connects_resolvable_address_public(self):
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_RESOLVABLE_ADDRESS,
+            rotation_irk=b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f',
+            minimum_rotation_time=7 * 60 * 1000,
+            maximum_rotation_time=15 * 60 * 1000)
+        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+        self.cert_advertises_resolvable()
+        dut_le_acl, cert_le_acl = self.dut_connects_cert_resolvable()
+
+        assertThat(cert_le_acl.handle).isNotNone()
+        assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_public_address)
+        assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_random_address)
+        assertThat(cert_le_acl.peer_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        assertThat(dut_le_acl.handle).isNotNone()
+        assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_public_address)
+        assertThat(dut_le_acl.remote_address_type).isEqualTo(hci_packets.AddressType.PUBLIC_DEVICE_ADDRESS)
+
+        self.send_receive_and_check(dut_le_acl, cert_le_acl)
 
     def test_dut_connects_non_resolvable_address(self):
         privacy_policy = le_initiator_address_facade.PrivacyPolicy(
@@ -189,28 +207,54 @@
             minimum_rotation_time=8 * 60 * 1000,
             maximum_rotation_time=14 * 60 * 1000)
         self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
-        self.dut_connects(check_address=False)
-        self.send_receive_and_check()
+        dut_le_acl, cert_le_acl = self.dut_connects()
+
+        assertThat(cert_le_acl.handle).isNotNone()
+        assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_public_address)
+        assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_random_address)
+        assertThat(cert_le_acl.peer_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        assertThat(dut_le_acl.handle).isNotNone()
+        assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address)
+        assertThat(dut_le_acl.remote_address_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        self.send_receive_and_check(dut_le_acl, cert_le_acl)
 
     def test_dut_connects_public_address(self):
         self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(
             le_initiator_address_facade.PrivacyPolicy(
                 address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS))
-        self.dut_connects(check_address=False)
-        self.send_receive_and_check()
+        dut_le_acl, cert_le_acl = self.dut_connects()
+
+        assertThat(cert_le_acl.handle).isNotNone()
+        assertThat(cert_le_acl.peer).isEqualTo(self.dut_public_address)
+        assertThat(cert_le_acl.peer_type).isEqualTo(hci_packets.AddressType.PUBLIC_DEVICE_ADDRESS)
+
+        assertThat(dut_le_acl.handle).isNotNone()
+        assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address)
+        assertThat(dut_le_acl.remote_address_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        self.send_receive_and_check(dut_le_acl, cert_le_acl)
 
     def test_dut_connects_public_address_cancelled(self):
+        # TODO (Add cancel)
         self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(
             le_initiator_address_facade.PrivacyPolicy(
                 address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS))
-        self.dut_connects(check_address=False)
-        self.send_receive_and_check()
+        dut_le_acl, cert_le_acl = self.dut_connects()
+
+        assertThat(cert_le_acl.handle).isNotNone()
+        assertThat(cert_le_acl.peer).isEqualTo(self.dut_public_address)
+        assertThat(cert_le_acl.peer_type).isEqualTo(hci_packets.AddressType.PUBLIC_DEVICE_ADDRESS)
+
+        assertThat(dut_le_acl.handle).isNotNone()
+        assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address)
+        assertThat(dut_le_acl.remote_address_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        self.send_receive_and_check(dut_le_acl, cert_le_acl)
 
     def test_cert_connects(self):
         self.set_privacy_policy_static()
-        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
-        self.register_for_le_event(hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE)
-
         self.dut_le_acl_manager.listen_for_incoming_connections()
 
         # DUT Advertises
@@ -233,127 +277,130 @@
         self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
 
         # Cert Connects
-        self.enqueue_hci_command(hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
-        phy_scan_params = hci_packets.LeCreateConnPhyScanParameters()
-        phy_scan_params.scan_interval = 0x60
-        phy_scan_params.scan_window = 0x30
-        phy_scan_params.conn_interval_min = 0x18
-        phy_scan_params.conn_interval_max = 0x28
-        phy_scan_params.conn_latency = 0
-        phy_scan_params.supervision_timeout = 0x1f4
-        phy_scan_params.min_ce_length = 0
-        phy_scan_params.max_ce_length = 0
-        self.enqueue_hci_command(
-            hci_packets.LeExtendedCreateConnectionBuilder(hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS,
-                                                          hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
-                                                          hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
-                                                          self.dut_address.decode(), 1, [phy_scan_params]))
+        self.cert_hci.set_random_le_address(self.cert_random_address)
+        self.cert_hci.initiate_le_connection(self.dut_random_address)
 
         # Cert gets ConnectionComplete with a handle and sends ACL data
-        handle = 0xfff
+        cert_le_acl = self.cert_hci.incoming_le_connection()
 
-        def get_handle(packet):
-            packet_bytes = packet.payload
-            nonlocal handle
-            if b'\x3e\x13\x01\x00' in packet_bytes:
-                cc_view = hci_packets.LeConnectionCompleteView(
-                    hci_packets.LeMetaEventView(
-                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
-                handle = cc_view.GetConnectionHandle()
-                return True
-            if b'\x3e\x1f\x0A\x00' in packet_bytes:
-                cc_view = hci_packets.LeEnhancedConnectionCompleteView(
-                    hci_packets.LeMetaEventView(
-                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
-                handle = cc_view.GetConnectionHandle()
-                return True
-            return False
-
-        self.cert_hci_le_event_stream.assert_event_occurs(get_handle)
-        self.cert_handle = handle
-
-        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
-                              hci_packets.BroadcastFlag.POINT_TO_POINT,
-                              bytes(b'\x19\x00\x07\x00SomeAclData from the Cert'))
+        cert_le_acl.send(hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                         hci_packets.BroadcastFlag.POINT_TO_POINT, b'\x19\x00\x07\x00SomeAclData from the Cert')
 
         # DUT gets a connection complete event and sends and receives
-        handle = 0xfff
-        self.dut_le_acl = self.dut_le_acl_manager.complete_incoming_connection()
+        dut_le_acl = self.dut_le_acl_manager.complete_incoming_connection()
+        assertThat(cert_le_acl.handle).isNotNone()
+        assertThat(cert_le_acl.peer).isEqualTo(self.dut_random_address)
+        assertThat(cert_le_acl.peer_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
 
-        self.send_receive_and_check()
+        assertThat(dut_le_acl.handle).isNotNone()
+        assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address)
+        assertThat(dut_le_acl.remote_address_type).isEqualTo(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)
+
+        self.send_receive_and_check(dut_le_acl, cert_le_acl)
 
     def test_recombination_l2cap_packet(self):
         self.set_privacy_policy_static()
-        self.dut_connects(check_address=True)
-
-        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+        dut_le_acl, cert_le_acl = self.dut_connects()
+        cert_handle = cert_le_acl.handle
+        self.enqueue_acl_data(cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
                               hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'\x06\x00\x07\x00Hello'))
-        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.CONTINUING_FRAGMENT,
+        self.enqueue_acl_data(cert_handle, hci_packets.PacketBoundaryFlag.CONTINUING_FRAGMENT,
                               hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'!'))
 
-        assertThat(self.dut_le_acl).emits(lambda packet: b'Hello!' in packet.payload)
+        assertThat(dut_le_acl).emits(lambda packet: b'Hello!' in packet.payload)
 
     def test_background_connection(self):
-        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
-        self.register_for_le_event(hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE)
         self.set_privacy_policy_static()
 
         # Start background and direct connection
-        token = self.dut_le_acl_manager.initiate_background_and_direct_connection(
+        token_direct = self.dut_le_acl_manager.initiate_connection(
             remote_addr=common.BluetoothAddressWithType(
-                address=common.BluetoothAddress(address=bytes('0C:05:04:03:02:01', 'utf8')),
+                address=common.BluetoothAddress(address=bytes('0C:05:04:03:02:02', 'utf8')),
                 type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)))
 
+        token_background = self.dut_le_acl_manager.initiate_connection(
+            remote_addr=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes(self.cert_random_address, 'utf8')),
+                type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)),
+            is_direct=False)
+
         # Wait for direct connection timeout
-        self.dut_le_acl_manager.wait_for_connection_fail(token)
+        self.dut_le_acl_manager.wait_for_connection_fail(token_direct)
 
         # Cert Advertises
         advertising_handle = 0
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
-                advertising_handle,
-                hci_packets.LegacyAdvertisingProperties.ADV_IND,
-                155,  # 100ms = 160 * .625ms "LOW_LATENCY"
-                165,
-                7,
-                hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
-                hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
-                '00:00:00:00:00:00',
-                hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
-                0xF8,
-                1,  #SID
-                hci_packets.Enable.DISABLED  # Scan request notification
-            ))
 
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0C:05:04:03:02:01'))
+        py_hci_adv = self.cert_hci.create_advertisement(advertising_handle, self.cert_random_address,
+                                                        hci_packets.LegacyAdvertisingProperties.ADV_IND, 155, 165)
 
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_A_Cert'))
-
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingDataBuilder(
-                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
-                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
-
-        gap_short_name = hci_packets.GapData()
-        gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
-        gap_short_name.data = list(bytes(b'Im_A_C'))
-
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
-                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
-                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name]))
-
-        enabled_set = hci_packets.EnabledSet()
-        enabled_set.advertising_handle = advertising_handle
-        enabled_set.duration = 0
-        enabled_set.max_extended_advertising_events = 0
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
+        py_hci_adv.set_data(b'Im_A_Cert')
+        py_hci_adv.set_scan_response(b'Im_A_C')
+        py_hci_adv.start()
 
         # Check background connection complete
+        self.dut_le_acl_manager.complete_outgoing_connection(token_background)
+
+    def skip_flaky_test_multiple_background_connections(self):
+        self.set_privacy_policy_static()
+
+        # Start two background connections
+        token_1 = self.dut_le_acl_manager.initiate_connection(
+            remote_addr=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes(self.cert_random_address, 'utf8')),
+                type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)),
+            is_direct=False)
+
+        token_2 = self.dut_le_acl_manager.initiate_connection(
+            remote_addr=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes('0C:05:04:03:02:02', 'utf8')),
+                type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)),
+            is_direct=False)
+
+        # Cert Advertises
+        advertising_handle = 0
+
+        py_hci_adv = self.cert_hci.create_advertisement(advertising_handle, self.cert_random_address,
+                                                        hci_packets.LegacyAdvertisingProperties.ADV_IND, 155, 165)
+
+        py_hci_adv.set_data(b'Im_A_Cert')
+        py_hci_adv.set_scan_response(b'Im_A_C')
+        py_hci_adv.start()
+
+        # First background connection completes
+        connection = self.dut_le_acl_manager.complete_outgoing_connection(token_1)
+        connection.close()
+
+        # Cert Advertises again
+        advertising_handle = 0
+
+        py_hci_adv = self.cert_hci.create_advertisement(advertising_handle, '0C:05:04:03:02:02',
+                                                        hci_packets.LegacyAdvertisingProperties.ADV_IND, 155, 165)
+
+        py_hci_adv.set_data(b'Im_A_Cert')
+        py_hci_adv.set_scan_response(b'Im_A_C')
+        py_hci_adv.start()
+
+        # Second background connection completes
+        connection = self.dut_le_acl_manager.complete_outgoing_connection(token_2)
+        connection.close()
+
+    def test_direct_connection(self):
+        self.set_privacy_policy_static()
+
+        advertising_handle = 0
+        py_hci_adv = self.cert_hci.create_advertisement(advertising_handle, self.cert_random_address,
+                                                        hci_packets.LegacyAdvertisingProperties.ADV_IND, 155, 165)
+
+        py_hci_adv.set_data(b'Im_A_Cert')
+        py_hci_adv.set_scan_response(b'Im_A_C')
+        py_hci_adv.start()
+
+        # Start direct connection
+        token = self.dut_le_acl_manager.initiate_connection(
+            remote_addr=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes(self.cert_random_address, 'utf8')),
+                type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)),
+            is_direct=True)
         self.dut_le_acl_manager.complete_outgoing_connection(token)
 
 
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 020a06d..e42d452 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -153,7 +153,10 @@
 // bta unit tests for target
 cc_test {
     name: "net_test_bta",
-    defaults: ["fluoride_bta_defaults"],
+    defaults: [
+        "fluoride_bta_defaults",
+        "mts_defaults"
+    ],
     test_suites: ["device-tests"],
     srcs: [
         ":TestMockStackBtm",
@@ -192,7 +195,10 @@
 
 cc_test {
     name: "bt_host_test_bta",
-    defaults: ["fluoride_bta_defaults"],
+    defaults: [
+        "fluoride_bta_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     host_supported: true,
     include_dirs: [
@@ -266,7 +272,10 @@
 // bta hf client add record tests for target
 cc_test {
     name: "net_test_hf_client_add_record",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     include_dirs: [
         "packages/modules/Bluetooth/system",
@@ -300,6 +309,7 @@
     defaults: [
         "fluoride_bta_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     include_dirs: [
@@ -353,6 +363,7 @@
     defaults: [
         "fluoride_bta_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     include_dirs: [
@@ -394,6 +405,7 @@
     defaults: [
         "fluoride_bta_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     include_dirs: [
@@ -512,6 +524,7 @@
     defaults: [
         "fluoride_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     target: {
@@ -591,6 +604,7 @@
     defaults: [
         "fluoride_bta_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     include_dirs: [
@@ -677,6 +691,7 @@
     defaults: [
         "fluoride_bta_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     include_dirs: [
@@ -724,6 +739,7 @@
     defaults: [
         "fluoride_bta_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     include_dirs: [
@@ -791,6 +807,7 @@
     defaults: [
         "fluoride_bta_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     include_dirs: [
diff --git a/system/bta/include/bta_le_audio_api.h b/system/bta/include/bta_le_audio_api.h
index b22bde7..2d44f9b 100644
--- a/system/bta/include/bta_le_audio_api.h
+++ b/system/bta/include/bta_le_audio_api.h
@@ -65,6 +65,7 @@
       int group_id,
       bluetooth::le_audio::btle_audio_codec_config_t input_codec_config,
       bluetooth::le_audio::btle_audio_codec_config_t output_codec_config) = 0;
+  virtual void SetCcidInformation(int ccid, int context_type) = 0;
   virtual std::vector<RawAddress> GetGroupDevices(const int group_id) = 0;
   static void AddFromStorage(const RawAddress& addr, bool autoconnect);
   static bool IsLeAudioClientRunning();
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 8158f61..287a9ac 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -307,6 +307,14 @@
     group_remove_node(group, address);
   }
 
+  int GetCcid(le_audio::types::LeAudioContextType context_type) {
+    if (ccids_.count(context_type) == 0) {
+      return -1;
+    }
+
+    return ccids_[context_type];
+  }
+
   /* This callback happens if kLeAudioDeviceSetStateTimeoutMs timeout happens
    * during transition from origin to target state
    */
@@ -612,7 +620,8 @@
     }
 
     bool result = groupStateMachine_->StartStream(
-        group, static_cast<LeAudioContextType>(final_context_type));
+        group, static_cast<LeAudioContextType>(final_context_type),
+        GetCcid(static_cast<LeAudioContextType>(final_context_type)));
     if (result)
       stream_setup_start_timestamp_ =
           bluetooth::common::time_get_os_boottime_us();
@@ -701,6 +710,21 @@
     // TODO Implement
   }
 
+  void SetCcidInformation(int ccid, int context_type) override {
+    LOG_DEBUG("Ccid: %d, context type %d", ccid, context_type);
+
+    std::bitset<16> test{static_cast<uint16_t>(context_type)};
+    auto ctx_type =
+        static_cast<le_audio::types::LeAudioContextType>(context_type);
+    if (test.count() > 1 ||
+        ctx_type >= le_audio::types::LeAudioContextType::RFU) {
+      LOG_ERROR("Unknownd context type %d", context_type);
+      return;
+    }
+
+    ccids_[ctx_type] = ccid;
+  }
+
   void StartAudioSession(LeAudioDeviceGroup* group,
                           LeAudioCodecConfiguration* source_config,
                           LeAudioCodecConfiguration* sink_config) {
@@ -3446,8 +3470,9 @@
         stream_setup_start_timestamp_ = 0;
         if (group && group->IsPendingConfiguration()) {
           SuspendedForReconfiguration();
-          if (groupStateMachine_->ConfigureStream(group,
-                                                  current_context_type_)) {
+          if (groupStateMachine_->ConfigureStream(
+                  group, current_context_type_,
+                  GetCcid(current_context_type_))) {
             /* If configuration succeed wait for new status. */
             return;
           }
@@ -3489,6 +3514,10 @@
   /* Speaker(s) */
   AudioState audio_sender_state_;
 
+  /* Ccid informations */
+  std::map<le_audio::types::LeAudioContextType /* context */, int /*ccid */>
+      ccids_;
+
   /* Current stream configuration */
   LeAudioCodecConfiguration current_source_codec_config;
   LeAudioCodecConfiguration current_sink_codec_config;
@@ -3782,6 +3811,8 @@
 
   IsoManager::GetInstance()->RegisterCigCallbacks(stateMachineHciCallbacks);
   CodecManager::GetInstance()->Start(offloading_preference);
+
+  callbacks_->OnInitialized();
 }
 
 void LeAudioClient::DebugDump(int fd) {
diff --git a/system/bta/le_audio/client_linux.cc b/system/bta/le_audio/client_linux.cc
index 87469ca..bde6996 100644
--- a/system/bta/le_audio/client_linux.cc
+++ b/system/bta/le_audio/client_linux.cc
@@ -38,6 +38,7 @@
       bluetooth::le_audio::btle_audio_codec_config_t input_codec_config,
       bluetooth::le_audio::btle_audio_codec_config_t output_codec_config)
       override {}
+  void SetCcidInformation(int ccid, int context_type) override {}
   std::vector<RawAddress> GetGroupDevices(const int group_id) override {
     return {};
   }
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index 86b2895..ba7f462 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -964,7 +964,8 @@
     types::LeAudioContextType context_type,
     uint8_t* number_of_already_active_group_ase,
     types::AudioLocations& group_snk_audio_locations,
-    types::AudioLocations& group_src_audio_locations, bool reuse_cis_id) {
+    types::AudioLocations& group_src_audio_locations, bool reuse_cis_id,
+    int ccid) {
   struct ase* ase = GetFirstInactiveAse(ent.direction, reuse_cis_id);
   if (!ase) return false;
 
@@ -1019,7 +1020,7 @@
     ase->retrans_nb = ent.qos.retransmission_number;
     ase->max_transport_latency = ent.qos.max_transport_latency;
 
-    ase->metadata = GetMetadata(context_type);
+    ase->metadata = GetMetadata(context_type, ccid);
 
     DLOG(INFO) << __func__ << " device=" << address_
                << ", activated ASE id=" << +ase->id
@@ -1040,7 +1041,7 @@
  */
 bool LeAudioDeviceGroup::ConfigureAses(
     const set_configurations::AudioSetConfiguration* audio_set_conf,
-    types::LeAudioContextType context_type) {
+    types::LeAudioContextType context_type, int ccid) {
   if (!set_configurations::check_if_may_cover_scenario(
           audio_set_conf, NumOfConnected(context_type)))
     return false;
@@ -1086,7 +1087,7 @@
 
       if (!device->ConfigureAses(ent, context_type, &active_ase_num,
                                  group_snk_audio_locations,
-                                 group_src_audio_locations, reuse_cis_id))
+                                 group_src_audio_locations, reuse_cis_id, ccid))
         continue;
 
       required_device_cnt--;
@@ -1174,10 +1175,10 @@
 }
 
 bool LeAudioDeviceGroup::IsMetadataChanged(
-    types::LeAudioContextType context_type) {
+    types::LeAudioContextType context_type, int ccid) {
   for (auto* leAudioDevice = GetFirstActiveDevice(); leAudioDevice;
        leAudioDevice = GetNextActiveDevice(leAudioDevice)) {
-    if (leAudioDevice->IsMetadataChanged(context_type)) return true;
+    if (leAudioDevice->IsMetadataChanged(context_type, ccid)) return true;
   }
 
   return false;
@@ -1226,7 +1227,7 @@
 /* This method should choose aproperiate ASEs to be active and set a cached
  * configuration for codec and qos.
  */
-bool LeAudioDeviceGroup::Configure(LeAudioContextType context_type) {
+bool LeAudioDeviceGroup::Configure(LeAudioContextType context_type, int ccid) {
   const set_configurations::AudioSetConfiguration* conf =
       active_context_to_configuration_map[context_type];
 
@@ -1241,7 +1242,7 @@
 
   DLOG(INFO) << __func__ << " setting context type: " << int(context_type);
 
-  if (!ConfigureAses(conf, context_type)) {
+  if (!ConfigureAses(conf, context_type, ccid)) {
     LOG(ERROR) << __func__ << ", requested pick ASE config context type: "
                << loghex(static_cast<uint16_t>(context_type))
                << ", is in mismatch with cached active contexts";
@@ -1776,20 +1777,21 @@
   }
 }
 
-std::vector<uint8_t> LeAudioDevice::GetMetadata(
-    LeAudioContextType context_type) {
+std::vector<uint8_t> LeAudioDevice::GetMetadata(LeAudioContextType context_type,
+                                                int ccid) {
   std::vector<uint8_t> metadata;
 
   AppendMetadataLtvEntryForStreamingContext(metadata, context_type);
-  AppendMetadataLtvEntryForCcidList(metadata, context_type);
+  AppendMetadataLtvEntryForCcidList(metadata, ccid);
 
   return std::move(metadata);
 }
 
-bool LeAudioDevice::IsMetadataChanged(types::LeAudioContextType context_type) {
+bool LeAudioDevice::IsMetadataChanged(types::LeAudioContextType context_type,
+                                      int ccid) {
   for (auto* ase = this->GetFirstActiveAse(); ase;
        ase = this->GetNextActiveAse(ase)) {
-    if (this->GetMetadata(context_type) != ase->metadata) return true;
+    if (this->GetMetadata(context_type, ccid) != ase->metadata) return true;
   }
 
   return false;
diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h
index 523e59f..30e4bbc 100644
--- a/system/bta/le_audio/devices.h
+++ b/system/bta/le_audio/devices.h
@@ -134,7 +134,7 @@
                      uint8_t* number_of_already_active_group_ase,
                      types::AudioLocations& group_snk_audio_locations,
                      types::AudioLocations& group_src_audio_locations,
-                     bool reconnect = false);
+                     bool reconnect = false, int ccid = -1);
   void SetSupportedContexts(types::AudioContexts snk_contexts,
                             types::AudioContexts src_contexts);
   types::AudioContexts GetAvailableContexts(void);
@@ -144,8 +144,9 @@
   void ActivateConfiguredAses(void);
   void Dump(int fd);
   void DisconnectAcl(void);
-  std::vector<uint8_t> GetMetadata(types::LeAudioContextType context_type);
-  bool IsMetadataChanged(types::LeAudioContextType context_type);
+  std::vector<uint8_t> GetMetadata(types::LeAudioContextType context_type,
+                                   int ccid);
+  bool IsMetadataChanged(types::LeAudioContextType context_type, int ccid);
 
  private:
   types::AudioContexts avail_snk_contexts_;
@@ -230,7 +231,7 @@
   bool IsGroupStreamReady(void);
   bool HaveAllActiveDevicesCisDisc(void);
   uint8_t GetFirstFreeCisId(void);
-  bool Configure(types::LeAudioContextType context_type);
+  bool Configure(types::LeAudioContextType context_type, int ccid = 1);
   bool SetContextType(types::LeAudioContextType context_type);
   types::LeAudioContextType GetContextType(void);
   uint32_t GetSduInterval(uint8_t direction);
@@ -256,7 +257,8 @@
   std::optional<LeAudioCodecConfiguration> GetCodecConfigurationByDirection(
       types::LeAudioContextType group_context_type, uint8_t direction);
   bool IsContextSupported(types::LeAudioContextType group_context_type);
-  bool IsMetadataChanged(types::LeAudioContextType group_context_type);
+  bool IsMetadataChanged(types::LeAudioContextType group_context_type,
+                         int ccid);
 
   inline types::AseState GetState(void) const { return current_state_; }
   void SetState(types::AseState state) {
@@ -293,7 +295,7 @@
   FindFirstSupportedConfiguration(types::LeAudioContextType context_type);
   bool ConfigureAses(
       const set_configurations::AudioSetConfiguration* audio_set_conf,
-      types::LeAudioContextType context_type);
+      types::LeAudioContextType context_type, int ccid = 1);
   bool IsConfigurationSupported(
       const set_configurations::AudioSetConfiguration* audio_set_configuration,
       types::LeAudioContextType context_type);
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index ad1bc82..f2983dc 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -123,6 +123,7 @@
 class MockLeAudioClientCallbacks
     : public bluetooth::le_audio::LeAudioClientCallbacks {
  public:
+  MOCK_METHOD((void), OnInitialized, (), (override));
   MOCK_METHOD((void), OnConnectionState,
               (ConnectionState state, const RawAddress& address), (override));
   MOCK_METHOD((void), OnGroupStatus, (int group_id, GroupStatus group_status),
@@ -548,9 +549,10 @@
     ON_CALL(mock_state_machine_, Initialize(_))
         .WillByDefault(SaveArg<0>(&state_machine_callbacks_));
 
-    ON_CALL(mock_state_machine_, ConfigureStream(_, _))
+    ON_CALL(mock_state_machine_, ConfigureStream(_, _, _))
         .WillByDefault([this](LeAudioDeviceGroup* group,
-                              types::LeAudioContextType context_type) {
+                              types::LeAudioContextType context_type,
+                              int ccid) {
           bool isReconfiguration = group->IsPendingConfiguration();
 
           /* This shall be called only for user reconfiguration */
@@ -620,9 +622,10 @@
           return true;
         });
 
-    ON_CALL(mock_state_machine_, StartStream(_, _))
+    ON_CALL(mock_state_machine_, StartStream(_, _, _))
         .WillByDefault([this](LeAudioDeviceGroup* group,
-                              types::LeAudioContextType context_type) {
+                              types::LeAudioContextType context_type,
+                              int ccid) {
           if (group->GetState() ==
               types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
             if (group->GetContextType() != context_type) {
@@ -2528,12 +2531,17 @@
   uint8_t cis_count_out = 1;
   uint8_t cis_count_in = 0;
 
+  int gmcs_ccid = 1;
+  int gtbs_ccid = 2;
+
   // Audio sessions are started only when device gets active
   EXPECT_CALL(*mock_unicast_audio_source_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_audio_sink_, Start(_, _)).Times(1);
+  LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
+  LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
   LeAudioClient::Get()->GroupSetActive(group_id);
 
-  EXPECT_CALL(mock_state_machine_, StartStream(_, _)).Times(1);
+  EXPECT_CALL(mock_state_machine_, StartStream(_, _, gmcs_ccid)).Times(1);
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
 
@@ -2789,7 +2797,7 @@
   // Start streaming with reconfiguration from default media stream setup
   EXPECT_CALL(
       mock_state_machine_,
-      StartStream(_, le_audio::types::LeAudioContextType::NOTIFICATIONS))
+      StartStream(_, le_audio::types::LeAudioContextType::NOTIFICATIONS, _))
       .Times(1);
 
   StartStreaming(AUDIO_USAGE_NOTIFICATION, AUDIO_CONTENT_TYPE_UNKNOWN,
@@ -2804,7 +2812,7 @@
   EXPECT_CALL(*mock_unicast_audio_source_, Stop).Times(0);
   EXPECT_CALL(*mock_unicast_audio_source_, Start).Times(0);
   EXPECT_CALL(mock_state_machine_,
-              StartStream(_, le_audio::types::LeAudioContextType::ALERTS))
+              StartStream(_, le_audio::types::LeAudioContextType::ALERTS, _))
       .Times(1);
   UpdateMetadata(AUDIO_USAGE_ALARM, AUDIO_CONTENT_TYPE_UNKNOWN);
   Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
@@ -2817,7 +2825,7 @@
 
   EXPECT_CALL(
       mock_state_machine_,
-      StartStream(_, le_audio::types::LeAudioContextType::EMERGENCYALARM))
+      StartStream(_, le_audio::types::LeAudioContextType::EMERGENCYALARM, _))
       .Times(1);
   UpdateMetadata(AUDIO_USAGE_EMERGENCY, AUDIO_CONTENT_TYPE_UNKNOWN);
   Mock::VerifyAndClearExpectations(mock_unicast_audio_source_);
@@ -2849,11 +2857,17 @@
                     codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                     group_id, 2 /* rank*/, true /*connect_through_csis*/);
 
+  int gmcs_ccid = 1;
+  int gtbs_ccid = 2;
+
   // Start streaming MEDIA
   EXPECT_CALL(*mock_unicast_audio_source_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_audio_sink_, Start(_, _)).Times(1);
+  LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
+  LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
   LeAudioClient::Get()->GroupSetActive(group_id);
 
+  EXPECT_CALL(mock_state_machine_, StartStream(_, _, gmcs_ccid)).Times(1);
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
 
   Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
@@ -2871,6 +2885,7 @@
   fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
   Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
 
+  EXPECT_CALL(mock_state_machine_, StartStream(_, _, gtbs_ccid)).Times(1);
   StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
                  group_id);
 
@@ -3113,7 +3128,7 @@
 
   EXPECT_CALL(
       mock_state_machine_,
-      StartStream(_, le_audio::types::LeAudioContextType::VOICEASSISTANTS))
+      StartStream(_, le_audio::types::LeAudioContextType::VOICEASSISTANTS, _))
       .Times(1);
   Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
   Mock::VerifyAndClearExpectations(mock_unicast_audio_source_);
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index ca33dc2..44d581b 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -392,34 +392,18 @@
 }  // namespace types
 
 void AppendMetadataLtvEntryForCcidList(std::vector<uint8_t>& metadata,
-                                       LeAudioContextType context_type) {
+                                       int ccid) {
+  if (ccid < 0) return;
+
   std::vector<uint8_t> ccid_ltv_entry;
-  /* TODO: Get CCID values from Service */
-  std::vector<uint8_t> ccid_conversational = {0x12};
-  std::vector<uint8_t> ccid_media = {0x56};
+  std::vector<uint8_t> ccid_value = {static_cast<uint8_t>(ccid)};
 
-  std::vector<uint8_t>* ccid_value = nullptr;
-
-  /* CCID list */
-  switch (context_type) {
-    case LeAudioContextType::CONVERSATIONAL:
-      ccid_value = &ccid_conversational;
-      break;
-    case LeAudioContextType::MEDIA:
-      ccid_value = &ccid_media;
-      break;
-    default:
-      break;
-  }
-
-  if (!ccid_value) return;
-
-  ccid_ltv_entry.push_back(static_cast<uint8_t>(types::kLeAudioMetadataTypeLen +
-                                                ccid_value->size()));
+  ccid_ltv_entry.push_back(
+      static_cast<uint8_t>(types::kLeAudioMetadataTypeLen + ccid_value.size()));
   ccid_ltv_entry.push_back(
       static_cast<uint8_t>(types::kLeAudioMetadataTypeCcidList));
-  ccid_ltv_entry.insert(ccid_ltv_entry.end(), ccid_value->begin(),
-                        ccid_value->end());
+  ccid_ltv_entry.insert(ccid_ltv_entry.end(), ccid_value.begin(),
+                        ccid_value.end());
 
   metadata.insert(metadata.end(), ccid_ltv_entry.begin(), ccid_ltv_entry.end());
 }
diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h
index b5067d4..7960359 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -687,7 +687,7 @@
 };
 
 void AppendMetadataLtvEntryForCcidList(std::vector<uint8_t>& metadata,
-                                       types::LeAudioContextType context_type);
+                                       int ccid);
 void AppendMetadataLtvEntryForStreamingContext(
     std::vector<uint8_t>& metadata, types::LeAudioContextType context_type);
 uint8_t GetMaxCodecFramesPerSduFromPac(const types::acs_ac_record* pac_record);
diff --git a/system/bta/le_audio/mock_state_machine.h b/system/bta/le_audio/mock_state_machine.h
index 017d710..63e850b 100644
--- a/system/bta/le_audio/mock_state_machine.h
+++ b/system/bta/le_audio/mock_state_machine.h
@@ -25,7 +25,7 @@
  public:
   MOCK_METHOD((bool), StartStream,
               (le_audio::LeAudioDeviceGroup * group,
-               le_audio::types::LeAudioContextType context_type),
+               le_audio::types::LeAudioContextType context_type, int ccid),
               (override));
   MOCK_METHOD((bool), AttachToStream,
               (le_audio::LeAudioDeviceGroup * group,
@@ -35,7 +35,7 @@
               (override));
   MOCK_METHOD((bool), ConfigureStream,
               (le_audio::LeAudioDeviceGroup * group,
-               le_audio::types::LeAudioContextType context_type),
+               le_audio::types::LeAudioContextType context_type, int ccid),
               (override));
   MOCK_METHOD((void), StopStream, (le_audio::LeAudioDeviceGroup * group),
               (override));
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 33b4b44..bb6f8f0 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -146,7 +146,8 @@
   }
 
   bool StartStream(LeAudioDeviceGroup* group,
-                   le_audio::types::LeAudioContextType context_type) override {
+                   le_audio::types::LeAudioContextType context_type,
+                   int ccid) override {
     LOG_INFO(" current state: %s", ToString(group->GetState()).c_str());
 
     switch (group->GetState()) {
@@ -161,7 +162,7 @@
         /* If configuration is needed */
         FALLTHROUGH;
       case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
-        if (!group->Configure(context_type)) {
+        if (!group->Configure(context_type, ccid)) {
           LOG(ERROR) << __func__ << ", failed to set ASE configuration";
           return false;
         }
@@ -189,7 +190,7 @@
         /* This case just updates the metadata for the stream, in case
          * stream configuration is satisfied
          */
-        if (!group->IsMetadataChanged(context_type)) return true;
+        if (!group->IsMetadataChanged(context_type, ccid)) return true;
 
         LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
         if (!leAudioDevice) {
@@ -197,7 +198,7 @@
           return false;
         }
 
-        PrepareAndSendUpdateMetadata(group, leAudioDevice, context_type);
+        PrepareAndSendUpdateMetadata(group, leAudioDevice, context_type, ccid);
         break;
       }
 
@@ -210,9 +211,9 @@
     return true;
   }
 
-  bool ConfigureStream(
-      LeAudioDeviceGroup* group,
-      le_audio::types::LeAudioContextType context_type) override {
+  bool ConfigureStream(LeAudioDeviceGroup* group,
+                       le_audio::types::LeAudioContextType context_type,
+                       int ccid) override {
     if (group->GetState() > AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
       LOG_ERROR(
           "Stream should be stopped or in configured stream. Current state: %s",
@@ -220,7 +221,7 @@
       return false;
     }
 
-    if (!group->Configure(context_type)) {
+    if (!group->Configure(context_type, ccid)) {
       LOG_ERROR("Could not configure ASEs for group %d content type %d",
                 group->group_id_, int(context_type));
 
@@ -1678,15 +1679,15 @@
 
   void PrepareAndSendUpdateMetadata(
       LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
-      le_audio::types::LeAudioContextType context_type) {
+      le_audio::types::LeAudioContextType context_type, int ccid) {
     std::vector<struct le_audio::client_parser::ascs::ctp_update_metadata>
         confs;
 
     for (; leAudioDevice;
          leAudioDevice = group->GetNextActiveDevice(leAudioDevice)) {
-      if (!leAudioDevice->IsMetadataChanged(context_type)) continue;
+      if (!leAudioDevice->IsMetadataChanged(context_type, ccid)) continue;
 
-      auto new_metadata = leAudioDevice->GetMetadata(context_type);
+      auto new_metadata = leAudioDevice->GetMetadata(context_type, ccid);
 
       /* Request server to update ASEs with new metadata */
       for (struct ase* ase = leAudioDevice->GetFirstActiveAse(); ase != nullptr;
@@ -1854,13 +1855,10 @@
         /* Cache current set up metadata values for for further possible
          * reconfiguration
          */
-        for (struct ase* ase = leAudioDevice->GetFirstActiveAse();
-             ase != nullptr; ase = leAudioDevice->GetNextActiveAse(ase)) {
+        if (!rsp.metadata.empty()) {
           ase->metadata = rsp.metadata;
         }
 
-        PrepareAndSendUpdateMetadata(group, leAudioDevice,
-                                     group->GetContextType());
         break;
       }
       default:
diff --git a/system/bta/le_audio/state_machine.h b/system/bta/le_audio/state_machine.h
index 1f440e2..06b63fb 100644
--- a/system/bta/le_audio/state_machine.h
+++ b/system/bta/le_audio/state_machine.h
@@ -48,11 +48,12 @@
   virtual bool AttachToStream(LeAudioDeviceGroup* group,
                               LeAudioDevice* leAudioDevice) = 0;
   virtual bool StartStream(LeAudioDeviceGroup* group,
-                           types::LeAudioContextType context_type) = 0;
+                           types::LeAudioContextType context_type,
+                           int ccid = -1) = 0;
   virtual void SuspendStream(LeAudioDeviceGroup* group) = 0;
-  virtual bool ConfigureStream(
-      LeAudioDeviceGroup* group,
-      le_audio::types::LeAudioContextType context_type) = 0;
+  virtual bool ConfigureStream(LeAudioDeviceGroup* group,
+                               le_audio::types::LeAudioContextType context_type,
+                               int ccid = -1) = 0;
   virtual void StopStream(LeAudioDeviceGroup* group) = 0;
   virtual void ProcessGattNotifEvent(uint8_t* value, uint16_t len,
                                      struct types::ase* ase,
diff --git a/system/btcore/Android.bp b/system/btcore/Android.bp
index 5a81793..82ca35b 100644
--- a/system/btcore/Android.bp
+++ b/system/btcore/Android.bp
@@ -64,7 +64,10 @@
 cc_test {
     name: "net_test_btcore",
     test_suites: ["device-tests"],
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: ["include"],
     include_dirs: ["packages/modules/Bluetooth/system"],
     srcs: [
diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 8fdcf037..4e016e6 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -196,7 +196,10 @@
 // btif unit tests for target
 cc_test {
     name: "net_test_btif",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     include_dirs: btifCommonIncludes,
     srcs: [
@@ -259,7 +262,10 @@
 // btif profile queue unit tests for target
 cc_test {
     name: "net_test_btif_profile_queue",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     include_dirs: btifCommonIncludes,
     srcs: [
@@ -286,7 +292,10 @@
 // btif rc unit tests for target
 cc_test {
     name: "net_test_btif_rc",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     host_supported: true,
     test_options: {
@@ -321,7 +330,10 @@
 // btif config cache unit tests for target
 cc_test {
     name: "net_test_btif_config_cache",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     host_supported: true,
     test_options: {
@@ -358,7 +370,10 @@
 // btif hf client service tests for target
 cc_test {
     name: "net_test_btif_hf_client_service",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     include_dirs: btifCommonIncludes,
     srcs: [
@@ -380,7 +395,10 @@
 cc_test {
     name: "net_test_btif_stack",
     host_supported: true,
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     include_dirs: [
         "frameworks/av/media/libaaudio/include",
diff --git a/system/btif/src/btif_le_audio.cc b/system/btif/src/btif_le_audio.cc
index ba004f3..b6d79b4 100644
--- a/system/btif/src/btif_le_audio.cc
+++ b/system/btif/src/btif_le_audio.cc
@@ -43,6 +43,11 @@
                                    public LeAudioClientCallbacks {
   ~LeAudioClientInterfaceImpl() = default;
 
+  void OnInitialized(void) {
+    do_in_jni_thread(FROM_HERE, Bind(&LeAudioClientCallbacks::OnInitialized,
+                                     Unretained(callbacks)));
+  }
+
   void OnConnectionState(ConnectionState state,
                          const RawAddress& address) override {
     do_in_jni_thread(FROM_HERE, Bind(&LeAudioClientCallbacks::OnConnectionState,
@@ -186,6 +191,14 @@
                            input_codec_config, output_codec_config));
   }
 
+  void SetCcidInformation(int ccid, int context_type) {
+    DVLOG(2) << __func__ << " ccid: " << ccid << " context_type"
+             << context_type;
+    do_in_main_thread(
+        FROM_HERE, Bind(&LeAudioClient::SetCcidInformation,
+                        Unretained(LeAudioClient::Get()), ccid, context_type));
+  }
+
  private:
   LeAudioClientCallbacks* callbacks;
 };
diff --git a/system/common/Android.bp b/system/common/Android.bp
index 8007573..64c9ce3 100644
--- a/system/common/Android.bp
+++ b/system/common/Android.bp
@@ -47,6 +47,7 @@
     defaults: [
         "fluoride_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     test_options: {
@@ -92,7 +93,10 @@
 
 cc_test {
     name: "net_test_performance",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     include_dirs: ["packages/modules/Bluetooth/system"],
     host_supported: true,
diff --git a/system/device/Android.bp b/system/device/Android.bp
index 774f946..105dbe5 100644
--- a/system/device/Android.bp
+++ b/system/device/Android.bp
@@ -33,7 +33,10 @@
 cc_test {
     name: "net_test_device",
     test_suites: ["device-tests"],
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     include_dirs: ["packages/modules/Bluetooth/system"],
     srcs: [
         "test/interop_test.cc",
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index 386585d..ca4c2b0 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -317,6 +317,7 @@
         "gd_defaults",
         "gd_clang_coverage_bin",
         "libchrome_support_defaults",
+        "mts_defaults",
     ],
     host_supported: true,
     test_options: {
@@ -406,6 +407,7 @@
         "gd_defaults",
         "gd_clang_coverage_bin",
         "libchrome_support_defaults",
+        "mts_defaults",
     ],
     include_dirs: ["packages/modules/Bluetooth/system/gd"],
     host_supported: true,
@@ -612,7 +614,10 @@
 
 rust_test_host {
     name: "libbt_packets_test",
-    defaults: ["gd_rust_defaults"],
+    defaults: [
+        "gd_rust_defaults",
+        "mts_defaults",
+    ],
     srcs: ["rust/packets/lib.rs", ":BluetoothGeneratedPackets_rust"],
     test_suites: ["general-tests"],
     edition: "2018",
@@ -642,7 +647,10 @@
 
 rust_test_host {
     name: "packets_test_rust",
-    defaults: ["gd_rust_defaults"],
+    defaults: [
+        "gd_rust_defaults",
+        "mts_defaults",
+    ],
     srcs: ["rust/packets/test_lib.rs", ":TestGeneratedPackets_rust"],
     test_suites: ["general-tests"],
     edition: "2018",
diff --git a/system/gd/dumpsys/Android.bp b/system/gd/dumpsys/Android.bp
index 7b742c3..0241b00 100644
--- a/system/gd/dumpsys/Android.bp
+++ b/system/gd/dumpsys/Android.bp
@@ -177,6 +177,7 @@
 cc_test {
     name: "bluetooth_flatbuffer_tests",
     test_suites: ["device-tests"],
+    defaults: ["mts_defaults"],
     host_supported: true,
     test_options: {
         unit_test: true,
diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h
index 0ae3016..0b82835 100644
--- a/system/gd/hci/acl_manager/le_impl.h
+++ b/system/gd/hci/acl_manager/le_impl.h
@@ -365,15 +365,20 @@
       on_le_connection_canceled_on_pause();
       return;
     }
-    AddressWithType remote_address(address, peer_address_type);
-    // The address added to the connect list is the one that callbacks
-    // should be sent for, given that is the address passed in
-    // call to BluetoothDevice#connectGatt and tied to app layer callbacks.
-    if (!peer_resolvable_address.IsEmpty() &&
-        is_device_in_connect_list(AddressWithType(peer_resolvable_address, AddressType::RANDOM_DEVICE_ADDRESS))) {
-      LOG_INFO("Using resolvable address");
-      remote_address = AddressWithType(peer_resolvable_address, AddressType::RANDOM_DEVICE_ADDRESS);
+
+    AddressType remote_address_type;
+
+    switch (peer_address_type) {
+      case AddressType::PUBLIC_DEVICE_ADDRESS:
+      case AddressType::PUBLIC_IDENTITY_ADDRESS:
+        remote_address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
+        break;
+      case AddressType::RANDOM_DEVICE_ADDRESS:
+      case AddressType::RANDOM_IDENTITY_ADDRESS:
+        remote_address_type = AddressType::RANDOM_DEVICE_ADDRESS;
+        break;
     }
+    AddressWithType remote_address(address, remote_address_type);
 
     on_common_le_connection_complete(remote_address);
     if (status == ErrorCode::UNKNOWN_CONNECTION) {
diff --git a/system/gd/hci/facade/le_acl_manager_facade.cc b/system/gd/hci/facade/le_acl_manager_facade.cc
index 1dd51ce..115c69c 100644
--- a/system/gd/hci/facade/le_acl_manager_facade.cc
+++ b/system/gd/hci/facade/le_acl_manager_facade.cc
@@ -63,37 +63,29 @@
 
   ::grpc::Status CreateConnection(
       ::grpc::ServerContext* context,
-      const ::blueberry::facade::BluetoothAddressWithType* request,
+      const CreateConnectionMsg* request,
       ::grpc::ServerWriter<LeConnectionEvent>* writer) override {
     Address peer_address;
-    ASSERT(Address::FromString(request->address().address(), peer_address));
-    AddressWithType peer(peer_address, static_cast<AddressType>(request->type()));
-    acl_manager_->CreateLeConnection(peer, /* is_direct */ true);
-    if (per_connection_events_.size() > current_connection_request_) {
-      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding request is supported");
-    }
-    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
-        std::string("connection attempt ") + std::to_string(current_connection_request_)));
-    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
-  }
+    ASSERT(Address::FromString(request->peer_address().address().address(), peer_address));
+    AddressWithType peer(peer_address, static_cast<AddressType>(request->peer_address().type()));
+    bool is_direct = request->is_direct();
+    acl_manager_->CreateLeConnection(peer, is_direct);
 
-  ::grpc::Status CreateBackgroundAndDirectConnection(
-      ::grpc::ServerContext* context,
-      const ::blueberry::facade::BluetoothAddressWithType* request,
-      ::grpc::ServerWriter<LeConnectionEvent>* writer) override {
-    Address peer_address;
-    ASSERT(Address::FromString(request->address().address(), peer_address));
-    AddressWithType peer(peer_address, static_cast<AddressType>(request->type()));
-    // Create background connection first
-    acl_manager_->CreateLeConnection(peer, /* is_direct */ false);
-    acl_manager_->CreateLeConnection(peer, /* is_direct */ true);
-    wait_for_background_connection_complete = true;
-    if (per_connection_events_.size() > current_connection_request_) {
-      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding request is supported");
+    if (is_direct) {
+      if (direct_connection_events_ != nullptr) {
+        return ::grpc::Status(
+            ::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding direct request is supported");
+      }
+      direct_connection_events_ = std::make_shared<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
+          std::string("direct connection attempt ") + peer.ToString());
+      direct_connection_address_ = peer;
+      return direct_connection_events_->RunLoop(context, writer);
     }
-    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
-        std::string("connection attempt ") + std::to_string(current_connection_request_)));
-    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+    per_connection_events_.emplace(
+        peer,
+        std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
+            std::string("connection attempt ") + peer.ToString()));
+    return per_connection_events_[peer]->RunLoop(context, writer);
   }
 
   ::grpc::Status CancelConnection(
@@ -103,9 +95,13 @@
     Address peer_address;
     ASSERT(Address::FromString(request->address().address(), peer_address));
     AddressWithType peer(peer_address, static_cast<AddressType>(request->type()));
-    if (per_connection_events_.size() == current_connection_request_) {
-      // Todo: Check that the address matches an outstanding connection request
-      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "No matching outstanding connection");
+    if (peer == direct_connection_address_) {
+      direct_connection_address_ = AddressWithType();
+      direct_connection_events_.reset();
+    } else {
+      if (per_connection_events_.count(peer) == 0) {
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "No matching outstanding connection");
+      }
     }
     acl_manager_->CancelLeConnect(peer);
     return ::grpc::Status::OK;
@@ -164,12 +160,43 @@
       ::grpc::ServerContext* context,
       const google::protobuf::Empty* request,
       ::grpc::ServerWriter<LeConnectionEvent>* writer) override {
-    if (per_connection_events_.size() > current_connection_request_) {
-      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding connection is supported");
+    if (incoming_connection_events_ != nullptr) {
+      return ::grpc::Status(
+          ::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding incoming connection is supported");
     }
-    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
-        std::string("incoming connection ") + std::to_string(current_connection_request_)));
-    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+    incoming_connection_events_ =
+        std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(std::string("incoming connection "));
+    return incoming_connection_events_->RunLoop(context, writer);
+  }
+
+  ::grpc::Status AddDeviceToResolvingList(
+      ::grpc::ServerContext* context, const IrkMsg* request, ::google::protobuf::Empty* response) override {
+    Address peer_address;
+    ASSERT(Address::FromString(request->peer().address().address(), peer_address));
+    AddressWithType peer(peer_address, static_cast<AddressType>(request->peer().type()));
+
+    auto request_peer_irk_length = request->peer_irk().end() - request->peer_irk().begin();
+
+    if (request_peer_irk_length != crypto_toolbox::OCTET16_LEN) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid Peer IRK");
+    }
+
+    auto request_local_irk_length = request->local_irk().end() - request->local_irk().begin();
+    if (request_local_irk_length != crypto_toolbox::OCTET16_LEN) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid Local IRK");
+    }
+
+    crypto_toolbox::Octet16 peer_irk = {};
+    crypto_toolbox::Octet16 local_irk = {};
+
+    std::vector<uint8_t> peer_irk_data(request->peer_irk().begin(), request->peer_irk().end());
+    std::copy_n(peer_irk_data.begin(), crypto_toolbox::OCTET16_LEN, peer_irk.begin());
+
+    std::vector<uint8_t> local_irk_data(request->local_irk().begin(), request->local_irk().end());
+    std::copy_n(local_irk_data.begin(), crypto_toolbox::OCTET16_LEN, local_irk.begin());
+
+    acl_manager_->AddDeviceToResolvingList(peer, peer_irk, local_irk);
+    return ::grpc::Status::OK;
   }
 
   ::grpc::Status SendAclData(
@@ -233,17 +260,28 @@
     connection_tracker->second.pending_acl_data_.OnIncomingEvent(acl_data);
   }
 
-  void OnLeConnectSuccess(AddressWithType address_with_type, std::unique_ptr<LeAclConnection> connection) override {
-    LOG_INFO("%s", address_with_type.ToString().c_str());
+  void OnLeConnectSuccess(AddressWithType peer, std::unique_ptr<LeAclConnection> connection) override {
+    LOG_INFO("%s", peer.ToString().c_str());
 
     std::unique_lock<std::mutex> lock(acl_connections_mutex_);
-    auto addr = address_with_type.GetAddress();
     std::shared_ptr<LeAclConnection> shared_connection = std::move(connection);
     uint16_t handle = shared_connection->GetHandle();
+    auto role = shared_connection->GetRole();
+    if (role == Role::PERIPHERAL) {
+      ASSERT(incoming_connection_events_ != nullptr);
+      per_connection_events_.emplace(peer, incoming_connection_events_);
+      incoming_connection_events_.reset();
+    } else if (direct_connection_address_ == peer) {
+      direct_connection_address_ = AddressWithType();
+      per_connection_events_.emplace(peer, direct_connection_events_);
+      direct_connection_events_.reset();
+    } else {
+      ASSERT_LOG(per_connection_events_.count(peer) > 0, "No connection request for %s", peer.ToString().c_str());
+    }
     acl_connections_.emplace(
         std::piecewise_construct,
         std::forward_as_tuple(handle),
-        std::forward_as_tuple(handle, shared_connection, per_connection_events_[current_connection_request_]));
+        std::forward_as_tuple(handle, shared_connection, per_connection_events_[peer]));
     shared_connection->GetAclQueueEnd()->RegisterDequeue(
         facade_handler_,
         common::Bind(&LeAclManagerFacadeService::on_incoming_acl, common::Unretained(this), shared_connection, handle));
@@ -251,21 +289,11 @@
     shared_connection->RegisterCallbacks(callbacks, facade_handler_);
     {
       std::unique_ptr<BasePacketBuilder> builder = LeConnectionCompleteBuilder::Create(
-          ErrorCode::SUCCESS,
-          handle,
-          Role::CENTRAL,
-          address_with_type.GetAddressType(),
-          addr,
-          1,
-          2,
-          3,
-          ClockAccuracy::PPM_20);
+          ErrorCode::SUCCESS, handle, role, peer.GetAddressType(), peer.GetAddress(), 1, 2, 3, ClockAccuracy::PPM_20);
       LeConnectionEvent success;
       success.set_payload(builder_to_string(std::move(builder)));
-      per_connection_events_[current_connection_request_]->OnIncomingEvent(success);
+      per_connection_events_[peer]->OnIncomingEvent(success);
     }
-    wait_for_background_connection_complete = false;
-    current_connection_request_++;
   }
 
   void OnLeConnectFail(AddressWithType address, ErrorCode reason) override {
@@ -273,9 +301,11 @@
         reason, 0, Role::CENTRAL, address.GetAddressType(), address.GetAddress(), 0, 0, 0, ClockAccuracy::PPM_20);
     LeConnectionEvent fail;
     fail.set_payload(builder_to_string(std::move(builder)));
-    per_connection_events_[current_connection_request_]->OnIncomingEvent(fail);
-    if (!wait_for_background_connection_complete) {
-      current_connection_request_++;
+    if (address == direct_connection_address_) {
+      direct_connection_address_ = AddressWithType();
+      direct_connection_events_->OnIncomingEvent(fail);
+    } else {
+      per_connection_events_[address]->OnIncomingEvent(fail);
     }
   }
 
@@ -332,10 +362,12 @@
   AclManager* acl_manager_;
   ::bluetooth::os::Handler* facade_handler_;
   mutable std::mutex acl_connections_mutex_;
-  std::vector<std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>> per_connection_events_;
+  std::map<bluetooth::hci::AddressWithType, std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>>
+      per_connection_events_;
+  std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> direct_connection_events_;
+  bluetooth::hci::AddressWithType direct_connection_address_;
+  std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> incoming_connection_events_;
   std::map<uint16_t, Connection> acl_connections_;
-  uint32_t current_connection_request_{0};
-  bool wait_for_background_connection_complete = false;
 };
 
 void LeAclManagerFacadeModule::ListDependencies(ModuleList* list) const {
diff --git a/system/gd/hci/le_address_manager.cc b/system/gd/hci/le_address_manager.cc
index e2b8f14..6d1806d 100644
--- a/system/gd/hci/le_address_manager.cc
+++ b/system/gd/hci/le_address_manager.cc
@@ -130,6 +130,7 @@
       minimum_rotation_time_ = minimum_rotation_time;
       maximum_rotation_time_ = maximum_rotation_time;
       address_rotation_alarm_ = std::make_unique<os::Alarm>(handler_);
+      set_random_address();
       break;
     case AddressPolicy::POLICY_NOT_SET:
       LOG_ALWAYS_FATAL("invalid parameters");
diff --git a/system/gd/rust/stack/Android.bp b/system/gd/rust/stack/Android.bp
index 19d373b..37d7136 100644
--- a/system/gd/rust/stack/Android.bp
+++ b/system/gd/rust/stack/Android.bp
@@ -54,7 +54,10 @@
 
 rust_test_host {
     name: "libbluetooth_rs_test",
-    defaults: ["gd_rust_defaults"],
+    defaults: [
+        "gd_rust_defaults",
+        "mts_defaults",
+    ],
     srcs: ["src/lib.rs"],
     test_suites: ["general-tests"],
     edition: "2018",
diff --git a/system/hci/Android.bp b/system/hci/Android.bp
index af49566..d9f9542 100644
--- a/system/hci/Android.bp
+++ b/system/hci/Android.bp
@@ -58,7 +58,11 @@
 cc_test {
     name: "net_test_hci",
     test_suites: ["device-tests"],
-    defaults: ["fluoride_basic_defaults", "fluoride_test_defaults"],
+    defaults: [
+        "fluoride_basic_defaults",
+        "fluoride_test_defaults",
+        "mts_defaults",
+    ],
     host_supported: false,
     local_include_dirs: [
         "include",
@@ -91,7 +95,10 @@
 cc_test {
     name: "net_test_hci_native",
     test_suites: ["device-tests"],
-    defaults: ["fluoride_unit_test_defaults"],
+    defaults: [
+        "fluoride_unit_test_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "include",
     ],
@@ -109,7 +116,10 @@
 cc_test {
     name: "net_test_hci_fragmenter_native",
     test_suites: ["device-tests"],
-    defaults: ["fluoride_unit_test_defaults"],
+    defaults: [
+        "fluoride_unit_test_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "include",
     ],
diff --git a/system/include/hardware/bt_le_audio.h b/system/include/hardware/bt_le_audio.h
index e8b4699..3188aa9 100644
--- a/system/include/hardware/bt_le_audio.h
+++ b/system/include/hardware/bt_le_audio.h
@@ -82,6 +82,9 @@
  public:
   virtual ~LeAudioClientCallbacks() = default;
 
+  /* Callback to notify Java that stack is ready */
+  virtual void OnInitialized(void) = 0;
+
   /** Callback for profile connection state change */
   virtual void OnConnectionState(ConnectionState state,
                                  const RawAddress& address) = 0;
@@ -146,6 +149,9 @@
   virtual void SetCodecConfigPreference(
       int group_id, btle_audio_codec_config_t input_codec_config,
       btle_audio_codec_config_t output_codec_config) = 0;
+
+  /* Set Ccid for context type */
+  virtual void SetCcidInformation(int ccid, int context_type) = 0;
 };
 
 /* Represents the broadcast source state. */
diff --git a/system/main/Android.bp b/system/main/Android.bp
index 3acba7c..8f8a245 100644
--- a/system/main/Android.bp
+++ b/system/main/Android.bp
@@ -152,7 +152,10 @@
     test_options: {
         unit_test: true,
     },
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     include_dirs: [
         "packages/modules/Bluetooth/system",
         "packages/modules/Bluetooth/system/gd",
diff --git a/system/osi/Android.bp b/system/osi/Android.bp
index e33701d..bcbac81 100644
--- a/system/osi/Android.bp
+++ b/system/osi/Android.bp
@@ -109,7 +109,10 @@
 cc_test {
     name: "net_test_osi",
     test_suites: ["device-tests"],
-    defaults: ["fluoride_osi_defaults"],
+    defaults: [
+        "fluoride_osi_defaults",
+        "mts_defaults",
+    ],
     host_supported: true,
     srcs: [
         "test/AlarmTestHarness.cc",
diff --git a/system/packet/Android.bp b/system/packet/Android.bp
index 4d8cb92..1033fc6 100644
--- a/system/packet/Android.bp
+++ b/system/packet/Android.bp
@@ -27,7 +27,10 @@
 
 cc_test {
     name: "net_test_btpackets",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     host_supported: true,
     test_options: {
diff --git a/system/profile/avrcp/Android.bp b/system/profile/avrcp/Android.bp
index 8622dea..72e9b1b 100644
--- a/system/profile/avrcp/Android.bp
+++ b/system/profile/avrcp/Android.bp
@@ -48,6 +48,7 @@
         "fluoride_defaults",
         "clang_coverage_bin",
         "libchrome_support_defaults",
+        "mts_defaults",
     ],
     host_supported: true,
     test_options: {
diff --git a/system/profile/sdp/Android.bp b/system/profile/sdp/Android.bp
index 7db1a4d..1c2f9ed 100644
--- a/system/profile/sdp/Android.bp
+++ b/system/profile/sdp/Android.bp
@@ -32,6 +32,7 @@
     defaults: [
         "fluoride_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     host_supported: true,
     include_dirs: [
diff --git a/system/service/Android.bp b/system/service/Android.bp
index 92d71fc..cfd2979 100644
--- a/system/service/Android.bp
+++ b/system/service/Android.bp
@@ -156,7 +156,10 @@
 cc_test {
     name: "bluetoothtbd_test",
     test_suites: ["device-tests"],
-    defaults: ["fluoride_service_defaults"],
+    defaults: [
+        "fluoride_service_defaults",
+        "mts_defaults",
+    ],
     srcs: btserviceBaseTestSrc +
         btserviceDaemonSrc + [
             "test/main.cc",
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 93690e0..85095aed 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -213,7 +213,10 @@
 // Bluetooth stack unit tests for target
 cc_test {
     name: "net_test_stack",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     local_include_dirs: [
         "include",
@@ -276,7 +279,10 @@
 
 cc_test {
     name: "net_test_stack_rfcomm",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     host_supported: true,
     local_include_dirs: [
@@ -335,7 +341,10 @@
 // Bluetooth stack smp unit tests for target
 cc_test {
     name: "net_test_stack_smp",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     host_supported: true,
     test_suites: ["device-tests"],
     local_include_dirs: [
@@ -395,7 +404,10 @@
 // Bluetooth stack multi-advertising unit tests for target
 cc_test {
     name: "net_test_stack_multi_adv",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     local_include_dirs: [
         "include",
@@ -434,7 +446,10 @@
 // Bluetooth stack advertise data parsing unit tests for target
 cc_test {
     name: "net_test_stack_ad_parser",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     local_include_dirs: [
         "include",
@@ -490,7 +505,10 @@
 
 cc_test {
     name: "net_test_stack_gatt_native",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     host_supported: true,
     test_options: {
@@ -535,7 +553,10 @@
 
 cc_test {
     name: "net_test_stack_avdtp",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     host_supported: true,
     test_options: {
@@ -586,7 +607,10 @@
 
 cc_test {
     name: "net_test_stack_a2dp_native",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     host_supported: true,
     test_options: {
@@ -629,7 +653,10 @@
 // gatt sr hash test
 cc_test {
     name: "net_test_stack_gatt_sr_hash_native",
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     test_suites: ["device-tests"],
     host_supported: true,
     include_dirs: [
@@ -673,7 +700,10 @@
     test_options: {
         unit_test: true,
     },
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "btm",
         "include",
@@ -719,6 +749,7 @@
     defaults: [
         "fluoride_defaults",
         "clang_coverage_bin",
+        "mts_defaults",
     ],
     local_include_dirs: [
         "include",
@@ -766,7 +797,10 @@
     test_options: {
         unit_test: true,
     },
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "include",
         "btm",
@@ -860,7 +894,10 @@
     name: "net_test_stack_hci",
     test_suites: ["device-tests"],
     host_supported: true,
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "include",
         "btm",
@@ -903,7 +940,10 @@
     name: "net_test_stack_hid",
     test_suites: ["device-tests"],
     host_supported: true,
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "include",
         "test/common",
@@ -952,7 +992,10 @@
     name: "net_test_stack_btu",
     test_suites: ["device-tests"],
     host_supported: true,
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "include",
         "test/common",
@@ -1011,7 +1054,10 @@
     name: "net_test_stack_gatt",
     test_suites: ["device-tests"],
     host_supported: true,
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "include",
         "test/common",
@@ -1082,7 +1128,10 @@
     name: "net_test_stack_l2cap",
     test_suites: ["device-tests"],
     host_supported: true,
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "include",
         "test/common",
@@ -1151,7 +1200,10 @@
     name: "net_test_stack_acl",
     test_suites: ["device-tests"],
     host_supported: true,
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     local_include_dirs: [
         "include",
         "test/common",
diff --git a/system/test/Android.bp b/system/test/Android.bp
index 7754163a..f99db16 100644
--- a/system/test/Android.bp
+++ b/system/test/Android.bp
@@ -451,3 +451,21 @@
       "mock/mock_legacy_hci_iterface.cc"
   ],
 }
+
+cc_defaults {
+    name: "mts_defaults",
+    target: {
+        android: {
+            test_suites: ["mts-bluetooth",],
+        },
+    },
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+}
diff --git a/system/test/headless/Android.bp b/system/test/headless/Android.bp
index 42b222f..87f94c1 100644
--- a/system/test/headless/Android.bp
+++ b/system/test/headless/Android.bp
@@ -10,7 +10,10 @@
 cc_test {
     name: "bt_headless",
     test_suites: ["device-tests"],
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     srcs: [
         "connect/connect.cc",
         "get_options.cc",
diff --git a/system/test/suite/Android.bp b/system/test/suite/Android.bp
index b7fd0ed..ec9545f 100644
--- a/system/test/suite/Android.bp
+++ b/system/test/suite/Android.bp
@@ -109,7 +109,10 @@
 cc_test {
     name: "net_test_bluetooth",
     test_suites: ["device-tests"],
-    defaults: ["net_test_defaults"],
+    defaults: [
+        "net_test_defaults",
+        "mts_defaults",
+    ],
     srcs: [
         "adapter/adapter_unittest.cc",
         "gatt/gatt_test.cc",
diff --git a/system/types/Android.bp b/system/types/Android.bp
index b0be0d7..2009df6 100644
--- a/system/types/Android.bp
+++ b/system/types/Android.bp
@@ -49,7 +49,10 @@
 cc_test {
     name: "net_test_types",
     test_suites: ["device-tests"],
-    defaults: ["fluoride_defaults"],
+    defaults: [
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
     include_dirs: [
             "packages/modules/Bluetooth/system",
     ],
diff --git a/tools/rootcanal/model/controller/acl_connection.cc b/tools/rootcanal/model/controller/acl_connection.cc
index db5c5e1..0f41176 100644
--- a/tools/rootcanal/model/controller/acl_connection.cc
+++ b/tools/rootcanal/model/controller/acl_connection.cc
@@ -17,9 +17,14 @@
 #include "acl_connection.h"
 
 namespace rootcanal {
-AclConnection::AclConnection(AddressWithType addr, AddressWithType own_addr,
+AclConnection::AclConnection(AddressWithType address,
+                             AddressWithType own_address,
+                             AddressWithType resolved_address,
                              Phy::Type phy_type)
-    : address_(addr), own_address_(own_addr), type_(phy_type) {}
+    : address_(address),
+      own_address_(own_address),
+      resolved_address_(resolved_address),
+      type_(phy_type) {}
 
 void AclConnection::Encrypt() { encrypted_ = true; };
 
@@ -31,8 +36,12 @@
 
 AddressWithType AclConnection::GetOwnAddress() const { return own_address_; }
 
-void AclConnection::SetOwnAddress(AddressWithType own_addr) {
-  own_address_ = own_addr;
+AddressWithType AclConnection::GetResolvedAddress() const {
+  return resolved_address_;
+}
+
+void AclConnection::SetOwnAddress(AddressWithType address) {
+  own_address_ = address;
 }
 
 Phy::Type AclConnection::GetPhyType() const { return type_; }
diff --git a/tools/rootcanal/model/controller/acl_connection.h b/tools/rootcanal/model/controller/acl_connection.h
index 80b35ad..c6e1393 100644
--- a/tools/rootcanal/model/controller/acl_connection.h
+++ b/tools/rootcanal/model/controller/acl_connection.h
@@ -28,8 +28,8 @@
 // Model the connection of a device to the controller.
 class AclConnection {
  public:
-  AclConnection(AddressWithType addr, AddressWithType own_addr,
-                Phy::Type phy_type);
+  AclConnection(AddressWithType address, AddressWithType own_address,
+                AddressWithType resolved_address, Phy::Type phy_type);
 
   virtual ~AclConnection() = default;
 
@@ -43,13 +43,16 @@
 
   AddressWithType GetOwnAddress() const;
 
-  void SetOwnAddress(AddressWithType own_addr);
+  void SetOwnAddress(AddressWithType address);
+
+  AddressWithType GetResolvedAddress() const;
 
   Phy::Type GetPhyType() const;
 
  private:
   AddressWithType address_;
   AddressWithType own_address_;
+  AddressWithType resolved_address_;
   Phy::Type type_{Phy::Type::BR_EDR};
 
   // State variables
diff --git a/tools/rootcanal/model/controller/acl_connection_handler.cc b/tools/rootcanal/model/controller/acl_connection_handler.cc
index 8aafaf9..28efac8 100644
--- a/tools/rootcanal/model/controller/acl_connection_handler.cc
+++ b/tools/rootcanal/model/controller/acl_connection_handler.cc
@@ -70,27 +70,35 @@
   }
   classic_connection_pending_ = false;
   pending_connection_address_ = Address::kEmpty;
+  pending_le_connection_resolved_address_ = AddressWithType();
   return true;
 }
 
-bool AclConnectionHandler::CreatePendingLeConnection(AddressWithType addr) {
-  bool device_connected = false;
+bool AclConnectionHandler::CreatePendingLeConnection(
+    AddressWithType peer, AddressWithType resolved_peer,
+    AddressWithType local_address) {
   for (auto pair : acl_connections_) {
     auto connection = std::get<AclConnection>(pair);
-    if (connection.GetAddress() == addr) {
-      device_connected = true;
+    if (connection.GetAddress() == peer ||
+        connection.GetResolvedAddress() == resolved_peer) {
+      LOG_INFO("%s: %s is already connected", __func__,
+               peer.ToString().c_str());
+      if (connection.GetResolvedAddress() == resolved_peer) {
+        LOG_INFO("%s: allowing a second connection with %s", __func__,
+                 resolved_peer.ToString().c_str());
+      } else {
+        return false;
+      }
     }
   }
-  if (device_connected) {
-    LOG_INFO("%s: %s is already connected", __func__, addr.ToString().c_str());
-    return false;
-  }
   if (le_connection_pending_) {
     LOG_INFO("%s: connection already pending", __func__);
     return false;
   }
   le_connection_pending_ = true;
-  pending_le_connection_address_ = addr;
+  pending_le_connection_address_ = peer;
+  pending_le_connection_own_address_ = local_address;
+  pending_le_connection_resolved_address_ = resolved_peer;
   return true;
 }
 
@@ -105,6 +113,8 @@
   le_connection_pending_ = false;
   pending_le_connection_address_ =
       AddressWithType{Address::kEmpty, AddressType::PUBLIC_DEVICE_ADDRESS};
+  pending_le_connection_resolved_address_ =
+      AddressWithType{Address::kEmpty, AddressType::PUBLIC_DEVICE_ADDRESS};
   return true;
 }
 
@@ -117,7 +127,7 @@
         AclConnection{
             AddressWithType{addr, AddressType::PUBLIC_DEVICE_ADDRESS},
             AddressWithType{own_addr, AddressType::PUBLIC_DEVICE_ADDRESS},
-            Phy::Type::BR_EDR});
+            AddressWithType(), Phy::Type::BR_EDR});
     return handle;
   }
   return kReservedHandle;
@@ -125,10 +135,12 @@
 
 uint16_t AclConnectionHandler::CreateLeConnection(AddressWithType addr,
                                                   AddressWithType own_addr) {
+  AddressWithType resolved_peer = pending_le_connection_resolved_address_;
   if (CancelPendingLeConnection(addr)) {
     uint16_t handle = GetUnusedHandle();
     acl_connections_.emplace(
-        handle, AclConnection{addr, own_addr, Phy::Type::LOW_ENERGY});
+        handle,
+        AclConnection{addr, own_addr, resolved_peer, Phy::Type::LOW_ENERGY});
     return handle;
   }
   return kReservedHandle;
@@ -184,6 +196,12 @@
   return acl_connections_.at(handle).GetOwnAddress();
 }
 
+AddressWithType AclConnectionHandler::GetResolvedAddress(
+    uint16_t handle) const {
+  ASSERT_LOG(HasHandle(handle), "Unknown handle %hd", handle);
+  return acl_connections_.at(handle).GetResolvedAddress();
+}
+
 void AclConnectionHandler::Encrypt(uint16_t handle) {
   if (!HasHandle(handle)) {
     return;
@@ -198,15 +216,6 @@
   return acl_connections_.at(handle).IsEncrypted();
 }
 
-void AclConnectionHandler::SetAddress(uint16_t handle,
-                                      AddressWithType address) {
-  if (!HasHandle(handle)) {
-    return;
-  }
-  auto connection = acl_connections_.at(handle);
-  connection.SetAddress(address);
-}
-
 Phy::Type AclConnectionHandler::GetPhyType(uint16_t handle) const {
   if (!HasHandle(handle)) {
     return Phy::Type::BR_EDR;
diff --git a/tools/rootcanal/model/controller/acl_connection_handler.h b/tools/rootcanal/model/controller/acl_connection_handler.h
index 7f8af70..d5a6b6d 100644
--- a/tools/rootcanal/model/controller/acl_connection_handler.h
+++ b/tools/rootcanal/model/controller/acl_connection_handler.h
@@ -58,7 +58,9 @@
       bluetooth::hci::Address addr) const;
   ScoLinkParameters GetScoLinkParameters(bluetooth::hci::Address addr) const;
 
-  bool CreatePendingLeConnection(bluetooth::hci::AddressWithType addr);
+  bool CreatePendingLeConnection(bluetooth::hci::AddressWithType peer,
+                                 bluetooth::hci::AddressWithType resolved_peer,
+                                 bluetooth::hci::AddressWithType local_address);
   bool HasPendingLeConnection(bluetooth::hci::AddressWithType addr) const;
   bool CancelPendingLeConnection(bluetooth::hci::AddressWithType addr);
 
@@ -75,12 +77,11 @@
   bluetooth::hci::AddressWithType GetAddress(uint16_t handle) const;
   bluetooth::hci::Address GetScoAddress(uint16_t handle) const;
   bluetooth::hci::AddressWithType GetOwnAddress(uint16_t handle) const;
+  bluetooth::hci::AddressWithType GetResolvedAddress(uint16_t handle) const;
 
   void Encrypt(uint16_t handle);
   bool IsEncrypted(uint16_t handle) const;
 
-  void SetAddress(uint16_t handle, bluetooth::hci::AddressWithType address);
-
   Phy::Type GetPhyType(uint16_t handle) const;
 
   std::unique_ptr<bluetooth::hci::LeSetCigParametersCompleteBuilder>
@@ -135,6 +136,12 @@
   bluetooth::hci::AddressWithType pending_le_connection_address_{
       bluetooth::hci::Address::kEmpty,
       bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  bluetooth::hci::AddressWithType pending_le_connection_own_address_{
+      bluetooth::hci::Address::kEmpty,
+      bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  bluetooth::hci::AddressWithType pending_le_connection_resolved_address_{
+      bluetooth::hci::Address::kEmpty,
+      bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS};
 
   uint16_t GetUnusedHandle();
   uint16_t last_handle_{kReservedHandle - 2};
diff --git a/tools/rootcanal/model/controller/dual_mode_controller.cc b/tools/rootcanal/model/controller/dual_mode_controller.cc
index af0c86c..2a83188 100644
--- a/tools/rootcanal/model/controller/dual_mode_controller.cc
+++ b/tools/rootcanal/model/controller/dual_mode_controller.cc
@@ -1602,13 +1602,18 @@
 void DualModeController::LeSetHostFeature(CommandView command) {
   auto command_view = gd_hci::LeSetHostFeatureView::Create(command);
   ASSERT(command_view.IsValid());
-  // TODO: if the controller has active connections, return COMMAND_DISALLOED
-  ErrorCode error_code =
-      properties_.SetLeHostFeature(
-          static_cast<uint8_t>(command_view.GetBitNumber()),
-          static_cast<uint8_t>(command_view.GetBitValue()))
-          ? ErrorCode::SUCCESS
-          : ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
+
+  ErrorCode error_code = ErrorCode::SUCCESS;
+  if (link_layer_controller_.HasAclConnection()) {
+    error_code = ErrorCode::COMMAND_DISALLOWED;
+  } else {
+    bool bit_was_set = properties_.SetLeHostFeature(
+        static_cast<uint8_t>(command_view.GetBitNumber()),
+        static_cast<uint8_t>(command_view.GetBitValue()));
+    if (!bit_was_set) {
+      error_code = ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
+    }
+  }
   send_event_(bluetooth::hci::LeSetHostFeatureCompleteBuilder::Create(
       kNumCommandPackets, error_code));
 }
@@ -1945,10 +1950,13 @@
           gd_hci::AclCommandView::Create(command)));
   ASSERT(command_view.IsValid());
 
-  uint8_t addr_type = static_cast<uint8_t>(command_view.GetAddressType());
-  Address address = command_view.GetAddress();
-  ErrorCode result =
-      link_layer_controller_.LeFilterAcceptListAddDevice(address, addr_type);
+  ErrorCode result = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  if (command_view.GetAddressType() !=
+      bluetooth::hci::FilterAcceptListAddressType::ANONYMOUS_ADVERTISERS) {
+    result = link_layer_controller_.LeFilterAcceptListAddDevice(
+        command_view.GetAddress(), static_cast<bluetooth::hci::AddressType>(
+                                       command_view.GetAddressType()));
+  }
   send_event_(
       bluetooth::hci::LeAddDeviceToFilterAcceptListCompleteBuilder::Create(
           kNumCommandPackets, result));
@@ -1961,12 +1969,18 @@
           gd_hci::AclCommandView::Create(command)));
   ASSERT(command_view.IsValid());
 
-  uint8_t addr_type = static_cast<uint8_t>(command_view.GetAddressType());
-  Address address = command_view.GetAddress();
-  link_layer_controller_.LeFilterAcceptListRemoveDevice(address, addr_type);
+  ErrorCode status = ErrorCode::SUCCESS;
+  if (command_view.GetAddressType() !=
+      bluetooth::hci::FilterAcceptListAddressType::ANONYMOUS_ADVERTISERS) {
+    link_layer_controller_.LeFilterAcceptListAddDevice(
+        command_view.GetAddress(), static_cast<bluetooth::hci::AddressType>(
+                                       command_view.GetAddressType()));
+  } else {
+    status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  }
   send_event_(
       bluetooth::hci::LeRemoveDeviceFromFilterAcceptListCompleteBuilder::Create(
-          kNumCommandPackets, ErrorCode::SUCCESS));
+          kNumCommandPackets, status));
 }
 
 void DualModeController::LeClearResolvingList(CommandView command) {
@@ -2037,14 +2051,18 @@
   auto command_view = gd_hci::LeAddDeviceToResolvingListView::Create(
       gd_hci::LeSecurityCommandView::Create(command));
   ASSERT(command_view.IsValid());
-
-  auto addr_type =
-      static_cast<uint8_t>(command_view.GetPeerIdentityAddressType());
-  Address address = command_view.GetPeerIdentityAddress();
-
+  AddressType peer_address_type;
+  switch (command_view.GetPeerIdentityAddressType()) {
+    case bluetooth::hci::PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS:
+      peer_address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
+      break;
+    case bluetooth::hci::PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS:
+      peer_address_type = AddressType::RANDOM_DEVICE_ADDRESS;
+      break;
+  }
   auto status = link_layer_controller_.LeResolvingListAddDevice(
-      address, addr_type, command_view.GetPeerIrk(),
-      command_view.GetLocalIrk());
+      command_view.GetPeerIdentityAddress(), peer_address_type,
+      command_view.GetPeerIrk(), command_view.GetLocalIrk());
   send_event_(bluetooth::hci::LeAddDeviceToResolvingListCompleteBuilder::Create(
       kNumCommandPackets, status));
 }
@@ -2054,10 +2072,17 @@
       gd_hci::LeSecurityCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
-  uint8_t addr_type =
-      static_cast<uint8_t>(command_view.GetPeerIdentityAddressType());
-  Address address = command_view.GetPeerIdentityAddress();
-  link_layer_controller_.LeResolvingListRemoveDevice(address, addr_type);
+  AddressType peer_address_type;
+  switch (command_view.GetPeerIdentityAddressType()) {
+    case bluetooth::hci::PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS:
+      peer_address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
+      break;
+    case bluetooth::hci::PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS:
+      peer_address_type = AddressType::RANDOM_DEVICE_ADDRESS;
+      break;
+  }
+  link_layer_controller_.LeResolvingListRemoveDevice(
+      command_view.GetPeerIdentityAddress(), peer_address_type);
   send_event_(
       bluetooth::hci::LeRemoveDeviceFromResolvingListCompleteBuilder::Create(
           kNumCommandPackets, ErrorCode::SUCCESS));
@@ -2146,11 +2171,18 @@
       gd_hci::LeSecurityCommandView::Create(command));
   ASSERT(command_view.IsValid());
 
-  uint8_t peer_identity_address_type =
-      static_cast<uint8_t>(command_view.GetPeerIdentityAddressType());
   Address peer_identity_address = command_view.GetPeerIdentityAddress();
   uint8_t privacy_mode = static_cast<uint8_t>(command_view.GetPrivacyMode());
 
+  AddressType peer_identity_address_type;
+  switch (command_view.GetPeerIdentityAddressType()) {
+    case bluetooth::hci::PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS:
+      peer_identity_address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
+      break;
+    case bluetooth::hci::PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS:
+      peer_identity_address_type = AddressType::RANDOM_DEVICE_ADDRESS;
+      break;
+  }
   if (link_layer_controller_.LeResolvingListContainsDevice(
           peer_identity_address, peer_identity_address_type)) {
     link_layer_controller_.LeSetPrivacyMode(
diff --git a/tools/rootcanal/model/controller/le_advertiser.cc b/tools/rootcanal/model/controller/le_advertiser.cc
index ed9c090..3f1d1fd 100644
--- a/tools/rootcanal/model/controller/le_advertiser.cc
+++ b/tools/rootcanal/model/controller/le_advertiser.cc
@@ -38,12 +38,16 @@
 }
 
 void LeAdvertiser::InitializeExtended(
-    unsigned advertising_handle, AddressType address_type,
-    AddressWithType peer_address, LeScanningFilterPolicy filter_policy,
+    unsigned advertising_handle, OwnAddressType address_type,
+    AddressWithType public_address, AddressWithType peer_address,
+    LeScanningFilterPolicy filter_policy,
     model::packets::AdvertisementType type,
-    std::chrono::steady_clock::duration interval, uint8_t tx_power) {
+    std::chrono::steady_clock::duration interval, uint8_t tx_power,
+    const std::function<bluetooth::hci::Address()>& get_address) {
+  get_address_ = get_address;
+  own_address_type_ = address_type;
+  public_address_ = public_address;
   advertising_handle_ = advertising_handle;
-  address_ = AddressWithType(address_.GetAddress(), address_type);
   peer_address_ = peer_address;
   filter_policy_ = filter_policy;
   type_ = type;
@@ -99,6 +103,34 @@
   Duration duration = duration_ms;
   TimePoint now = std::chrono::steady_clock::now();
 
+  bluetooth::hci::Address resolvable_address = get_address_();
+  switch (own_address_type_) {
+    case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS:
+      address_ = public_address_;
+      break;
+    case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS:
+      address_ = AddressWithType(address_.GetAddress(),
+                                 AddressType::RANDOM_DEVICE_ADDRESS);
+      break;
+    case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
+      if (resolvable_address != Address::kEmpty) {
+        address_ = AddressWithType(resolvable_address,
+                                   AddressType::RANDOM_DEVICE_ADDRESS);
+      } else {
+        address_ = public_address_;
+      }
+      break;
+    case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
+      if (resolvable_address != Address::kEmpty) {
+        address_ = AddressWithType(resolvable_address,
+                                   AddressType::RANDOM_DEVICE_ADDRESS);
+      } else {
+        address_ = AddressWithType(address_.GetAddress(),
+                                   AddressType::RANDOM_DEVICE_ADDRESS);
+      }
+      break;
+  }
+
   switch (type_) {
     // [Vol 6] Part B. 4.4.2.4.3 High duty cycle connectable directed
     // advertising
diff --git a/tools/rootcanal/model/controller/le_advertiser.h b/tools/rootcanal/model/controller/le_advertiser.h
index 0b7a92b7..afd1ad1 100644
--- a/tools/rootcanal/model/controller/le_advertiser.h
+++ b/tools/rootcanal/model/controller/le_advertiser.h
@@ -40,13 +40,14 @@
                   const std::vector<uint8_t>& scan_response,
                   std::chrono::steady_clock::duration interval);
 
-  void InitializeExtended(unsigned advertising_handle,
-                          bluetooth::hci::AddressType address_type,
-                          bluetooth::hci::AddressWithType peer_address,
-                          bluetooth::hci::LeScanningFilterPolicy filter_policy,
-                          model::packets::AdvertisementType type,
-                          std::chrono::steady_clock::duration interval,
-                          uint8_t tx_power);
+  void InitializeExtended(
+      unsigned advertising_handle, bluetooth::hci::OwnAddressType address_type,
+      bluetooth::hci::AddressWithType public_address,
+      bluetooth::hci::AddressWithType peer_address,
+      bluetooth::hci::LeScanningFilterPolicy filter_policy,
+      model::packets::AdvertisementType type,
+      std::chrono::steady_clock::duration interval, uint8_t tx_power,
+      const std::function<bluetooth::hci::Address()>& get_address);
 
   void SetAddress(bluetooth::hci::Address address);
 
@@ -80,7 +81,13 @@
   bluetooth::hci::AddressWithType GetAddress() const;
 
  private:
+  std::function<bluetooth::hci::Address()> default_get_address_ = []() {
+    return bluetooth::hci::Address::kEmpty;
+  };
+  std::function<bluetooth::hci::Address()>& get_address_ = default_get_address_;
   bluetooth::hci::AddressWithType address_{};
+  bluetooth::hci::AddressWithType public_address_{};
+  bluetooth::hci::OwnAddressType own_address_type_;
   bluetooth::hci::AddressWithType
       peer_address_{};  // For directed advertisements
   bluetooth::hci::LeScanningFilterPolicy filter_policy_{};
diff --git a/tools/rootcanal/model/controller/link_layer_controller.cc b/tools/rootcanal/model/controller/link_layer_controller.cc
index 3404eb3..780dff6 100644
--- a/tools/rootcanal/model/controller/link_layer_controller.cc
+++ b/tools/rootcanal/model/controller/link_layer_controller.cc
@@ -1361,8 +1361,8 @@
         adv_type == model::packets::AdvertisementType::ADV_DIRECT_IND)) {
     return;
   }
-  Address resolved_address = address;
-  uint8_t resolved_address_type = static_cast<uint8_t>(address_type);
+  Address resolved_address = Address::kEmpty;
+  AddressType resolved_address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
   bool resolved = false;
   Address rpa;
   if (le_resolving_list_enabled_) {
@@ -1381,17 +1381,10 @@
   // Connect
   if ((le_peer_address_ == address &&
        le_peer_address_type_ == static_cast<uint8_t>(address_type)) ||
-      (LeFilterAcceptListContainsDevice(address,
-                                        static_cast<uint8_t>(address_type))) ||
-      (resolved &&
-       LeFilterAcceptListContainsDevice(
-           resolved_address, static_cast<uint8_t>(resolved_address_type)))) {
-    if (!connections_.CreatePendingLeConnection(AddressWithType(
-            address, static_cast<bluetooth::hci::AddressType>(address_type)))) {
-      LOG_WARN(
-          "CreatePendingLeConnection failed for connection to %s (type %hhx)",
-          incoming.GetSourceAddress().ToString().c_str(), address_type);
-    }
+      (LeFilterAcceptListContainsDevice(
+          address, static_cast<AddressType>(address_type))) ||
+      (resolved && LeFilterAcceptListContainsDevice(resolved_address,
+                                                    resolved_address_type))) {
     Address own_address;
     auto own_address_type =
         static_cast<bluetooth::hci::OwnAddressType>(le_address_type_);
@@ -1425,6 +1418,18 @@
     le_connect_ = false;
     le_scan_enable_ = bluetooth::hci::OpCode::NONE;
 
+    if (!connections_.CreatePendingLeConnection(
+            AddressWithType(
+                incoming.GetSourceAddress(),
+                static_cast<bluetooth::hci::AddressType>(address_type)),
+            AddressWithType(resolved_address, resolved_address_type),
+            AddressWithType(
+                own_address,
+                static_cast<bluetooth::hci::AddressType>(own_address_type)))) {
+      LOG_WARN(
+          "CreatePendingLeConnection failed for connection to %s (type %hhx)",
+          incoming.GetSourceAddress().ToString().c_str(), address_type);
+    }
     SendLeLinkLayerPacket(model::packets::LeConnectBuilder::Create(
         own_address, incoming.GetSourceAddress(), le_connection_interval_min_,
         le_connection_interval_max_, le_connection_latency_,
@@ -1572,12 +1577,37 @@
   if (properties_.IsUnmasked(EventCode::LE_META_EVENT) &&
       properties_.GetLeEventSupported(
           SubeventCode::ENHANCED_CONNECTION_COMPLETE)) {
+    AddressWithType peer_resolved_address =
+        connections_.GetResolvedAddress(handle);
+    Address peer_resolvable_private_address;
+    Address connection_address = address.GetAddress();
+    AddressType peer_address_type = address.GetAddressType();
+    if (peer_resolved_address != AddressWithType()) {
+      peer_resolvable_private_address = address.GetAddress();
+      if (peer_resolved_address.GetAddressType() ==
+          AddressType::PUBLIC_DEVICE_ADDRESS) {
+        peer_address_type = AddressType::PUBLIC_IDENTITY_ADDRESS;
+      } else if (peer_resolved_address.GetAddressType() ==
+                 AddressType::RANDOM_DEVICE_ADDRESS) {
+        peer_address_type = AddressType::RANDOM_IDENTITY_ADDRESS;
+      } else {
+        LOG_WARN("Unhandled resolved address type %s -> %s",
+                 address.ToString().c_str(),
+                 peer_resolved_address.ToString().c_str());
+      }
+      connection_address = peer_resolved_address.GetAddress();
+    }
+    Address local_resolved_address = own_address.GetAddress();
+    if (local_resolved_address == properties_.GetAddress() ||
+        local_resolved_address == properties_.GetLeAddress()) {
+      local_resolved_address = Address::kEmpty;
+    }
+
     send_event_(bluetooth::hci::LeEnhancedConnectionCompleteBuilder::Create(
         ErrorCode::SUCCESS, handle, static_cast<bluetooth::hci::Role>(role),
-        address.GetAddressType(), address.GetAddress(),
-        Address(),  // TODO local resolvable private address, if applicable
-        Address(),  // TODO Peer resolvable private address, if applicable
-        connection_interval, connection_latency, supervision_timeout,
+        peer_address_type, connection_address, local_resolved_address,
+        peer_resolvable_private_address, connection_interval,
+        connection_latency, supervision_timeout,
         static_cast<bluetooth::hci::ClockAccuracy>(0x00)));
   } else if (properties_.IsUnmasked(EventCode::LE_META_EVENT) &&
              properties_.GetLeEventSupported(
@@ -1602,16 +1632,6 @@
   uint16_t connection_interval = (connect.GetLeConnectionIntervalMax() +
                                   connect.GetLeConnectionIntervalMin()) /
                                  2;
-  if (!connections_.CreatePendingLeConnection(AddressWithType(
-          incoming.GetSourceAddress(), static_cast<bluetooth::hci::AddressType>(
-                                           connect.GetAddressType())))) {
-    LOG_WARN(
-        "CreatePendingLeConnection failed for connection from %s (type "
-        "%hhx)",
-        incoming.GetSourceAddress().ToString().c_str(),
-        connect.GetAddressType());
-    return;
-  }
   bluetooth::hci::AddressWithType my_address{};
   bool matched_advertiser = false;
   size_t set = 0;
@@ -1637,6 +1657,21 @@
     return;
   }
 
+  // TODO: Implement for Directed Advertisements
+  AddressWithType peer_resolved_address;
+
+  if (!connections_.CreatePendingLeConnection(
+          AddressWithType(incoming.GetSourceAddress(),
+                          static_cast<bluetooth::hci::AddressType>(
+                              connect.GetAddressType())),
+          peer_resolved_address, my_address)) {
+    LOG_WARN(
+        "CreatePendingLeConnection failed for connection from %s (type "
+        "%hhx)",
+        incoming.GetSourceAddress().ToString().c_str(),
+        connect.GetAddressType());
+    return;
+  }
   uint16_t handle = HandleLeConnection(
       AddressWithType(
           incoming.GetSourceAddress(),
@@ -3009,28 +3044,6 @@
     bluetooth::hci::PeerAddressType peer_address_type, Address peer,
     bluetooth::hci::AdvertisingFilterPolicy filter_policy, uint8_t tx_power) {
   model::packets::AdvertisementType ad_type;
-  switch (type) {
-    case bluetooth::hci::LegacyAdvertisingProperties::ADV_IND:
-      ad_type = model::packets::AdvertisementType::ADV_IND;
-      peer = Address::kEmpty;
-      break;
-    case bluetooth::hci::LegacyAdvertisingProperties::ADV_NONCONN_IND:
-      ad_type = model::packets::AdvertisementType::ADV_NONCONN_IND;
-      peer = Address::kEmpty;
-      break;
-    case bluetooth::hci::LegacyAdvertisingProperties::ADV_SCAN_IND:
-      ad_type = model::packets::AdvertisementType::ADV_SCAN_IND;
-      peer = Address::kEmpty;
-      break;
-    case bluetooth::hci::LegacyAdvertisingProperties::ADV_DIRECT_IND_HIGH:
-      ad_type = model::packets::AdvertisementType::ADV_DIRECT_IND;
-      break;
-    case bluetooth::hci::LegacyAdvertisingProperties::ADV_DIRECT_IND_LOW:
-      ad_type = model::packets::AdvertisementType::SCAN_RESPONSE;
-      break;
-  }
-  auto interval_ms =
-      static_cast<int>((interval_max + interval_min) * 0.625 / 2);
 
   AddressWithType peer_address;
   switch (peer_address_type) {
@@ -3044,25 +3057,33 @@
       break;
   }
 
-  bluetooth::hci::AddressType own_address_address_type;
-  switch (own_address_type) {
-    case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS:
-      own_address_address_type =
-          bluetooth::hci::AddressType::RANDOM_DEVICE_ADDRESS;
+  AddressWithType directed_address{};
+  switch (type) {
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_IND:
+      ad_type = model::packets::AdvertisementType::ADV_IND;
       break;
-    case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS:
-      own_address_address_type =
-          bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS;
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_NONCONN_IND:
+      ad_type = model::packets::AdvertisementType::ADV_NONCONN_IND;
       break;
-    case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
-      own_address_address_type =
-          bluetooth::hci::AddressType::PUBLIC_IDENTITY_ADDRESS;
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_SCAN_IND:
+      ad_type = model::packets::AdvertisementType::ADV_SCAN_IND;
       break;
-    case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
-      own_address_address_type =
-          bluetooth::hci::AddressType::RANDOM_IDENTITY_ADDRESS;
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_DIRECT_IND_HIGH:
+      ad_type = model::packets::AdvertisementType::ADV_DIRECT_IND;
+      directed_address = peer_address;
+      break;
+    case bluetooth::hci::LegacyAdvertisingProperties::ADV_DIRECT_IND_LOW:
+      ad_type = model::packets::AdvertisementType::SCAN_RESPONSE;
+      directed_address = peer_address;
       break;
   }
+  auto interval_ms =
+      static_cast<int>((interval_max + interval_min) * 0.625 / 2);
+
+  LOG_INFO("peer %s", peer.ToString().c_str());
+  LOG_INFO("peer_address_type %s",
+           bluetooth::hci::PeerAddressTypeText(peer_address_type).c_str());
+  LOG_INFO("peer_address %s", peer_address.ToString().c_str());
 
   bluetooth::hci::LeScanningFilterPolicy scanning_filter_policy;
   switch (filter_policy) {
@@ -3085,8 +3106,26 @@
   }
 
   advertisers_[set].InitializeExtended(
-      set, own_address_address_type, peer_address, scanning_filter_policy,
-      ad_type, std::chrono::milliseconds(interval_ms), tx_power);
+      set, own_address_type,
+      bluetooth::hci::AddressWithType(
+          properties_.GetAddress(),
+          bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS),
+      directed_address, scanning_filter_policy, ad_type,
+      std::chrono::milliseconds(interval_ms), tx_power,
+      [this, own_address_type, peer_address]() {
+        if (own_address_type ==
+                bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS ||
+            own_address_type ==
+                bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS) {
+          for (const auto& entry : le_resolving_list_) {
+            if (entry.address == peer_address.GetAddress() &&
+                entry.address_type == peer_address.GetAddressType()) {
+              return generate_rpa(entry.local_irk);
+            }
+          }
+        }
+        return bluetooth::hci::Address::kEmpty;
+      });
   return ErrorCode::SUCCESS;
 }
 
@@ -3221,26 +3260,25 @@
   return ErrorCode::SUCCESS;
 }
 
-ErrorCode LinkLayerController::LeFilterAcceptListAddDevice(Address addr,
-                                                           uint8_t addr_type) {
+ErrorCode LinkLayerController::LeFilterAcceptListAddDevice(
+    Address addr, AddressType addr_type) {
   if (FilterAcceptListBusy()) {
     return ErrorCode::COMMAND_DISALLOWED;
   }
-  std::tuple<Address, uint8_t> new_tuple = std::make_tuple(addr, addr_type);
   for (auto dev : le_connect_list_) {
-    if (dev == new_tuple) {
+    if (dev.address == addr && dev.address_type == addr_type) {
       return ErrorCode::SUCCESS;
     }
   }
   if (LeFilterAcceptListFull()) {
     return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
   }
-  le_connect_list_.emplace_back(new_tuple);
+  le_connect_list_.emplace_back(ConnectListEntry{addr, addr_type});
   return ErrorCode::SUCCESS;
 }
 
 ErrorCode LinkLayerController::LeResolvingListAddDevice(
-    Address addr, uint8_t addr_type, std::array<uint8_t, kIrkSize> peerIrk,
+    Address addr, AddressType addr_type, std::array<uint8_t, kIrkSize> peerIrk,
     std::array<uint8_t, kIrkSize> localIrk) {
   if (ResolvingListBusy()) {
     return ErrorCode::COMMAND_DISALLOWED;
@@ -3253,10 +3291,14 @@
   return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::LeSetPrivacyMode(uint8_t address_type, Address addr,
-                                           uint8_t mode) {
+bool LinkLayerController::HasAclConnection() {
+  return (connections_.GetAclHandles().size() > 0);
+}
+
+void LinkLayerController::LeSetPrivacyMode(AddressType address_type,
+                                           Address addr, uint8_t mode) {
   // set mode for addr
-  LOG_INFO("address type = %d ", address_type);
+  LOG_INFO("address type = %s ", AddressTypeText(address_type).c_str());
   LOG_INFO("address = %s ", addr.ToString().c_str());
   LOG_INFO("mode = %d ", mode);
 }
@@ -3597,38 +3639,38 @@
 }
 
 ErrorCode LinkLayerController::LeFilterAcceptListRemoveDevice(
-    Address addr, uint8_t addr_type) {
+    Address addr, AddressType addr_type) {
   if (FilterAcceptListBusy()) {
     return ErrorCode::COMMAND_DISALLOWED;
   }
-  std::tuple<Address, uint8_t> erase_tuple = std::make_tuple(addr, addr_type);
   for (size_t i = 0; i < le_connect_list_.size(); i++) {
-    if (le_connect_list_[i] == erase_tuple) {
+    if (le_connect_list_[i].address == addr &&
+        le_connect_list_[i].address_type == addr_type) {
       le_connect_list_.erase(le_connect_list_.begin() + i);
     }
   }
   return ErrorCode::SUCCESS;
 }
 
-ErrorCode LinkLayerController::LeResolvingListRemoveDevice(Address addr,
-                                                           uint8_t addr_type) {
+ErrorCode LinkLayerController::LeResolvingListRemoveDevice(
+    Address addr, AddressType addr_type) {
   if (ResolvingListBusy()) {
     return ErrorCode::COMMAND_DISALLOWED;
   }
-  for (size_t i = 0; i < le_connect_list_.size(); i++) {
-    auto curr = le_connect_list_[i];
-    if (std::get<0>(curr) == addr && std::get<1>(curr) == addr_type) {
+  for (size_t i = 0; i < le_resolving_list_.size(); i++) {
+    auto curr = le_resolving_list_[i];
+    if (curr.address == addr && curr.address_type == addr_type) {
       le_resolving_list_.erase(le_resolving_list_.begin() + i);
     }
   }
   return ErrorCode::SUCCESS;
 }
 
-bool LinkLayerController::LeFilterAcceptListContainsDevice(Address addr,
-                                                           uint8_t addr_type) {
-  std::tuple<Address, uint8_t> sought_tuple = std::make_tuple(addr, addr_type);
+bool LinkLayerController::LeFilterAcceptListContainsDevice(
+    Address addr, AddressType addr_type) {
   for (size_t i = 0; i < le_connect_list_.size(); i++) {
-    if (le_connect_list_[i] == sought_tuple) {
+    if (le_connect_list_[i].address == addr &&
+        le_connect_list_[i].address_type == addr_type) {
       return true;
     }
   }
@@ -3636,10 +3678,10 @@
 }
 
 bool LinkLayerController::LeResolvingListContainsDevice(Address addr,
-                                                        uint8_t addr_type) {
+                                                        AddressType addr_type) {
   for (size_t i = 0; i < le_connect_list_.size(); i++) {
     auto curr = le_connect_list_[i];
-    if (std::get<0>(curr) == addr && std::get<1>(curr) == addr_type) {
+    if (curr.address == addr && curr.address_type == addr_type) {
       return true;
     }
   }
diff --git a/tools/rootcanal/model/controller/link_layer_controller.h b/tools/rootcanal/model/controller/link_layer_controller.h
index 9bd55f4..4643e7b 100644
--- a/tools/rootcanal/model/controller/link_layer_controller.h
+++ b/tools/rootcanal/model/controller/link_layer_controller.h
@@ -29,6 +29,7 @@
 namespace rootcanal {
 
 using ::bluetooth::hci::Address;
+using ::bluetooth::hci::AddressType;
 using ::bluetooth::hci::ErrorCode;
 using ::bluetooth::hci::OpCode;
 
@@ -188,20 +189,20 @@
 
   bool FilterAcceptListBusy();
   ErrorCode LeFilterAcceptListClear();
-  ErrorCode LeFilterAcceptListAddDevice(Address addr, uint8_t addr_type);
-  ErrorCode LeFilterAcceptListRemoveDevice(Address addr, uint8_t addr_type);
-  bool LeFilterAcceptListContainsDevice(Address addr, uint8_t addr_type);
+  ErrorCode LeFilterAcceptListAddDevice(Address addr, AddressType addr_type);
+  ErrorCode LeFilterAcceptListRemoveDevice(Address addr, AddressType addr_type);
+  bool LeFilterAcceptListContainsDevice(Address addr, AddressType addr_type);
   bool LeFilterAcceptListFull();
   bool ResolvingListBusy();
   ErrorCode LeSetAddressResolutionEnable(bool enable);
   ErrorCode LeResolvingListClear();
-  ErrorCode LeResolvingListAddDevice(Address addr, uint8_t addr_type,
+  ErrorCode LeResolvingListAddDevice(Address addr, AddressType addr_type,
                                      std::array<uint8_t, kIrkSize> peerIrk,
                                      std::array<uint8_t, kIrkSize> localIrk);
-  ErrorCode LeResolvingListRemoveDevice(Address addr, uint8_t addr_type);
-  bool LeResolvingListContainsDevice(Address addr, uint8_t addr_type);
+  ErrorCode LeResolvingListRemoveDevice(Address addr, AddressType addr_type);
+  bool LeResolvingListContainsDevice(Address addr, AddressType addr_type);
   bool LeResolvingListFull();
-  void LeSetPrivacyMode(uint8_t address_type, Address addr, uint8_t mode);
+  void LeSetPrivacyMode(AddressType address_type, Address addr, uint8_t mode);
 
   void LeReadIsoTxSync(uint16_t handle);
   void LeSetCigParameters(
@@ -367,6 +368,8 @@
       uint8_t retransmission_effort, uint16_t packet_types);
   ErrorCode RejectSynchronousConnection(Address bd_addr, uint16_t reason);
 
+  bool HasAclConnection();
+
   void HandleIso(bluetooth::hci::IsoView iso);
 
  protected:
@@ -480,10 +483,14 @@
   uint32_t key_id_ = 1;
 
   // LE state
-  std::vector<std::tuple<Address, uint8_t>> le_connect_list_;
+  struct ConnectListEntry {
+    Address address;
+    AddressType address_type;
+  };
+  std::vector<ConnectListEntry> le_connect_list_;
   struct ResolvingListEntry {
     Address address;
-    uint8_t address_type;
+    AddressType address_type;
     std::array<uint8_t, kIrkSize> peer_irk;
     std::array<uint8_t, kIrkSize> local_irk;
   };