Snap for 11967491 from 413076efd674d5c558cd754253082fb0a78e8b5c to 24Q3-release

Change-Id: I0b563b9f97044be9c6d4366199dec232c9a48bcd
diff --git a/src/com/android/server/telecom/CallAudioRouteController.java b/src/com/android/server/telecom/CallAudioRouteController.java
index 820219d..76555c3 100644
--- a/src/com/android/server/telecom/CallAudioRouteController.java
+++ b/src/com/android/server/telecom/CallAudioRouteController.java
@@ -53,6 +53,7 @@
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.flags.FeatureFlags;
 
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -76,6 +77,9 @@
         ROUTE_MAP.put(AudioRoute.TYPE_STREAMING, CallAudioState.ROUTE_STREAMING);
     }
 
+    /** Valid values for the first argument for SWITCH_BASELINE_ROUTE */
+    public static final int INCLUDE_BLUETOOTH_IN_BASELINE = 1;
+
     private final CallsManager mCallsManager;
     private final Context mContext;
     private AudioManager mAudioManager;
@@ -91,6 +95,8 @@
     private AudioRoute mStreamingRoute;
     private Set<AudioRoute> mStreamingRoutes;
     private Map<AudioRoute, BluetoothDevice> mBluetoothRoutes;
+    private Pair<Integer, String> mActiveBluetoothDevice;
+    private Map<Integer, String> mActiveDeviceCache;
     private Map<Integer, AudioRoute> mTypeRoutes;
     private PendingAudioRoute mPendingAudioRoute;
     private AudioRoute.Factory mAudioRouteFactory;
@@ -260,9 +266,13 @@
                             handleSwitchSpeaker();
                             break;
                         case SWITCH_BASELINE_ROUTE:
-                        case USER_SWITCH_BASELINE_ROUTE:
                             address = (String) ((SomeArgs) msg.obj).arg2;
-                            handleSwitchBaselineRoute(address);
+                            handleSwitchBaselineRoute(msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE,
+                                    address);
+                            break;
+                        case USER_SWITCH_BASELINE_ROUTE:
+                            handleSwitchBaselineRoute(msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE,
+                                    null);
                             break;
                         case SPEAKER_ON:
                             handleSpeakerOn();
@@ -312,6 +322,11 @@
     public void initialize() {
         mAvailableRoutes = new HashSet<>();
         mBluetoothRoutes = new LinkedHashMap<>();
+        mActiveDeviceCache = new HashMap<>();
+        mActiveDeviceCache.put(AudioRoute.TYPE_BLUETOOTH_SCO, null);
+        mActiveDeviceCache.put(AudioRoute.TYPE_BLUETOOTH_HA, null);
+        mActiveDeviceCache.put(AudioRoute.TYPE_BLUETOOTH_LE, null);
+        mActiveBluetoothDevice = null;
         mTypeRoutes = new ArrayMap<>();
         mStreamingRoutes = new HashSet<>();
         mPendingAudioRoute = new PendingAudioRoute(this, mAudioManager, mBluetoothRouteManager);
@@ -805,11 +820,16 @@
         Log.i(this, "handle switch to bluetooth with address %s", address);
         AudioRoute bluetoothRoute = null;
         BluetoothDevice bluetoothDevice = null;
-        for (AudioRoute route : getAvailableRoutes()) {
-            if (Objects.equals(address, route.getBluetoothAddress())) {
-                bluetoothRoute = route;
-                bluetoothDevice = mBluetoothRoutes.get(route);
-                break;
+        if (address == null) {
+            bluetoothRoute = getArbitraryBluetoothDevice();
+            bluetoothDevice = mBluetoothRoutes.get(bluetoothRoute);
+        } else {
+            for (AudioRoute route : getAvailableRoutes()) {
+                if (Objects.equals(address, route.getBluetoothAddress())) {
+                    bluetoothRoute = route;
+                    bluetoothDevice = mBluetoothRoutes.get(route);
+                    break;
+                }
             }
         }
 
@@ -825,6 +845,20 @@
         }
     }
 
+    /**
+     * Retrieve the active BT device, if available, otherwise return the most recently tracked
+     * active device, or null if none are available.
+     * @return {@link AudioRoute} of the BT device.
+     */
+    private AudioRoute getArbitraryBluetoothDevice() {
+        if (mActiveBluetoothDevice != null) {
+            return getBluetoothRoute(mActiveBluetoothDevice.first, mActiveBluetoothDevice.second);
+        } else if (!mBluetoothRoutes.isEmpty()) {
+            return mBluetoothRoutes.keySet().stream().toList().get(mBluetoothRoutes.size() - 1);
+        }
+        return null;
+    }
+
     private void handleSwitchHeadset() {
         AudioRoute headsetRoute = mTypeRoutes.get(AudioRoute.TYPE_WIRED);
         if (headsetRoute != null && getAvailableRoutes().contains(headsetRoute)) {
@@ -842,8 +876,8 @@
         }
     }
 
-    private void handleSwitchBaselineRoute(String btAddressToExclude) {
-        routeTo(mIsActive, getBaseRoute(true, btAddressToExclude));
+    private void handleSwitchBaselineRoute(boolean includeBluetooth, String btAddressToExclude) {
+        routeTo(mIsActive, getBaseRoute(includeBluetooth, btAddressToExclude));
     }
 
     private void handleSpeakerOn() {
@@ -1085,7 +1119,7 @@
 
     public AudioRoute getBaseRoute(boolean includeBluetooth, String btAddressToExclude) {
         AudioRoute destRoute = getPreferredAudioRouteFromStrategy();
-        if (destRoute == null) {
+        if (destRoute == null || (destRoute.getBluetoothAddress() != null && !includeBluetooth)) {
             destRoute = getPreferredAudioRouteFromDefault(includeBluetooth, btAddressToExclude);
         }
         if (destRoute != null && !getAvailableRoutes().contains(destRoute)) {
@@ -1243,6 +1277,39 @@
         mIsScoAudioConnected = value;
     }
 
+    /**
+     * Update the active bluetooth device being tracked (as well as for individual profiles).
+     * We need to keep track of active devices for individual profiles because of potential
+     * inconsistencies found in BluetoothStateReceiver#handleActiveDeviceChanged. When multiple
+     * profiles are paired, we could have a scenario where an active device A is replaced
+     * with an active device B (from a different profile), which is then removed as an active
+     * device shortly after, causing device A to be reactive. It's possible that the active device
+     * changed intent is never received again for device A so an active device cache is necessary
+     * to track these devices at a profile level.
+     * @param device {@link Pair} containing the BT audio route type (i.e. SCO/HA/LE) and the
+     *                           address of the device.
+     */
+    public void updateActiveBluetoothDevice(Pair<Integer, String> device) {
+        mActiveDeviceCache.put(device.first, device.second);
+        // Update most recently active device if address isn't null (meaning some device is active).
+        if (device.second != null) {
+            mActiveBluetoothDevice = device;
+        } else {
+            // If a device was removed, check to ensure that no other device is still considered
+            // active.
+            boolean hasActiveDevice = false;
+            for (String address : mActiveDeviceCache.values()) {
+                if (address != null) {
+                    hasActiveDevice = true;
+                    break;
+                }
+            }
+            if (!hasActiveDevice) {
+                mActiveBluetoothDevice = null;
+            }
+        }
+    }
+
     @VisibleForTesting
     public void setActive(boolean active) {
         if (active) {
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 79ce5a3..c3c0c1c 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -167,10 +167,17 @@
                 ParcelableConference conference, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE,
                     mPackageAbbreviation);
+            UserHandle callingUserHandle = Binder.getCallingUserHandle();
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("handleCreateConferenceComplete %s", callId);
+                    // Check status hints image for cross user access
+                    if (conference.getStatusHints() != null) {
+                        Icon icon = conference.getStatusHints().getIcon();
+                        conference.getStatusHints().setIcon(StatusHints.
+                                validateAccountIconUserBoundary(icon, callingUserHandle));
+                    }
                     Call call = mCallIdMapper.getCall(callId);
                     if (mScheduledFutureMap.containsKey(call)) {
                         ScheduledFuture<?> existingTimeout = mScheduledFutureMap.get(call);
diff --git a/src/com/android/server/telecom/PendingAudioRoute.java b/src/com/android/server/telecom/PendingAudioRoute.java
index f9cdc35..396aca0 100644
--- a/src/com/android/server/telecom/PendingAudioRoute.java
+++ b/src/com/android/server/telecom/PendingAudioRoute.java
@@ -18,6 +18,7 @@
 
 import static com.android.server.telecom.CallAudioRouteAdapter.PENDING_ROUTE_FAILED;
 import static com.android.server.telecom.CallAudioRouteAdapter.SWITCH_BASELINE_ROUTE;
+import static com.android.server.telecom.CallAudioRouteController.INCLUDE_BLUETOOTH_IN_BASELINE;
 
 import android.bluetooth.BluetoothDevice;
 import android.media.AudioManager;
@@ -96,7 +97,7 @@
         if (message.first == PENDING_ROUTE_FAILED) {
             // Fallback to base route
             mCallAudioRouteController.sendMessageWithSessionInfo(
-                    SWITCH_BASELINE_ROUTE, 0, btAddressToExclude);
+                    SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE, btAddressToExclude);
             return;
         }
 
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 0a8ce5a..3c97d41 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -20,6 +20,7 @@
 import static com.android.server.telecom.AudioRoute.TYPE_BLUETOOTH_SCO;
 import static com.android.server.telecom.CallAudioRouteAdapter.BT_DEVICE_REMOVED;
 import static com.android.server.telecom.CallAudioRouteAdapter.SWITCH_BASELINE_ROUTE;
+import static com.android.server.telecom.CallAudioRouteController.INCLUDE_BLUETOOTH_IN_BASELINE;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
@@ -257,7 +258,7 @@
                     BT_DEVICE_REMOVED, route.getType(), device);
         }
         mCallAudioRouteAdapter.sendMessageWithSessionInfo(
-                SWITCH_BASELINE_ROUTE, 0, (String) null);
+                SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE, (String) null);
     }
 
     private final LinkedHashMap<String, BluetoothDevice> mHfpDevicesByAddress =
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 9168388..f76391c 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -236,10 +236,15 @@
                 BluetoothDeviceManager.getDeviceTypeString(deviceType));
 
         if (Flags.useRefactoredAudioRouteSwitching()) {
+            CallAudioRouteController audioRouteController = (CallAudioRouteController)
+                    mCallAudioRouteAdapter;
             if (device == null) {
+                audioRouteController.updateActiveBluetoothDevice(new Pair(audioRouteType, null));
                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_GONE,
                         audioRouteType);
             } else {
+                audioRouteController.updateActiveBluetoothDevice(
+                        new Pair(audioRouteType, device.getAddress()));
                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_PRESENT,
                         audioRouteType, device.getAddress());
                 if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID