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

Commit 9f52b6d8 authored by Hui Wang's avatar Hui Wang Committed by Android (Google) Code Review
Browse files

Merge "Telecom SCO new audio HAL support" into main

parents 1c6c76ac d3503298
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothStatusCodes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.sysprop.BluetoothProperties;
import android.telecom.Log;
import android.util.Pair;

@@ -139,6 +140,7 @@ public class AudioRoute {
    private String mBluetoothAddress;
    private AudioDeviceInfo mInfo;
    private boolean mIsDestRouteForWatch;
    private boolean mIsScoManagedByAudio;
    public static final Set<Integer> BT_AUDIO_DEVICE_INFO_TYPES = Set.of(
            AudioDeviceInfo.TYPE_BLE_HEADSET,
            AudioDeviceInfo.TYPE_BLE_SPEAKER,
@@ -265,7 +267,7 @@ public class AudioRoute {
                boolean connectedBtAudio = connectBtAudio(pendingAudioRoute, device,
                        audioManager, bluetoothRouteManager);
                // Special handling for SCO case.
                if (mAudioRouteType == TYPE_BLUETOOTH_SCO) {
                if (!mIsScoManagedByAudio && mAudioRouteType == TYPE_BLUETOOTH_SCO) {
                    // Set whether the dest route is for the watch
                    mIsDestRouteForWatch = bluetoothRouteManager.isWatch(device);
                    // Check if the communication device was set for the device, even if
@@ -308,6 +310,10 @@ public class AudioRoute {
                    result = audioManager.setCommunicationDevice(mInfo);
                    if (result) {
                        pendingAudioRoute.setCommunicationDeviceType(mAudioRouteType);
                        if (mAudioRouteType == TYPE_BLUETOOTH_SCO && !isScoAudioConnected
                                && mIsScoManagedByAudio) {
                            pendingAudioRoute.addMessage(BT_AUDIO_CONNECTED, mBluetoothAddress);
                        }
                    }
                    Log.i(this, "onDestRouteAsPendingRoute: route=%s, "
                            + "AudioManager#setCommunicationDevice(%s)=%b", this,
@@ -355,6 +361,9 @@ public class AudioRoute {
        mAudioRouteType = type;
        mBluetoothAddress = bluetoothAddress;
        mInfo = info;
        // Indication that SCO is managed by audio (i.e. supports setCommunicationDevice).
        mIsScoManagedByAudio = android.media.audio.Flags.scoManagedByAudio()
                && BluetoothProperties.isScoManagedByAudioEnabled().orElse(false);
    }

    @Override
@@ -398,7 +407,7 @@ public class AudioRoute {
        boolean success = false;
        if (device != null) {
            success = bluetoothRouteManager.getDeviceManager()
                    .connectAudio(device, mAudioRouteType);
                    .connectAudio(device, mAudioRouteType, mIsScoManagedByAudio);
        }

        Log.i(this, "connectBtAudio: routeToConnectTo = %s, successful = %b",
@@ -429,8 +438,9 @@ public class AudioRoute {
        }

        int result = BluetoothStatusCodes.SUCCESS;
        if (pendingAudioRoute.getCommunicationDeviceType() == TYPE_BLUETOOTH_SCO) {
            Log.i(this, "clearCommunicationDevice: Disconnecting SCO device.");
        if (pendingAudioRoute.getCommunicationDeviceType() == TYPE_BLUETOOTH_SCO
                && !mIsScoManagedByAudio) {
            Log.i(this, "Disconnecting SCO device via BluetoothHeadset.");
            result = bluetoothRouteManager.getDeviceManager().disconnectSco();
        } else {
            // Only clear communication device if the destination route will be inactive; route to
+3 −2
Original line number Diff line number Diff line
@@ -906,7 +906,8 @@ public class BluetoothDeviceManager {
     * @param type {@link AudioRoute.AudioRouteType} associated with the device.
     * @return {@code true} if device was successfully connected, {@code false} otherwise.
     */
    public boolean connectAudio(BluetoothDevice device, @AudioRoute.AudioRouteType int type) {
    public boolean connectAudio(BluetoothDevice device, @AudioRoute.AudioRouteType int type,
            boolean isScoManagedByAudio) {
        String address = device.getAddress();
        int callProfile = BluetoothProfile.LE_AUDIO;
        if (type == TYPE_BLUETOOTH_SCO) {
@@ -924,7 +925,7 @@ public class BluetoothDeviceManager {
        }

        if (callProfile == BluetoothProfile.LE_AUDIO
                || callProfile == BluetoothProfile.HEARING_AID) {
                || callProfile == BluetoothProfile.HEARING_AID || isScoManagedByAudio) {
            return mBluetoothAdapter.setActiveDevice(device, BluetoothAdapter.ACTIVE_DEVICE_ALL);
        } else if (callProfile == BluetoothProfile.HEADSET) {
            boolean success = mBluetoothAdapter.setActiveDevice(device,
+12 −6
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioDeviceInfo;
import android.os.Bundle;
import android.sysprop.BluetoothProperties;
import android.telecom.Log;
import android.telecom.Logging.Session;
import android.util.Pair;
@@ -50,7 +51,6 @@ import com.android.server.telecom.CallAudioCommunicationDeviceTracker;
import com.android.server.telecom.CallAudioRouteAdapter;
import com.android.server.telecom.CallAudioRouteController;
import com.android.server.telecom.flags.FeatureFlags;
import com.android.server.telecom.flags.Flags;

public class BluetoothStateReceiver extends BroadcastReceiver {
    private static final String LOG_TAG = BluetoothStateReceiver.class.getSimpleName();
@@ -74,6 +74,7 @@ public class BluetoothStateReceiver extends BroadcastReceiver {
    private final BluetoothDeviceManager mBluetoothDeviceManager;
    private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker;
    private FeatureFlags mFeatureFlags;
    private boolean mIsScoManagedByAudio;
    private CallAudioRouteAdapter mCallAudioRouteAdapter;

    public void onReceive(Context context, Intent intent) {
@@ -269,7 +270,8 @@ public class BluetoothStateReceiver extends BroadcastReceiver {
                mCallAudioRouteAdapter.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_PRESENT,
                        audioRouteType, device.getAddress());
                if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID
                        || deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) {
                        || deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO
                        || mIsScoManagedByAudio) {
                    if (!mBluetoothDeviceManager.setCommunicationDeviceForAddress(
                            device.getAddress())) {
                        Log.i(this, "handleActiveDeviceChanged: Failed to set "
@@ -286,11 +288,12 @@ public class BluetoothStateReceiver extends BroadcastReceiver {
                        }
                    } else {
                        // Track the currently set communication device.
                        int routeType = deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO
                                ? AudioRoute.TYPE_BLUETOOTH_LE
                                : AudioRoute.TYPE_BLUETOOTH_HA;
                        mCallAudioRouteAdapter.getPendingAudioRoute()
                                .setCommunicationDeviceType(routeType);
                                .setCommunicationDeviceType(audioRouteType);
                        if (audioRouteType == AudioRoute.TYPE_BLUETOOTH_SCO) {
                            mCallAudioRouteAdapter.getPendingAudioRoute()
                                    .addMessage(BT_AUDIO_CONNECTED, device.getAddress());
                        }
                    }
                }
            }
@@ -379,6 +382,9 @@ public class BluetoothStateReceiver extends BroadcastReceiver {
        mBluetoothRouteManager = routeManager;
        mCommunicationDeviceTracker = communicationDeviceTracker;
        mFeatureFlags = featureFlags;
        // Indication that SCO is managed by audio (i.e. supports setCommunicationDevice).
        mIsScoManagedByAudio = android.media.audio.Flags.scoManagedByAudio()
                && BluetoothProperties.isScoManagedByAudioEnabled().orElse(false);
    }

    public void setIsInCall(boolean isInCall) {
+62 −46
Original line number Diff line number Diff line
@@ -44,7 +44,6 @@ import static com.android.server.telecom.CallAudioRouteAdapter.USER_SWITCH_EARPI
import static com.android.server.telecom.CallAudioRouteAdapter.USER_SWITCH_HEADSET;
import static com.android.server.telecom.CallAudioRouteAdapter.USER_SWITCH_SPEAKER;
import static com.android.server.telecom.CallAudioRouteController.INCLUDE_BLUETOOTH_IN_BASELINE;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -108,35 +107,53 @@ import java.util.Set;

@RunWith(JUnit4.class)
public class CallAudioRouteControllerTest extends TelecomTestCase {
    private CallAudioRouteController mController;
    @Mock WiredHeadsetManager mWiredHeadsetManager;
    @Mock AudioManager mAudioManager;
    @Mock AudioDeviceInfo mEarpieceDeviceInfo;
    @Mock CallsManager mCallsManager;
    @Mock CallAudioManager.AudioServiceFactory mAudioServiceFactory;
    @Mock IAudioService mAudioService;
    @Mock BluetoothRouteManager mBluetoothRouteManager;
    @Mock BluetoothDeviceManager mBluetoothDeviceManager;
    @Mock BluetoothAdapter mBluetoothAdapter;
    @Mock StatusBarNotifier mockStatusBarNotifier;
    @Mock AudioDeviceInfo mAudioDeviceInfo;
    @Mock BluetoothLeAudio mBluetoothLeAudio;
    @Mock CallAudioManager mCallAudioManager;
    @Mock Call mCall;
    @Mock private TelecomSystem.SyncRoot mLock;
    @Mock private TelecomMetricsController mMockTelecomMetricsController;
    private AudioRoute mEarpieceRoute;
    private AudioRoute mSpeakerRoute;
    private boolean mOverrideSpeakerToBus;
    private static final String BT_ADDRESS_1 = "00:00:00:00:00:01";
    private static final BluetoothDevice BLUETOOTH_DEVICE_1 =
            BluetoothRouteManagerTest.makeBluetoothDevice("00:00:00:00:00:01");
    private static final Set<BluetoothDevice> BLUETOOTH_DEVICES;
    private static final int TEST_TIMEOUT = 500;

    static {
        BLUETOOTH_DEVICES = new HashSet<>();
        BLUETOOTH_DEVICES.add(BLUETOOTH_DEVICE_1);
    }
    private static final int TEST_TIMEOUT = 500;

    @Mock
    WiredHeadsetManager mWiredHeadsetManager;
    @Mock
    AudioManager mAudioManager;
    @Mock
    AudioDeviceInfo mEarpieceDeviceInfo;
    @Mock
    CallsManager mCallsManager;
    @Mock
    CallAudioManager.AudioServiceFactory mAudioServiceFactory;
    @Mock
    IAudioService mAudioService;
    @Mock
    BluetoothRouteManager mBluetoothRouteManager;
    @Mock
    BluetoothDeviceManager mBluetoothDeviceManager;
    @Mock
    BluetoothAdapter mBluetoothAdapter;
    @Mock
    StatusBarNotifier mockStatusBarNotifier;
    @Mock
    AudioDeviceInfo mAudioDeviceInfo;
    @Mock
    BluetoothLeAudio mBluetoothLeAudio;
    @Mock
    CallAudioManager mCallAudioManager;
    @Mock
    Call mCall;
    private CallAudioRouteController mController;
    @Mock
    private TelecomSystem.SyncRoot mLock;
    @Mock
    private TelecomMetricsController mMockTelecomMetricsController;
    private AudioRoute mEarpieceRoute;
    private AudioRoute mSpeakerRoute;
    private boolean mOverrideSpeakerToBus;
    AudioRoute.Factory mAudioRouteFactory = new AudioRoute.Factory() {
        @Override
        public AudioRoute create(@AudioRoute.AudioRouteType int type, String bluetoothAddress,
@@ -173,7 +190,8 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
        when(mCallsManager.getLock()).thenReturn(mLock);
        when(mCallsManager.getForegroundCall()).thenReturn(mCall);
        when(mBluetoothRouteManager.getDeviceManager()).thenReturn(mBluetoothDeviceManager);
        when(mBluetoothDeviceManager.connectAudio(any(BluetoothDevice.class), anyInt()))
        when(mBluetoothDeviceManager.connectAudio(any(BluetoothDevice.class), anyInt(),
                anyBoolean()))
                .thenReturn(true);
        when(mBluetoothDeviceManager.getBluetoothAdapter()).thenReturn(mBluetoothAdapter);
        when(mBluetoothAdapter.getActiveDevices(anyInt())).thenReturn(List.of(BLUETOOTH_DEVICE_1));
@@ -408,7 +426,7 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {

        mController.sendMessageWithSessionInfo(SWITCH_FOCUS, RINGING_FOCUS, 0);
        verify(mBluetoothDeviceManager, timeout(TEST_TIMEOUT))
                .connectAudio(BLUETOOTH_DEVICE_1, AudioRoute.TYPE_BLUETOOTH_SCO);
                .connectAudio(BLUETOOTH_DEVICE_1, AudioRoute.TYPE_BLUETOOTH_SCO, false);
        assertTrue(mController.isActive());

        mController.sendMessageWithSessionInfo(SWITCH_FOCUS, ACTIVE_FOCUS, 0);
@@ -643,7 +661,6 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
        assertTrue(foundValid);
    }


    @SmallTest
    @Test
    public void testToggleMute() throws Exception {
@@ -794,7 +811,6 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
        verifyDisconnectBluetoothDevice(AudioRoute.TYPE_BLUETOOTH_HA);
    }


    @SmallTest
    @Test
    public void testSwitchBetweenLeAndScoDevices() {
@@ -837,7 +853,8 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
    @SmallTest
    @Test
    public void testFallbackWhenBluetoothConnectionFails() {
        when(mBluetoothDeviceManager.connectAudio(any(BluetoothDevice.class), anyInt()))
        when(mBluetoothDeviceManager.connectAudio(any(BluetoothDevice.class), anyInt(),
                anyBoolean()))
                .thenReturn(false);

        AudioDeviceInfo mockAudioDeviceInfo = mock(AudioDeviceInfo.class);
@@ -860,7 +877,7 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
        mController.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_PRESENT,
                AudioRoute.TYPE_BLUETOOTH_SCO, scoDevice.getAddress());
        verify(mBluetoothDeviceManager, timeout(TEST_TIMEOUT))
                .connectAudio(scoDevice, AudioRoute.TYPE_BLUETOOTH_SCO);
                .connectAudio(scoDevice, AudioRoute.TYPE_BLUETOOTH_SCO, false);
        expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH
                        | CallAudioState.ROUTE_SPEAKER, BLUETOOTH_DEVICE_1, BLUETOOTH_DEVICES);
@@ -1218,7 +1235,6 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
        BLUETOOTH_DEVICES.remove(watchDevice);
    }


    @Test
    @SmallTest
    public void testAbandonCallAudioFocusAfterCallEnd() {
@@ -1265,7 +1281,7 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
        mController.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_PRESENT, audioType, BT_ADDRESS_1);
        if (audioType == AudioRoute.TYPE_BLUETOOTH_SCO) {
            verify(mBluetoothDeviceManager, timeout(TEST_TIMEOUT))
                    .connectAudio(BLUETOOTH_DEVICE_1, AudioRoute.TYPE_BLUETOOTH_SCO);
                    .connectAudio(BLUETOOTH_DEVICE_1, AudioRoute.TYPE_BLUETOOTH_SCO, false);
            mController.sendMessageWithSessionInfo(BT_AUDIO_CONNECTED,
                    0, BLUETOOTH_DEVICE_1);
        } else {