Loading android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +94 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.SuppressLint; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHapClient; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; Loading Loading @@ -116,6 +117,8 @@ class ActiveDeviceManager { private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 6; private static final int MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE_CHANGED = 8; private static final int MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED = 9; private static final int MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED = 10; private static final int MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED = 11; private final AdapterService mAdapterService; private final ServiceFactory mFactory; Loading @@ -127,10 +130,12 @@ class ActiveDeviceManager { private final List<BluetoothDevice> mA2dpConnectedDevices = new LinkedList<>(); private final List<BluetoothDevice> mHfpConnectedDevices = new LinkedList<>(); private final List<BluetoothDevice> mLeAudioConnectedDevices = new LinkedList<>(); private final List<BluetoothDevice> mLeHearingAidConnectedDevices = new LinkedList<>(); private BluetoothDevice mA2dpActiveDevice = null; private BluetoothDevice mHfpActiveDevice = null; private BluetoothDevice mHearingAidActiveDevice = null; private BluetoothDevice mLeAudioActiveDevice = null; private BluetoothDevice mLeHearingAidActiveDevice = null; // Broadcast receiver for all changes private final BroadcastReceiver mReceiver = new BroadcastReceiver() { Loading Loading @@ -174,6 +179,14 @@ class ActiveDeviceManager { mHandler.obtainMessage(MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED, intent).sendToTarget(); break; case BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED: mHandler.obtainMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED, intent).sendToTarget(); break; case BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE: mHandler.obtainMessage(MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED, intent).sendToTarget(); break; default: Log.e(TAG, "Received unexpected intent, action=" + action); break; Loading Loading @@ -223,7 +236,7 @@ class ActiveDeviceManager { break; // The device is already connected } mA2dpConnectedDevices.add(device); if (mHearingAidActiveDevice == null) { if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) { // New connected device: select it as active setA2dpActiveDevice(device); setLeAudioActiveDevice(null); Loading Loading @@ -285,7 +298,7 @@ class ActiveDeviceManager { break; // The device is already connected } mHfpConnectedDevices.add(device); if (mHearingAidActiveDevice == null) { if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) { // New connected device: select it as active setHfpActiveDevice(device); setLeAudioActiveDevice(null); Loading Loading @@ -364,7 +377,7 @@ class ActiveDeviceManager { break; // The device is already connected } mLeAudioConnectedDevices.add(device); if (mHearingAidActiveDevice == null) { if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) { // New connected device: select it as active setLeAudioActiveDevice(device); setA2dpActiveDevice(null); Loading Loading @@ -407,6 +420,79 @@ class ActiveDeviceManager { mLeAudioActiveDevice = device; } break; case MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED: { Intent intent = (Intent) msg.obj; BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); if (prevState == nextState) { // Nothing has changed break; } if (nextState == BluetoothProfile.STATE_CONNECTED) { // Device connected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE" + "_CHANGED): device " + device + " connected"); } if (mLeHearingAidConnectedDevices.contains(device)) { break; // The device is already connected } mLeHearingAidConnectedDevices.add(device); if (!mLeAudioConnectedDevices.contains(device)) { mLeAudioConnectedDevices.add(device); } // New connected device: select it as active setLeAudioActiveDevice(device); if (mLeAudioActiveDevice == device) { // setLeAudioActiveDevice succeed mLeHearingAidActiveDevice = device; } setA2dpActiveDevice(null); setHfpActiveDevice(null); break; } if (prevState == BluetoothProfile.STATE_CONNECTED) { // Device disconnected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE" + "_CHANGED): device " + device + " disconnected"); } mLeHearingAidConnectedDevices.remove(device); mLeAudioConnectedDevices.remove(device); // mLeAudioConnectedDevices should contain all of // mLeHearingAidConnectedDevices. Call setLeAudioActiveDevice(null) // only if there are no LE audio devices. if (mLeAudioConnectedDevices.isEmpty() && Objects.equals(mLeHearingAidConnectedDevices, device)) { setLeAudioActiveDevice(null); } } } break; case MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED: { Intent intent = (Intent) msg.obj; BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device != null && !mLeAudioConnectedDevices.contains(device)) { mLeAudioConnectedDevices.add(device); } if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); } // Just assign locally the new value if (device != null && !Objects.equals(mLeHearingAidActiveDevice, device)) { setA2dpActiveDevice(null); setHfpActiveDevice(null); setHearingAidActiveDevice(null); } mLeHearingAidActiveDevice = mLeAudioActiveDevice = device; } break; } } } Loading Loading @@ -477,6 +563,8 @@ class ActiveDeviceManager { filter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); filter.addAction(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE); mAdapterService.registerReceiver(mReceiver, filter); mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler); Loading Loading @@ -565,6 +653,9 @@ class ActiveDeviceManager { return; } mLeAudioActiveDevice = device; if (device == null) { mLeHearingAidActiveDevice = null; } } private void resetState() { Loading android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java +55 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static org.mockito.Mockito.*; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHapClient; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; Loading Loading @@ -107,7 +108,7 @@ public class ActiveDeviceManagerTest { mA2dpHeadsetDevice = TestUtils.getTestDevice(mAdapter, 2); mHearingAidDevice = TestUtils.getTestDevice(mAdapter, 3); mLeAudioDevice = TestUtils.getTestDevice(mAdapter, 4); mSecondaryAudioDevice = TestUtils.getTestDevice(mAdapter, 4); mSecondaryAudioDevice = TestUtils.getTestDevice(mAdapter, 5); } @After Loading Loading @@ -472,6 +473,29 @@ public class ActiveDeviceManagerTest { Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice()); } /** * One LE Hearing Aid is connected. */ @Test public void onlyLeHearingAIdConnected_setHeadsetActive() { leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); } /** * LE audio is connected after LE Hearing Aid device. * Keep LE hearing Aid active. */ @Test public void leAudioConnectedAfterLeHearingAid_setLeAudioActiveShouldNotBeCalled() { leHearingAidConnected(mSecondaryAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice); leAudioConnected(mLeAudioDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); } /** * A wired audio device is connected. Then all active devices are set to null. */ Loading Loading @@ -590,4 +614,34 @@ public class ActiveDeviceManagerTest { mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate LE Hearing Aid connected for a device. */ private void leHearingAidConnected(BluetoothDevice device) { Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate LE Hearing Aid disconnected for a device. */ private void leHearingAidDisconnected(BluetoothDevice device) { Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED); intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate LE Audio Hearing Aid device changed for a device. */ private void leHearingAidActiveDeviceChanged(BluetoothDevice device) { Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } } Loading
android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +94 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.SuppressLint; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHapClient; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; Loading Loading @@ -116,6 +117,8 @@ class ActiveDeviceManager { private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 6; private static final int MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE_CHANGED = 8; private static final int MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED = 9; private static final int MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED = 10; private static final int MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED = 11; private final AdapterService mAdapterService; private final ServiceFactory mFactory; Loading @@ -127,10 +130,12 @@ class ActiveDeviceManager { private final List<BluetoothDevice> mA2dpConnectedDevices = new LinkedList<>(); private final List<BluetoothDevice> mHfpConnectedDevices = new LinkedList<>(); private final List<BluetoothDevice> mLeAudioConnectedDevices = new LinkedList<>(); private final List<BluetoothDevice> mLeHearingAidConnectedDevices = new LinkedList<>(); private BluetoothDevice mA2dpActiveDevice = null; private BluetoothDevice mHfpActiveDevice = null; private BluetoothDevice mHearingAidActiveDevice = null; private BluetoothDevice mLeAudioActiveDevice = null; private BluetoothDevice mLeHearingAidActiveDevice = null; // Broadcast receiver for all changes private final BroadcastReceiver mReceiver = new BroadcastReceiver() { Loading Loading @@ -174,6 +179,14 @@ class ActiveDeviceManager { mHandler.obtainMessage(MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED, intent).sendToTarget(); break; case BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED: mHandler.obtainMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED, intent).sendToTarget(); break; case BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE: mHandler.obtainMessage(MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED, intent).sendToTarget(); break; default: Log.e(TAG, "Received unexpected intent, action=" + action); break; Loading Loading @@ -223,7 +236,7 @@ class ActiveDeviceManager { break; // The device is already connected } mA2dpConnectedDevices.add(device); if (mHearingAidActiveDevice == null) { if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) { // New connected device: select it as active setA2dpActiveDevice(device); setLeAudioActiveDevice(null); Loading Loading @@ -285,7 +298,7 @@ class ActiveDeviceManager { break; // The device is already connected } mHfpConnectedDevices.add(device); if (mHearingAidActiveDevice == null) { if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) { // New connected device: select it as active setHfpActiveDevice(device); setLeAudioActiveDevice(null); Loading Loading @@ -364,7 +377,7 @@ class ActiveDeviceManager { break; // The device is already connected } mLeAudioConnectedDevices.add(device); if (mHearingAidActiveDevice == null) { if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) { // New connected device: select it as active setLeAudioActiveDevice(device); setA2dpActiveDevice(null); Loading Loading @@ -407,6 +420,79 @@ class ActiveDeviceManager { mLeAudioActiveDevice = device; } break; case MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED: { Intent intent = (Intent) msg.obj; BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); if (prevState == nextState) { // Nothing has changed break; } if (nextState == BluetoothProfile.STATE_CONNECTED) { // Device connected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE" + "_CHANGED): device " + device + " connected"); } if (mLeHearingAidConnectedDevices.contains(device)) { break; // The device is already connected } mLeHearingAidConnectedDevices.add(device); if (!mLeAudioConnectedDevices.contains(device)) { mLeAudioConnectedDevices.add(device); } // New connected device: select it as active setLeAudioActiveDevice(device); if (mLeAudioActiveDevice == device) { // setLeAudioActiveDevice succeed mLeHearingAidActiveDevice = device; } setA2dpActiveDevice(null); setHfpActiveDevice(null); break; } if (prevState == BluetoothProfile.STATE_CONNECTED) { // Device disconnected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE" + "_CHANGED): device " + device + " disconnected"); } mLeHearingAidConnectedDevices.remove(device); mLeAudioConnectedDevices.remove(device); // mLeAudioConnectedDevices should contain all of // mLeHearingAidConnectedDevices. Call setLeAudioActiveDevice(null) // only if there are no LE audio devices. if (mLeAudioConnectedDevices.isEmpty() && Objects.equals(mLeHearingAidConnectedDevices, device)) { setLeAudioActiveDevice(null); } } } break; case MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED: { Intent intent = (Intent) msg.obj; BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device != null && !mLeAudioConnectedDevices.contains(device)) { mLeAudioConnectedDevices.add(device); } if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); } // Just assign locally the new value if (device != null && !Objects.equals(mLeHearingAidActiveDevice, device)) { setA2dpActiveDevice(null); setHfpActiveDevice(null); setHearingAidActiveDevice(null); } mLeHearingAidActiveDevice = mLeAudioActiveDevice = device; } break; } } } Loading Loading @@ -477,6 +563,8 @@ class ActiveDeviceManager { filter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); filter.addAction(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE); mAdapterService.registerReceiver(mReceiver, filter); mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler); Loading Loading @@ -565,6 +653,9 @@ class ActiveDeviceManager { return; } mLeAudioActiveDevice = device; if (device == null) { mLeHearingAidActiveDevice = null; } } private void resetState() { Loading
android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java +55 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static org.mockito.Mockito.*; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHapClient; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; Loading Loading @@ -107,7 +108,7 @@ public class ActiveDeviceManagerTest { mA2dpHeadsetDevice = TestUtils.getTestDevice(mAdapter, 2); mHearingAidDevice = TestUtils.getTestDevice(mAdapter, 3); mLeAudioDevice = TestUtils.getTestDevice(mAdapter, 4); mSecondaryAudioDevice = TestUtils.getTestDevice(mAdapter, 4); mSecondaryAudioDevice = TestUtils.getTestDevice(mAdapter, 5); } @After Loading Loading @@ -472,6 +473,29 @@ public class ActiveDeviceManagerTest { Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice()); } /** * One LE Hearing Aid is connected. */ @Test public void onlyLeHearingAIdConnected_setHeadsetActive() { leAudioConnected(mLeAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); } /** * LE audio is connected after LE Hearing Aid device. * Keep LE hearing Aid active. */ @Test public void leAudioConnectedAfterLeHearingAid_setLeAudioActiveShouldNotBeCalled() { leHearingAidConnected(mSecondaryAudioDevice); verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice); leAudioConnected(mLeAudioDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); } /** * A wired audio device is connected. Then all active devices are set to null. */ Loading Loading @@ -590,4 +614,34 @@ public class ActiveDeviceManagerTest { mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate LE Hearing Aid connected for a device. */ private void leHearingAidConnected(BluetoothDevice device) { Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate LE Hearing Aid disconnected for a device. */ private void leHearingAidDisconnected(BluetoothDevice device) { Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED); intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate LE Audio Hearing Aid device changed for a device. */ private void leHearingAidActiveDeviceChanged(BluetoothDevice device) { Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } }