Merge "Telecom SCO new audio HAL support" into main
diff --git a/src/com/android/server/telecom/AudioRoute.java b/src/com/android/server/telecom/AudioRoute.java
index db734ff..9898ca0 100644
--- a/src/com/android/server/telecom/AudioRoute.java
+++ b/src/com/android/server/telecom/AudioRoute.java
@@ -28,6 +28,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 27f7f96..8d7c2bf 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -906,7 +906,8 @@
* @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 @@
}
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,
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 1cea531..beddcbe 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -40,6 +40,7 @@
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.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 @@
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 @@
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 @@
}
} 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 @@
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) {
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
index 993ca55..fa1afbb 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
@@ -44,7 +44,6 @@
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,39 +107,57 @@
@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,
- AudioManager audioManager) {
+ AudioManager audioManager) {
if (mOverrideSpeakerToBus && type == AudioRoute.TYPE_SPEAKER) {
type = AudioRoute.TYPE_BUS;
}
@@ -154,7 +171,7 @@
when(mWiredHeadsetManager.isPluggedIn()).thenReturn(false);
when(mEarpieceDeviceInfo.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE);
when(mAudioManager.getDevices(eq(AudioManager.GET_DEVICES_OUTPUTS))).thenReturn(
- new AudioDeviceInfo[] {
+ new AudioDeviceInfo[]{
mEarpieceDeviceInfo
});
when(mAudioManager.getPreferredDeviceForStrategy(nullable(AudioProductStrategy.class)))
@@ -173,7 +190,8 @@
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));
@@ -221,7 +239,7 @@
@Test
public void testInitializeWithoutEarpiece() {
when(mAudioManager.getDevices(eq(AudioManager.GET_DEVICES_OUTPUTS))).thenReturn(
- new AudioDeviceInfo[] {});
+ new AudioDeviceInfo[]{});
mController.initialize();
assertEquals(mSpeakerRoute, mController.getCurrentRoute());
@@ -408,7 +426,7 @@
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 @@
assertTrue(foundValid);
}
-
@SmallTest
@Test
public void testToggleMute() throws Exception {
@@ -740,7 +757,7 @@
mController.sendMessageWithSessionInfo(DISCONNECT_WIRED_HEADSET);
expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
- | CallAudioState.ROUTE_BLUETOOTH, null , BLUETOOTH_DEVICES);
+ | CallAudioState.ROUTE_BLUETOOTH, null, BLUETOOTH_DEVICES);
verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
any(CallAudioState.class), eq(expectedState));
}
@@ -794,7 +811,6 @@
verifyDisconnectBluetoothDevice(AudioRoute.TYPE_BLUETOOTH_HA);
}
-
@SmallTest
@Test
public void testSwitchBetweenLeAndScoDevices() {
@@ -837,7 +853,8 @@
@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 @@
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);
@@ -897,31 +914,31 @@
mController.initialize();
mController.sendMessageWithSessionInfo(BT_DEVICE_ADDED, AudioRoute.TYPE_BLUETOOTH_SCO,
- BLUETOOTH_DEVICE_1);
+ BLUETOOTH_DEVICE_1);
CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
- CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH
- | CallAudioState.ROUTE_SPEAKER, null, BLUETOOTH_DEVICES);
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH
+ | CallAudioState.ROUTE_SPEAKER, null, BLUETOOTH_DEVICES);
verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
- any(CallAudioState.class), eq(expectedState));
+ any(CallAudioState.class), eq(expectedState));
mController.sendMessageWithSessionInfo(SWITCH_FOCUS, RINGING_FOCUS, 0);
assertFalse(mController.isActive());
// BT device should be cached. Verify routing into BT device once focus becomes active.
mController.sendMessageWithSessionInfo(USER_SWITCH_BLUETOOTH, 0,
- BLUETOOTH_DEVICE_1.getAddress());
+ BLUETOOTH_DEVICE_1.getAddress());
expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
- CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH
- | CallAudioState.ROUTE_SPEAKER, BLUETOOTH_DEVICE_1, BLUETOOTH_DEVICES);
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH
+ | CallAudioState.ROUTE_SPEAKER, BLUETOOTH_DEVICE_1, BLUETOOTH_DEVICES);
verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
- any(CallAudioState.class), eq(expectedState));
+ any(CallAudioState.class), eq(expectedState));
mController.sendMessageWithSessionInfo(SWITCH_FOCUS, ACTIVE_FOCUS, 0);
mController.sendMessageWithSessionInfo(BT_AUDIO_CONNECTED, 0, BLUETOOTH_DEVICE_1);
expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
- CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH
- | CallAudioState.ROUTE_SPEAKER, BLUETOOTH_DEVICE_1, BLUETOOTH_DEVICES);
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH
+ | CallAudioState.ROUTE_SPEAKER, BLUETOOTH_DEVICE_1, BLUETOOTH_DEVICES);
verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
- any(CallAudioState.class), eq(expectedState));
+ any(CallAudioState.class), eq(expectedState));
}
@SmallTest
@@ -1179,7 +1196,7 @@
watchDevice);
CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
- | CallAudioState.ROUTE_BLUETOOTH, BLUETOOTH_DEVICE_1, BLUETOOTH_DEVICES);
+ | CallAudioState.ROUTE_BLUETOOTH, BLUETOOTH_DEVICE_1, BLUETOOTH_DEVICES);
verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
any(CallAudioState.class), eq(expectedState));
@@ -1218,7 +1235,6 @@
BLUETOOTH_DEVICES.remove(watchDevice);
}
-
@Test
@SmallTest
public void testAbandonCallAudioFocusAfterCallEnd() {
@@ -1265,7 +1281,7 @@
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 {