Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d765c99e authored by Sungsoo Lim's avatar Sungsoo Lim Committed by Gerrit Code Review
Browse files

Merge changes I1eee9d44,I60a21b60 into main

* changes:
  Refactoring: Audio Routing Handler (Step 4)
  Refactoring: Audio Routing Handler (Step 3)
parents ca016322 83980e79
Loading
Loading
Loading
Loading
+34 −198
Original line number Diff line number Diff line
@@ -50,7 +50,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -83,12 +82,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
    @GuardedBy("mLock")
    private final List<BluetoothDevice> mLeAudioConnectedDevices = new ArrayList<>();

    @GuardedBy("mLock")
    private final List<BluetoothDevice> mLeHearingAidConnectedDevices = new ArrayList<>();

    @GuardedBy("mLock")
    private List<BluetoothDevice> mPendingLeHearingAidActiveDevice = new ArrayList<>();

    @GuardedBy("mLock")
    private BluetoothDevice mA2dpActiveDevice = null;

@@ -101,9 +94,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
    @GuardedBy("mLock")
    private BluetoothDevice mLeAudioActiveDevice = null;

    @GuardedBy("mLock")
    private BluetoothDevice mLeHearingAidActiveDevice = null;

    @GuardedBy("mLock")
    private BluetoothDevice mPendingActiveDevice = null;

@@ -127,57 +117,9 @@ public class AudioRoutingManager extends ActiveDeviceManager {
    public void profileConnectionStateChanged(
            int profile, BluetoothDevice device, int fromState, int toState) {
        if (toState == BluetoothProfile.STATE_CONNECTED) {
            switch (profile) {
                case BluetoothProfile.A2DP:
                case BluetoothProfile.HEADSET:
                case BluetoothProfile.LE_AUDIO:
            mHandler.post(() -> mHandler.handleDeviceConnected(device, profile));
                    break;
                case BluetoothProfile.HEARING_AID:
                    mHandler.post(
                            () -> {
                                AudioRoutingHandler.AudioRoutingDevice arDevice =
                                        mHandler.getAudioRoutingDevice(device);
                                arDevice.connectedProfiles.add(profile);
                                handleHearingAidConnected(device);
                            });
                    break;
                case BluetoothProfile.HAP_CLIENT:
                    mHandler.post(
                            () -> {
                                AudioRoutingHandler.AudioRoutingDevice arDevice =
                                        mHandler.getAudioRoutingDevice(device);
                                arDevice.connectedProfiles.add(profile);
                                handleHapConnected(device);
                            });
                    break;
            }
        } else if (fromState == BluetoothProfile.STATE_CONNECTED) {
            switch (profile) {
                case BluetoothProfile.A2DP:
                case BluetoothProfile.HEADSET:
                case BluetoothProfile.LE_AUDIO:
            mHandler.post(() -> mHandler.handleDeviceDisconnected(device, profile));
                    break;
                case BluetoothProfile.HEARING_AID:
                    mHandler.post(
                            () -> {
                                AudioRoutingHandler.AudioRoutingDevice arDevice =
                                        mHandler.getAudioRoutingDevice(device);
                                arDevice.connectedProfiles.remove(profile);
                                handleHearingAidDisconnected(device);
                            });
                    break;
                case BluetoothProfile.HAP_CLIENT:
                    mHandler.post(
                            () -> {
                                AudioRoutingHandler.AudioRoutingDevice arDevice =
                                        mHandler.getAudioRoutingDevice(device);
                                arDevice.connectedProfiles.remove(profile);
                                handleHapDisconnected(device);
                            });
                    break;
            }
        }
    }

@@ -194,7 +136,9 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                mHandler.post(
                        () -> {
                            if (device != null) {
                                mHandler.mActiveDevices.put(profile, Arrays.asList(device));
                                ArrayList<BluetoothDevice> devices = new ArrayList<>();
                                devices.add(device);
                                mHandler.mActiveDevices.put(profile, devices);
                            } else {
                                mHandler.mActiveDevices.remove(profile);
                            }
@@ -205,7 +149,9 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                mHandler.post(
                        () -> {
                            if (device != null) {
                                mHandler.mActiveDevices.put(profile, Arrays.asList(device));
                                ArrayList<BluetoothDevice> devices = new ArrayList<>();
                                devices.add(device);
                                mHandler.mActiveDevices.put(profile, devices);
                            } else {
                                mHandler.mActiveDevices.remove(profile);
                            }
@@ -216,7 +162,9 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                mHandler.post(
                        () -> {
                            if (device != null) {
                                mHandler.mActiveDevices.put(profile, Arrays.asList(device));
                                ArrayList<BluetoothDevice> devices = new ArrayList<>();
                                devices.add(device);
                                mHandler.mActiveDevices.put(profile, devices);
                            } else {
                                mHandler.mActiveDevices.remove(profile);
                            }
@@ -227,7 +175,9 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                mHandler.post(
                        () -> {
                            if (device != null) {
                                mHandler.mActiveDevices.put(profile, Arrays.asList(device));
                                ArrayList<BluetoothDevice> devices = new ArrayList<>();
                                devices.add(device);
                                mHandler.mActiveDevices.put(profile, devices);
                            } else {
                                mHandler.mActiveDevices.remove(profile);
                            }
@@ -246,91 +196,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
        }
    }

    private void handleHearingAidConnected(BluetoothDevice device) {
        synchronized (mLock) {
            if (DBG) {
                Log.d(TAG, "handleHearingAidConnected: " + device);
            }
            if (mHearingAidConnectedDevices.contains(device)) {
                if (DBG) {
                    Log.d(TAG, "This device is already connected: " + device);
                }
                return;
            }
            mHearingAidConnectedDevices.add(device);
        }
        // New connected device: select it as active
        if (setHearingAidActiveDevice(device)) {
            setA2dpActiveDevice(null, true);
            setHfpActiveDevice(null);
            setLeAudioActiveDevice(null, true);
        }
    }

    private void handleHapConnected(BluetoothDevice device) {
        synchronized (mLock) {
            if (DBG) {
                Log.d(TAG, "handleHapConnected: " + device);
            }
            if (mLeHearingAidConnectedDevices.contains(device)) {
                if (DBG) {
                    Log.d(TAG, "This device is already connected: " + device);
                }
                return;
            }
            mLeHearingAidConnectedDevices.add(device);
            if (!mLeAudioConnectedDevices.contains(device)) {
                mPendingLeHearingAidActiveDevice.add(device);
            } else if (Objects.equals(mLeAudioActiveDevice, device)) {
                mLeHearingAidActiveDevice = device;
            } else {
                // New connected device: select it as active
                if (setLeHearingAidActiveDevice(device)) {
                    setHearingAidActiveDevice(null, true);
                    setA2dpActiveDevice(null, true);
                    setHfpActiveDevice(null);
                }
            }
        }
    }

    private void handleHearingAidDisconnected(BluetoothDevice device) {
        synchronized (mLock) {
            if (DBG) {
                Log.d(
                        TAG,
                        "handleHearingAidDisconnected: "
                                + device
                                + ", mHearingAidActiveDevices="
                                + mHearingAidActiveDevices);
            }
            mHearingAidConnectedDevices.remove(device);
            if (mHearingAidActiveDevices.remove(device) && mHearingAidActiveDevices.isEmpty()) {
                if (!setFallbackDeviceActiveLocked()) {
                    setHearingAidActiveDevice(null, false);
                }
            }
        }
    }

    private void handleHapDisconnected(BluetoothDevice device) {
        synchronized (mLock) {
            if (DBG) {
                Log.d(
                        TAG,
                        "handleHapDisconnected: "
                                + device
                                + ", mLeHearingAidActiveDevice="
                                + mLeHearingAidActiveDevice);
            }
            mLeHearingAidConnectedDevices.remove(device);
            mPendingLeHearingAidActiveDevice.remove(device);
            if (Objects.equals(mLeHearingAidActiveDevice, device)) {
                mLeHearingAidActiveDevice = null;
            }
        }
    }

    /**
     * Handles the active device logic for when the A2DP active device changes. Does the following:
     * 1. Clear the active hearing aid. 2. If dual mode is enabled and all supported classic audio
@@ -521,10 +386,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                setHearingAidActiveDevice(null, true);
            }

            if (mLeHearingAidConnectedDevices.contains(device)) {
                mLeHearingAidActiveDevice = device;
            }

            mLeAudioActiveDevice = device;
        }
    }
@@ -740,7 +601,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
            mHearingAidActiveDevices.clear();
            mHearingAidActiveDevices.addAll(hearingAidService.getConnectedPeerDevices(hiSyncId));
        }
        mHandler.mActiveDevices.put(BluetoothProfile.HEARING_AID, Arrays.asList(device));
        return true;
    }

@@ -775,30 +635,9 @@ public class AudioRoutingManager extends ActiveDeviceManager {

        synchronized (mLock) {
            mLeAudioActiveDevice = device;
            if (device == null) {
                mLeHearingAidActiveDevice = null;
                mPendingLeHearingAidActiveDevice.remove(device);
            }
        }
        return true;
    }

    private boolean setLeHearingAidActiveDevice(BluetoothDevice device) {
        synchronized (mLock) {
            if (!Objects.equals(mLeAudioActiveDevice, device)) {
                if (!setLeAudioActiveDevice(device)) {
                    return false;
                }
        }
            if (Objects.equals(mLeAudioActiveDevice, device)) {
                // setLeAudioActiveDevice succeed
                mLeHearingAidActiveDevice = device;
                mPendingLeHearingAidActiveDevice.remove(device);
        return true;
    }
        }
        return false;
    }

    /**
     * TODO: This method can return true when a fallback device for an unrelated profile is found.
@@ -817,9 +656,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
        if (!mHearingAidConnectedDevices.isEmpty()) {
            connectedHearingAidDevices.addAll(mHearingAidConnectedDevices);
        }
        if (!mLeHearingAidConnectedDevices.isEmpty()) {
            connectedHearingAidDevices.addAll(mLeHearingAidConnectedDevices);
        }
        if (!connectedHearingAidDevices.isEmpty()) {
            BluetoothDevice device =
                    mDbManager.getMostRecentlyConnectedDevicesInList(connectedHearingAidDevices);
@@ -836,7 +672,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                    if (DBG) {
                        Log.d(TAG, "Found a LE hearing aid fallback device: " + device);
                    }
                    setLeHearingAidActiveDevice(device);
                    setHearingAidActiveDevice(null, true);
                    setA2dpActiveDevice(null, true);
                    setHfpActiveDevice(null);
@@ -945,10 +780,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {

            mLeAudioConnectedDevices.clear();
            mLeAudioActiveDevice = null;

            mLeHearingAidConnectedDevices.clear();
            mLeHearingAidActiveDevice = null;
            mPendingLeHearingAidActiveDevice.clear();
        }
    }

@@ -1094,6 +925,7 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                    case BluetoothProfile.HEADSET -> mHfpConnectedDevices.add(device);
                    case BluetoothProfile.A2DP -> mA2dpConnectedDevices.add(device);
                    case BluetoothProfile.LE_AUDIO -> mLeAudioConnectedDevices.add(device);
                    case BluetoothProfile.HEARING_AID -> mHearingAidConnectedDevices.add(device);
                }
            }
            if (isWatch(device)) {
@@ -1132,6 +964,7 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                    case BluetoothProfile.HEADSET -> mHfpConnectedDevices.remove(device);
                    case BluetoothProfile.A2DP -> mA2dpConnectedDevices.remove(device);
                    case BluetoothProfile.LE_AUDIO -> mLeAudioConnectedDevices.remove(device);
                    case BluetoothProfile.HEARING_AID -> mHearingAidConnectedDevices.remove(device);
                }
            }
            List<BluetoothDevice> activeDevices = mActiveDevices.get(profile);
@@ -1177,12 +1010,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
            } else {
                arDevice.supportedProfiles.remove(BluetoothProfile.HEARING_AID);
            }
            if (mDbManager.getProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT)
                    == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
                arDevice.supportedProfiles.add(BluetoothProfile.HAP_CLIENT);
            } else {
                arDevice.supportedProfiles.remove(BluetoothProfile.HAP_CLIENT);
            }
            if (mDbManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO)
                    == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
                arDevice.supportedProfiles.add(BluetoothProfile.LE_AUDIO);
@@ -1201,7 +1028,7 @@ public class AudioRoutingManager extends ActiveDeviceManager {
            public boolean canActivateNow(int profile) {
                if (!connectedProfiles.contains(profile)) return false;
                // TODO: Return false if there are another active remote streaming an audio.
                // TODO: consider LE audio and HearingAid, HapClient.
                // TODO: consider LE audio and HearingAid.
                return switch (profile) {
                    case BluetoothProfile.HEADSET -> !supportedProfiles.contains(
                                    BluetoothProfile.A2DP)
@@ -1290,8 +1117,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                        case BluetoothProfile.LE_AUDIO -> setLeAudioActiveDevice(device);
                        case BluetoothProfile.HEARING_AID -> setHearingAidActiveDevice(
                                device);
                        case BluetoothProfile.HAP_CLIENT -> setLeHearingAidActiveDevice(
                                device);
                        default -> false;
                    };
                    if (activated) {
@@ -1317,7 +1142,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                        case BluetoothProfile.HEADSET -> setHfpActiveDevice(null);
                        case BluetoothProfile.LE_AUDIO -> setLeAudioActiveDevice(null, true);
                        case BluetoothProfile.HEARING_AID -> setHearingAidActiveDevice(null, true);
                        case BluetoothProfile.HAP_CLIENT -> setLeHearingAidActiveDevice(null);
                    }
                }
                return true;
@@ -1331,21 +1155,19 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                    case BluetoothProfile.HEADSET -> setHfpActiveDevice(null);
                    case BluetoothProfile.LE_AUDIO -> setLeAudioActiveDevice(null, false);
                    case BluetoothProfile.HEARING_AID -> setHearingAidActiveDevice(null, false);
                    case BluetoothProfile.HAP_CLIENT -> setLeHearingAidActiveDevice(null);
                }
                mActiveDevices.remove(profile);
            }

            private boolean canActivateTogether(
                    int profile, BluetoothDevice device, List<BluetoothDevice> group) {
                if (group == null || group.isEmpty()) {
                if (device == null || group == null || group.isEmpty()) {
                    return false;
                }
                switch (profile) {
                    // TODO: handle HAP_CLIENT and HEARING_AID
                    case BluetoothProfile.LE_AUDIO:
                    case BluetoothProfile.LE_AUDIO: {
                        final LeAudioService leAudioService = mFactory.getLeAudioService();
                        if (leAudioService == null || device == null) {
                        if (leAudioService == null) {
                            return false;
                        }
                        int groupId = leAudioService.getGroupId(device);
@@ -1353,6 +1175,20 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                                && groupId == leAudioService.getGroupId(group.get(0))) {
                            return true;
                        }
                        break;
                    }
                    case BluetoothProfile.HEARING_AID: {
                        final HearingAidService hearingAidService = mFactory.getHearingAidService();
                        if (hearingAidService == null) {
                            return false;
                        }
                        long hiSyncId = hearingAidService.getHiSyncId(device);
                        if (hiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID
                                && hiSyncId == hearingAidService.getHiSyncId(group.get(0))) {
                            return true;
                        }
                        break;
                    }
                }
                return false;
            }
+13 −4
Original line number Diff line number Diff line
@@ -51,6 +51,8 @@ import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.bluetooth.flags.FeatureFlags;
import com.android.bluetooth.flags.FeatureFlagsImpl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.SynchronousResultReceiver;

@@ -88,6 +90,7 @@ public class HapClientService extends ProfileService {
    private final Map<BluetoothDevice, Integer> mDeviceFeaturesMap = new HashMap<>();
    private final Map<BluetoothDevice, List<BluetoothHapPresetInfo>> mPresetsMap =
            new HashMap<>();
    private final FeatureFlags mFeatureFlags;

    @VisibleForTesting
    RemoteCallbackList<IBluetoothHapClientCallback> mCallbacks;
@@ -124,6 +127,10 @@ public class HapClientService extends ProfileService {
        return sHapClient;
    }

    protected HapClientService() {
        mFeatureFlags = new FeatureFlagsImpl();
    }

    @Override
    protected void create() {
        if (DBG) {
@@ -445,12 +452,14 @@ public class HapClientService extends ProfileService {
                removeStateMachine(device);
            }
        }
        if (!mFeatureFlags.audioRoutingCentralization()) {
            ActiveDeviceManager adManager = mAdapterService.getActiveDeviceManager();
            if (adManager != null) {
                adManager.profileConnectionStateChanged(
                        BluetoothProfile.HAP_CLIENT, device, fromState, toState);
            }
        }
    }

    /**
     * Connects the hearing access service client to the passed in device
+9 −8
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ public class AudioRoutingManagerTest {
    private ArrayList<BluetoothDevice> mDeviceConnectionStack;
    private BluetoothDevice mMostRecentDevice;
    private AudioRoutingManager mAudioRoutingManager;
    private long mHearingAidHiSyncId = 1010;
    private static final long HEARING_AID_HISYNC_ID = 1010;

    private static final int TIMEOUT_MS = 1_000;
    private static final int A2DP_HFP_SYNC_CONNECTION_TIMEOUT_MS =
@@ -156,8 +156,8 @@ public class AudioRoutingManagerTest {

        List<BluetoothDevice> connectedHearingAidDevices = new ArrayList<>();
        connectedHearingAidDevices.add(mHearingAidDevice);
        when(mHearingAidService.getHiSyncId(mHearingAidDevice)).thenReturn(mHearingAidHiSyncId);
        when(mHearingAidService.getConnectedPeerDevices(mHearingAidHiSyncId))
        when(mHearingAidService.getHiSyncId(mHearingAidDevice)).thenReturn(HEARING_AID_HISYNC_ID);
        when(mHearingAidService.getConnectedPeerDevices(HEARING_AID_HISYNC_ID))
                .thenReturn(connectedHearingAidDevices);
    }

@@ -557,8 +557,9 @@ public class AudioRoutingManagerTest {
        List<BluetoothDevice> connectedHearingAidDevices = new ArrayList<>();
        connectedHearingAidDevices.add(mHearingAidDevice);
        connectedHearingAidDevices.add(mSecondaryAudioDevice);
        when(mHearingAidService.getHiSyncId(mSecondaryAudioDevice)).thenReturn(mHearingAidHiSyncId);
        when(mHearingAidService.getConnectedPeerDevices(mHearingAidHiSyncId))
        when(mHearingAidService.getHiSyncId(mSecondaryAudioDevice))
                .thenReturn(HEARING_AID_HISYNC_ID);
        when(mHearingAidService.getConnectedPeerDevices(HEARING_AID_HISYNC_ID))
                .thenReturn(connectedHearingAidDevices);

        hearingAidConnected(mHearingAidDevice);
@@ -867,8 +868,8 @@ public class AudioRoutingManagerTest {
        List<BluetoothDevice> connectedHearingAidDevices = new ArrayList<>();
        connectedHearingAidDevices.add(mSecondaryAudioDevice);
        when(mHearingAidService.getHiSyncId(mSecondaryAudioDevice))
                .thenReturn(mHearingAidHiSyncId + 1);
        when(mHearingAidService.getConnectedPeerDevices(mHearingAidHiSyncId + 1))
                .thenReturn(HEARING_AID_HISYNC_ID + 1);
        when(mHearingAidService.getConnectedPeerDevices(HEARING_AID_HISYNC_ID + 1))
                .thenReturn(connectedHearingAidDevices);

        hearingAidConnected(mSecondaryAudioDevice);
@@ -1049,7 +1050,7 @@ public class AudioRoutingManagerTest {
        leAudioConnected(mLeHearingAidDevice);
        leHearingAidConnected(mLeHearingAidDevice);
        TestUtils.waitForLooperToFinishScheduledTask(mAudioRoutingManager.getHandlerLooper());
        verify(mLeAudioService, times(2)).setActiveDevice(mLeHearingAidDevice);
        verify(mLeAudioService).setActiveDevice(mLeHearingAidDevice);
        verify(mA2dpService).removeActiveDevice(anyBoolean());
        verify(mHeadsetService).setActiveDevice(null);
        verify(mHearingAidService).removeActiveDevice(anyBoolean());