diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java index 4e9ee4eb175..43cd29f5e28 100644 --- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java +++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java @@ -16,8 +16,12 @@ package com.android.settings.sound; - import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION; +import static android.media.AudioManager.STREAM_MUSIC; +import static android.media.AudioManager.STREAM_VOICE_CALL; +import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP; +import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO; +import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID; import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY; import android.bluetooth.BluetoothDevice; @@ -43,14 +47,18 @@ import com.android.settings.R; import com.android.settings.bluetooth.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.FeatureFlags; +import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.HeadsetProfile; +import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +import java.util.ArrayList; import java.util.List; /** @@ -198,6 +206,72 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont return mAudioManager.getDevicesForStream(streamType) == device; } + protected boolean isOngoingCallStatus() { + final int audioMode = mAudioManager.getMode(); + return audioMode == AudioManager.MODE_RINGTONE + || audioMode == AudioManager.MODE_IN_CALL + || audioMode == AudioManager.MODE_IN_COMMUNICATION; + } + + /** + * get hands free profile(HFP) connected device + */ + protected List getConnectedHfpDevices() { + final List connectedDevices = new ArrayList<>(); + final HeadsetProfile hfpProfile = mProfileManager.getHeadsetProfile(); + if (hfpProfile == null) { + return connectedDevices; + } + final List devices = hfpProfile.getConnectedDevices(); + for (BluetoothDevice device : devices) { + if (device.isConnected()) { + connectedDevices.add(device); + } + } + return connectedDevices; + } + + /** + * get A2dp connected device + */ + protected List getConnectedA2dpDevices() { + final List connectedDevices = new ArrayList<>(); + final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile(); + if (a2dpProfile == null) { + return connectedDevices; + } + final List devices = a2dpProfile.getConnectedDevices(); + for (BluetoothDevice device : devices) { + if (device.isConnected()) { + connectedDevices.add(device); + } + } + return connectedDevices; + } + + /** + * get hearing aid profile connected device, exclude other devices with same hiSyncId. + */ + protected List getConnectedHearingAidDevices() { + final List connectedDevices = new ArrayList<>(); + final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile(); + if (hapProfile == null) { + return connectedDevices; + } + final List devicesHiSyncIds = new ArrayList<>(); + final List devices = hapProfile.getConnectedDevices(); + for (BluetoothDevice device : devices) { + final long hiSyncId = hapProfile.getHiSyncId(device); + // device with same hiSyncId should not be shown in the UI. + // So do not add it into connectedDevices. + if (!devicesHiSyncIds.contains(hiSyncId) && device.isConnected()) { + devicesHiSyncIds.add(hiSyncId); + connectedDevices.add(device); + } + } + return connectedDevices; + } + int getDefaultDeviceIndex() { // Default device is after all connected devices. return ArrayUtils.size(mConnectedDevices); @@ -261,7 +335,7 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont mContext.unregisterReceiver(mReceiver); } - /** Callback for headset plugged and unplugged events. */ + /** Notifications of audio device connection and disconnection events. */ private class AudioManagerAudioDeviceCallback extends AudioDeviceCallback { @Override public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java index cc1785b3944..df652975ad8 100644 --- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java @@ -48,7 +48,8 @@ import com.android.settings.testutils.shadow.ShadowMediaRouter; import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.BluetoothEventManager; -import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.HeadsetProfile; +import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; @@ -77,19 +78,24 @@ public class AudioOutputSwitchPreferenceControllerTest { private static final String TEST_KEY = "Test_Key"; private static final String TEST_DEVICE_NAME_1 = "Test_A2DP_BT_Device_NAME_1"; private static final String TEST_DEVICE_NAME_2 = "Test_A2DP_BT_Device_NAME_2"; - private static final String TEST_DEVICE_ADDRESS_1 = "00:07:80:78:A4:69"; - private static final String TEST_DEVICE_ADDRESS_2 = "00:00:00:00:00:00"; + private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1"; + private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2"; + private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3"; + private final static long HISYNCID1 = 10; + private final static long HISYNCID2 = 11; @Mock private LocalBluetoothManager mLocalManager; @Mock private BluetoothEventManager mBluetoothEventManager; @Mock - private CachedBluetoothDevice mCachedBluetoothDevice; - @Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager; @Mock private A2dpProfile mA2dpProfile; + @Mock + private HeadsetProfile mHeadsetProfile; + @Mock + private HearingAidProfile mHearingAidProfile; private Context mContext; private PreferenceScreen mScreen; @@ -99,10 +105,13 @@ public class AudioOutputSwitchPreferenceControllerTest { private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private BluetoothDevice mBluetoothDevice; - private ShadowBluetoothDevice mShadowBluetoothDevice; + private BluetoothDevice mBluetoothHapDevice; + private BluetoothDevice mSecondBluetoothHapDevice; private LocalBluetoothManager mLocalBluetoothManager; private AudioSwitchPreferenceController mController; private List mConnectedDevices; + private List mHearingAidActiveDevices; + private List mEmptyDevices; @Before public void setUp() { @@ -118,20 +127,27 @@ public class AudioOutputSwitchPreferenceControllerTest { when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager); when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); + when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile); + when(mLocalBluetoothProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile); mBluetoothManager = new BluetoothManager(mContext); mBluetoothAdapter = mBluetoothManager.getAdapter(); - mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1); - mShadowBluetoothDevice = Shadows.shadowOf(mBluetoothDevice); - mShadowBluetoothDevice.setName(TEST_DEVICE_NAME_1); - when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1)); + when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1); + when(mBluetoothDevice.isConnected()).thenReturn(true); + + mBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2)); + when(mBluetoothHapDevice.isConnected()).thenReturn(true); + mSecondBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3)); + when(mSecondBluetoothHapDevice.isConnected()).thenReturn(true); mController = new AudioSwitchPreferenceControllerTestable(mContext, TEST_KEY); mScreen = spy(new PreferenceScreen(mContext, null)); mPreference = new ListPreference(mContext); - mConnectedDevices = new ArrayList<>(1); - mConnectedDevices.add(mBluetoothDevice); + mConnectedDevices = new ArrayList<>(2); + mHearingAidActiveDevices = new ArrayList<>(2); + mEmptyDevices = new ArrayList<>(2); when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class)); when(mScreen.getContext()).thenReturn(mContext); @@ -179,6 +195,8 @@ public class AudioOutputSwitchPreferenceControllerTest { @Test public void onPreferenceChange_toThisDevice_shouldSetDefaultSummary() { + mConnectedDevices.clear(); + mConnectedDevices.add(mBluetoothDevice); mController.mConnectedDevices = mConnectedDevices; mController.onPreferenceChange(mPreference, @@ -194,6 +212,8 @@ public class AudioOutputSwitchPreferenceControllerTest { */ @Test public void onPreferenceChange_toBtDevice_shouldSetBtDeviceName() { + mConnectedDevices.clear(); + mConnectedDevices.add(mBluetoothDevice); mController.mConnectedDevices = mConnectedDevices; mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1); @@ -212,10 +232,10 @@ public class AudioOutputSwitchPreferenceControllerTest { secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2); shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice); shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2); - List connectedDevices = new ArrayList<>(2); - connectedDevices.add(mBluetoothDevice); - connectedDevices.add(secondBluetoothDevice); - mController.mConnectedDevices = connectedDevices; + mConnectedDevices.clear(); + mConnectedDevices.add(mBluetoothDevice); + mConnectedDevices.add(secondBluetoothDevice); + mController.mConnectedDevices = mConnectedDevices; mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_2); @@ -233,6 +253,112 @@ public class AudioOutputSwitchPreferenceControllerTest { assertThat(mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1)).isFalse(); } + /** + * Two hearing aid devices with different HisyncId + * getConnectedHearingAidDevices should add both device to list. + */ + @Test + public void getConnectedHearingAidDevices_deviceHisyncIdIsDifferent_shouldAddBothToList() { + mEmptyDevices.clear(); + mConnectedDevices.clear(); + mConnectedDevices.add(mBluetoothHapDevice); + mConnectedDevices.add(mSecondBluetoothHapDevice); + when(mHearingAidProfile.getConnectedDevices()).thenReturn(mConnectedDevices); + when(mHearingAidProfile.getHiSyncId(mBluetoothHapDevice)).thenReturn(HISYNCID1); + when(mHearingAidProfile.getHiSyncId(mSecondBluetoothHapDevice)).thenReturn( + HISYNCID2); + + mEmptyDevices.addAll(mController.getConnectedHearingAidDevices()); + + assertThat(mEmptyDevices).containsExactly(mBluetoothHapDevice, mSecondBluetoothHapDevice); + } + + /** + * Two hearing aid devices with same HisyncId + * getConnectedHearingAidDevices should only add first device to list. + */ + @Test + public void getConnectedHearingAidDevices_deviceHisyncIdIsSame_shouldAddOneToList() { + mEmptyDevices.clear(); + mConnectedDevices.clear(); + mConnectedDevices.add(mBluetoothHapDevice); + mConnectedDevices.add(mSecondBluetoothHapDevice); + when(mHearingAidProfile.getConnectedDevices()).thenReturn(mConnectedDevices); + when(mHearingAidProfile.getHiSyncId(mBluetoothHapDevice)).thenReturn(HISYNCID1); + when(mHearingAidProfile.getHiSyncId(mSecondBluetoothHapDevice)).thenReturn( + HISYNCID1); + + mEmptyDevices.addAll(mController.getConnectedHearingAidDevices()); + + assertThat(mEmptyDevices).containsExactly(mBluetoothHapDevice); + } + + /** + * One A2dp device is connected. + * getConnectedA2dpDevices should add this device to list. + */ + @Test + public void getConnectedA2dpDevices_oneConnectedA2dpDevice_shouldAddDeviceToList() { + mEmptyDevices.clear(); + mConnectedDevices.clear(); + mConnectedDevices.add(mBluetoothDevice); + when(mA2dpProfile.getConnectedDevices()).thenReturn(mConnectedDevices); + + mEmptyDevices.addAll(mController.getConnectedA2dpDevices()); + + assertThat(mEmptyDevices).containsExactly(mBluetoothDevice); + } + + /** + * More than one A2dp devices are connected. + * getConnectedA2dpDevices should add all devices to list. + */ + @Test + public void getConnectedA2dpDevices_moreThanOneConnectedA2dpDevice_shouldAddDeviceToList() { + mEmptyDevices.clear(); + mConnectedDevices.clear(); + mConnectedDevices.add(mBluetoothDevice); + mConnectedDevices.add(mBluetoothHapDevice); + when(mA2dpProfile.getConnectedDevices()).thenReturn(mConnectedDevices); + + mEmptyDevices.addAll(mController.getConnectedA2dpDevices()); + + assertThat(mEmptyDevices).containsExactly(mBluetoothDevice, mBluetoothHapDevice); + } + + /** + * One hands free profile device is connected. + * getConnectedA2dpDevices should add this device to list. + */ + @Test + public void getConnectedHfpDevices_oneConnectedHfpDevice_shouldAddDeviceToList() { + mEmptyDevices.clear(); + mConnectedDevices.clear(); + mConnectedDevices.add(mBluetoothDevice); + when(mHeadsetProfile.getConnectedDevices()).thenReturn(mConnectedDevices); + + mEmptyDevices.addAll(mController.getConnectedHfpDevices()); + + assertThat(mEmptyDevices).containsExactly(mBluetoothDevice); + } + + /** + * More than one hands free profile devices are connected. + * getConnectedA2dpDevices should add all devices to list. + */ + @Test + public void getConnectedHfpDevices_moreThanOneConnectedHfpDevice_shouldAddDeviceToList() { + mEmptyDevices.clear(); + mConnectedDevices.clear(); + mConnectedDevices.add(mBluetoothDevice); + mConnectedDevices.add(mBluetoothHapDevice); + when(mHeadsetProfile.getConnectedDevices()).thenReturn(mConnectedDevices); + + mEmptyDevices.addAll(mController.getConnectedHfpDevices()); + + assertThat(mEmptyDevices).containsExactly(mBluetoothDevice, mBluetoothHapDevice); + } + private class AudioSwitchPreferenceControllerTestable extends AudioSwitchPreferenceController { AudioSwitchPreferenceControllerTestable(Context context, String key) {