summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flags/telecom_callaudioroutestatemachine_flags.aconfig9
-rw-r--r--src/com/android/server/telecom/CallAudioRouteStateMachine.java26
-rw-r--r--src/com/android/server/telecom/TelecomSystem.java3
-rw-r--r--src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java47
-rw-r--r--tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java2
-rw-r--r--tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java2
-rw-r--r--tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java69
7 files changed, 149 insertions, 9 deletions
diff --git a/flags/telecom_callaudioroutestatemachine_flags.aconfig b/flags/telecom_callaudioroutestatemachine_flags.aconfig
index 9da150762..7ff144063 100644
--- a/flags/telecom_callaudioroutestatemachine_flags.aconfig
+++ b/flags/telecom_callaudioroutestatemachine_flags.aconfig
@@ -15,8 +15,15 @@ flag {
}
flag {
+ name: "ignore_auto_route_to_watch_device"
+ namespace: "telecom"
+ description: "Ignore auto routing to wearable devices."
+ bug: "294378768"
+}
+
+flag {
name: "transit_route_before_audio_disconnect_bt"
namespace: "telecom"
description: "Fix audio route transition issue on call disconnection when bt audio connected."
bug: "306113816"
-}
+} \ No newline at end of file
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index 44f738098..166e8b15d 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -2022,6 +2022,30 @@ public class CallAudioRouteStateMachine extends StateMachine {
return false;
}
+ private boolean isWatchActiveOrOnlyWatchesAvailable() {
+ if (!mFeatureFlags.ignoreAutoRouteToWatchDevice()) {
+ return false;
+ }
+
+ boolean containsWatchDevice = false;
+ boolean containsNonWatchDevice = false;
+ Collection<BluetoothDevice> connectedBtDevices =
+ mBluetoothRouteManager.getConnectedDevices();
+
+ for (BluetoothDevice connectedDevice: connectedBtDevices) {
+ if (mBluetoothRouteManager.isWatch(connectedDevice)) {
+ containsWatchDevice = true;
+ } else {
+ containsNonWatchDevice = true;
+ }
+ }
+
+ // Don't ignore switch if watch is already the active device.
+ return containsWatchDevice && !containsNonWatchDevice
+ && !mBluetoothRouteManager.isWatch(
+ mBluetoothRouteManager.getBluetoothAudioConnectedDevice());
+ }
+
private int calculateBaselineRouteMessage(boolean isExplicitUserRequest,
boolean includeBluetooth) {
boolean isSkipEarpiece = false;
@@ -2034,7 +2058,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
}
if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0
&& !mHasUserExplicitlyLeftBluetooth
- && includeBluetooth) {
+ && includeBluetooth && !isWatchActiveOrOnlyWatchesAvailable()) {
return isExplicitUserRequest ? USER_SWITCH_BLUETOOTH : SWITCH_BLUETOOTH;
} else if ((mAvailableRoutes & ROUTE_EARPIECE) != 0 && !isSkipEarpiece) {
return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE;
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 57d7139f2..76777da47 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -261,7 +261,8 @@ public class TelecomSystem {
mContext.getSystemService(BluetoothManager.class).getAdapter(),
communicationDeviceTracker);
BluetoothRouteManager bluetoothRouteManager = new BluetoothRouteManager(mContext, mLock,
- bluetoothDeviceManager, new Timeouts.Adapter(), communicationDeviceTracker);
+ bluetoothDeviceManager, new Timeouts.Adapter(),
+ communicationDeviceTracker, featureFlags);
BluetoothStateReceiver bluetoothStateReceiver = new BluetoothStateReceiver(
bluetoothDeviceManager, bluetoothRouteManager, communicationDeviceTracker);
mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index b411b25cf..516ca02a7 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -17,6 +17,7 @@
package com.android.server.telecom.bluetooth;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
@@ -37,6 +38,7 @@ import com.android.internal.util.StateMachine;
import com.android.server.telecom.CallAudioCommunicationDeviceTracker;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.Timeouts;
+import com.android.server.telecom.flags.FeatureFlags;
import java.util.Collection;
import java.util.HashMap;
@@ -470,10 +472,12 @@ public class BluetoothRouteManager extends StateMachine {
private BluetoothDevice mLeAudioActiveDeviceCache = null;
private BluetoothDevice mMostRecentlyReportedActiveDevice = null;
private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker;
+ private FeatureFlags mFeatureFlags;
public BluetoothRouteManager(Context context, TelecomSystem.SyncRoot lock,
BluetoothDeviceManager deviceManager, Timeouts.Adapter timeoutsAdapter,
- CallAudioCommunicationDeviceTracker communicationDeviceTracker) {
+ CallAudioCommunicationDeviceTracker communicationDeviceTracker,
+ FeatureFlags featureFlags) {
super(BluetoothRouteManager.class.getSimpleName());
mContext = context;
mLock = lock;
@@ -481,6 +485,7 @@ public class BluetoothRouteManager extends StateMachine {
mDeviceManager.setBluetoothRouteManager(this);
mTimeoutsAdapter = timeoutsAdapter;
mCommunicationDeviceTracker = communicationDeviceTracker;
+ mFeatureFlags = featureFlags;
mAudioOffState = new AudioOffState();
addState(mAudioOffState);
@@ -672,6 +677,31 @@ public class BluetoothRouteManager extends StateMachine {
return mDeviceManager.getUniqueConnectedDevices();
}
+ public boolean isWatch(BluetoothDevice device) {
+ if (device == null) {
+ Log.i(this, "isWatch: device is null. Returning false");
+ return false;
+ }
+
+ BluetoothClass deviceClass = device.getBluetoothClass();
+ if (deviceClass != null && deviceClass.getDeviceClass()
+ == BluetoothClass.Device.WEARABLE_WRIST_WATCH) {
+ return true;
+ }
+
+ // Check metadata
+ byte[] deviceType = device.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE);
+ if (deviceType == null) {
+ return false;
+ }
+ String deviceTypeStr = new String(deviceType);
+ if (deviceTypeStr.equals(BluetoothDevice.DEVICE_TYPE_WATCH)) {
+ return true;
+ }
+
+ return false;
+ }
+
private String connectBtAudio(String address, boolean switchingBtDevices) {
return connectBtAudio(address, 0, switchingBtDevices);
}
@@ -701,10 +731,19 @@ public class BluetoothRouteManager extends StateMachine {
? address : getActiveDeviceAddress();
if (actualAddress == null) {
Log.i(this, "No device specified and BT stack has no active device."
- + " Using arbitrary device");
+ + " Using arbitrary device - except watch");
if (deviceList.size() > 0) {
- actualAddress = deviceList.iterator().next().getAddress();
- } else {
+ for (BluetoothDevice device : deviceList) {
+ if (mFeatureFlags.ignoreAutoRouteToWatchDevice() && isWatch(device)) {
+ Log.i(this, "Skipping a watch device: " + device);
+ continue;
+ }
+ actualAddress = device.getAddress();
+ break;
+ }
+ }
+
+ if (actualAddress == null) {
Log.i(this, "No devices available at all. Not connecting.");
return null;
}
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 8e31f9c45..3ed96a07c 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -190,7 +190,7 @@ public class BluetoothRouteManagerTest extends TelecomTestCase {
resetMocks();
BluetoothRouteManager sm = new BluetoothRouteManager(mContext,
new TelecomSystem.SyncRoot() { }, mDeviceManager,
- mTimeoutsAdapter, mCommunicationDeviceTracker);
+ mTimeoutsAdapter, mCommunicationDeviceTracker, mFeatureFlags);
sm.setListener(mListener);
sm.setInitialStateForTesting(initialState, initialDevice);
waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
index 15a81d4c0..65854af35 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -419,7 +419,7 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase {
nullable(ContentResolver.class))).thenReturn(100000L);
BluetoothRouteManager sm = new BluetoothRouteManager(mContext,
new TelecomSystem.SyncRoot() { }, mDeviceManager,
- mTimeoutsAdapter, mCommunicationDeviceTracker);
+ mTimeoutsAdapter, mCommunicationDeviceTracker, mFeatureFlags);
sm.setListener(mListener);
sm.setInitialStateForTesting(initialState, initialDevice);
waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index dcead7445..253381c0b 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -98,6 +98,7 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase {
@Mock Call fakeSelfManagedCall;
@Mock Call fakeCall;
@Mock CallAudioManager mockCallAudioManager;
+ @Mock BluetoothDevice mockWatchDevice;
private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
private static final int TEST_TIMEOUT = 500;
@@ -140,6 +141,7 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase {
doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class),
any(CallAudioState.class));
+ when(mFeatureFlags.ignoreAutoRouteToWatchDevice()).thenReturn(false);
}
@Override
@@ -1107,6 +1109,73 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase {
assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
}
+ @MediumTest
+ @Test
+ public void testIgnoreImplicitBTSwitchWhenDeviceIsWatch() {
+ when(mFeatureFlags.ignoreAutoRouteToWatchDevice()).thenReturn(true);
+ CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+ mContext,
+ mockCallsManager,
+ mockBluetoothRouteManager,
+ mockWiredHeadsetManager,
+ mockStatusBarNotifier,
+ mAudioServiceFactory,
+ CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+ mThreadHandler.getLooper(),
+ Runnable::run /** do async stuff sync for test purposes */,
+ mCommunicationDeviceTracker,
+ mFeatureFlags);
+ stateMachine.setCallAudioManager(mockCallAudioManager);
+
+ AudioDeviceInfo headset = mock(AudioDeviceInfo.class);
+ when(headset.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
+ when(headset.getAddress()).thenReturn("");
+ List<AudioDeviceInfo> devices = new ArrayList<>();
+ devices.add(headset);
+
+ when(mockAudioManager.getAvailableCommunicationDevices())
+ .thenReturn(devices);
+ when(mockAudioManager.setCommunicationDevice(eq(headset)))
+ .thenReturn(true);
+ when(mockAudioManager.getCommunicationDevice()).thenReturn(headset);
+
+ CallAudioState initState = new CallAudioState(false,
+ CallAudioState.ROUTE_WIRED_HEADSET, CallAudioState.ROUTE_WIRED_HEADSET
+ | CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH);
+ stateMachine.initialize(initState);
+
+ // Switch to active
+ stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
+ CallAudioRouteStateMachine.ACTIVE_FOCUS);
+ waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+
+ // Make sure that we've successfully switched to the active headset.
+ assertTrue(stateMachine.isInActiveState());
+ ArgumentCaptor<AudioDeviceInfo> infoArgumentCaptor = ArgumentCaptor.forClass(
+ AudioDeviceInfo.class);
+ verify(mockAudioManager).setCommunicationDevice(infoArgumentCaptor.capture());
+ assertEquals(AudioDeviceInfo.TYPE_WIRED_HEADSET, infoArgumentCaptor.getValue().getType());
+
+ // Set up watch device as only available BT device.
+ Collection<BluetoothDevice> availableDevices = Collections.singleton(mockWatchDevice);
+
+ when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
+ when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
+ when(mockBluetoothRouteManager.getConnectedDevices()).thenReturn(availableDevices);
+ when(mockBluetoothRouteManager.isWatch(any(BluetoothDevice.class))).thenReturn(true);
+
+ // Disconnect wired headset to force switch to BT (verify that we ignore the implicit switch
+ // to BT when the watch is the only connected device and that we move into the next
+ // available route.
+ stateMachine.sendMessageWithSessionInfo(
+ CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET);
+ waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+ CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH,
+ null, availableDevices);
+ assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
+ }
+
private void initializationTestHelper(CallAudioState expectedState,
int earpieceControl) {
when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(