Merge "Add new API for new feature of display name in Japanese."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 45e3151..12481af 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -56,6 +56,7 @@
     <uses-permission android:name="android.permission.READ_BLOCKED_NUMBERS"/>
     <uses-permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"/>
     <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
+    <uses-permission android:name="com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"/>
 
     <permission android:name="android.permission.BROADCAST_CALLLOG_INFO"
          android:label="Broadcast the call type/duration information"
@@ -314,6 +315,9 @@
             <intent-filter>
                 <action android:name="android.bluetooth.IBluetoothHeadsetPhone"/>
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.bluetooth.IBluetoothLeCallControlCallback" />
+            </intent-filter>
         </service>
 
         <service android:name=".components.TelecomService"
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 7bccb58..746ab57 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -85,6 +85,7 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  *  Encapsulates all aspects of a given phone call throughout its lifecycle, starting
@@ -3163,6 +3164,13 @@
     void setConferenceableCalls(List<Call> conferenceableCalls) {
         mConferenceableCalls.clear();
         mConferenceableCalls.addAll(conferenceableCalls);
+        String confCallIds = "";
+        if (!conferenceableCalls.isEmpty()) {
+            confCallIds = conferenceableCalls.stream()
+                    .map(c -> c.getId())
+                    .collect(Collectors.joining(","));
+        }
+        Log.addEvent(this, LogUtils.Events.CONF_CALLS_CHANGED, confCallIds);
 
         for (Listener l : mListeners) {
             l.onConferenceableCallsChanged(this);
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index e5a6ecc..f6f710e 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -875,8 +875,8 @@
                     return HANDLED;
                 case SWITCH_FOCUS:
                     if (msg.arg1 == NO_FOCUS) {
-                        // Only disconnect SCO audio here instead of routing away from BT entirely.
-                        mBluetoothRouteManager.disconnectSco();
+                        // Only disconnect audio here instead of routing away from BT entirely.
+                        mBluetoothRouteManager.disconnectAudio();
                         reinitialize();
                         mCallAudioManager.notifyAudioOperationsComplete();
                     } else if (msg.arg1 == RINGING_FOCUS
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 21c6844..4018d54 100755
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -45,6 +45,8 @@
 import android.telecom.StatusHints;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.telephony.CellIdentity;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.IConnectionService;
@@ -920,6 +922,45 @@
                         } else {
                             connectIdToCheck = callId;
                         }
+
+                        // Handle the case where an existing connection was added by Telephony via
+                        // a connection manager.  The remote connection service API does not include
+                        // the ability to specify a parent connection when adding an existing
+                        // connection, so we stash the desired parent in the connection extras.
+                        if (connectionExtras != null
+                                && connectionExtras.containsKey(
+                                        Connection.EXTRA_ADD_TO_CONFERENCE_ID)
+                                && connection.getParentCallId() == null) {
+                            String parentId = connectionExtras.getString(
+                                    Connection.EXTRA_ADD_TO_CONFERENCE_ID);
+                            Log.i(ConnectionServiceWrapper.this, "addExistingConnection: remote "
+                                    + "connection will auto-add to parent %s", parentId);
+                            // Replace parcelable connection instance, swapping the new desired
+                            // parent in.
+                            connection = new ParcelableConnection(
+                                    connection.getPhoneAccount(),
+                                    connection.getState(),
+                                    connection.getConnectionCapabilities(),
+                                    connection.getConnectionProperties(),
+                                    connection.getSupportedAudioRoutes(),
+                                    connection.getHandle(),
+                                    connection.getHandlePresentation(),
+                                    connection.getCallerDisplayName(),
+                                    connection.getCallerDisplayNamePresentation(),
+                                    connection.getVideoProvider(),
+                                    connection.getVideoState(),
+                                    connection.isRingbackRequested(),
+                                    connection.getIsVoipAudioMode(),
+                                    connection.getConnectTimeMillis(),
+                                    connection.getConnectElapsedTimeMillis(),
+                                    connection.getStatusHints(),
+                                    connection.getDisconnectCause(),
+                                    connection.getConferenceableConnectionIds(),
+                                    connection.getExtras(),
+                                    parentId,
+                                    connection.getCallDirection(),
+                                    connection.getCallerNumberVerificationStatus());
+                        }
                         // Check to see if this Connection has already been added.
                         Call alreadyAddedConnection = mCallsManager
                                 .getAlreadyAddedConnection(connectIdToCheck);
@@ -1197,6 +1238,24 @@
         }
     }
 
+    private CellIdentity getLastKnownCellIdentity() {
+        TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+        if (telephonyManager != null) {
+            CellIdentity lastKnownCellIdentity = telephonyManager.getLastKnownCellIdentity();
+            try {
+                mAppOpsManager.noteOp(AppOpsManager.OP_FINE_LOCATION,
+                        mContext.getPackageManager().getPackageUid(
+                                getComponentName().getPackageName(), 0),
+                        getComponentName().getPackageName());
+            } catch (PackageManager.NameNotFoundException nameNotFoundException) {
+                Log.e(this, nameNotFoundException, "could not find the package -- %s",
+                        getComponentName().getPackageName());
+            }
+            return lastKnownCellIdentity;
+        }
+        return null;
+    }
+
     /**
      * Creates a conference for a new outgoing call or attach to an existing incoming call.
      */
@@ -1316,6 +1375,11 @@
                         Log.piiHandle(call.getHandle()) + " via:" +
                                 getComponentName().getPackageName());
 
+                if (call.isEmergencyCall()) {
+                    extras.putParcelable(Connection.EXTRA_LAST_KNOWN_CELL_IDENTITY,
+                            getLastKnownCellIdentity());
+                }
+
                 ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
                         .setAccountHandle(call.getTargetPhoneAccount())
                         .setAddress(call.getHandle())
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index e3b8dee..3561211 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -39,6 +39,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * This class creates connections to place new outgoing calls or to attach to an existing incoming
@@ -386,8 +387,13 @@
             mAttemptRecords.clear();
             // Phone accounts in profile do not handle emergency call, use phone accounts in
             // current user.
+            // ONLY include phone accounts which are NOT self-managed; we will never consider a self
+            // managed phone account for placing an emergency call.
             List<PhoneAccount> allAccounts = mPhoneAccountRegistrar
-                    .getAllPhoneAccountsOfCurrentUser();
+                    .getAllPhoneAccountsOfCurrentUser()
+                    .stream()
+                    .filter(act -> !act.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED))
+                    .collect(Collectors.toList());
 
             if (allAccounts.isEmpty() && mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TELEPHONY)) {
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 138e441..f53f239 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -138,6 +138,7 @@
         public static final String REMOVE_CHILD = "REMOVE_CHILD";
         public static final String SET_PARENT = "SET_PARENT";
         public static final String CONF_STATE_CHANGED = "CONF_STATE_CHANGED";
+        public static final String CONF_CALLS_CHANGED = "CONF_CALLS_CHANGED";
         public static final String CALL_DIRECTION_CHANGED = "CALL_DIRECTION_CHANGED";
         public static final String MUTE = "MUTE";
         public static final String UNMUTE = "UNMUTE";
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index e9b760a..8d6e324 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -852,6 +852,7 @@
         public boolean hasManageOngoingCallsPermission(String callingPackage) {
             try {
                 Log.startSession("TSI.hMOCP");
+                enforceCallingPackage(callingPackage);
                 return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(
                         mContext, Manifest.permission.MANAGE_ONGOING_CALLS,
                         Binder.getCallingPid(),
@@ -1466,6 +1467,7 @@
                 enforceCallingPackage(callingPackage);
 
                 PhoneAccountHandle phoneAccountHandle = null;
+                boolean clearPhoneAccountHandleExtra = false;
                 if (extras != null) {
                     phoneAccountHandle = extras.getParcelable(
                             TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
@@ -1477,17 +1479,24 @@
                 boolean isSelfManaged = phoneAccountHandle != null &&
                         isSelfManagedConnectionService(phoneAccountHandle);
                 if (isSelfManaged) {
-                    mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_OWN_CALLS,
-                            "Self-managed ConnectionServices require MANAGE_OWN_CALLS permission.");
+                    try {
+                        mContext.enforceCallingOrSelfPermission(
+                                Manifest.permission.MANAGE_OWN_CALLS,
+                                "Self-managed ConnectionServices require "
+                                        + "MANAGE_OWN_CALLS permission.");
+                    } catch (SecurityException e) {
+                        // Fallback to use mobile network to avoid disclosing phone account handle
+                        // package information
+                        clearPhoneAccountHandleExtra = true;
+                    }
 
-                    if (!callingPackage.equals(
+                    if (!clearPhoneAccountHandleExtra && !callingPackage.equals(
                             phoneAccountHandle.getComponentName().getPackageName())
                             && !canCallPhone(callingPackage, callingFeatureId,
                             "CALL_PHONE permission required to place calls.")) {
-                        // The caller is not allowed to place calls, so we want to ensure that it
-                        // can only place calls through itself.
-                        throw new SecurityException("Self-managed ConnectionServices can only "
-                                + "place calls through their own ConnectionService.");
+                        // The caller is not allowed to place calls, so fallback to use mobile
+                        // network.
+                        clearPhoneAccountHandleExtra = true;
                     }
                 } else if (!canCallPhone(callingPackage, callingFeatureId, "placeCall")) {
                     throw new SecurityException("Package " + callingPackage
@@ -1522,6 +1531,9 @@
                         final Intent intent = new Intent(hasCallPrivilegedPermission ?
                                 Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
                         if (extras != null) {
+                            if (clearPhoneAccountHandleExtra) {
+                                extras.remove(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+                            }
                             extras.setDefusable(true);
                             intent.putExtras(extras);
                         }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index c7671e3..cb31990 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -20,6 +20,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.telecom.Log;
@@ -37,6 +38,11 @@
 import java.util.Set;
 
 public class BluetoothDeviceManager {
+
+    public static final int DEVICE_TYPE_HEADSET = 0;
+    public static final int DEVICE_TYPE_HEARING_AID = 1;
+    public static final int DEVICE_TYPE_LE_AUDIO = 2;
+
     private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
             new BluetoothProfile.ServiceListener() {
                 @Override
@@ -52,6 +58,10 @@
                                 mBluetoothHearingAid = (BluetoothHearingAid) proxy;
                                 logString = "Got BluetoothHearingAid: "
                                         + mBluetoothHearingAid;
+                            } else if (profile == BluetoothProfile.LE_AUDIO) {
+                                mBluetoothLeAudioService = (BluetoothLeAudio) proxy;
+                                logString = "Got BluetoothLeAudio: "
+                                        + mBluetoothLeAudioService;
                             } else {
                                 logString = "Connected to non-requested bluetooth service." +
                                         " Not changing bluetooth headset.";
@@ -74,7 +84,8 @@
                             if (profile == BluetoothProfile.HEADSET) {
                                 mBluetoothHeadset = null;
                                 lostServiceDevices = mHfpDevicesByAddress;
-                                mBluetoothRouteManager.onActiveDeviceChanged(null, false);
+                                mBluetoothRouteManager.onActiveDeviceChanged(null,
+                                        DEVICE_TYPE_HEADSET);
                                 logString = "Lost BluetoothHeadset service. " +
                                         "Removing all tracked devices";
                             } else if (profile == BluetoothProfile.HEARING_AID) {
@@ -82,7 +93,15 @@
                                 logString = "Lost BluetoothHearingAid service. " +
                                         "Removing all tracked devices.";
                                 lostServiceDevices = mHearingAidDevicesByAddress;
-                                mBluetoothRouteManager.onActiveDeviceChanged(null, true);
+                                mBluetoothRouteManager.onActiveDeviceChanged(null,
+                                        DEVICE_TYPE_HEARING_AID);
+                            } else if (profile == BluetoothProfile.LE_AUDIO) {
+                                mBluetoothLeAudioService = null;
+                                logString = "Lost BluetoothLeAudio service. " +
+                                        "Removing all tracked devices.";
+                                lostServiceDevices = mLeAudioDevicesByAddress;
+                                mBluetoothRouteManager.onActiveDeviceChanged(null,
+                                        DEVICE_TYPE_LE_AUDIO);
                             } else {
                                 return;
                             }
@@ -108,6 +127,12 @@
             new LinkedHashMap<>();
     private final LinkedHashMap<BluetoothDevice, Long> mHearingAidDeviceSyncIds =
             new LinkedHashMap<>();
+    private final LinkedHashMap<String, BluetoothDevice> mLeAudioDevicesByAddress =
+            new LinkedHashMap<>();
+    private final LinkedHashMap<BluetoothDevice, Integer> mGroupsByDevice =
+            new LinkedHashMap<>();
+    private int mGroupIdActive = BluetoothLeAudio.GROUP_ID_INVALID;
+    private int mGroupIdPending = BluetoothLeAudio.GROUP_ID_INVALID;
     private final LocalLog mLocalLog = new LocalLog(20);
 
     // This lock only protects internal state -- it doesn't lock on anything going into Telecom.
@@ -116,6 +141,7 @@
     private BluetoothRouteManager mBluetoothRouteManager;
     private BluetoothHeadset mBluetoothHeadset;
     private BluetoothHearingAid mBluetoothHearingAid;
+    private BluetoothLeAudio mBluetoothLeAudioService;
     private BluetoothDevice mBluetoothHearingAidActiveDeviceCache;
     private BluetoothAdapter mBluetoothAdapter;
 
@@ -126,6 +152,8 @@
                     BluetoothProfile.HEADSET);
             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
                     BluetoothProfile.HEARING_AID);
+            bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
+                    BluetoothProfile.LE_AUDIO);
         }
     }
 
@@ -133,9 +161,20 @@
         mBluetoothRouteManager = brm;
     }
 
+    private List<BluetoothDevice> getLeAudioConnectedDevices() {
+        synchronized (mLock) {
+            // Filter out disconnected devices and/or those that have no group assigned
+            ArrayList<BluetoothDevice> devices = new ArrayList<>(mGroupsByDevice.keySet());
+            devices.removeIf(device -> !mLeAudioDevicesByAddress.containsValue(device));
+            return devices;
+        }
+    }
+
     public int getNumConnectedDevices() {
         synchronized (mLock) {
-            return mHfpDevicesByAddress.size() + mHearingAidDevicesByAddress.size();
+            return mHfpDevicesByAddress.size() +
+                    mHearingAidDevicesByAddress.size() +
+                    getLeAudioConnectedDevices().size();
         }
     }
 
@@ -143,6 +182,7 @@
         synchronized (mLock) {
             ArrayList<BluetoothDevice> result = new ArrayList<>(mHfpDevicesByAddress.values());
             result.addAll(mHearingAidDevicesByAddress.values());
+            result.addAll(getLeAudioConnectedDevices());
             return Collections.unmodifiableCollection(result);
         }
     }
@@ -177,6 +217,31 @@
                 seenHiSyncIds.add(hiSyncId);
             }
         }
+
+        Set<Integer> seenGroupIds = new LinkedHashSet<>();
+        if (mBluetoothAdapter != null) {
+            for (BluetoothDevice device : mBluetoothAdapter.getActiveDevices(
+                        BluetoothProfile.LE_AUDIO)) {
+                if (device != null) {
+                    result.add(device);
+                    seenGroupIds.add(mGroupsByDevice.getOrDefault(device, -1));
+                    break;
+                }
+            }
+        }
+        synchronized (mLock) {
+            for (BluetoothDevice d : getLeAudioConnectedDevices()) {
+                int groupId = mGroupsByDevice.getOrDefault(d,
+                        BluetoothLeAudio.GROUP_ID_INVALID);
+                if (groupId == BluetoothLeAudio.GROUP_ID_INVALID
+                        || seenGroupIds.contains(groupId)) {
+                    continue;
+                }
+                result.add(d);
+                seenGroupIds.add(groupId);
+            }
+        }
+
         return Collections.unmodifiableCollection(result);
     }
 
@@ -192,6 +257,10 @@
         return mBluetoothHearingAid;
     }
 
+    public BluetoothLeAudio getLeAudioService() {
+        return mBluetoothLeAudioService;
+    }
+
     public void setHeadsetServiceForTesting(BluetoothHeadset bluetoothHeadset) {
         mBluetoothHeadset = bluetoothHeadset;
     }
@@ -200,12 +269,33 @@
         mBluetoothHearingAid = bluetoothHearingAid;
     }
 
-    void onDeviceConnected(BluetoothDevice device, boolean isHearingAid) {
-        mLocalLog.log("Device connected -- address: " + device.getAddress() + " isHeadingAid: "
-                + isHearingAid);
+    public void setLeAudioServiceForTesting(BluetoothLeAudio bluetoothLeAudio) {
+        mBluetoothLeAudioService = bluetoothLeAudio;
+    }
+
+    public static String getDeviceTypeString(int deviceType) {
+        switch (deviceType) {
+            case DEVICE_TYPE_LE_AUDIO:
+                return "LeAudio";
+            case DEVICE_TYPE_HEARING_AID:
+                return "HearingAid";
+            case DEVICE_TYPE_HEADSET:
+                return "HFP";
+            default:
+                return "unknown type";
+        }
+    }
+
+    void onDeviceConnected(BluetoothDevice device, int deviceType) {
         synchronized (mLock) {
             LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
-            if (isHearingAid) {
+            if (deviceType == DEVICE_TYPE_LE_AUDIO) {
+                if (mBluetoothLeAudioService == null) {
+                    Log.w(this, "LE audio service null when receiving device added broadcast");
+                    return;
+                }
+                targetDeviceMap = mLeAudioDevicesByAddress;
+            } else if (deviceType == DEVICE_TYPE_HEARING_AID) {
                 if (mBluetoothHearingAid == null) {
                     Log.w(this, "Hearing aid service null when receiving device added broadcast");
                     return;
@@ -213,12 +303,16 @@
                 long hiSyncId = mBluetoothHearingAid.getHiSyncId(device);
                 mHearingAidDeviceSyncIds.put(device, hiSyncId);
                 targetDeviceMap = mHearingAidDevicesByAddress;
-            } else {
+            } else if (deviceType == DEVICE_TYPE_HEADSET) {
                 if (mBluetoothHeadset == null) {
                     Log.w(this, "Headset service null when receiving device added broadcast");
                     return;
                 }
                 targetDeviceMap = mHfpDevicesByAddress;
+            } else {
+                Log.w(this, "Device: " + device.getAddress() + " with invalid type: "
+                            + getDeviceTypeString(deviceType));
+                return;
             }
             if (!targetDeviceMap.containsKey(device.getAddress())) {
                 targetDeviceMap.put(device.getAddress(), device);
@@ -227,16 +321,22 @@
         }
     }
 
-    void onDeviceDisconnected(BluetoothDevice device, boolean isHearingAid) {
-        mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " isHeadingAid: "
-                + isHearingAid);
+    void onDeviceDisconnected(BluetoothDevice device, int deviceType) {
+        mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " deviceType: "
+                + deviceType);
         synchronized (mLock) {
             LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
-            if (isHearingAid) {
+            if (deviceType == DEVICE_TYPE_LE_AUDIO) {
+                targetDeviceMap = mLeAudioDevicesByAddress;
+            } else if (deviceType == DEVICE_TYPE_HEARING_AID) {
                 mHearingAidDeviceSyncIds.remove(device);
                 targetDeviceMap = mHearingAidDevicesByAddress;
-            } else {
+            } else if (deviceType == DEVICE_TYPE_HEADSET) {
                 targetDeviceMap = mHfpDevicesByAddress;
+            } else {
+                Log.w(this, "Device: " + device.getAddress() + " with invalid type: "
+                            + getDeviceTypeString(deviceType));
+                return;
             }
             if (targetDeviceMap.containsKey(device.getAddress())) {
                 targetDeviceMap.remove(device.getAddress());
@@ -245,16 +345,34 @@
         }
     }
 
+    void onGroupNodeAdded(BluetoothDevice device, int groupId) {
+        Log.i(this, device.getAddress() + " group added " + groupId);
+        if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
+            Log.w(this, "invalid parameter");
+            return;
+        }
+
+        synchronized (mLock) {
+            mGroupsByDevice.put(device, groupId);
+        }
+    }
+
+    void onGroupNodeRemoved(BluetoothDevice device, int groupId) {
+        if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
+            Log.w(this, "invalid parameter");
+            return;
+        }
+
+        synchronized (mLock) {
+            mGroupsByDevice.remove(device);
+        }
+    }
+
     public void disconnectAudio() {
         if (mBluetoothAdapter != null) {
-            for (BluetoothDevice device: mBluetoothAdapter.getActiveDevices(
-                        BluetoothProfile.HEARING_AID)) {
-                if (device != null) {
-                    mBluetoothAdapter.removeActiveDevice(BluetoothAdapter.ACTIVE_DEVICE_ALL);
-                }
-            }
+            mBluetoothAdapter.removeActiveDevice(BluetoothAdapter.ACTIVE_DEVICE_ALL);
+            disconnectSco();
         }
-        disconnectSco();
     }
 
     public void disconnectSco() {
@@ -265,10 +383,18 @@
         }
     }
 
-    // Connect audio to the bluetooth device at address, checking to see whether it's a hearing aid
-    // or a HFP device, and using the proper BT API.
+    // Connect audio to the bluetooth device at address, checking to see whether it's
+    // le audio, hearing aid or a HFP device, and using the proper BT API.
     public boolean connectAudio(String address) {
-        if (mHearingAidDevicesByAddress.containsKey(address)) {
+        if (mLeAudioDevicesByAddress.containsKey(address)) {
+            if (mBluetoothLeAudioService == null) {
+                Log.w(this, "Attempting to turn on audio when the le audio service is null");
+                return false;
+            }
+            BluetoothDevice device = mLeAudioDevicesByAddress.get(address);
+            return mBluetoothAdapter.setActiveDevice(
+                    device, BluetoothAdapter.ACTIVE_DEVICE_ALL);
+        } else if (mHearingAidDevicesByAddress.containsKey(address)) {
             if (mBluetoothHearingAid == null) {
                 Log.w(this, "Attempting to turn on audio when the hearing aid service is null");
                 return false;
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 81f4ab6..163dbb2 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothLeAudio;
 import android.content.Context;
 import android.os.Message;
 import android.telecom.Log;
@@ -35,10 +36,12 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.Timeouts;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -294,7 +297,7 @@
                         break;
                     case BT_AUDIO_IS_ON:
                         if (Objects.equals(mDeviceAddress, address)) {
-                            Log.i(LOG_TAG, "HFP connection success for device %s.", mDeviceAddress);
+                            Log.i(LOG_TAG, "BT connection success for device %s.", mDeviceAddress);
                             transitionTo(mAudioConnectedStates.get(mDeviceAddress));
                         } else {
                             Log.w(LOG_TAG, "In connecting state for device %s but %s" +
@@ -451,6 +454,7 @@
     // Tracks the active devices in the BT stack (HFP or hearing aid).
     private BluetoothDevice mHfpActiveDeviceCache = null;
     private BluetoothDevice mHearingAidActiveDeviceCache = null;
+    private BluetoothDevice mLeAudioDeviceCache = null;
     private BluetoothDevice mMostRecentlyReportedActiveDevice = null;
 
     public BluetoothRouteManager(Context context, TelecomSystem.SyncRoot lock,
@@ -548,8 +552,8 @@
         sendMessage(DISCONNECT_HFP, args);
     }
 
-    public void disconnectSco() {
-        mDeviceManager.disconnectSco();
+    public void disconnectAudio() {
+        mDeviceManager.disconnectAudio();
     }
 
     public void cacheHearingAidDevice() {
@@ -582,19 +586,37 @@
         mListener.onBluetoothDeviceListChanged();
     }
 
-    public void onActiveDeviceChanged(BluetoothDevice device, boolean isHearingAid) {
-        boolean wasActiveDevicePresent = mHearingAidActiveDeviceCache != null
-                || mHfpActiveDeviceCache != null;
-        if (isHearingAid) {
+    public void onAudioOn(String address) {
+        Session session = Log.createSubsession();
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = session;
+        args.arg2 = address;
+        sendMessage(BT_AUDIO_IS_ON, args);
+    }
+
+    public void onAudioLost(String address) {
+        Session session = Log.createSubsession();
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = session;
+        args.arg2 = address;
+        sendMessage(BT_AUDIO_LOST, args);
+    }
+
+    public void onActiveDeviceChanged(BluetoothDevice device, int deviceType) {
+        boolean wasActiveDevicePresent = hasBtActiveDevice();
+        if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) {
+            mLeAudioDeviceCache = device;
+        } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID) {
             mHearingAidActiveDeviceCache = device;
-        } else {
+        } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEADSET) {
             mHfpActiveDeviceCache = device;
+        } else {
+            return;
         }
 
         if (device != null) mMostRecentlyReportedActiveDevice = device;
 
-        boolean isActiveDevicePresent = mHearingAidActiveDeviceCache != null
-                || mHfpActiveDeviceCache != null;
+        boolean isActiveDevicePresent = hasBtActiveDevice();
 
         if (wasActiveDevicePresent && !isActiveDevicePresent) {
             mListener.onBluetoothActiveDeviceGone();
@@ -604,7 +626,9 @@
     }
 
     public boolean hasBtActiveDevice() {
-        return mHearingAidActiveDeviceCache != null || mHfpActiveDeviceCache != null;
+        return mLeAudioDeviceCache != null ||
+                mHearingAidActiveDeviceCache != null ||
+                mHfpActiveDeviceCache != null;
     }
 
     public Collection<BluetoothDevice> getConnectedDevices() {
@@ -682,6 +706,9 @@
         if (mHearingAidActiveDeviceCache != null) {
             return mHearingAidActiveDeviceCache.getAddress();
         }
+        if (mLeAudioDeviceCache != null) {
+            return mLeAudioDeviceCache.getAddress();
+        }
         return null;
     }
 
@@ -705,29 +732,33 @@
         BluetoothAdapter bluetoothAdapter = mDeviceManager.getBluetoothAdapter();
         BluetoothHeadset bluetoothHeadset = mDeviceManager.getBluetoothHeadset();
         BluetoothHearingAid bluetoothHearingAid = mDeviceManager.getBluetoothHearingAid();
+        BluetoothLeAudio bluetoothLeAudio = mDeviceManager.getLeAudioService();
 
         BluetoothDevice hfpAudioOnDevice = null;
         BluetoothDevice hearingAidActiveDevice = null;
+        BluetoothDevice leAudioActiveDevice = null;
 
         if (bluetoothAdapter == null) {
             Log.i(this, "getBluetoothAudioConnectedDevice: no adapter available.");
             return null;
         }
-        if (bluetoothHeadset == null && bluetoothHearingAid == null) {
+        if (bluetoothHeadset == null && bluetoothHearingAid == null && bluetoothLeAudio == null) {
             Log.i(this, "getBluetoothAudioConnectedDevice: no service available.");
             return null;
         }
 
+        int activeDevices = 0;
         if (bluetoothHeadset != null) {
             for (BluetoothDevice device : bluetoothAdapter.getActiveDevices(
                         BluetoothProfile.HEADSET)) {
                 hfpAudioOnDevice = device;
                 break;
             }
-
             if (bluetoothHeadset.getAudioState(hfpAudioOnDevice)
                     == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
                 hfpAudioOnDevice = null;
+            } else {
+                activeDevices++;
             }
         }
 
@@ -736,22 +767,39 @@
                         BluetoothProfile.HEARING_AID)) {
                 if (device != null) {
                     hearingAidActiveDevice = device;
+                    activeDevices++;
                     break;
                 }
             }
         }
 
-        // Return the active device reported by either HFP or hearing aid. If both are reporting
-        // active devices, go with the most recent one as reported by the receiver.
-        if (hfpAudioOnDevice != null) {
-            if (hearingAidActiveDevice != null) {
-                Log.i(this, "Both HFP and hearing aid are reporting active devices. Going with"
-                        + " the most recently reported active device: %s");
-                return mMostRecentlyReportedActiveDevice;
+        if (bluetoothLeAudio != null) {
+            for (BluetoothDevice device : bluetoothLeAudio.getActiveDevices()) {
+                if (device != null) {
+                    leAudioActiveDevice = device;
+                    activeDevices++;
+                    break;
+                }
             }
-            return hfpAudioOnDevice;
         }
-        return hearingAidActiveDevice;
+
+        // Return the active device reported by either HFP, hearing aid or le audio. If more than
+        // one is reporting active devices, go with the most recent one as reported by the receiver.
+        if (activeDevices > 1) {
+            Log.i(this, "More than one profile reporting active devices. Going with the most"
+                    + " recently reported active device: %s", mMostRecentlyReportedActiveDevice);
+            return mMostRecentlyReportedActiveDevice;
+        }
+
+        if (leAudioActiveDevice != null) {
+            return leAudioActiveDevice;
+        }
+
+        if (hearingAidActiveDevice != null) {
+            return hearingAidActiveDevice;
+        }
+
+        return hfpAudioOnDevice;
     }
 
     /**
@@ -847,10 +895,12 @@
     }
 
     @VisibleForTesting
-    public void setActiveDeviceCacheForTesting(BluetoothDevice device, boolean isHearingAid) {
-        if (isHearingAid) {
+    public void setActiveDeviceCacheForTesting(BluetoothDevice device, int deviceType) {
+        if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) {
+          mLeAudioDeviceCache = device;
+        } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID) {
             mHearingAidActiveDeviceCache = device;
-        } else {
+        } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEADSET) {
             mHfpActiveDeviceCache = device;
         }
     }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 8a14cbd..49489f4 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -19,6 +19,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -43,6 +44,10 @@
         INTENT_FILTER.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
         INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
         INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
+        INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
+        INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
+        INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED);
+        INTENT_FILTER.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_STATUS_CHANGED);
     }
 
     // If not in a call, BSR won't listen to the Bluetooth stack's HFP on/off messages, since
@@ -59,14 +64,19 @@
                 case BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED:
                     handleAudioStateChanged(intent);
                     break;
+                case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
                 case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
                 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
                     handleConnectionStateChanged(intent);
                     break;
+                case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
                 case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
                 case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED:
                     handleActiveDeviceChanged(intent);
                     break;
+                case BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED:
+                    handleGroupNodeStatusChanged(intent);
+                    break;
             }
         } finally {
             Log.endSession();
@@ -117,29 +127,52 @@
             return;
         }
 
-        Log.i(LOG_TAG, "Device %s changed state to %d",
+        int deviceType;
+        if (BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
+            deviceType = BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO;
+        } else if (BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
+            deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID;
+        } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
+            deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEADSET;
+        } else {
+            Log.w(LOG_TAG, "handleConnectionStateChanged: %s invalid device type", device);
+            return;
+        }
+
+        Log.i(LOG_TAG, "%s device %s changed state to %d",
+                BluetoothDeviceManager.getDeviceTypeString(deviceType),
                 device.getAddress(), bluetoothHeadsetState);
 
-        boolean isHearingAid = BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED
-                .equals(intent.getAction());
         if (bluetoothHeadsetState == BluetoothProfile.STATE_CONNECTED) {
-            mBluetoothDeviceManager.onDeviceConnected(device, isHearingAid);
+            mBluetoothDeviceManager.onDeviceConnected(device, deviceType);
         } else if (bluetoothHeadsetState == BluetoothProfile.STATE_DISCONNECTED
                 || bluetoothHeadsetState == BluetoothProfile.STATE_DISCONNECTING) {
-            mBluetoothDeviceManager.onDeviceDisconnected(device, isHearingAid);
+            mBluetoothDeviceManager.onDeviceDisconnected(device, deviceType);
         }
     }
 
     private void handleActiveDeviceChanged(Intent intent) {
         BluetoothDevice device =
                 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-        boolean isHearingAid =
-                BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction());
-        Log.i(LOG_TAG, "Device %s is now the preferred BT device for %s", device,
-                isHearingAid ? "hearing aid" : "HFP");
 
-        mBluetoothRouteManager.onActiveDeviceChanged(device, isHearingAid);
-        if (isHearingAid) {
+        int deviceType;
+        if (BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) {
+            deviceType = BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO;
+        } else if (BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) {
+            deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID;
+        } else if (BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) {
+            deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEADSET;
+        } else {
+            Log.w(LOG_TAG, "handleActiveDeviceChanged: %s invalid device type", device);
+            return;
+        }
+
+        Log.i(LOG_TAG, "Device %s is now the preferred BT device for %s", device,
+                BluetoothDeviceManager.getDeviceTypeString(deviceType));
+
+        mBluetoothRouteManager.onActiveDeviceChanged(device, deviceType);
+        if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID ||
+            deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) {
             Session session = Log.createSubsession();
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = session;
@@ -147,12 +180,28 @@
                 mBluetoothRouteManager.sendMessage(BT_AUDIO_LOST, args);
             } else {
                 if (!mIsInCall) {
-                    Log.i(LOG_TAG, "Ignoring hearing aid audio on since we're not in a call");
+                    Log.i(LOG_TAG, "Ignoring audio on since we're not in a call");
                     return;
                 }
                 args.arg2 = device.getAddress();
                 mBluetoothRouteManager.sendMessage(BT_AUDIO_IS_ON, args);
-            }
+           }
+        }
+    }
+
+    private void handleGroupNodeStatusChanged(Intent intent) {
+        BluetoothDevice device =
+                intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+        int groupId = intent.getIntExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_ID,
+                BluetoothLeAudio.GROUP_ID_INVALID);
+        int groupNodeStatus = intent.getIntExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_NODE_STATUS,
+                -1);
+
+        if (groupNodeStatus == BluetoothLeAudio.GROUP_NODE_ADDED) {
+            mBluetoothDeviceManager.onGroupNodeAdded(device, groupId);
+        } else {
+            mBluetoothDeviceManager.onGroupNodeRemoved(device, groupId);
         }
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index db26aaf..fe156dc 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -20,6 +20,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Intent;
@@ -44,6 +45,7 @@
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -55,6 +57,7 @@
     @Mock BluetoothHeadset mBluetoothHeadset;
     @Mock BluetoothAdapter mAdapter;
     @Mock BluetoothHearingAid mBluetoothHearingAid;
+    @Mock BluetoothLeAudio mBluetoothLeAudio;
 
     BluetoothDeviceManager mBluetoothDeviceManager;
     BluetoothProfile.ServiceListener serviceListenerUnderTest;
@@ -64,6 +67,8 @@
     private BluetoothDevice device2;
     private BluetoothDevice device3;
     private BluetoothDevice device4;
+    private BluetoothDevice device5;
+    private BluetoothDevice device6;
 
     @Override
     @Before
@@ -75,6 +80,9 @@
         device3 = makeBluetoothDevice("00:00:00:00:00:03");
         // hearing aid
         device4 = makeBluetoothDevice("00:00:00:00:00:04");
+        // le audio
+        device5 = makeBluetoothDevice("00:00:00:00:00:05");
+        device6 = makeBluetoothDevice("00:00:00:00:00:06");
 
         when(mBluetoothHearingAid.getHiSyncId(device2)).thenReturn(100L);
         when(mBluetoothHearingAid.getHiSyncId(device4)).thenReturn(100L);
@@ -93,6 +101,7 @@
 
         mBluetoothDeviceManager.setHeadsetServiceForTesting(mBluetoothHeadset);
         mBluetoothDeviceManager.setHearingAidServiceForTesting(mBluetoothHearingAid);
+        mBluetoothDeviceManager.setLeAudioServiceForTesting(mBluetoothLeAudio);
     }
 
     @Override
@@ -105,10 +114,12 @@
     @Test
     public void testSingleDeviceConnectAndDisconnect() {
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         assertEquals(1, mBluetoothDeviceManager.getNumConnectedDevices());
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device1, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         assertEquals(0, mBluetoothDeviceManager.getNumConnectedDevices());
     }
 
@@ -119,9 +130,14 @@
         mBluetoothDeviceManager.setHearingAidServiceForTesting(null);
 
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
 
         assertEquals(0, mBluetoothDeviceManager.getNumConnectedDevices());
     }
@@ -130,16 +146,37 @@
     @Test
     public void testMultiDeviceConnectAndDisconnect() {
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device1, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device3, false));
-        assertEquals(2, mBluetoothDeviceManager.getNumConnectedDevices());
+                buildGroupNodeStatusChangedIntent(1, device5, BluetoothLeAudio.GROUP_NODE_ADDED));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device3, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device6,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        receiverUnderTest.onReceive(mContext,
+                buildGroupNodeStatusChangedIntent(2, device6, BluetoothLeAudio.GROUP_NODE_ADDED));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device3,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        assertEquals(3, mBluetoothDeviceManager.getNumConnectedDevices());
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device3,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device6,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
         assertEquals(1, mBluetoothDeviceManager.getNumConnectedDevices());
     }
 
@@ -147,11 +184,34 @@
     @Test
     public void testHearingAidDedup() {
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device4, true));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device4,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID));
+        assertEquals(3, mBluetoothDeviceManager.getNumConnectedDevices());
+        assertEquals(2, mBluetoothDeviceManager.getUniqueConnectedDevices().size());
+    }
+
+    @SmallTest
+    @Test
+    public void testLeAudioDedup() {
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        receiverUnderTest.onReceive(mContext,
+                buildGroupNodeStatusChangedIntent(1, device5, BluetoothLeAudio.GROUP_NODE_ADDED));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device6,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        receiverUnderTest.onReceive(mContext,
+                buildGroupNodeStatusChangedIntent(1, device6, BluetoothLeAudio.GROUP_NODE_ADDED));
         assertEquals(3, mBluetoothDeviceManager.getNumConnectedDevices());
         assertEquals(2, mBluetoothDeviceManager.getUniqueConnectedDevices().size());
     }
@@ -160,14 +220,18 @@
     @Test
     public void testHeadsetServiceDisconnect() {
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device3, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device3,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID));
         serviceListenerUnderTest.onServiceDisconnected(BluetoothProfile.HEADSET);
 
-        verify(mRouteManager).onActiveDeviceChanged(isNull(), eq(false));
+        verify(mRouteManager).onActiveDeviceChanged(isNull(),
+                eq(BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         verify(mRouteManager).onDeviceLost(device1.getAddress());
         verify(mRouteManager).onDeviceLost(device3.getAddress());
         verify(mRouteManager, never()).onDeviceLost(device2.getAddress());
@@ -179,14 +243,18 @@
     @Test
     public void testHearingAidServiceDisconnect() {
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device3, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device3,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID));
         serviceListenerUnderTest.onServiceDisconnected(BluetoothProfile.HEARING_AID);
 
-        verify(mRouteManager).onActiveDeviceChanged(isNull(), eq(true));
+        verify(mRouteManager).onActiveDeviceChanged(isNull(),
+                eq(BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID));
         verify(mRouteManager).onDeviceLost(device2.getAddress());
         verify(mRouteManager, never()).onDeviceLost(device1.getAddress());
         verify(mRouteManager, never()).onDeviceLost(device3.getAddress());
@@ -196,16 +264,58 @@
 
     @SmallTest
     @Test
+    public void testLeAudioServiceDisconnect() {
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device3,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothLeAudio.STATE_CONNECTED, device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        serviceListenerUnderTest.onServiceDisconnected(BluetoothProfile.LE_AUDIO);
+
+        verify(mRouteManager).onActiveDeviceChanged(isNull(),
+                eq(BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        verify(mRouteManager).onDeviceLost(device5.getAddress());
+        verify(mRouteManager, never()).onDeviceLost(device1.getAddress());
+        verify(mRouteManager, never()).onDeviceLost(device3.getAddress());
+        assertNull(mBluetoothDeviceManager.getLeAudioService());
+        assertEquals(2, mBluetoothDeviceManager.getNumConnectedDevices());
+    }
+
+    @SmallTest
+    @Test
     public void testHearingAidChangesIgnoredWhenNotInCall() {
         receiverUnderTest.setIsInCall(false);
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID));
         Intent activeDeviceChangedIntent =
                 new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
         activeDeviceChangedIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device2);
         receiverUnderTest.onReceive(mContext, activeDeviceChangedIntent);
 
-        verify(mRouteManager).onActiveDeviceChanged(device2, true);
+        verify(mRouteManager).onActiveDeviceChanged(device2,
+                BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID);
+        verify(mRouteManager, never()).sendMessage(BluetoothRouteManager.BT_AUDIO_IS_ON);
+    }
+
+    @SmallTest
+    @Test
+    public void testLeAudioGroupChangesIgnoredWhenNotInCall() {
+        receiverUnderTest.setIsInCall(false);
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothLeAudio.STATE_CONNECTED, device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        Intent activeDeviceChangedIntent =
+                        new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
+        activeDeviceChangedIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device5);
+        receiverUnderTest.onReceive(mContext, activeDeviceChangedIntent);
+
+        verify(mRouteManager).onActiveDeviceChanged(device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO);
         verify(mRouteManager, never()).sendMessage(BluetoothRouteManager.BT_AUDIO_IS_ON);
     }
 
@@ -213,7 +323,8 @@
     @Test
     public void testConnectDisconnectAudioHeadset() {
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEADSET));
         when(mAdapter.setActiveDevice(nullable(BluetoothDevice.class),
                     eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true);
         mBluetoothDeviceManager.connectAudio(device1.getAddress());
@@ -228,7 +339,10 @@
     @Test
     public void testConnectDisconnectAudioHearingAid() {
         receiverUnderTest.onReceive(mContext,
-                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2,
+                        BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID));
+        when(mAdapter.setActiveDevice(nullable(BluetoothDevice.class),
+                eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true);
         mBluetoothDeviceManager.connectAudio(device2.getAddress());
         verify(mAdapter).setActiveDevice(device2, BluetoothAdapter.ACTIVE_DEVICE_ALL);
         verify(mBluetoothHeadset, never()).connectAudio();
@@ -243,16 +357,98 @@
         verify(mBluetoothHeadset).disconnectAudio();
     }
 
-    private Intent buildConnectionActionIntent(int state, BluetoothDevice device,
-            boolean isHearingAid) {
-        Intent i = new Intent(isHearingAid
-                ? BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED
-                : BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+    @SmallTest
+    @Test
+    public void testConnectDisconnectAudioLeAudio() {
+        receiverUnderTest.setIsInCall(true);
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        receiverUnderTest.onReceive(mContext,
+                buildGroupNodeStatusChangedIntent(1, device5, BluetoothLeAudio.GROUP_NODE_ADDED));
+        when(mAdapter.setActiveDevice(nullable(BluetoothDevice.class),
+                eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true);
+        mBluetoothDeviceManager.connectAudio(device5.getAddress());
+        verify(mAdapter).setActiveDevice(device5, BluetoothAdapter.ACTIVE_DEVICE_ALL);
+        verify(mBluetoothHeadset, never()).connectAudio();
+        verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class),
+                eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL));
+
+        mBluetoothDeviceManager.disconnectAudio();
+        verify(mAdapter).removeActiveDevice(BluetoothAdapter.ACTIVE_DEVICE_ALL);
+    }
+
+    @SmallTest
+    @Test
+    public void testConnectEarbudLeAudio() {
+        receiverUnderTest.setIsInCall(true);
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        receiverUnderTest.onReceive(mContext,
+                buildGroupNodeStatusChangedIntent(1, device5, BluetoothLeAudio.GROUP_NODE_ADDED));
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothLeAudio.STATE_CONNECTED, device6,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+        receiverUnderTest.onReceive(mContext,
+                buildGroupNodeStatusChangedIntent(1, device6, BluetoothLeAudio.GROUP_NODE_ADDED));
+        when(mAdapter.setActiveDevice(nullable(BluetoothDevice.class),
+                eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true);
+        mBluetoothDeviceManager.connectAudio(device5.getAddress());
+        verify(mAdapter).setActiveDevice(device5, BluetoothAdapter.ACTIVE_DEVICE_ALL);
+        verify(mBluetoothHeadset, never()).connectAudio();
+        verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class),
+                eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL));
+
+        when(mBluetoothLeAudio.getActiveDevices()).thenReturn(Arrays.asList(device5, device6));
+
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_DISCONNECTED, device5,
+                        BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO));
+
+        mBluetoothDeviceManager.connectAudio(device6.getAddress());
+        verify(mAdapter).setActiveDevice(device6, BluetoothAdapter.ACTIVE_DEVICE_ALL);
+    }
+
+    private Intent buildConnectionActionIntent(int state, BluetoothDevice device, int deviceType) {
+        String intentString;
+
+        switch (deviceType) {
+            case BluetoothDeviceManager.DEVICE_TYPE_HEADSET:
+                intentString = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED;
+                break;
+            case BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID:
+                intentString = BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED;
+                break;
+            case BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO:
+                intentString = BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED;
+                break;
+            default:
+                return null;
+        }
+
+        Intent i = new Intent(intentString);
         i.putExtra(BluetoothHeadset.EXTRA_STATE, state);
         i.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         return i;
     }
 
+    private Intent buildGroupNodeStatusChangedIntent(int groupId, BluetoothDevice device,
+                int nodeStatus) {
+        Intent i = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED);
+        i.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        i.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_ID, groupId);
+        i.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_NODE_STATUS, nodeStatus);
+        return i;
+    }
+
+    private Intent buildGroupStatusChangedIntent(int groupId, int groupStatus) {
+        Intent i = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_GROUP_STATUS_CHANGED);
+        i.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_ID, groupId);
+        i.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_GROUP_STATUS, groupStatus);
+        return i;
+    }
+
     private BluetoothDevice makeBluetoothDevice(String address) {
         Parcel p1 = Parcel.obtain();
         p1.writeString(address);
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 28f6966..d6a6d11 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothLeAudio;
 import android.content.ContentResolver;
 import android.os.Parcel;
 import android.telecom.Log;
@@ -65,6 +66,7 @@
     @Mock private BluetoothDeviceManager mDeviceManager;
     @Mock private BluetoothHeadset mBluetoothHeadset;
     @Mock private BluetoothHearingAid mBluetoothHearingAid;
+    @Mock private BluetoothLeAudio mBluetoothLeAudio;
     @Mock private Timeouts.Adapter mTimeoutsAdapter;
     @Mock private BluetoothRouteManager.BluetoothStateListener mListener;
 
@@ -85,7 +87,7 @@
     public void testConnectHfpRetryWhileNotConnected() {
         BluetoothRouteManager sm = setupStateMachine(
                 BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
-        setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null, null, null);
+        setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null, null, null, null, null);
         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
                 nullable(ContentResolver.class))).thenReturn(0L);
         when(mBluetoothHeadset.connectAudio()).thenReturn(false);
@@ -108,13 +110,17 @@
         BluetoothRouteManager sm = setupStateMachine(
                 BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
         setupConnectedDevices(new BluetoothDevice[]{DEVICE1},
-                new BluetoothDevice[]{HEARING_AID_DEVICE}, DEVICE1, HEARING_AID_DEVICE);
-        sm.onActiveDeviceChanged(DEVICE1, false);
-        sm.onActiveDeviceChanged(HEARING_AID_DEVICE, true);
+                new BluetoothDevice[]{HEARING_AID_DEVICE}, new BluetoothDevice[]{DEVICE2},
+                DEVICE1, HEARING_AID_DEVICE, DEVICE2);
+        sm.onActiveDeviceChanged(DEVICE1, BluetoothDeviceManager.DEVICE_TYPE_HEADSET);
+        sm.onActiveDeviceChanged(DEVICE2, BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO);
+        sm.onActiveDeviceChanged(HEARING_AID_DEVICE,
+                BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID);
         executeRoutingAction(sm, BluetoothRouteManager.BT_AUDIO_LOST, DEVICE1.getAddress());
 
         verifyConnectionAttempt(HEARING_AID_DEVICE, 0);
         verifyConnectionAttempt(DEVICE1, 0);
+        verifyConnectionAttempt(DEVICE2, 0);
         assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
                         + ":" + HEARING_AID_DEVICE.getAddress(),
                 sm.getCurrentState().getName());
@@ -126,7 +132,7 @@
     public void testAudioOnDeviceWithScoOffActiveDevice() {
         BluetoothRouteManager sm = setupStateMachine(
                 BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
-        setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null, DEVICE1, null);
+        setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null, null, DEVICE1, null, null);
         when(mBluetoothHeadset.getAudioState(DEVICE1))
                 .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
         executeRoutingAction(sm, BluetoothRouteManager.BT_AUDIO_LOST, DEVICE1.getAddress());
@@ -142,7 +148,8 @@
     public void testConnectHfpRetryWhileConnectedToAnotherDevice() {
         BluetoothRouteManager sm = setupStateMachine(
                 BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
-        setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null, null, null);
+        setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null, null, null, null,
+                              null);
         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
                 nullable(ContentResolver.class))).thenReturn(0L);
         when(mBluetoothHeadset.connectAudio()).thenReturn(false);
@@ -175,16 +182,18 @@
     }
 
     private void setupConnectedDevices(BluetoothDevice[] hfpDevices,
-            BluetoothDevice[] hearingAidDevices,
-            BluetoothDevice hfpActiveDevice, BluetoothDevice hearingAidActiveDevice) {
+            BluetoothDevice[] hearingAidDevices, BluetoothDevice[] leAudioDevices,
+            BluetoothDevice hfpActiveDevice, BluetoothDevice hearingAidActiveDevice,
+            BluetoothDevice leAudioDevice) {
         if (hfpDevices == null) hfpDevices = new BluetoothDevice[]{};
         if (hearingAidDevices == null) hearingAidDevices = new BluetoothDevice[]{};
+        if (leAudioDevice == null) leAudioDevices = new BluetoothDevice[]{};
 
         when(mDeviceManager.getNumConnectedDevices()).thenReturn(
-                hfpDevices.length + hearingAidDevices.length);
-        List<BluetoothDevice> allDevices = Stream.concat(
-                Arrays.stream(hfpDevices), Arrays.stream(hearingAidDevices))
-                .collect(Collectors.toList());
+                hfpDevices.length + hearingAidDevices.length + leAudioDevices.length);
+        List<BluetoothDevice> allDevices = Stream.of(
+                Arrays.stream(hfpDevices), Arrays.stream(hearingAidDevices),
+                Arrays.stream(leAudioDevices)).flatMap(i -> i).collect(Collectors.toList());
 
         when(mDeviceManager.getConnectedDevices()).thenReturn(allDevices);
         when(mBluetoothHeadset.getConnectedDevices()).thenReturn(Arrays.asList(hfpDevices));
@@ -197,6 +206,8 @@
                 .thenReturn(Arrays.asList(hearingAidDevices));
         when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEARING_AID)))
                 .thenReturn(Arrays.asList(hearingAidActiveDevice, null));
+        when(mBluetoothLeAudio.getActiveDevices())
+                .thenReturn(Arrays.asList(leAudioDevice, null));
     }
 
     static void executeRoutingAction(BluetoothRouteManager brm, int message, String
@@ -222,6 +233,7 @@
         when(mDeviceManager.getBluetoothHeadset()).thenReturn(mBluetoothHeadset);
         when(mDeviceManager.getBluetoothHearingAid()).thenReturn(mBluetoothHearingAid);
         when(mDeviceManager.getBluetoothAdapter()).thenReturn(mBluetoothAdapter);
+        when(mDeviceManager.getLeAudioService()).thenReturn(mBluetoothLeAudio);
         when(mBluetoothHeadset.connectAudio()).thenReturn(true);
         when(mBluetoothHeadset.setActiveDevice(nullable(BluetoothDevice.class))).thenReturn(true);
         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
index d96b687..ef16eff 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothLeAudio;
 import android.content.ContentResolver;
 import android.telecom.Log;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -43,6 +44,8 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Stream;
+import java.util.stream.Collectors;
 
 import static com.android.server.telecom.tests.BluetoothRouteManagerTest.DEVICE1;
 import static com.android.server.telecom.tests.BluetoothRouteManagerTest.DEVICE2;
@@ -80,6 +83,7 @@
         // the active device as returned by BluetoothAdapter#getActiveDevices
         private BluetoothDevice activeDevice = null;
         private List<BluetoothDevice> hearingAidBtDevices = Collections.emptyList();
+        private List<BluetoothDevice> leAudioDevices = Collections.emptyList();
 
         public BluetoothRouteTestParametersBuilder setName(String name) {
             this.name = name;
@@ -154,6 +158,12 @@
             return this;
         }
 
+        public BluetoothRouteTestParametersBuilder setLeAudioDevices(
+                List<BluetoothDevice> leAudioDevices) {
+            this.leAudioDevices = leAudioDevices;
+            return this;
+        }
+
         public BluetoothRouteTestParameters build() {
             return new BluetoothRouteTestParameters(name,
                     initialBluetoothState,
@@ -167,7 +177,8 @@
                     messageDevice,
                     audioOnDevice,
                     activeDevice,
-                    hearingAidBtDevices);
+                    hearingAidBtDevices,
+                    leAudioDevices);
 
         }
     }
@@ -187,6 +198,7 @@
         // the active device as returned by BluetoothAdapter#getActiveDevices
         private BluetoothDevice activeDevice = null;
         private List<BluetoothDevice> hearingAidBtDevices;
+        private List<BluetoothDevice> leAudioDevices;
 
         public BluetoothRouteTestParameters(String name, String initialBluetoothState,
                 BluetoothDevice initialDevice, int messageType, ListenerUpdate[]
@@ -194,7 +206,7 @@
                 expectedConnectionDevice, String expectedFinalStateName,
                 BluetoothDevice[] connectedDevices, BluetoothDevice messageDevice,
                 BluetoothDevice audioOnDevice, BluetoothDevice activeDevice,
-                List<BluetoothDevice> hearingAidBtDevices) {
+                List<BluetoothDevice> hearingAidBtDevices, List<BluetoothDevice> leAudioDevices) {
             this.name = name;
             this.initialBluetoothState = initialBluetoothState;
             this.initialDevice = initialDevice;
@@ -208,6 +220,7 @@
             this.audioOnDevice = audioOnDevice;
             this.activeDevice = activeDevice;
             this.hearingAidBtDevices = hearingAidBtDevices;
+            this.leAudioDevices = leAudioDevices;
         }
 
         @Override
@@ -225,6 +238,7 @@
                     ", connectedDevices=" + Arrays.toString(connectedDevices) +
                     ", activeDevice='" + activeDevice + '\'' +
                     ", hearingAidBtDevices ='" + hearingAidBtDevices + '\'' +
+                    ", leAudioDevices ='" + leAudioDevices + '\'' +
                     '}';
         }
     }
@@ -240,6 +254,7 @@
     @Mock private BluetoothAdapter mBluetoothAdapter;
     @Mock private BluetoothHeadset mBluetoothHeadset;
     @Mock private BluetoothHearingAid mBluetoothHearingAid;
+    @Mock private BluetoothLeAudio mBluetoothLeAudio;
     @Mock private Timeouts.Adapter mTimeoutsAdapter;
     @Mock private BluetoothRouteManager.BluetoothStateListener mListener;
 
@@ -266,10 +281,16 @@
         BluetoothRouteManager sm = setupStateMachine(
                 mParams.initialBluetoothState, mParams.initialDevice);
 
+        int deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEADSET;
+        if (mParams.hearingAidBtDevices.contains(mParams.messageDevice)) {
+            deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID;
+        } else if (mParams.hearingAidBtDevices.contains(mParams.messageDevice)) {
+            deviceType = BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO;
+        }
+
         setupConnectedDevices(mParams.connectedDevices,
                 mParams.audioOnDevice, mParams.activeDevice);
-        sm.setActiveDeviceCacheForTesting(mParams.activeDevice,
-                mParams.hearingAidBtDevices.contains(mParams.messageDevice));
+        sm.setActiveDeviceCacheForTesting(mParams.activeDevice, deviceType);
         if (mParams.initialDevice != null) {
             doAnswer(invocation -> {
                 SomeArgs args = SomeArgs.obtain();
@@ -285,14 +306,16 @@
         // Go through the utility methods for these two messages
         if (mParams.messageType == BluetoothRouteManager.NEW_DEVICE_CONNECTED) {
             sm.onDeviceAdded(mParams.messageDevice.getAddress());
-            sm.onActiveDeviceChanged(mParams.messageDevice,
-                    mParams.hearingAidBtDevices.contains(mParams.messageDevice));
+            sm.onActiveDeviceChanged(mParams.messageDevice, deviceType);
         } else if (mParams.messageType == BluetoothRouteManager.LOST_DEVICE) {
-            sm.onActiveDeviceChanged(null,
-                    mParams.hearingAidBtDevices.contains(mParams.messageDevice));
+            sm.onActiveDeviceChanged(null, deviceType);
             if (mParams.hearingAidBtDevices.contains(mParams.messageDevice)) {
                 when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEARING_AID)))
                     .thenReturn(Arrays.asList(null, null));
+                when(mBluetoothLeAudio.getActiveDevices())
+                    .thenReturn(mParams.leAudioDevices.stream()
+                       .filter(device -> device != mParams.messageDevice)
+                       .collect(Collectors.toList()));
             } else {
                 when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET)))
                     .thenReturn(Arrays.asList((BluetoothDevice) null));
@@ -368,6 +391,7 @@
         resetMocks();
         when(mDeviceManager.getBluetoothHeadset()).thenReturn(mBluetoothHeadset);
         when(mDeviceManager.getBluetoothHearingAid()).thenReturn(mBluetoothHearingAid);
+        when(mDeviceManager.getLeAudioService()).thenReturn(mBluetoothLeAudio);
         when(mDeviceManager.connectAudio(nullable(String.class))).thenReturn(true);
         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
                 nullable(ContentResolver.class))).thenReturn(100000L);
@@ -660,6 +684,36 @@
                         + ":" + DEVICE2)
                 .build());
 
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("le audio device disconnects with hearing aid present")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2, DEVICE3)
+                .setLeAudioDevices(Collections.singletonList(DEVICE2))
+                .setHearingAidBtDevices(Collections.singletonList(DEVICE3))
+                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
+                .setMessageDevice(DEVICE2)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED,
+                        ListenerUpdate.DEVICE_LIST_CHANGED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("le audio device disconnects with another one connected")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE1)
+                .setConnectedDevices(DEVICE1, DEVICE2, DEVICE3)
+                .setHearingAidBtDevices(Collections.singletonList(DEVICE3))
+                .setLeAudioDevices(Arrays.asList(DEVICE1, DEVICE2))
+                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
+                .setMessageDevice(DEVICE1)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED,
+                        ListenerUpdate.DEVICE_LIST_CHANGED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
         return result;
     }
 }
diff --git a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
index 845a838..1a649dc 100644
--- a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
@@ -41,6 +41,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -546,6 +547,26 @@
         verify(mMockCreateConnectionResponse).handleCreateConnectionSuccess(mockCallIdMapper, null);
     }
 
+    /**
+     * Ensures that a self-managed phone account won't be considered when attempting to place an
+     * emergency call.
+     */
+    @SmallTest
+    @Test
+    public void testDontAttemptSelfManaged() {
+        when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(false);
+        when(mMockCall.getHandle()).thenReturn(Uri.parse(""));
+
+        PhoneAccount selfManagedAcct = makePhoneAccount("sm-acct",
+                PhoneAccount.CAPABILITY_SELF_MANAGED
+                        | PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS);
+        phoneAccounts.add(selfManagedAcct);
+
+        mTestCreateConnectionProcessor.process();
+        verify(mMockCall, never()).setTargetPhoneAccount(any(PhoneAccountHandle.class));
+    }
+
     @SmallTest
     @Test
     public void testEmergencyCallSimFailToConnectionManager() throws Exception {