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

Commit 83980e79 authored by Sungsoo Lim's avatar Sungsoo Lim
Browse files

Refactoring: Audio Routing Handler (Step 4)

This CL refactors connection and activation events to be handled in
the AudioRoutingHandler. This will remove the mLock and migrate the
variables guarded by the mLock into the handler.

It will also remove the priority of hearing aid devices so that they
are activated at the same level as other BT devices.

This CL refactors the connection events of LE hearing aid, and more CLs
will follow.

Bug: 299023147
Test: atest BluetoothInstrumentationTests
Change-Id: I1eee9d44d42a927480c55f9cda94cb4c31ecee3f
parent e78816e3
Loading
Loading
Loading
Loading
+3 −134
Original line number Diff line number Diff line
@@ -82,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;

@@ -100,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;

@@ -126,42 +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:
                case BluetoothProfile.HEARING_AID:
            mHandler.post(() -> mHandler.handleDeviceConnected(device, profile));
                    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:
                case BluetoothProfile.HEARING_AID:
            mHandler.post(() -> mHandler.handleDeviceDisconnected(device, profile));
                    break;
                case BluetoothProfile.HAP_CLIENT:
                    mHandler.post(
                            () -> {
                                AudioRoutingHandler.AudioRoutingDevice arDevice =
                                        mHandler.getAudioRoutingDevice(device);
                                arDevice.connectedProfiles.remove(profile);
                                handleHapDisconnected(device);
                            });
                    break;
            }
        }
    }

@@ -238,51 +196,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
        }
    }

    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 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
@@ -473,10 +386,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                setHearingAidActiveDevice(null, true);
            }

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

            mLeAudioActiveDevice = device;
        }
    }
@@ -726,31 +635,10 @@ 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.
     * Take disconnected profile as an argument, and find the exact fallback device. Also, split
@@ -768,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);
@@ -787,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);
@@ -896,10 +780,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {

            mLeAudioConnectedDevices.clear();
            mLeAudioActiveDevice = null;

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

@@ -1130,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);
@@ -1154,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)
@@ -1243,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) {
@@ -1270,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;
@@ -1284,7 +1155,6 @@ 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);
            }
@@ -1295,7 +1165,6 @@ public class AudioRoutingManager extends ActiveDeviceManager {
                    return false;
                }
                switch (profile) {
                    // TODO: handle HAP_CLIENT
                    case BluetoothProfile.LE_AUDIO: {
                        final LeAudioService leAudioService = mFactory.getLeAudioService();
                        if (leAudioService == null) {
+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
+1 −1
Original line number Diff line number Diff line
@@ -1050,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());