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

Commit 44e05800 authored by Yuyang Huang's avatar Yuyang Huang Committed by Gerrit Code Review
Browse files

Merge "Let AudioManager handle SCO connection during Voice Recognition" into main

parents c9d8b02a ff1d0a2c
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);