Loading android/app/src/com/android/bluetooth/btservice/PhonePolicy.java +22 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,7 @@ import java.util.Objects; // 2. When the profile connection-state changes: At this point if a new profile gets CONNECTED we // will try to connect other profiles on the same device. This is to avoid collision if devices // somehow end up trying to connect at same time or general connection issues. class PhonePolicy implements AdapterService.BluetoothStateCallback { public class PhonePolicy implements AdapterService.BluetoothStateCallback { private static final boolean DBG = true; private static final String TAG = "BluetoothPhonePolicy"; Loading @@ -83,6 +83,13 @@ class PhonePolicy implements AdapterService.BluetoothStateCallback { static boolean sIsHfpAutoConnectEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, HFP_AUTO_CONNECT, false); /** flag for multi auto connect */ public static boolean sIsHfpMultiAutoConnectEnabled = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_BLUETOOTH, "com.android.bluetooth.hfp_multi_auto_connect", false); // Timeouts @VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s Loading Loading @@ -623,6 +630,20 @@ class PhonePolicy implements AdapterService.BluetoothStateCallback { return; } if (sIsHfpMultiAutoConnectEnabled) { final List<BluetoothDevice> mostRecentlyConnectedHfpDevices = mDatabaseManager.getMostRecentlyActiveHfpDevices(); for (BluetoothDevice hfpDevice : mostRecentlyConnectedHfpDevices) { debugLog("autoConnect: Headset device: " + hfpDevice); autoConnectHeadset(hfpDevice); } if (mostRecentlyConnectedHfpDevices.size() == 0) { Log.i(TAG, "autoConnect: No device to reconnect to"); } return; } debugLog("HFP multi auto connect is not enabled"); // Try to autoConnect with Hfp only if there was no a2dp valid device final BluetoothDevice mostRecentlyConnectedHfpDevice = mDatabaseManager.getMostRecentlyActiveHfpDevice(); Loading android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java +16 −1 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.util.Log; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.PhonePolicy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; Loading @@ -57,6 +58,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * The active device manager is responsible to handle a Room database Loading Loading @@ -646,7 +648,7 @@ public class DatabaseManager { if (isA2dpDevice) { resetActiveA2dpDevice(); } if (isHfpDevice) { if (isHfpDevice && !PhonePolicy.sIsHfpMultiAutoConnectEnabled) { resetActiveHfpDevice(); } Loading Loading @@ -845,6 +847,19 @@ public class DatabaseManager { return null; } /** * @return the list of device registered as HFP active */ public List<BluetoothDevice> getMostRecentlyActiveHfpDevices() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); synchronized (mMetadataCache) { return mMetadataCache.entrySet().stream() .filter(x -> x.getValue().isActiveHfpDevice) .map(x -> adapter.getRemoteDevice(x.getValue().getAddress())) .collect(Collectors.toList()); } } /** * * @param metadataList is the list of metadata Loading android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java +110 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.bluetooth.btservice; import static com.android.bluetooth.TestUtils.getTestDevice; import static com.android.bluetooth.TestUtils.waitForLooperToFinishScheduledTask; import static com.android.bluetooth.btservice.PhonePolicy.sIsHfpAutoConnectEnabled; import static com.android.bluetooth.btservice.PhonePolicy.sIsHfpMultiAutoConnectEnabled; import static org.mockito.Mockito.*; Loading Loading @@ -660,6 +661,115 @@ public class PhonePolicyTest { } } @Test public void autoConnect_whenMultiHfp_startConnection() { sIsHfpAutoConnectEnabled = true; sIsHfpMultiAutoConnectEnabled = true; try { // Return desired values from the mocked object(s) doReturn(BluetoothAdapter.STATE_ON).when(mAdapterService).getState(); doReturn(false).when(mAdapterService).isQuietModeEnabled(); MetadataDatabase mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getInstrumentation().getTargetContext(), MetadataDatabase.class) .build(); DatabaseManager db = new DatabaseManager(mAdapterService); doReturn(db).when(mAdapterService).getDatabase(); PhonePolicy phonePolicy = new PhonePolicy(mAdapterService, mServiceFactory); db.start(mDatabase); TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); List<BluetoothDevice> devices = List.of( getTestDevice(mAdapter, 1), getTestDevice(mAdapter, 2), getTestDevice(mAdapter, 3)); for (BluetoothDevice device : devices) { db.setConnection(device, BluetoothProfile.HEADSET); doReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED) .when(mHeadsetService) .getConnectionPolicy(eq(device)); } // wait for all MSG_UPDATE_DATABASE TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); phonePolicy.autoConnect(); // Check that we got a request to connect over HFP for each device for (BluetoothDevice device : devices) { verify(mHeadsetService).connect(eq(device)); } } finally { sIsHfpMultiAutoConnectEnabled = false; sIsHfpAutoConnectEnabled = false; } } @Test public void autoConnect_whenMultiHfpAndDeconnection_startConnection() { sIsHfpAutoConnectEnabled = true; sIsHfpMultiAutoConnectEnabled = true; try { // Return desired values from the mocked object(s) doReturn(BluetoothAdapter.STATE_ON).when(mAdapterService).getState(); doReturn(false).when(mAdapterService).isQuietModeEnabled(); MetadataDatabase mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getInstrumentation().getTargetContext(), MetadataDatabase.class) .build(); DatabaseManager db = new DatabaseManager(mAdapterService); doReturn(db).when(mAdapterService).getDatabase(); PhonePolicy phonePolicy = new PhonePolicy(mAdapterService, mServiceFactory); db.start(mDatabase); TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); BluetoothDevice deviceToDeconnect = getTestDevice(mAdapter, 0); db.setConnection(deviceToDeconnect, BluetoothProfile.HEADSET); doReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED) .when(mHeadsetService) .getConnectionPolicy(eq(deviceToDeconnect)); List<BluetoothDevice> devices = List.of( getTestDevice(mAdapter, 1), getTestDevice(mAdapter, 2), getTestDevice(mAdapter, 3)); for (BluetoothDevice device : devices) { db.setConnection(device, BluetoothProfile.HEADSET); doReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED) .when(mHeadsetService) .getConnectionPolicy(eq(device)); } db.setDisconnection(deviceToDeconnect, BluetoothProfile.HEADSET); // wait for all MSG_UPDATE_DATABASE TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); phonePolicy.autoConnect(); // Check that we got a request to connect over HFP for each device for (BluetoothDevice device : devices) { verify(mHeadsetService).connect(eq(device)); } // Except for the device that was manually disconnected verify(mHeadsetService, times(0)).connect(eq(deviceToDeconnect)); } finally { sIsHfpMultiAutoConnectEnabled = false; sIsHfpAutoConnectEnabled = false; } } /** * Test that a second device will auto-connect if there is already one connected device. * Loading Loading
android/app/src/com/android/bluetooth/btservice/PhonePolicy.java +22 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,7 @@ import java.util.Objects; // 2. When the profile connection-state changes: At this point if a new profile gets CONNECTED we // will try to connect other profiles on the same device. This is to avoid collision if devices // somehow end up trying to connect at same time or general connection issues. class PhonePolicy implements AdapterService.BluetoothStateCallback { public class PhonePolicy implements AdapterService.BluetoothStateCallback { private static final boolean DBG = true; private static final String TAG = "BluetoothPhonePolicy"; Loading @@ -83,6 +83,13 @@ class PhonePolicy implements AdapterService.BluetoothStateCallback { static boolean sIsHfpAutoConnectEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, HFP_AUTO_CONNECT, false); /** flag for multi auto connect */ public static boolean sIsHfpMultiAutoConnectEnabled = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_BLUETOOTH, "com.android.bluetooth.hfp_multi_auto_connect", false); // Timeouts @VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s Loading Loading @@ -623,6 +630,20 @@ class PhonePolicy implements AdapterService.BluetoothStateCallback { return; } if (sIsHfpMultiAutoConnectEnabled) { final List<BluetoothDevice> mostRecentlyConnectedHfpDevices = mDatabaseManager.getMostRecentlyActiveHfpDevices(); for (BluetoothDevice hfpDevice : mostRecentlyConnectedHfpDevices) { debugLog("autoConnect: Headset device: " + hfpDevice); autoConnectHeadset(hfpDevice); } if (mostRecentlyConnectedHfpDevices.size() == 0) { Log.i(TAG, "autoConnect: No device to reconnect to"); } return; } debugLog("HFP multi auto connect is not enabled"); // Try to autoConnect with Hfp only if there was no a2dp valid device final BluetoothDevice mostRecentlyConnectedHfpDevice = mDatabaseManager.getMostRecentlyActiveHfpDevice(); Loading
android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java +16 −1 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.util.Log; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.PhonePolicy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; Loading @@ -57,6 +58,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * The active device manager is responsible to handle a Room database Loading Loading @@ -646,7 +648,7 @@ public class DatabaseManager { if (isA2dpDevice) { resetActiveA2dpDevice(); } if (isHfpDevice) { if (isHfpDevice && !PhonePolicy.sIsHfpMultiAutoConnectEnabled) { resetActiveHfpDevice(); } Loading Loading @@ -845,6 +847,19 @@ public class DatabaseManager { return null; } /** * @return the list of device registered as HFP active */ public List<BluetoothDevice> getMostRecentlyActiveHfpDevices() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); synchronized (mMetadataCache) { return mMetadataCache.entrySet().stream() .filter(x -> x.getValue().isActiveHfpDevice) .map(x -> adapter.getRemoteDevice(x.getValue().getAddress())) .collect(Collectors.toList()); } } /** * * @param metadataList is the list of metadata Loading
android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java +110 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.bluetooth.btservice; import static com.android.bluetooth.TestUtils.getTestDevice; import static com.android.bluetooth.TestUtils.waitForLooperToFinishScheduledTask; import static com.android.bluetooth.btservice.PhonePolicy.sIsHfpAutoConnectEnabled; import static com.android.bluetooth.btservice.PhonePolicy.sIsHfpMultiAutoConnectEnabled; import static org.mockito.Mockito.*; Loading Loading @@ -660,6 +661,115 @@ public class PhonePolicyTest { } } @Test public void autoConnect_whenMultiHfp_startConnection() { sIsHfpAutoConnectEnabled = true; sIsHfpMultiAutoConnectEnabled = true; try { // Return desired values from the mocked object(s) doReturn(BluetoothAdapter.STATE_ON).when(mAdapterService).getState(); doReturn(false).when(mAdapterService).isQuietModeEnabled(); MetadataDatabase mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getInstrumentation().getTargetContext(), MetadataDatabase.class) .build(); DatabaseManager db = new DatabaseManager(mAdapterService); doReturn(db).when(mAdapterService).getDatabase(); PhonePolicy phonePolicy = new PhonePolicy(mAdapterService, mServiceFactory); db.start(mDatabase); TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); List<BluetoothDevice> devices = List.of( getTestDevice(mAdapter, 1), getTestDevice(mAdapter, 2), getTestDevice(mAdapter, 3)); for (BluetoothDevice device : devices) { db.setConnection(device, BluetoothProfile.HEADSET); doReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED) .when(mHeadsetService) .getConnectionPolicy(eq(device)); } // wait for all MSG_UPDATE_DATABASE TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); phonePolicy.autoConnect(); // Check that we got a request to connect over HFP for each device for (BluetoothDevice device : devices) { verify(mHeadsetService).connect(eq(device)); } } finally { sIsHfpMultiAutoConnectEnabled = false; sIsHfpAutoConnectEnabled = false; } } @Test public void autoConnect_whenMultiHfpAndDeconnection_startConnection() { sIsHfpAutoConnectEnabled = true; sIsHfpMultiAutoConnectEnabled = true; try { // Return desired values from the mocked object(s) doReturn(BluetoothAdapter.STATE_ON).when(mAdapterService).getState(); doReturn(false).when(mAdapterService).isQuietModeEnabled(); MetadataDatabase mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getInstrumentation().getTargetContext(), MetadataDatabase.class) .build(); DatabaseManager db = new DatabaseManager(mAdapterService); doReturn(db).when(mAdapterService).getDatabase(); PhonePolicy phonePolicy = new PhonePolicy(mAdapterService, mServiceFactory); db.start(mDatabase); TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); BluetoothDevice deviceToDeconnect = getTestDevice(mAdapter, 0); db.setConnection(deviceToDeconnect, BluetoothProfile.HEADSET); doReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED) .when(mHeadsetService) .getConnectionPolicy(eq(deviceToDeconnect)); List<BluetoothDevice> devices = List.of( getTestDevice(mAdapter, 1), getTestDevice(mAdapter, 2), getTestDevice(mAdapter, 3)); for (BluetoothDevice device : devices) { db.setConnection(device, BluetoothProfile.HEADSET); doReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED) .when(mHeadsetService) .getConnectionPolicy(eq(device)); } db.setDisconnection(deviceToDeconnect, BluetoothProfile.HEADSET); // wait for all MSG_UPDATE_DATABASE TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); phonePolicy.autoConnect(); // Check that we got a request to connect over HFP for each device for (BluetoothDevice device : devices) { verify(mHeadsetService).connect(eq(device)); } // Except for the device that was manually disconnected verify(mHeadsetService, times(0)).connect(eq(deviceToDeconnect)); } finally { sIsHfpMultiAutoConnectEnabled = false; sIsHfpAutoConnectEnabled = false; } } /** * Test that a second device will auto-connect if there is already one connected device. * Loading