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

Commit ff1d0a2c authored by Yuyang Huang's avatar Yuyang Huang
Browse files

Let AudioManager handle SCO connection during Voice Recognition

Bug: 312592568
Bug: 315234036
Test: atest HeadsetServiceAndStateMachineTest
Change-Id: I201e6847dbe18dd71cf959cf6f2c9cf1f39ddbef
parent d83c04e4
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.net.Uri;
import android.os.BatteryManager;
@@ -74,6 +75,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application.
@@ -1179,6 +1181,30 @@ public class HeadsetService extends ProfileService {
            } else {
                stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device);
            }
            if (Flags.isScoManagedByAudio()) {
                // when isScoManagedByAudio is on, tell AudioManager to connect SCO
                AudioManager am = mSystemInterface.getAudioManager();
                BluetoothDevice finalDevice = device;
                Optional<AudioDeviceInfo> audioDeviceInfo =
                        am.getAvailableCommunicationDevices().stream()
                                .filter(
                                        x ->
                                                x.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
                                                        && x.getAddress()
                                                                .equals(finalDevice.getAddress()))
                                .findFirst();
                if (audioDeviceInfo.isPresent()) {
                    am.setCommunicationDevice(audioDeviceInfo.get());
                    Log.i(TAG, "Audio Manager will initiate the SCO connection");
                    return true;
                }
                Log.w(
                        TAG,
                        "Cannot find audioDeviceInfo that matches device="
                                + device
                                + " to create the SCO");
                return false;
            }
            stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
        }
        return true;
@@ -1209,6 +1235,10 @@ public class HeadsetService extends ProfileService {
            }
            mVoiceRecognitionStarted = false;
            stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device);
            if (Flags.isScoManagedByAudio()) {
                mSystemInterface.getAudioManager().clearCommunicationDevice();
                return true;
            }
            stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
        }
        return true;
+76 −1
Original line number Diff line number Diff line
@@ -41,7 +41,9 @@ import android.os.Looper;
import android.os.ParcelUuid;
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.flag.junit.SetFlagsRule;
import android.telecom.PhoneAccount;
import android.util.Log;

import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.intent.Intents;
@@ -55,11 +57,13 @@ import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.RemoteDevices;
import com.android.bluetooth.btservice.SilenceDeviceManager;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.Flags;

import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -92,7 +96,7 @@ public class HeadsetServiceAndStateMachineTest {
    private static final String TEST_PHONE_NUMBER = "1234567890";
    private static final String TEST_CALLER_ID = "Test Name";


    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
    private Context mTargetContext;
    private HeadsetService mHeadsetService;
    private BluetoothAdapter mAdapter;
@@ -686,6 +690,26 @@ public class HeadsetServiceAndStateMachineTest {
        startVoiceRecognitionFromHf(device);
    }

    /**
     * Same process as {@link
     * HeadsetServiceAndStateMachineTest#testVoiceRecognition_SingleHfInitiatedSuccess()} except the
     * SCO connection is handled by the Audio Framework
     */
    @Test
    public void testVoiceRecognition_SingleHfInitiatedSuccess_ScoManagedByAudio() {
        mSetFlagsRule.enableFlags(Flags.FLAG_IS_SCO_MANAGED_BY_AUDIO);
        // 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));
        // Start voice recognition
        startVoiceRecognitionFromHf_ScoManagedByAudio(device);
    }

    /**
     * Test to verify the following behavior regarding active HF stop voice recognition
     * in the successful scenario
@@ -811,6 +835,26 @@ public class HeadsetServiceAndStateMachineTest {
        startVoiceRecognitionFromAg();
    }

    /**
     * Same process as {@link
     * HeadsetServiceAndStateMachineTest#testVoiceRecognition_SingleAgInitiatedSuccess()} except the
     * SCO connection is handled by the Audio Framework
     */
    @Test
    public void testVoiceRecognition_SingleAgInitiatedSuccess_ScoManagedByAudio() {
        mSetFlagsRule.enableFlags(Flags.FLAG_IS_SCO_MANAGED_BY_AUDIO);
        // 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));
        // Start voice recognition
        startVoiceRecognitionFromAg_ScoManagedByAudio();
    }

    /**
     * Test to verify the following behavior regarding AG initiated voice recognition
     * in the successful scenario
@@ -1167,6 +1211,26 @@ public class HeadsetServiceAndStateMachineTest {
        verifyNoMoreInteractions(mNativeInterface);
    }

    private void startVoiceRecognitionFromHf_ScoManagedByAudio(BluetoothDevice device) {
        if (!Flags.isScoManagedByAudio()) {
            Log.i(TAG, "isScoManagedByAudio is disabled");
            return;
        }
        // Start voice recognition
        HeadsetStackEvent startVrEvent =
                new HeadsetStackEvent(
                        HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
                        HeadsetHalConstants.VR_STATE_STARTED,
                        device);
        mHeadsetService.messageFromNative(startVrEvent);
        verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
        // has not add verification AudioDeviceInfo because it is final, unless add a wrapper
        mHeadsetService.startVoiceRecognition(device);
        verify(mAudioManager, times(0)).setA2dpSuspended(true);
        verify(mAudioManager, times(0)).setLeAudioSuspended(true);
        verify(mNativeInterface, times(0)).connectAudio(device);
    }

    private void startVoiceRecognitionFromAg() {
        BluetoothDevice device = mHeadsetService.getActiveDevice();
        Assert.assertNotNull(device);
@@ -1185,6 +1249,17 @@ public class HeadsetServiceAndStateMachineTest {
        verifyNoMoreInteractions(mNativeInterface);
    }

    private void startVoiceRecognitionFromAg_ScoManagedByAudio() {
        BluetoothDevice device = mHeadsetService.getActiveDevice();
        Assert.assertNotNull(device);
        mHeadsetService.startVoiceRecognition(device);
        // has not add verification AudioDeviceInfo because it is final, unless add a wrapper
        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).startVoiceRecognition(device);
        verify(mAudioManager, times(0)).setA2dpSuspended(true);
        verify(mAudioManager, times(0)).setLeAudioSuspended(true);
        verify(mNativeInterface, times(0)).connectAudio(device);
    }

    private void connectTestDevice(BluetoothDevice device) {
        when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET))
                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);