Loading android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +68 −88 Original line number Original line Diff line number Diff line Loading @@ -42,7 +42,7 @@ import java.util.Objects; /** /** * The active device manager is responsible for keeping track of the * The active device manager is responsible for keeping track of the * connected A2DP/HFP/AVRCP devices and select which device is * connected A2DP/HFP/AVRCP/HearingAid devices and select which device is * active (for each profile). * active (for each profile). * * * Current policy (subject to change): * Current policy (subject to change): Loading @@ -52,36 +52,30 @@ import java.util.Objects; * devices is more than one, the rules below will apply. * devices is more than one, the rules below will apply. * 2) The selected A2DP active device is the one used for AVRCP as well. * 2) The selected A2DP active device is the one used for AVRCP as well. * 3) The HFP active device might be different from the A2DP active device. * 3) The HFP active device might be different from the A2DP active device. * 4) The Active Device Manager always listens for * 4) The Active Device Manager always listens for ACTION_ACTIVE_DEVICE_CHANGED * ACTION_ACTIVE_DEVICE_CHANGED broadcasts for each profile: * broadcasts for each profile: * - BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED for A2DP * - BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED for A2DP * - BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED for HFP * - BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED for HFP * - BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED for HearingAid * If such broadcast is received (e.g., triggered indirectly by user * If such broadcast is received (e.g., triggered indirectly by user * action on the UI), the device in the received broacast is marked * action on the UI), the device in the received broacast is marked * as the current active device for that profile. * as the current active device for that profile. * 5) If there are no connected devices (e.g., during startup, or after all * 5) If there is a HearingAid active device, then A2DP and HFP active devices * must be set to null (i.e., A2DP and HFP cannot have active devices). * The reason is because A2DP or HFP cannot be used together with HearingAid. * 6) If there are no connected devices (e.g., during startup, or after all * devices have been disconnected, the active device per profile * devices have been disconnected, the active device per profile * (either A2DP or HFP) is selected as follows: * (A2DP/HFP/HearingAid) is selected as follows: * 5.1) The first connected device (for either A2DP or HFP) is immediately * 6.1) The last connected HearingAid device is selected as active. * selected as active for that profile. Assume the first connected device * If there is an active A2DP or HFP device, those must be set to null. * is for A2DP. * 6.2) The last connected A2DP or HFP device is selected as active. * 5.2) A timer is started: if the same device is connected for the other * However, if there is an active HearingAid device, then the * profile as well (HFP in this example) while the timer is running, * A2DP or HFP active device is not set (must remain null). * and there is no active HFP device yet, that device is selected as * 7) If the currently active device (per profile) is disconnected, the * active for HFP as well. The purpose is to select by default the same * device as active for both profiles. * 5.3) While the timer is running, all other HFP connected devices are * listed locally, but none of those devices is selected as active. * 5.4) While the timer is running, if ACTION_ACTIVE_DEVICE_CHANGED broadcast * is received for HFP, the device contained in the broadcast is * marked as active. * 5.5) If the timer expires and no HFP device has been selected as active, * the first HFP connected device is selected as active. * 6) If the currently active device (per profile) is disconnected, the * Active Device Manager just marks that the profile has no active device, * Active Device Manager just marks that the profile has no active device, * but does not attempt to select a new one. Currently, the expectation is * but does not attempt to select a new one. Currently, the expectation is * that the user will explicitly select the new active device. * that the user will explicitly select the new active device. * 7) If there is already an active device, and the corresponding * 8) If there is already an active device, and the corresponding * ACTION_ACTIVE_DEVICE_CHANGED broadcast is received, the device * ACTION_ACTIVE_DEVICE_CHANGED broadcast is received, the device * contained in the broadcast is marked as active. However, if * contained in the broadcast is marked as active. However, if * the contained device is null, the corresponding profile is marked * the contained device is null, the corresponding profile is marked Loading @@ -93,15 +87,11 @@ class ActiveDeviceManager { // Message types for the handler // Message types for the handler private static final int MESSAGE_ADAPTER_ACTION_STATE_CHANGED = 1; private static final int MESSAGE_ADAPTER_ACTION_STATE_CHANGED = 1; private static final int MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT = 2; private static final int MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED = 2; private static final int MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED = 3; private static final int MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED = 3; private static final int MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED = 4; private static final int MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED = 4; private static final int MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED = 5; private static final int MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED = 5; private static final int MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED = 6; private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 6; private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 7; // Timeouts private static final int SELECT_ACTIVE_DEVICE_TIMEOUT_MS = 6000; // 6s private final AdapterService mAdapterService; private final AdapterService mAdapterService; private final ServiceFactory mFactory; private final ServiceFactory mFactory; Loading Loading @@ -155,8 +145,8 @@ class ActiveDeviceManager { } } }; }; class ActivePoliceManagerHandler extends Handler { class ActiveDeviceManagerHandler extends Handler { ActivePoliceManagerHandler(Looper looper) { ActiveDeviceManagerHandler(Looper looper) { super(looper); super(looper); } } Loading @@ -176,28 +166,16 @@ class ActiveDeviceManager { } } break; break; case MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT: { if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT)"); } // Set the first connected device as active if ((mA2dpActiveDevice == null) && !mA2dpConnectedDevices.isEmpty() && mHearingAidActiveDevice == null) { setA2dpActiveDevice(mA2dpConnectedDevices.get(0)); } if ((mHfpActiveDevice == null) && !mHfpConnectedDevices.isEmpty() && mHearingAidActiveDevice == null) { setHfpActiveDevice(mHfpConnectedDevices.get(0)); } } break; case MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED: { case MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED: { Intent intent = (Intent) msg.obj; Intent intent = (Intent) msg.obj; BluetoothDevice device = BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); if (prevState == nextState) { // Nothing has changed break; } if (nextState == BluetoothProfile.STATE_CONNECTED) { if (nextState == BluetoothProfile.STATE_CONNECTED) { // Device connected // Device connected if (DBG) { if (DBG) { Loading @@ -206,34 +184,17 @@ class ActiveDeviceManager { + "device " + device + " connected"); + "device " + device + " connected"); } } if (mA2dpConnectedDevices.contains(device)) { if (mA2dpConnectedDevices.contains(device)) { break; break; // The device is already connected } } if (!hasConnectedClassicDevices() && mHearingAidActiveDevice == null) { // First connected device: select it as active and start the timer mA2dpConnectedDevices.add(device); mA2dpConnectedDevices.add(device); Message m = obtainMessage(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); if (mHearingAidActiveDevice == null) { sendMessageDelayed(m, SELECT_ACTIVE_DEVICE_TIMEOUT_MS); // New connected device: select it as active setA2dpActiveDevice(device); setA2dpActiveDevice(device); break; break; } } mA2dpConnectedDevices.add(device); // Check whether the active device for the other profile is same if ((mA2dpActiveDevice == null) && matchesActiveDevice(device) && mHearingAidActiveDevice == null) { setA2dpActiveDevice(device); break; } // Check whether the active device selection timer is not running if ((mA2dpActiveDevice == null) && !hasMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT) && mHearingAidActiveDevice == null) { setA2dpActiveDevice(mA2dpConnectedDevices.get(0)); break; } break; break; } } if ((prevState == BluetoothProfile.STATE_CONNECTED) if (prevState == BluetoothProfile.STATE_CONNECTED) { && (nextState != prevState)) { // Device disconnected // Device disconnected if (DBG) { if (DBG) { Log.d(TAG, Log.d(TAG, Loading @@ -256,7 +217,6 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED): " Log.d(TAG, "handleMessage(MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); + "device= " + device); } } removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); if (device != null && !Objects.equals(mA2dpActiveDevice, device)) { if (device != null && !Objects.equals(mA2dpActiveDevice, device)) { setHearingAidActiveDevice(null); setHearingAidActiveDevice(null); } } Loading @@ -271,8 +231,40 @@ class ActiveDeviceManager { intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); // TODO: Copy the corresponding logic from the processing of if (prevState == nextState) { // message MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED // Nothing has changed break; } if (nextState == BluetoothProfile.STATE_CONNECTED) { // Device connected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED): " + "device " + device + " connected"); } if (mHfpConnectedDevices.contains(device)) { break; // The device is already connected } mHfpConnectedDevices.add(device); if (mHearingAidActiveDevice == null) { // New connected device: select it as active setHfpActiveDevice(device); break; } break; } if (prevState == BluetoothProfile.STATE_CONNECTED) { // Device disconnected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED): " + "device " + device + " disconnected"); } mHfpConnectedDevices.remove(device); if (Objects.equals(mHfpActiveDevice, device)) { setHfpActiveDevice(null); } } } } break; break; Loading @@ -284,7 +276,6 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED): " Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); + "device= " + device); } } removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); if (device != null && !Objects.equals(mHfpActiveDevice, device)) { if (device != null && !Objects.equals(mHfpActiveDevice, device)) { setHearingAidActiveDevice(null); setHearingAidActiveDevice(null); } } Loading @@ -301,7 +292,6 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_HA_ACTION_ACTIVE_DEVICE_CHANGED): " Log.d(TAG, "handleMessage(MESSAGE_HA_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); + "device= " + device); } } removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); // Just assign locally the new value // Just assign locally the new value mHearingAidActiveDevice = device; mHearingAidActiveDevice = device; if (device != null) { if (device != null) { Loading @@ -326,7 +316,7 @@ class ActiveDeviceManager { mHandlerThread = new HandlerThread("BluetoothActiveDeviceManager"); mHandlerThread = new HandlerThread("BluetoothActiveDeviceManager"); mHandlerThread.start(); mHandlerThread.start(); mHandler = new ActivePoliceManagerHandler(mHandlerThread.getLooper()); mHandler = new ActiveDeviceManagerHandler(mHandlerThread.getLooper()); IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); Loading Loading @@ -393,23 +383,13 @@ class ActiveDeviceManager { mHearingAidActiveDevice = device; mHearingAidActiveDevice = device; } } private boolean hasConnectedClassicDevices() { return (!mA2dpConnectedDevices.isEmpty() || !mHfpConnectedDevices.isEmpty()); } private boolean matchesActiveDevice(BluetoothDevice device) { return (Objects.equals(mA2dpActiveDevice, device) || Objects.equals(mHfpActiveDevice, device)); } private void resetState() { private void resetState() { if (mHandler != null) { mHandler.removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); } mA2dpConnectedDevices.clear(); mA2dpConnectedDevices.clear(); mA2dpActiveDevice = null; mA2dpActiveDevice = null; mHfpConnectedDevices.clear(); mHfpConnectedDevices.clear(); mHfpActiveDevice = null; mHfpActiveDevice = null; mHearingAidActiveDevice = null; } } } } android/app/src/com/android/bluetooth/hfp/HeadsetService.java +0 −3 Original line number Original line Diff line number Diff line Loading @@ -1156,9 +1156,6 @@ public class HeadsetService extends ProfileService { stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, 0)); 0)); } } if (mActiveDevice == null) { setActiveDevice(device); } MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET); } } if (fromState == BluetoothProfile.STATE_CONNECTED if (fromState == BluetoothProfile.STATE_CONNECTED Loading android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java +0 −1 Original line number Original line Diff line number Diff line Loading @@ -206,7 +206,6 @@ public class HeadsetServiceAndStateMachineTest { Assert.assertEquals(Collections.singletonList(mCurrentDevice), Assert.assertEquals(Collections.singletonList(mCurrentDevice), mHeadsetService.getDevicesMatchingConnectionStates( mHeadsetService.getDevicesMatchingConnectionStates( new int[]{BluetoothProfile.STATE_CONNECTED})); new int[]{BluetoothProfile.STATE_CONNECTED})); Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); } } /** /** Loading android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java +10 −12 Original line number Original line Diff line number Diff line Loading @@ -354,7 +354,8 @@ public class HeadsetServiceTest { mHeadsetService.getConnectedDevices()); mHeadsetService.getConnectedDevices()); mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); // Test connect audio, the first connected device should be the default active device // Test connect audio - set the device first as the active device Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice); mCurrentDevice); Loading Loading @@ -418,16 +419,11 @@ public class HeadsetServiceTest { Assert.assertThat(mHeadsetService.getConnectedDevices(), Assert.assertThat(mHeadsetService.getConnectedDevices(), Matchers.containsInAnyOrder(connectedDevices.toArray())); Matchers.containsInAnyOrder(connectedDevices.toArray())); // Try to connect audio // Try to connect audio if (i == 0) { // Should fail // Should only succeed with the first device Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); } else { // Should fail for other devices Assert.assertFalse(mHeadsetService.connectAudio(mCurrentDevice)); Assert.assertFalse(mHeadsetService.connectAudio(mCurrentDevice)); // Should succeed after setActiveDevice() // Should succeed after setActiveDevice() Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); } verify(mStateMachines.get(mCurrentDevice)).sendMessage( verify(mStateMachines.get(mCurrentDevice)).sendMessage( HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice); HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice); // Put device to audio connecting state // Put device to audio connecting state Loading Loading @@ -500,7 +496,8 @@ public class HeadsetServiceTest { // Try to connect audio // Try to connect audio BluetoothDevice firstDevice = connectedDevices.get(0); BluetoothDevice firstDevice = connectedDevices.get(0); BluetoothDevice secondDevice = connectedDevices.get(1); BluetoothDevice secondDevice = connectedDevices.get(1); // First device is the default device // Set the first device as the active device Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice)); Assert.assertTrue(mHeadsetService.connectAudio(firstDevice)); Assert.assertTrue(mHeadsetService.connectAudio(firstDevice)); verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, firstDevice); firstDevice); Loading Loading @@ -572,6 +569,7 @@ public class HeadsetServiceTest { } } // Try to connect audio // Try to connect audio BluetoothDevice firstDevice = connectedDevices.get(0); BluetoothDevice firstDevice = connectedDevices.get(0); Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice)); Assert.assertTrue(mHeadsetService.connectAudio()); Assert.assertTrue(mHeadsetService.connectAudio()); verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, firstDevice); firstDevice); Loading Loading
android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +68 −88 Original line number Original line Diff line number Diff line Loading @@ -42,7 +42,7 @@ import java.util.Objects; /** /** * The active device manager is responsible for keeping track of the * The active device manager is responsible for keeping track of the * connected A2DP/HFP/AVRCP devices and select which device is * connected A2DP/HFP/AVRCP/HearingAid devices and select which device is * active (for each profile). * active (for each profile). * * * Current policy (subject to change): * Current policy (subject to change): Loading @@ -52,36 +52,30 @@ import java.util.Objects; * devices is more than one, the rules below will apply. * devices is more than one, the rules below will apply. * 2) The selected A2DP active device is the one used for AVRCP as well. * 2) The selected A2DP active device is the one used for AVRCP as well. * 3) The HFP active device might be different from the A2DP active device. * 3) The HFP active device might be different from the A2DP active device. * 4) The Active Device Manager always listens for * 4) The Active Device Manager always listens for ACTION_ACTIVE_DEVICE_CHANGED * ACTION_ACTIVE_DEVICE_CHANGED broadcasts for each profile: * broadcasts for each profile: * - BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED for A2DP * - BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED for A2DP * - BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED for HFP * - BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED for HFP * - BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED for HearingAid * If such broadcast is received (e.g., triggered indirectly by user * If such broadcast is received (e.g., triggered indirectly by user * action on the UI), the device in the received broacast is marked * action on the UI), the device in the received broacast is marked * as the current active device for that profile. * as the current active device for that profile. * 5) If there are no connected devices (e.g., during startup, or after all * 5) If there is a HearingAid active device, then A2DP and HFP active devices * must be set to null (i.e., A2DP and HFP cannot have active devices). * The reason is because A2DP or HFP cannot be used together with HearingAid. * 6) If there are no connected devices (e.g., during startup, or after all * devices have been disconnected, the active device per profile * devices have been disconnected, the active device per profile * (either A2DP or HFP) is selected as follows: * (A2DP/HFP/HearingAid) is selected as follows: * 5.1) The first connected device (for either A2DP or HFP) is immediately * 6.1) The last connected HearingAid device is selected as active. * selected as active for that profile. Assume the first connected device * If there is an active A2DP or HFP device, those must be set to null. * is for A2DP. * 6.2) The last connected A2DP or HFP device is selected as active. * 5.2) A timer is started: if the same device is connected for the other * However, if there is an active HearingAid device, then the * profile as well (HFP in this example) while the timer is running, * A2DP or HFP active device is not set (must remain null). * and there is no active HFP device yet, that device is selected as * 7) If the currently active device (per profile) is disconnected, the * active for HFP as well. The purpose is to select by default the same * device as active for both profiles. * 5.3) While the timer is running, all other HFP connected devices are * listed locally, but none of those devices is selected as active. * 5.4) While the timer is running, if ACTION_ACTIVE_DEVICE_CHANGED broadcast * is received for HFP, the device contained in the broadcast is * marked as active. * 5.5) If the timer expires and no HFP device has been selected as active, * the first HFP connected device is selected as active. * 6) If the currently active device (per profile) is disconnected, the * Active Device Manager just marks that the profile has no active device, * Active Device Manager just marks that the profile has no active device, * but does not attempt to select a new one. Currently, the expectation is * but does not attempt to select a new one. Currently, the expectation is * that the user will explicitly select the new active device. * that the user will explicitly select the new active device. * 7) If there is already an active device, and the corresponding * 8) If there is already an active device, and the corresponding * ACTION_ACTIVE_DEVICE_CHANGED broadcast is received, the device * ACTION_ACTIVE_DEVICE_CHANGED broadcast is received, the device * contained in the broadcast is marked as active. However, if * contained in the broadcast is marked as active. However, if * the contained device is null, the corresponding profile is marked * the contained device is null, the corresponding profile is marked Loading @@ -93,15 +87,11 @@ class ActiveDeviceManager { // Message types for the handler // Message types for the handler private static final int MESSAGE_ADAPTER_ACTION_STATE_CHANGED = 1; private static final int MESSAGE_ADAPTER_ACTION_STATE_CHANGED = 1; private static final int MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT = 2; private static final int MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED = 2; private static final int MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED = 3; private static final int MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED = 3; private static final int MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED = 4; private static final int MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED = 4; private static final int MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED = 5; private static final int MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED = 5; private static final int MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED = 6; private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 6; private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 7; // Timeouts private static final int SELECT_ACTIVE_DEVICE_TIMEOUT_MS = 6000; // 6s private final AdapterService mAdapterService; private final AdapterService mAdapterService; private final ServiceFactory mFactory; private final ServiceFactory mFactory; Loading Loading @@ -155,8 +145,8 @@ class ActiveDeviceManager { } } }; }; class ActivePoliceManagerHandler extends Handler { class ActiveDeviceManagerHandler extends Handler { ActivePoliceManagerHandler(Looper looper) { ActiveDeviceManagerHandler(Looper looper) { super(looper); super(looper); } } Loading @@ -176,28 +166,16 @@ class ActiveDeviceManager { } } break; break; case MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT: { if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT)"); } // Set the first connected device as active if ((mA2dpActiveDevice == null) && !mA2dpConnectedDevices.isEmpty() && mHearingAidActiveDevice == null) { setA2dpActiveDevice(mA2dpConnectedDevices.get(0)); } if ((mHfpActiveDevice == null) && !mHfpConnectedDevices.isEmpty() && mHearingAidActiveDevice == null) { setHfpActiveDevice(mHfpConnectedDevices.get(0)); } } break; case MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED: { case MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED: { Intent intent = (Intent) msg.obj; Intent intent = (Intent) msg.obj; BluetoothDevice device = BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); if (prevState == nextState) { // Nothing has changed break; } if (nextState == BluetoothProfile.STATE_CONNECTED) { if (nextState == BluetoothProfile.STATE_CONNECTED) { // Device connected // Device connected if (DBG) { if (DBG) { Loading @@ -206,34 +184,17 @@ class ActiveDeviceManager { + "device " + device + " connected"); + "device " + device + " connected"); } } if (mA2dpConnectedDevices.contains(device)) { if (mA2dpConnectedDevices.contains(device)) { break; break; // The device is already connected } } if (!hasConnectedClassicDevices() && mHearingAidActiveDevice == null) { // First connected device: select it as active and start the timer mA2dpConnectedDevices.add(device); mA2dpConnectedDevices.add(device); Message m = obtainMessage(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); if (mHearingAidActiveDevice == null) { sendMessageDelayed(m, SELECT_ACTIVE_DEVICE_TIMEOUT_MS); // New connected device: select it as active setA2dpActiveDevice(device); setA2dpActiveDevice(device); break; break; } } mA2dpConnectedDevices.add(device); // Check whether the active device for the other profile is same if ((mA2dpActiveDevice == null) && matchesActiveDevice(device) && mHearingAidActiveDevice == null) { setA2dpActiveDevice(device); break; } // Check whether the active device selection timer is not running if ((mA2dpActiveDevice == null) && !hasMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT) && mHearingAidActiveDevice == null) { setA2dpActiveDevice(mA2dpConnectedDevices.get(0)); break; } break; break; } } if ((prevState == BluetoothProfile.STATE_CONNECTED) if (prevState == BluetoothProfile.STATE_CONNECTED) { && (nextState != prevState)) { // Device disconnected // Device disconnected if (DBG) { if (DBG) { Log.d(TAG, Log.d(TAG, Loading @@ -256,7 +217,6 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED): " Log.d(TAG, "handleMessage(MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); + "device= " + device); } } removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); if (device != null && !Objects.equals(mA2dpActiveDevice, device)) { if (device != null && !Objects.equals(mA2dpActiveDevice, device)) { setHearingAidActiveDevice(null); setHearingAidActiveDevice(null); } } Loading @@ -271,8 +231,40 @@ class ActiveDeviceManager { intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); // TODO: Copy the corresponding logic from the processing of if (prevState == nextState) { // message MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED // Nothing has changed break; } if (nextState == BluetoothProfile.STATE_CONNECTED) { // Device connected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED): " + "device " + device + " connected"); } if (mHfpConnectedDevices.contains(device)) { break; // The device is already connected } mHfpConnectedDevices.add(device); if (mHearingAidActiveDevice == null) { // New connected device: select it as active setHfpActiveDevice(device); break; } break; } if (prevState == BluetoothProfile.STATE_CONNECTED) { // Device disconnected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED): " + "device " + device + " disconnected"); } mHfpConnectedDevices.remove(device); if (Objects.equals(mHfpActiveDevice, device)) { setHfpActiveDevice(null); } } } } break; break; Loading @@ -284,7 +276,6 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED): " Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); + "device= " + device); } } removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); if (device != null && !Objects.equals(mHfpActiveDevice, device)) { if (device != null && !Objects.equals(mHfpActiveDevice, device)) { setHearingAidActiveDevice(null); setHearingAidActiveDevice(null); } } Loading @@ -301,7 +292,6 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_HA_ACTION_ACTIVE_DEVICE_CHANGED): " Log.d(TAG, "handleMessage(MESSAGE_HA_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); + "device= " + device); } } removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); // Just assign locally the new value // Just assign locally the new value mHearingAidActiveDevice = device; mHearingAidActiveDevice = device; if (device != null) { if (device != null) { Loading @@ -326,7 +316,7 @@ class ActiveDeviceManager { mHandlerThread = new HandlerThread("BluetoothActiveDeviceManager"); mHandlerThread = new HandlerThread("BluetoothActiveDeviceManager"); mHandlerThread.start(); mHandlerThread.start(); mHandler = new ActivePoliceManagerHandler(mHandlerThread.getLooper()); mHandler = new ActiveDeviceManagerHandler(mHandlerThread.getLooper()); IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); Loading Loading @@ -393,23 +383,13 @@ class ActiveDeviceManager { mHearingAidActiveDevice = device; mHearingAidActiveDevice = device; } } private boolean hasConnectedClassicDevices() { return (!mA2dpConnectedDevices.isEmpty() || !mHfpConnectedDevices.isEmpty()); } private boolean matchesActiveDevice(BluetoothDevice device) { return (Objects.equals(mA2dpActiveDevice, device) || Objects.equals(mHfpActiveDevice, device)); } private void resetState() { private void resetState() { if (mHandler != null) { mHandler.removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT); } mA2dpConnectedDevices.clear(); mA2dpConnectedDevices.clear(); mA2dpActiveDevice = null; mA2dpActiveDevice = null; mHfpConnectedDevices.clear(); mHfpConnectedDevices.clear(); mHfpActiveDevice = null; mHfpActiveDevice = null; mHearingAidActiveDevice = null; } } } }
android/app/src/com/android/bluetooth/hfp/HeadsetService.java +0 −3 Original line number Original line Diff line number Diff line Loading @@ -1156,9 +1156,6 @@ public class HeadsetService extends ProfileService { stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, 0)); 0)); } } if (mActiveDevice == null) { setActiveDevice(device); } MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET); } } if (fromState == BluetoothProfile.STATE_CONNECTED if (fromState == BluetoothProfile.STATE_CONNECTED Loading
android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java +0 −1 Original line number Original line Diff line number Diff line Loading @@ -206,7 +206,6 @@ public class HeadsetServiceAndStateMachineTest { Assert.assertEquals(Collections.singletonList(mCurrentDevice), Assert.assertEquals(Collections.singletonList(mCurrentDevice), mHeadsetService.getDevicesMatchingConnectionStates( mHeadsetService.getDevicesMatchingConnectionStates( new int[]{BluetoothProfile.STATE_CONNECTED})); new int[]{BluetoothProfile.STATE_CONNECTED})); Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); } } /** /** Loading
android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java +10 −12 Original line number Original line Diff line number Diff line Loading @@ -354,7 +354,8 @@ public class HeadsetServiceTest { mHeadsetService.getConnectedDevices()); mHeadsetService.getConnectedDevices()); mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); // Test connect audio, the first connected device should be the default active device // Test connect audio - set the device first as the active device Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice); mCurrentDevice); Loading Loading @@ -418,16 +419,11 @@ public class HeadsetServiceTest { Assert.assertThat(mHeadsetService.getConnectedDevices(), Assert.assertThat(mHeadsetService.getConnectedDevices(), Matchers.containsInAnyOrder(connectedDevices.toArray())); Matchers.containsInAnyOrder(connectedDevices.toArray())); // Try to connect audio // Try to connect audio if (i == 0) { // Should fail // Should only succeed with the first device Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); } else { // Should fail for other devices Assert.assertFalse(mHeadsetService.connectAudio(mCurrentDevice)); Assert.assertFalse(mHeadsetService.connectAudio(mCurrentDevice)); // Should succeed after setActiveDevice() // Should succeed after setActiveDevice() Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); } verify(mStateMachines.get(mCurrentDevice)).sendMessage( verify(mStateMachines.get(mCurrentDevice)).sendMessage( HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice); HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice); // Put device to audio connecting state // Put device to audio connecting state Loading Loading @@ -500,7 +496,8 @@ public class HeadsetServiceTest { // Try to connect audio // Try to connect audio BluetoothDevice firstDevice = connectedDevices.get(0); BluetoothDevice firstDevice = connectedDevices.get(0); BluetoothDevice secondDevice = connectedDevices.get(1); BluetoothDevice secondDevice = connectedDevices.get(1); // First device is the default device // Set the first device as the active device Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice)); Assert.assertTrue(mHeadsetService.connectAudio(firstDevice)); Assert.assertTrue(mHeadsetService.connectAudio(firstDevice)); verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, firstDevice); firstDevice); Loading Loading @@ -572,6 +569,7 @@ public class HeadsetServiceTest { } } // Try to connect audio // Try to connect audio BluetoothDevice firstDevice = connectedDevices.get(0); BluetoothDevice firstDevice = connectedDevices.get(0); Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice)); Assert.assertTrue(mHeadsetService.connectAudio()); Assert.assertTrue(mHeadsetService.connectAudio()); verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, firstDevice); firstDevice); Loading