Loading android/app/src/com/android/bluetooth/a2dp/A2dpService.java +0 −10 Original line number Original line Diff line number Diff line Loading @@ -1275,16 +1275,6 @@ public class A2dpService extends ProfileService { .a2dpConnectionStateChanged(device, fromState, toState); .a2dpConnectionStateChanged(device, fromState, toState); } } /** * Retrieves the most recently connected device in the A2DP connected devices list. */ public BluetoothDevice getFallbackDevice() { DatabaseManager dbManager = mAdapterService.getDatabase(); return dbManager != null ? dbManager .getMostRecentlyConnectedDevicesInList(getConnectedDevices()) : null; } /** /** * Binder object: must be a static class or memory leak may occur. * Binder object: must be a static class or memory leak may occur. */ */ Loading android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +46 −2 Original line number Original line Diff line number Diff line Loading @@ -1052,13 +1052,13 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac A2dpService a2dpService = mFactory.getA2dpService(); A2dpService a2dpService = mFactory.getA2dpService(); BluetoothDevice a2dpFallbackDevice = null; BluetoothDevice a2dpFallbackDevice = null; if (a2dpService != null) { if (a2dpService != null) { a2dpFallbackDevice = a2dpService.getFallbackDevice(); a2dpFallbackDevice = getA2dpFallbackDevice(); } } HeadsetService headsetService = mFactory.getHeadsetService(); HeadsetService headsetService = mFactory.getHeadsetService(); BluetoothDevice headsetFallbackDevice = null; BluetoothDevice headsetFallbackDevice = null; if (headsetService != null) { if (headsetService != null) { headsetFallbackDevice = headsetService.getFallbackDevice(); headsetFallbackDevice = getHfpFallbackDevice(); } } List<BluetoothDevice> connectedDevices = new ArrayList<>(); List<BluetoothDevice> connectedDevices = new ArrayList<>(); Loading Loading @@ -1168,6 +1168,50 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac } } } } /** Retrieves the most recently connected device in the A2DP connected devices list. */ public BluetoothDevice getA2dpFallbackDevice() { DatabaseManager dbManager = mAdapterService.getDatabase(); synchronized (mLock) { return dbManager != null ? dbManager.getMostRecentlyConnectedDevicesInList(mA2dpConnectedDevices) : null; } } /** Retrieves the most recently connected device in the A2DP connected devices list. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public BluetoothDevice getHfpFallbackDevice() { DatabaseManager dbManager = mAdapterService.getDatabase(); return dbManager != null ? dbManager.getMostRecentlyConnectedDevicesInList(getHfpFallbackCandidates()) : null; } @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) List<BluetoothDevice> getHfpFallbackCandidates() { List<BluetoothDevice> fallbackCandidates; synchronized (mLock) { fallbackCandidates = new ArrayList<>(mHfpConnectedDevices); } List<BluetoothDevice> uninterestedCandidates = new ArrayList<>(); for (BluetoothDevice device : fallbackCandidates) { byte[] deviceType = mDbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); BluetoothClass deviceClass = device.getBluetoothClass(); if ((deviceClass != null && deviceClass.getMajorDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) || (deviceType != null && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) { uninterestedCandidates.add(device); } } for (BluetoothDevice device : uninterestedCandidates) { fallbackCandidates.remove(device); } return fallbackCandidates; } @VisibleForTesting @VisibleForTesting BluetoothDevice getA2dpActiveDevice() { BluetoothDevice getA2dpActiveDevice() { return mA2dpActiveDevice; return mA2dpActiveDevice; Loading android/app/src/com/android/bluetooth/hfp/HeadsetService.java +5 −37 Original line number Original line Diff line number Diff line Loading @@ -24,7 +24,6 @@ import static com.android.modules.utils.build.SdkLevel.isAtLeastU; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.RequiresPermission; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile; Loading Loading @@ -54,6 +53,7 @@ import com.android.bluetooth.BluetoothMetricsProto; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.Utils; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.btservice.ActiveDeviceManager; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ProfileService; Loading Loading @@ -125,6 +125,7 @@ public class HeadsetService extends ProfileService { private int mMaxHeadsetConnections = 1; private int mMaxHeadsetConnections = 1; private BluetoothDevice mActiveDevice; private BluetoothDevice mActiveDevice; private AdapterService mAdapterService; private AdapterService mAdapterService; private ActiveDeviceManager mActiveDeviceManager; private DatabaseManager mDatabaseManager; private DatabaseManager mDatabaseManager; private HandlerThread mStateMachinesThread; private HandlerThread mStateMachinesThread; private Handler mStateMachinesThreadHandler; private Handler mStateMachinesThreadHandler; Loading Loading @@ -183,6 +184,7 @@ public class HeadsetService extends ProfileService { "AdapterService cannot be null when HeadsetService starts"); "AdapterService cannot be null when HeadsetService starts"); mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(), mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(), "DatabaseManager cannot be null when HeadsetService starts"); "DatabaseManager cannot be null when HeadsetService starts"); mActiveDeviceManager = Objects.requireNonNull(mAdapterService.getActiveDeviceManager()); // Step 2: Start handler thread for state machines // Step 2: Start handler thread for state machines mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines"); mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines"); mStateMachinesThread.start(); mStateMachinesThread.start(); Loading Loading @@ -1351,7 +1353,7 @@ public class HeadsetService extends ProfileService { // we need to check if another device is connected and set it active instead. // we need to check if another device is connected and set it active instead. // Calling this before any other active related calls has the same effect as // Calling this before any other active related calls has the same effect as // a classic active device switch. // a classic active device switch. BluetoothDevice fallbackDevice = getFallbackDevice(); BluetoothDevice fallbackDevice = mActiveDeviceManager.getHfpFallbackDevice(); if (fallbackDevice != null && mActiveDevice != null if (fallbackDevice != null && mActiveDevice != null && getConnectionState(mActiveDevice) != BluetoothProfile.STATE_CONNECTED) { && getConnectionState(mActiveDevice) != BluetoothProfile.STATE_CONNECTED) { setActiveDevice(fallbackDevice); setActiveDevice(fallbackDevice); Loading Loading @@ -2005,9 +2007,7 @@ public class HeadsetService extends ProfileService { setActiveDevice(null); setActiveDevice(null); } } } } mAdapterService mActiveDeviceManager.hfpConnectionStateChanged(device, fromState, toState); .getActiveDeviceManager() .hfpConnectionStateChanged(device, fromState, toState); } } /** /** Loading Loading @@ -2255,38 +2255,6 @@ public class HeadsetService extends ProfileService { == mStateMachinesThread.getId()); == mStateMachinesThread.getId()); } } /** * Retrieves the most recently connected device in the A2DP connected devices list. */ public BluetoothDevice getFallbackDevice() { DatabaseManager dbManager = mAdapterService.getDatabase(); return dbManager != null ? dbManager .getMostRecentlyConnectedDevicesInList(getFallbackCandidates(dbManager)) : null; } @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) List<BluetoothDevice> getFallbackCandidates(DatabaseManager dbManager) { List<BluetoothDevice> fallbackCandidates = getConnectedDevices(); List<BluetoothDevice> uninterestedCandidates = new ArrayList<>(); for (BluetoothDevice device : fallbackCandidates) { byte[] deviceType = dbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); BluetoothClass deviceClass = device.getBluetoothClass(); if ((deviceClass != null && deviceClass.getMajorDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) || (deviceType != null && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) { uninterestedCandidates.add(device); } } for (BluetoothDevice device : uninterestedCandidates) { fallbackCandidates.remove(device); } return fallbackCandidates; } @Override @Override public void dump(StringBuilder sb) { public void dump(StringBuilder sb) { boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn(); boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn(); Loading android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java +66 −16 Original line number Original line Diff line number Diff line Loading @@ -63,8 +63,9 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Map; @MediumTest @MediumTest @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) Loading Loading @@ -148,21 +149,6 @@ public class ActiveDeviceManagerTest { when(mHearingAidService.getHiSyncId(mHearingAidDevice)).thenReturn(mHearingAidHiSyncId); when(mHearingAidService.getHiSyncId(mHearingAidDevice)).thenReturn(mHearingAidHiSyncId); when(mHearingAidService.getConnectedPeerDevices(mHearingAidHiSyncId)) when(mHearingAidService.getConnectedPeerDevices(mHearingAidHiSyncId)) .thenReturn(connectedHearingAidDevices); .thenReturn(connectedHearingAidDevices); when(mA2dpService.getFallbackDevice()).thenAnswer(invocation -> { if (!mDeviceConnectionStack.isEmpty() && Objects.equals(mA2dpDevice, mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1))) { return mA2dpDevice; } return null; }); when(mHeadsetService.getFallbackDevice()).thenAnswer(invocation -> { if (!mDeviceConnectionStack.isEmpty() && Objects.equals(mHeadsetDevice, mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1))) { return mHeadsetDevice; } return null; }); } } @After @After Loading Loading @@ -1131,6 +1117,49 @@ public class ActiveDeviceManagerTest { verify(mHearingAidService, timeout(TIMEOUT_MS)).removeActiveDevice(false); verify(mHearingAidService, timeout(TIMEOUT_MS)).removeActiveDevice(false); } } @Test public void testGetFallbackCandidates() { BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0); BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1); // No connected device Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().isEmpty()); // One connected device headsetConnected(deviceA, true); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceA)); // Two connected devices headsetConnected(deviceB, false); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceA)); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceB)); } @Test public void testGetFhpFallbackCandidates_HasWatchDevice() { BluetoothDevice deviceWatch = TestUtils.getTestDevice(mAdapter, 0); BluetoothDevice deviceRegular = TestUtils.getTestDevice(mAdapter, 1); // Make deviceWatch a watch mDatabaseManager.setCustomMeta( deviceWatch, BluetoothDevice.METADATA_DEVICE_TYPE, BluetoothDevice.DEVICE_TYPE_WATCH.getBytes()); // Has a connected watch device headsetConnected(deviceWatch, false); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().isEmpty()); // Two connected devices with one watch headsetConnected(deviceRegular, true); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Assert.assertFalse(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceWatch)); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceRegular)); } /** /** * Helper to indicate A2dp connected for a device. * Helper to indicate A2dp connected for a device. */ */ Loading Loading @@ -1297,6 +1326,7 @@ public class ActiveDeviceManagerTest { private class TestDatabaseManager extends DatabaseManager { private class TestDatabaseManager extends DatabaseManager { ArrayMap<BluetoothDevice, SparseIntArray> mProfileConnectionPolicy; ArrayMap<BluetoothDevice, SparseIntArray> mProfileConnectionPolicy; final Map<String, Map<Integer, byte[]>> mMetadataCache = new HashMap<>(); TestDatabaseManager(AdapterService service) { TestDatabaseManager(AdapterService service) { super(service); super(service); Loading Loading @@ -1345,5 +1375,25 @@ public class ActiveDeviceManagerTest { } } return policy.get(profile, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); return policy.get(profile, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } } @Override public boolean setCustomMeta(BluetoothDevice device, int key, byte[] newValue) { Map<Integer, byte[]> metadata = mMetadataCache.get(device.getAddress()); if (metadata == null) { metadata = new ArrayMap<>(); mMetadataCache.put(device.getAddress(), metadata); } metadata.put(key, newValue); return true; } @Override public byte[] getCustomMeta(BluetoothDevice device, int key) { Map<Integer, byte[]> metadata = mMetadataCache.get(device.getAddress()); if (metadata == null) { return null; } return metadata.get(key); } } } } } android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,7 @@ public class ProfileServiceTest { @Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule(); @Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule(); @Mock private AdapterService mMockAdapterService; @Mock private AdapterService mMockAdapterService; @Mock private DatabaseManager mDatabaseManager; @Mock private DatabaseManager mDatabaseManager; @Mock private ActiveDeviceManager mActiveDeviceManager; @Mock private LocationManager mLocationManager; @Mock private LocationManager mLocationManager; private Class[] mProfiles; private Class[] mProfiles; Loading Loading @@ -148,6 +149,7 @@ public class ProfileServiceTest { } } }); }); doReturn(mDatabaseManager).when(mMockAdapterService).getDatabase(); doReturn(mDatabaseManager).when(mMockAdapterService).getDatabase(); doReturn(mActiveDeviceManager).when(mMockAdapterService).getActiveDeviceManager(); when(mMockAdapterService.getSystemService(Context.LOCATION_SERVICE)) when(mMockAdapterService.getSystemService(Context.LOCATION_SERVICE)) .thenReturn(mLocationManager); .thenReturn(mLocationManager); Loading Loading
android/app/src/com/android/bluetooth/a2dp/A2dpService.java +0 −10 Original line number Original line Diff line number Diff line Loading @@ -1275,16 +1275,6 @@ public class A2dpService extends ProfileService { .a2dpConnectionStateChanged(device, fromState, toState); .a2dpConnectionStateChanged(device, fromState, toState); } } /** * Retrieves the most recently connected device in the A2DP connected devices list. */ public BluetoothDevice getFallbackDevice() { DatabaseManager dbManager = mAdapterService.getDatabase(); return dbManager != null ? dbManager .getMostRecentlyConnectedDevicesInList(getConnectedDevices()) : null; } /** /** * Binder object: must be a static class or memory leak may occur. * Binder object: must be a static class or memory leak may occur. */ */ Loading
android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +46 −2 Original line number Original line Diff line number Diff line Loading @@ -1052,13 +1052,13 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac A2dpService a2dpService = mFactory.getA2dpService(); A2dpService a2dpService = mFactory.getA2dpService(); BluetoothDevice a2dpFallbackDevice = null; BluetoothDevice a2dpFallbackDevice = null; if (a2dpService != null) { if (a2dpService != null) { a2dpFallbackDevice = a2dpService.getFallbackDevice(); a2dpFallbackDevice = getA2dpFallbackDevice(); } } HeadsetService headsetService = mFactory.getHeadsetService(); HeadsetService headsetService = mFactory.getHeadsetService(); BluetoothDevice headsetFallbackDevice = null; BluetoothDevice headsetFallbackDevice = null; if (headsetService != null) { if (headsetService != null) { headsetFallbackDevice = headsetService.getFallbackDevice(); headsetFallbackDevice = getHfpFallbackDevice(); } } List<BluetoothDevice> connectedDevices = new ArrayList<>(); List<BluetoothDevice> connectedDevices = new ArrayList<>(); Loading Loading @@ -1168,6 +1168,50 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac } } } } /** Retrieves the most recently connected device in the A2DP connected devices list. */ public BluetoothDevice getA2dpFallbackDevice() { DatabaseManager dbManager = mAdapterService.getDatabase(); synchronized (mLock) { return dbManager != null ? dbManager.getMostRecentlyConnectedDevicesInList(mA2dpConnectedDevices) : null; } } /** Retrieves the most recently connected device in the A2DP connected devices list. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public BluetoothDevice getHfpFallbackDevice() { DatabaseManager dbManager = mAdapterService.getDatabase(); return dbManager != null ? dbManager.getMostRecentlyConnectedDevicesInList(getHfpFallbackCandidates()) : null; } @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) List<BluetoothDevice> getHfpFallbackCandidates() { List<BluetoothDevice> fallbackCandidates; synchronized (mLock) { fallbackCandidates = new ArrayList<>(mHfpConnectedDevices); } List<BluetoothDevice> uninterestedCandidates = new ArrayList<>(); for (BluetoothDevice device : fallbackCandidates) { byte[] deviceType = mDbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); BluetoothClass deviceClass = device.getBluetoothClass(); if ((deviceClass != null && deviceClass.getMajorDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) || (deviceType != null && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) { uninterestedCandidates.add(device); } } for (BluetoothDevice device : uninterestedCandidates) { fallbackCandidates.remove(device); } return fallbackCandidates; } @VisibleForTesting @VisibleForTesting BluetoothDevice getA2dpActiveDevice() { BluetoothDevice getA2dpActiveDevice() { return mA2dpActiveDevice; return mA2dpActiveDevice; Loading
android/app/src/com/android/bluetooth/hfp/HeadsetService.java +5 −37 Original line number Original line Diff line number Diff line Loading @@ -24,7 +24,6 @@ import static com.android.modules.utils.build.SdkLevel.isAtLeastU; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.RequiresPermission; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile; Loading Loading @@ -54,6 +53,7 @@ import com.android.bluetooth.BluetoothMetricsProto; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.Utils; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.btservice.ActiveDeviceManager; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ProfileService; Loading Loading @@ -125,6 +125,7 @@ public class HeadsetService extends ProfileService { private int mMaxHeadsetConnections = 1; private int mMaxHeadsetConnections = 1; private BluetoothDevice mActiveDevice; private BluetoothDevice mActiveDevice; private AdapterService mAdapterService; private AdapterService mAdapterService; private ActiveDeviceManager mActiveDeviceManager; private DatabaseManager mDatabaseManager; private DatabaseManager mDatabaseManager; private HandlerThread mStateMachinesThread; private HandlerThread mStateMachinesThread; private Handler mStateMachinesThreadHandler; private Handler mStateMachinesThreadHandler; Loading Loading @@ -183,6 +184,7 @@ public class HeadsetService extends ProfileService { "AdapterService cannot be null when HeadsetService starts"); "AdapterService cannot be null when HeadsetService starts"); mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(), mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(), "DatabaseManager cannot be null when HeadsetService starts"); "DatabaseManager cannot be null when HeadsetService starts"); mActiveDeviceManager = Objects.requireNonNull(mAdapterService.getActiveDeviceManager()); // Step 2: Start handler thread for state machines // Step 2: Start handler thread for state machines mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines"); mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines"); mStateMachinesThread.start(); mStateMachinesThread.start(); Loading Loading @@ -1351,7 +1353,7 @@ public class HeadsetService extends ProfileService { // we need to check if another device is connected and set it active instead. // we need to check if another device is connected and set it active instead. // Calling this before any other active related calls has the same effect as // Calling this before any other active related calls has the same effect as // a classic active device switch. // a classic active device switch. BluetoothDevice fallbackDevice = getFallbackDevice(); BluetoothDevice fallbackDevice = mActiveDeviceManager.getHfpFallbackDevice(); if (fallbackDevice != null && mActiveDevice != null if (fallbackDevice != null && mActiveDevice != null && getConnectionState(mActiveDevice) != BluetoothProfile.STATE_CONNECTED) { && getConnectionState(mActiveDevice) != BluetoothProfile.STATE_CONNECTED) { setActiveDevice(fallbackDevice); setActiveDevice(fallbackDevice); Loading Loading @@ -2005,9 +2007,7 @@ public class HeadsetService extends ProfileService { setActiveDevice(null); setActiveDevice(null); } } } } mAdapterService mActiveDeviceManager.hfpConnectionStateChanged(device, fromState, toState); .getActiveDeviceManager() .hfpConnectionStateChanged(device, fromState, toState); } } /** /** Loading Loading @@ -2255,38 +2255,6 @@ public class HeadsetService extends ProfileService { == mStateMachinesThread.getId()); == mStateMachinesThread.getId()); } } /** * Retrieves the most recently connected device in the A2DP connected devices list. */ public BluetoothDevice getFallbackDevice() { DatabaseManager dbManager = mAdapterService.getDatabase(); return dbManager != null ? dbManager .getMostRecentlyConnectedDevicesInList(getFallbackCandidates(dbManager)) : null; } @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) List<BluetoothDevice> getFallbackCandidates(DatabaseManager dbManager) { List<BluetoothDevice> fallbackCandidates = getConnectedDevices(); List<BluetoothDevice> uninterestedCandidates = new ArrayList<>(); for (BluetoothDevice device : fallbackCandidates) { byte[] deviceType = dbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); BluetoothClass deviceClass = device.getBluetoothClass(); if ((deviceClass != null && deviceClass.getMajorDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) || (deviceType != null && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) { uninterestedCandidates.add(device); } } for (BluetoothDevice device : uninterestedCandidates) { fallbackCandidates.remove(device); } return fallbackCandidates; } @Override @Override public void dump(StringBuilder sb) { public void dump(StringBuilder sb) { boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn(); boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn(); Loading
android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java +66 −16 Original line number Original line Diff line number Diff line Loading @@ -63,8 +63,9 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Map; @MediumTest @MediumTest @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) Loading Loading @@ -148,21 +149,6 @@ public class ActiveDeviceManagerTest { when(mHearingAidService.getHiSyncId(mHearingAidDevice)).thenReturn(mHearingAidHiSyncId); when(mHearingAidService.getHiSyncId(mHearingAidDevice)).thenReturn(mHearingAidHiSyncId); when(mHearingAidService.getConnectedPeerDevices(mHearingAidHiSyncId)) when(mHearingAidService.getConnectedPeerDevices(mHearingAidHiSyncId)) .thenReturn(connectedHearingAidDevices); .thenReturn(connectedHearingAidDevices); when(mA2dpService.getFallbackDevice()).thenAnswer(invocation -> { if (!mDeviceConnectionStack.isEmpty() && Objects.equals(mA2dpDevice, mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1))) { return mA2dpDevice; } return null; }); when(mHeadsetService.getFallbackDevice()).thenAnswer(invocation -> { if (!mDeviceConnectionStack.isEmpty() && Objects.equals(mHeadsetDevice, mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1))) { return mHeadsetDevice; } return null; }); } } @After @After Loading Loading @@ -1131,6 +1117,49 @@ public class ActiveDeviceManagerTest { verify(mHearingAidService, timeout(TIMEOUT_MS)).removeActiveDevice(false); verify(mHearingAidService, timeout(TIMEOUT_MS)).removeActiveDevice(false); } } @Test public void testGetFallbackCandidates() { BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0); BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1); // No connected device Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().isEmpty()); // One connected device headsetConnected(deviceA, true); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceA)); // Two connected devices headsetConnected(deviceB, false); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceA)); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceB)); } @Test public void testGetFhpFallbackCandidates_HasWatchDevice() { BluetoothDevice deviceWatch = TestUtils.getTestDevice(mAdapter, 0); BluetoothDevice deviceRegular = TestUtils.getTestDevice(mAdapter, 1); // Make deviceWatch a watch mDatabaseManager.setCustomMeta( deviceWatch, BluetoothDevice.METADATA_DEVICE_TYPE, BluetoothDevice.DEVICE_TYPE_WATCH.getBytes()); // Has a connected watch device headsetConnected(deviceWatch, false); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().isEmpty()); // Two connected devices with one watch headsetConnected(deviceRegular, true); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); Assert.assertFalse(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceWatch)); Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceRegular)); } /** /** * Helper to indicate A2dp connected for a device. * Helper to indicate A2dp connected for a device. */ */ Loading Loading @@ -1297,6 +1326,7 @@ public class ActiveDeviceManagerTest { private class TestDatabaseManager extends DatabaseManager { private class TestDatabaseManager extends DatabaseManager { ArrayMap<BluetoothDevice, SparseIntArray> mProfileConnectionPolicy; ArrayMap<BluetoothDevice, SparseIntArray> mProfileConnectionPolicy; final Map<String, Map<Integer, byte[]>> mMetadataCache = new HashMap<>(); TestDatabaseManager(AdapterService service) { TestDatabaseManager(AdapterService service) { super(service); super(service); Loading Loading @@ -1345,5 +1375,25 @@ public class ActiveDeviceManagerTest { } } return policy.get(profile, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); return policy.get(profile, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } } @Override public boolean setCustomMeta(BluetoothDevice device, int key, byte[] newValue) { Map<Integer, byte[]> metadata = mMetadataCache.get(device.getAddress()); if (metadata == null) { metadata = new ArrayMap<>(); mMetadataCache.put(device.getAddress(), metadata); } metadata.put(key, newValue); return true; } @Override public byte[] getCustomMeta(BluetoothDevice device, int key) { Map<Integer, byte[]> metadata = mMetadataCache.get(device.getAddress()); if (metadata == null) { return null; } return metadata.get(key); } } } } }
android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,7 @@ public class ProfileServiceTest { @Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule(); @Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule(); @Mock private AdapterService mMockAdapterService; @Mock private AdapterService mMockAdapterService; @Mock private DatabaseManager mDatabaseManager; @Mock private DatabaseManager mDatabaseManager; @Mock private ActiveDeviceManager mActiveDeviceManager; @Mock private LocationManager mLocationManager; @Mock private LocationManager mLocationManager; private Class[] mProfiles; private Class[] mProfiles; Loading Loading @@ -148,6 +149,7 @@ public class ProfileServiceTest { } } }); }); doReturn(mDatabaseManager).when(mMockAdapterService).getDatabase(); doReturn(mDatabaseManager).when(mMockAdapterService).getDatabase(); doReturn(mActiveDeviceManager).when(mMockAdapterService).getActiveDeviceManager(); when(mMockAdapterService.getSystemService(Context.LOCATION_SERVICE)) when(mMockAdapterService.getSystemService(Context.LOCATION_SERVICE)) .thenReturn(mLocationManager); .thenReturn(mLocationManager); Loading