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

Commit 8df39a27 authored by Yuyang Huang's avatar Yuyang Huang Committed by Gerrit Code Review
Browse files

Merge "Resume LE Audio active device when HFP handover is occured." into main

parents 2941ad79 6f6d95a4
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -634,8 +634,15 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
                        && mAdapterService.isAllSupportedClassicAudioProfilesActive(device)) {
                    setLeAudioActiveDevice(device);
                } else {
                    if (Flags.leaudioResumeActiveAfterHfpHandover()) {
                        if (device != null) {
                            // remove LE audio active device when it is not null, and not dual mode
                            setLeAudioActiveDevice(null, true);
                        }
                    } else {
                        setLeAudioActiveDevice(null, true);
                    }
                }
            }
            // Just assign locally the new value
            mHfpActiveDevice = device;
+11 −1
Original line number Diff line number Diff line
@@ -158,7 +158,7 @@ public class HeadsetService extends ProfileService {
    @VisibleForTesting boolean mIsAptXSwbEnabled = false;
    @VisibleForTesting boolean mIsAptXSwbPmEnabled = false;

    private final ServiceFactory mFactory = new ServiceFactory();
    @VisibleForTesting ServiceFactory mFactory = new ServiceFactory();

    public HeadsetService(Context ctx) {
        super(ctx);
@@ -2143,6 +2143,16 @@ public class HeadsetService extends ProfileService {
                    mSystemInterface.getAudioManager().setA2dpSuspended(false);
                    if (isAtLeastU()) {
                        mSystemInterface.getAudioManager().setLeAudioSuspended(false);

                        // Resumes LE audio previous active device if HFP handover happened before.
                        // Do it here because some controllers cannot handle SCO and CIS
                        // co-existence see {@link LeAudioService#setInactiveForHfpHandover}
                        if (Flags.leaudioResumeActiveAfterHfpHandover()) {
                            LeAudioService leAudioService = mFactory.getLeAudioService();
                            if (leAudioService != null) {
                                leAudioService.setActiveAfterHfpHandover();
                            }
                        }
                    }
                }
            }
+42 −3
Original line number Diff line number Diff line
@@ -180,7 +180,16 @@ public class LeAudioService extends ProfileService {
    boolean mLeAudioNativeIsInitialized = false;
    boolean mLeAudioInbandRingtoneSupportedByPlatform = true;
    boolean mBluetoothEnabled = false;

    /**
     * During a call that has LE Audio -> HFP handover, the HFP device that is going to connect SCO
     * after LE Audio group becomes idle
     */
    BluetoothDevice mHfpHandoverDevice = null;

    /** LE audio active device that was removed from active because of HFP handover */
    BluetoothDevice mLeAudioDeviceInactivatedForHfpHandover = null;

    LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null;
    private DialingOutTimeoutEvent mDialingOutTimeoutEvent = null;
    @VisibleForTesting
@@ -554,6 +563,7 @@ public class LeAudioService extends ProfileService {
        mLeAudioNativeIsInitialized = false;
        mBluetoothEnabled = false;
        mHfpHandoverDevice = null;
        mLeAudioDeviceInactivatedForHfpHandover = null;

        mActiveAudioOutDevice = null;
        mActiveAudioInDevice = null;
@@ -3603,7 +3613,11 @@ public class LeAudioService extends ProfileService {
    }

    /**
     * Set Inactive by HFP during handover
     * Set Inactive by HFP during handover This is a work around to handle controllers that cannot
     * have SCO and CIS at the same time. So remove active device to tear down CIS, and re-connect
     * the SCO in {@link LeAudioService#handleGroupIdleDuringCall()}
     *
     * @param hfpHandoverDevice is the hfp device that was set to active
     */
    public void setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice) {
        if (!mLeAudioNativeIsInitialized) {
@@ -3612,10 +3626,29 @@ public class LeAudioService extends ProfileService {
        }
        if (getActiveGroupId() != LE_AUDIO_GROUP_ID_INVALID) {
            mHfpHandoverDevice = hfpHandoverDevice;
            if (Flags.leaudioResumeActiveAfterHfpHandover()) {
                // record the lead device
                mLeAudioDeviceInactivatedForHfpHandover = mExposedActiveDevice;
            }
            removeActiveDevice(true);
        }
    }

    /** Resume prior active device after HFP phone call hand over */
    public void setActiveAfterHfpHandover() {
        if (!mLeAudioNativeIsInitialized) {
            Log.e(TAG, "Le Audio not initialized properly.");
            return;
        }
        if (mLeAudioDeviceInactivatedForHfpHandover != null) {
            Log.i(TAG, "handover to LE audio device=" + mLeAudioDeviceInactivatedForHfpHandover);
            setActiveDevice(mLeAudioDeviceInactivatedForHfpHandover);
            mLeAudioDeviceInactivatedForHfpHandover = null;
        } else {
            Log.d(TAG, "nothing to hand over back");
        }
    }

    /**
     * Set connection policy of the profile and connects it if connectionPolicy is
     * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is
@@ -4880,10 +4913,16 @@ public class LeAudioService extends ProfileService {
        ProfileService.println(sb, "  mActiveAudioInDevice: " + mActiveAudioInDevice);
        ProfileService.println(sb, "  mUnicastGroupIdDeactivatedForBroadcastTransition: "
                + mUnicastGroupIdDeactivatedForBroadcastTransition);
        ProfileService.println(sb, "  mBroadcastIdDeactivatedForUnicastTransition: "
        ProfileService.println(
                sb,
                "  mBroadcastIdDeactivatedForUnicastTransition: "
                        + mBroadcastIdDeactivatedForUnicastTransition);
        ProfileService.println(sb, "  mExposedActiveDevice: " + mExposedActiveDevice);
        ProfileService.println(sb, "  mHfpHandoverDevice:" + mHfpHandoverDevice);
        ProfileService.println(
                sb,
                " mLeAudioDeviceInactivatedForHfpHandover:"
                        + mLeAudioDeviceInactivatedForHfpHandover);
        ProfileService.println(sb, "  mLeAudioIsInbandRingtoneSupported:"
                                + mLeAudioInbandRingtoneSupportedByPlatform);

+41 −0
Original line number Diff line number Diff line
@@ -60,9 +60,11 @@ import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ActiveDeviceManager;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.RemoteDevices;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.SilenceDeviceManager;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.le_audio.LeAudioService;

import org.hamcrest.Matchers;
import org.junit.After;
@@ -123,6 +125,10 @@ public class HeadsetServiceAndStateMachineTest {

    @Mock private HeadsetNativeInterface mNativeInterface;

    @Mock private LeAudioService mLeAudioService;

    @Mock private ServiceFactory mServiceFactory;

    private class HeadsetIntentReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
@@ -1596,6 +1602,41 @@ public class HeadsetServiceAndStateMachineTest {
        configureHeadsetServiceForAptxVoice(false);
    }

    @Test
    public void testHfpHandoverToLeAudioAfterScoDisconnect() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_RESUME_ACTIVE_AFTER_HFP_HANDOVER);

        Assert.assertNotNull(mHeadsetService.mFactory);
        doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService();
        mHeadsetService.mFactory = mServiceFactory;
        doReturn(true).when(mSystemInterface).isCallIdle();

        // Connect HF
        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
        connectTestDevice(device);
        // Make device active
        Assert.assertTrue(mHeadsetService.setActiveDevice(device));
        verify(mNativeInterface).setActiveDevice(device);
        Assert.assertEquals(device, mHeadsetService.getActiveDevice());
        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(eq(device), eq(true));

        mHeadsetService.messageFromNative(
                new HeadsetStackEvent(
                        HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED,
                        HeadsetHalConstants.AUDIO_STATE_CONNECTED,
                        device));
        TestUtils.waitForLooperToFinishScheduledTask(
                mHeadsetService.getStateMachinesThreadLooper());

        // Audio disconnected
        mHeadsetService.messageFromNative(
                new HeadsetStackEvent(
                        HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED,
                        HeadsetHalConstants.AUDIO_STATE_DISCONNECTED,
                        device));
        verify(mLeAudioService, timeout(1000).atLeastOnce()).setActiveAfterHfpHandover();
    }

    private void startVoiceRecognitionFromHf(BluetoothDevice device) {
        // Start voice recognition
        HeadsetStackEvent startVrEvent =