diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java index 03662a023f65656524c4c7d4389459250957e442..cffc4ed63306836fef00bb06d5ba126e131591bc 100644 --- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java @@ -494,13 +494,32 @@ public class A2dpService extends ProfileService { previousActiveDevice = mActiveDevice; } + int prevActiveConnectionState = getConnectionState(previousActiveDevice); + + // As per b/202602952, if we remove the active device due to a disconnection, + // 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 + // a classic active device switch. + BluetoothDevice fallbackdevice = getFallbackDevice(); + if (fallbackdevice != null && prevActiveConnectionState + != BluetoothProfile.STATE_CONNECTED) { + setActiveDevice(fallbackdevice); + return; + } + // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why. updateAndBroadcastActiveDevice(null); - // Make sure the Audio Manager knows the previous Active device is removed. + // Make sure the Audio Manager knows the previous Active device is disconnected. + // However, if A2DP is still connected and not forcing stop audio for that remote + // device, the user has explicitly switched the output to the local device and music + // should continue playing. Otherwise, the remote device has been indeed disconnected + // and audio should be suspended before switching the output to the local device. + boolean stopAudio = forceStopPlayingAudio || (prevActiveConnectionState + != BluetoothProfile.STATE_CONNECTED); mAudioManager.handleBluetoothActiveDeviceChanged(null, previousActiveDevice, - BluetoothProfileConnectionInfo.createA2dpInfo(!forceStopPlayingAudio, -1)); + BluetoothProfileConnectionInfo.createA2dpInfo(!stopAudio, -1)); synchronized (mStateMachines) { // Make sure the Active device in native layer is set to null and audio is off @@ -544,22 +563,10 @@ public class A2dpService extends ProfileService { * @return true on success, otherwise false */ public boolean setActiveDevice(BluetoothDevice device) { - return setActiveDevice(device, false); - } - - /** - * Set the active device. - * - * @param device the active device - * @param hasFallbackDevice whether it has fallback device when the {@code device} - * is {@code null}. - * @return true on success, otherwise false - */ - public boolean setActiveDevice(BluetoothDevice device, boolean hasFallbackDevice) { synchronized (mActiveSwitchingGuard) { if (device == null) { // Remove active device and continue playing audio only if necessary. - removeActiveDevice(!hasFallbackDevice); + removeActiveDevice(false); return true; } @@ -1246,9 +1253,10 @@ public class A2dpService extends ProfileService { if (toState == BluetoothProfile.STATE_CONNECTED && (mMaxConnectedAudioDevices == 1)) { setActiveDevice(device); } - - // When disconnected, ActiveDeviceManager will call setActiveDevice(null) - + // Check if the active device is not connected anymore + if (isActiveDevice(device) && (fromState == BluetoothProfile.STATE_CONNECTED)) { + setActiveDevice(null); + } // Check if the device is disconnected - if unbond, remove the state machine if (toState == BluetoothProfile.STATE_DISCONNECTED) { if (mAdapterService.getBondState(device) == BluetoothDevice.BOND_NONE) { diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java index 1d2666edc6ce644f59cdec5c242533297a7b7415..f043394a59ac762b3bca2aca68f9ecd48a9d328a 100644 --- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java @@ -49,7 +49,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.RejectedExecutionException; /** * The active device manager is responsible for keeping track of the @@ -125,32 +124,24 @@ class ActiveDeviceManager { private static final int MESSAGE_HAP_ACTION_CONNECTION_STATE_CHANGED = 10; private static final int MESSAGE_HAP_ACTION_ACTIVE_DEVICE_CHANGED = 11; - // Used when it is needed to find a fallback device - private static final int PROFILE_NOT_DECIDED_YET = -1; - // Used for built-in audio device - private static final int PROFILE_USE_BUILTIN_AUDIO_DEVICE = 0; - private final AdapterService mAdapterService; private final ServiceFactory mFactory; private HandlerThread mHandlerThread = null; private Handler mHandler = null; private final AudioManager mAudioManager; private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback; - private final AudioManagerOnModeChangedListener mAudioManagerOnModeChangedListener; private final List<BluetoothDevice> mA2dpConnectedDevices = new ArrayList<>(); private final List<BluetoothDevice> mHfpConnectedDevices = new ArrayList<>(); private final List<BluetoothDevice> mHearingAidConnectedDevices = new ArrayList<>(); private final List<BluetoothDevice> mLeAudioConnectedDevices = new ArrayList<>(); private final List<BluetoothDevice> mLeHearingAidConnectedDevices = new ArrayList<>(); - private final List<BluetoothDevice> mPendingLeHearingAidActiveDevice = new ArrayList<>(); + private List<BluetoothDevice> mPendingLeHearingAidActiveDevice = new ArrayList<>(); private BluetoothDevice mA2dpActiveDevice = null; private BluetoothDevice mHfpActiveDevice = null; private BluetoothDevice mHearingAidActiveDevice = null; private BluetoothDevice mLeAudioActiveDevice = null; private BluetoothDevice mLeHearingAidActiveDevice = null; - private int mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - private int mActiveCallProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; // Broadcast receiver for all changes private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -220,7 +211,6 @@ class ActiveDeviceManager { @Override public void handleMessage(Message msg) { - boolean isMediaMode = isMediaMode(mAudioManager.getMode()); switch (msg.what) { case MESSAGE_ADAPTER_ACTION_STATE_CHANGED: { Intent intent = (Intent) msg.obj; @@ -255,24 +245,16 @@ class ActiveDeviceManager { if (mA2dpConnectedDevices.contains(device)) { break; // The device is already connected } - // New connected A2DP device mA2dpConnectedDevices.add(device); - if (mActiveMediaProfile != BluetoothProfile.HEARING_AID - && mActiveMediaProfile != BluetoothProfile.HAP_CLIENT) { - if (isMediaMode || mActiveCallProfile == BluetoothProfile.HEADSET) { - // select the device as active if not lazy active - setA2dpActiveDevice(device); - setLeAudioActiveDevice(null, true); - mActiveMediaProfile = BluetoothProfile.A2DP; - } else { - // Lazy active A2DP if it is not being used. - mActiveMediaProfile = PROFILE_NOT_DECIDED_YET; - } + if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) { + // New connected device: select it as active + setA2dpActiveDevice(device); + setLeAudioActiveDevice(null); } break; } if (prevState == BluetoothProfile.STATE_CONNECTED) { - // A2DP device disconnected + // Device disconnected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED): " @@ -280,15 +262,10 @@ class ActiveDeviceManager { } mA2dpConnectedDevices.remove(device); if (Objects.equals(mA2dpActiveDevice, device)) { - mActiveMediaProfile = PROFILE_NOT_DECIDED_YET; - if (isMediaMode) { - if (!setFallbackDeviceActive()) { - mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - setA2dpActiveDevice(null); - } - } else { + if (mA2dpConnectedDevices.isEmpty()) { setA2dpActiveDevice(null); } + setFallbackDeviceActive(); } } } @@ -302,12 +279,12 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); } - if (device != null) { - mActiveMediaProfile = BluetoothProfile.A2DP; - if (!Objects.equals(mA2dpActiveDevice, device)) { - setHearingAidActiveDevice(null, true); - setLeAudioActiveDevice(null, true); - } + if (device != null && !Objects.equals(mA2dpActiveDevice, device)) { + setHearingAidActiveDevice(null); + setLeAudioActiveDevice(null); + } + if (mHfpConnectedDevices.contains(device)) { + setHfpActiveDevice(device); } // Just assign locally the new value mA2dpActiveDevice = device; @@ -334,24 +311,16 @@ class ActiveDeviceManager { if (mHfpConnectedDevices.contains(device)) { break; // The device is already connected } - // New connected HFP device. mHfpConnectedDevices.add(device); - if (mActiveCallProfile != BluetoothProfile.HEARING_AID - && mActiveCallProfile != BluetoothProfile.HAP_CLIENT) { - if (!isMediaMode || mActiveMediaProfile == BluetoothProfile.A2DP) { - // select the device as active if not lazy active - setHfpActiveDevice(device); - setLeAudioActiveDevice(null); - mActiveCallProfile = BluetoothProfile.HEADSET; - } else { - // Lazy active HFP if it is not being used. - mActiveCallProfile = PROFILE_NOT_DECIDED_YET; - } + if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) { + // New connected device: select it as active + setHfpActiveDevice(device); + setLeAudioActiveDevice(null); } break; } if (prevState == BluetoothProfile.STATE_CONNECTED) { - // HFP device disconnected + // Device disconnected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED): " @@ -359,15 +328,10 @@ class ActiveDeviceManager { } mHfpConnectedDevices.remove(device); if (Objects.equals(mHfpActiveDevice, device)) { - mActiveCallProfile = PROFILE_NOT_DECIDED_YET; - if (!isMediaMode) { - if (!setFallbackDeviceActive()) { - mActiveCallProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - setHfpActiveDevice(null); - } - } else { + if (mHfpConnectedDevices.isEmpty()) { setHfpActiveDevice(null); } + setFallbackDeviceActive(); } } } @@ -381,12 +345,12 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); } - if (device != null) { - mActiveCallProfile = BluetoothProfile.HEADSET; - if (!Objects.equals(mHfpActiveDevice, device)) { - setHearingAidActiveDevice(null, true); - setLeAudioActiveDevice(null, true); - } + if (device != null && !Objects.equals(mHfpActiveDevice, device)) { + setHearingAidActiveDevice(null); + setLeAudioActiveDevice(null); + } + if (mA2dpConnectedDevices.contains(device)) { + setA2dpActiveDevice(device); } // Just assign locally the new value mHfpActiveDevice = device; @@ -413,38 +377,25 @@ class ActiveDeviceManager { break; // The device is already connected } mHearingAidConnectedDevices.add(device); - // New connected hearing aid device: select it as active - mActiveMediaProfile = BluetoothProfile.HEARING_AID; - mActiveCallProfile = BluetoothProfile.HEARING_AID; + // New connected device: select it as active setHearingAidActiveDevice(device); - setA2dpActiveDevice(null, true); + setA2dpActiveDevice(null); setHfpActiveDevice(null); - setLeAudioActiveDevice(null, true); + setLeAudioActiveDevice(null); break; } if (prevState == BluetoothProfile.STATE_CONNECTED) { - // Hearing aid device disconnected + // Device disconnected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HEARING_AID_ACTION_CONNECTION_STATE" + "_CHANGED): device " + device + " disconnected"); } mHearingAidConnectedDevices.remove(device); if (Objects.equals(mHearingAidActiveDevice, device)) { - if (mActiveMediaProfile == BluetoothProfile.HEARING_AID) { - mActiveMediaProfile = PROFILE_NOT_DECIDED_YET; - } - if (mActiveCallProfile == BluetoothProfile.HEARING_AID) { - mActiveCallProfile = PROFILE_NOT_DECIDED_YET; - } - if (!setFallbackDeviceActive()) { + if (mHearingAidConnectedDevices.isEmpty()) { setHearingAidActiveDevice(null); - if (mActiveMediaProfile == PROFILE_NOT_DECIDED_YET) { - mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - if (mActiveCallProfile == PROFILE_NOT_DECIDED_YET) { - mActiveCallProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } } + setFallbackDeviceActive(); } } } @@ -458,18 +409,13 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_HA_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); } - if (device != null && !Objects.equals(mHearingAidActiveDevice, device)) { - setA2dpActiveDevice(null, true); - setHfpActiveDevice(null); - setLeAudioActiveDevice(null, true); - if (isMediaMode) { - mActiveMediaProfile = BluetoothProfile.HEARING_AID; - } else { - mActiveCallProfile = BluetoothProfile.HEARING_AID; - } - } // Just assign locally the new value mHearingAidActiveDevice = device; + if (device != null) { + setA2dpActiveDevice(null); + setHfpActiveDevice(null); + setLeAudioActiveDevice(null); + } } break; @@ -483,80 +429,43 @@ class ActiveDeviceManager { // Nothing has changed break; } - final LeAudioService leAudioService = mFactory.getLeAudioService(); - if (nextState == BluetoothProfile.STATE_CONNECTED) { // Device connected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE" + "_CHANGED): device " + device + " connected"); } - if (leAudioService != null && device != null) { - leAudioService.deviceConnected(device); - } if (mLeAudioConnectedDevices.contains(device)) { break; // The device is already connected } mLeAudioConnectedDevices.add(device); - if (mPendingLeHearingAidActiveDevice.contains(device)) { - // LE hearing aid connected - mActiveMediaProfile = BluetoothProfile.HAP_CLIENT; - mActiveCallProfile = BluetoothProfile.HAP_CLIENT; + if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null + && mPendingLeHearingAidActiveDevice.isEmpty()) { + // New connected device: select it as active + setLeAudioActiveDevice(device); + setA2dpActiveDevice(null); + setHfpActiveDevice(null); + } else if (mPendingLeHearingAidActiveDevice.contains(device)) { setLeHearingAidActiveDevice(device); - setHearingAidActiveDevice(null, true); - setA2dpActiveDevice(null, true); + setHearingAidActiveDevice(null); + setA2dpActiveDevice(null); setHfpActiveDevice(null); - } else { - boolean setLeAudioActive = false; - if (mActiveMediaProfile != BluetoothProfile.HEARING_AID - && mActiveMediaProfile != BluetoothProfile.HAP_CLIENT) { - mActiveMediaProfile = BluetoothProfile.LE_AUDIO; - setLeAudioActive |= isMediaMode; - } - if (mActiveCallProfile != BluetoothProfile.HEARING_AID - && mActiveCallProfile != BluetoothProfile.HAP_CLIENT) { - mActiveCallProfile = BluetoothProfile.LE_AUDIO; - setLeAudioActive |= !isMediaMode; - } - if (setLeAudioActive) { - setLeAudioActiveDevice(device); - setA2dpActiveDevice(null, true); - setHfpActiveDevice(null); - setHearingAidActiveDevice(null, true); - } } break; } if (prevState == BluetoothProfile.STATE_CONNECTED) { - // LE audio device disconnected + // Device disconnected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE" + "_CHANGED): device " + device + " disconnected"); } mLeAudioConnectedDevices.remove(device); mLeHearingAidConnectedDevices.remove(device); - boolean hasFallbackDevice = false; if (Objects.equals(mLeAudioActiveDevice, device)) { - if (mActiveMediaProfile == BluetoothProfile.LE_AUDIO - || mActiveMediaProfile == BluetoothProfile.HAP_CLIENT) { - mActiveMediaProfile = PROFILE_NOT_DECIDED_YET; - } - if (mActiveCallProfile == BluetoothProfile.LE_AUDIO - || mActiveCallProfile == BluetoothProfile.HAP_CLIENT) { - mActiveCallProfile = PROFILE_NOT_DECIDED_YET; - } - if (!setFallbackDeviceActive()) { + if (mLeAudioConnectedDevices.isEmpty()) { setLeAudioActiveDevice(null); - if (mActiveMediaProfile == PROFILE_NOT_DECIDED_YET) { - mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - if (mActiveCallProfile == PROFILE_NOT_DECIDED_YET) { - mActiveCallProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } } - } - if (leAudioService != null && device != null) { - leAudioService.deviceDisconnected(device, hasFallbackDevice); + setFallbackDeviceActive(); } } } @@ -573,20 +482,12 @@ class ActiveDeviceManager { Log.d(TAG, "handleMessage(MESSAGE_LE_AUDIO_ACTION_ACTIVE_DEVICE_CHANGED): " + "device= " + device); } - + // Just assign locally the new value if (device != null && !Objects.equals(mLeAudioActiveDevice, device)) { - setA2dpActiveDevice(null, true); + setA2dpActiveDevice(null); setHfpActiveDevice(null); - setHearingAidActiveDevice(null, true); - int profile = mLeHearingAidConnectedDevices.contains(device) - ? BluetoothProfile.HAP_CLIENT : BluetoothProfile.LE_AUDIO; - if (isMediaMode) { - mActiveMediaProfile = profile; - } else { - mActiveCallProfile = profile; - } + setHearingAidActiveDevice(null); } - // Just assign locally the new value mLeAudioActiveDevice = device; } break; @@ -613,22 +514,19 @@ class ActiveDeviceManager { mLeHearingAidConnectedDevices.add(device); if (!mLeAudioConnectedDevices.contains(device)) { mPendingLeHearingAidActiveDevice.add(device); + } else if (Objects.equals(mLeAudioActiveDevice, device)) { + mLeHearingAidActiveDevice = device; } else { - mActiveMediaProfile = BluetoothProfile.HAP_CLIENT; - mActiveCallProfile = BluetoothProfile.HAP_CLIENT; - if (Objects.equals(mLeAudioActiveDevice, device)) { - mLeHearingAidActiveDevice = device; - } else { - setLeHearingAidActiveDevice(device); - setHearingAidActiveDevice(null, true); - setA2dpActiveDevice(null, true); - setHfpActiveDevice(null); - } + // New connected device: select it as active + setLeHearingAidActiveDevice(device); + setHearingAidActiveDevice(null); + setA2dpActiveDevice(null); + setHfpActiveDevice(null); } break; } if (prevState == BluetoothProfile.STATE_CONNECTED) { - // LE hearing aid device disconnected + // Device disconnected if (DBG) { Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE" + "_CHANGED): device " + device + " disconnected"); @@ -655,14 +553,9 @@ class ActiveDeviceManager { } // Just assign locally the new value if (device != null && !Objects.equals(mLeHearingAidActiveDevice, device)) { - if (isMediaMode) { - mActiveMediaProfile = BluetoothProfile.HAP_CLIENT; - } else { - mActiveCallProfile = BluetoothProfile.HAP_CLIENT; - } - setHearingAidActiveDevice(null, true); - setA2dpActiveDevice(null, true); + setA2dpActiveDevice(null); setHfpActiveDevice(null); + setHearingAidActiveDevice(null); } mLeHearingAidActiveDevice = mLeAudioActiveDevice = device; } @@ -671,167 +564,6 @@ class ActiveDeviceManager { } } - private class AudioManagerOnModeChangedListener implements AudioManager.OnModeChangedListener { - public void onModeChanged(int mode) { - if (isMediaMode(mode)) { - setMediaProfileActive(); - } else { - setCallProfileActive(); - } - } - - private void setMediaProfileActive() { - BluetoothDevice device = null; - switch (mActiveMediaProfile) { - case PROFILE_NOT_DECIDED_YET: { - if (!setFallbackDeviceActive()) { - mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - break; - } - - case BluetoothProfile.A2DP: { - if (mA2dpActiveDevice == null) { - A2dpService a2dpService = mFactory.getA2dpService(); - if (a2dpService != null) { - device = a2dpService.getFallbackDevice(); - } - if (device != null) { - setA2dpActiveDevice(device); - setHearingAidActiveDevice(null, true); - setLeAudioActiveDevice(null, true); - } else { - mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - } - break; - } - - case BluetoothProfile.HEARING_AID: { - if (mHearingAidActiveDevice == null) { - DatabaseManager dbManager = mAdapterService.getDatabase(); - if (dbManager != null) { - device = dbManager.getMostRecentlyConnectedDevicesInList( - mHearingAidConnectedDevices); - } - if (device != null) { - setHearingAidActiveDevice(device); - setA2dpActiveDevice(null, true); - setLeAudioActiveDevice(null, true); - } else { - mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - } - break; - } - - case BluetoothProfile.LE_AUDIO: { - if (mLeAudioActiveDevice == null) { - DatabaseManager dbManager = mAdapterService.getDatabase(); - if (dbManager != null) { - device = dbManager.getMostRecentlyConnectedDevicesInList( - mLeAudioConnectedDevices); - } - if (device != null) { - setLeAudioActiveDevice(device); - setA2dpActiveDevice(null, true); - setHfpActiveDevice(null); - setHearingAidActiveDevice(null, true); - } else { - mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - } - break; - } - - case BluetoothProfile.HAP_CLIENT: { - if (mLeHearingAidActiveDevice == null) { - DatabaseManager dbManager = mAdapterService.getDatabase(); - if (dbManager != null) { - device = dbManager.getMostRecentlyConnectedDevicesInList( - mLeHearingAidConnectedDevices); - } - if (device != null) { - setLeHearingAidActiveDevice(device); - setA2dpActiveDevice(null, true); - setHfpActiveDevice(null); - setHearingAidActiveDevice(null, true); - } else { - mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - } - break; - } - } - } - - private void setCallProfileActive() { - BluetoothDevice device = null; - switch (mActiveCallProfile) { - case PROFILE_NOT_DECIDED_YET: { - if (!setFallbackDeviceActive()) { - mActiveCallProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - break; - } - - case BluetoothProfile.HEADSET: { - if (mHfpActiveDevice == null) { - HeadsetService headsetService = mFactory.getHeadsetService(); - if (headsetService != null) { - device = headsetService.getFallbackDevice(); - } - if (device != null) { - setHfpActiveDevice(device); - setHearingAidActiveDevice(null, true); - setLeAudioActiveDevice(null, true); - } else { - mActiveCallProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - } - break; - } - - case BluetoothProfile.HEARING_AID: { - if (mHearingAidActiveDevice == null) { - DatabaseManager dbManager = mAdapterService.getDatabase(); - if (dbManager != null) { - device = dbManager.getMostRecentlyConnectedDevicesInList( - mHearingAidConnectedDevices); - } - if (device != null) { - setHearingAidActiveDevice(device); - setHfpActiveDevice(null); - setLeAudioActiveDevice(null); - } else { - mActiveCallProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - } - break; - } - - case BluetoothProfile.LE_AUDIO: { - if (mLeAudioActiveDevice == null) { - DatabaseManager dbManager = mAdapterService.getDatabase(); - if (dbManager != null) { - device = dbManager.getMostRecentlyConnectedDevicesInList( - mLeAudioConnectedDevices); - } - if (device != null) { - setLeAudioActiveDevice(device); - setA2dpActiveDevice(null, true); - setHfpActiveDevice(null); - setHearingAidActiveDevice(null, true); - } else { - mActiveCallProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - } - } - break; - } - } - } - } - /** Notifications of audio device connection and disconnection events. */ @SuppressLint("AndroidFrameworkRequiresPermission") private class AudioManagerAudioDeviceCallback extends AudioDeviceCallback { @@ -878,7 +610,6 @@ class ActiveDeviceManager { mFactory = factory; mAudioManager = service.getSystemService(AudioManager.class); mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback(); - mAudioManagerOnModeChangedListener = new AudioManagerOnModeChangedListener(); } void start() { @@ -905,11 +636,6 @@ class ActiveDeviceManager { mAdapterService.registerReceiver(mReceiver, filter); mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler); - mAudioManager.addOnModeChangedListener(command -> { - if (!mHandler.post(command)) { - throw new RejectedExecutionException(mHandler + " is shutting down"); - } - }, mAudioManagerOnModeChangedListener); } void cleanup() { @@ -918,7 +644,6 @@ class ActiveDeviceManager { } mAudioManager.unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback); - mAudioManager.removeOnModeChangedListener(mAudioManagerOnModeChangedListener); mAdapterService.unregisterReceiver(mReceiver); if (mHandlerThread != null) { mHandlerThread.quit(); @@ -942,10 +667,6 @@ class ActiveDeviceManager { } private void setA2dpActiveDevice(BluetoothDevice device) { - setA2dpActiveDevice(device, false); - } - - private void setA2dpActiveDevice(BluetoothDevice device, boolean hasFallbackDevice) { if (DBG) { Log.d(TAG, "setA2dpActiveDevice(" + device + ")"); } @@ -953,7 +674,7 @@ class ActiveDeviceManager { if (a2dpService == null) { return; } - if (!a2dpService.setActiveDevice(device, hasFallbackDevice)) { + if (!a2dpService.setActiveDevice(device)) { return; } mA2dpActiveDevice = device; @@ -975,10 +696,6 @@ class ActiveDeviceManager { } private void setHearingAidActiveDevice(BluetoothDevice device) { - setHearingAidActiveDevice(device, false); - } - - private void setHearingAidActiveDevice(BluetoothDevice device, boolean hasFallbackDevice) { if (DBG) { Log.d(TAG, "setHearingAidActiveDevice(" + device + ")"); } @@ -986,16 +703,13 @@ class ActiveDeviceManager { if (hearingAidService == null) { return; } - if (!hearingAidService.setActiveDevice(device, hasFallbackDevice)) { + if (!hearingAidService.setActiveDevice(device)) { return; } mHearingAidActiveDevice = device; } - private void setLeAudioActiveDevice(BluetoothDevice device) { - setLeAudioActiveDevice(device, false); - } - private void setLeAudioActiveDevice(BluetoothDevice device, boolean hasFallbackDevice) { + private void setLeAudioActiveDevice(BluetoothDevice device) { if (DBG) { Log.d(TAG, "setLeAudioActiveDevice(" + device + ")"); } @@ -1003,7 +717,7 @@ class ActiveDeviceManager { if (leAudioService == null) { return; } - if (!leAudioService.setActiveDevice(device, hasFallbackDevice)) { + if (!leAudioService.setActiveDevice(device)) { return; } mLeAudioActiveDevice = device; @@ -1014,9 +728,6 @@ class ActiveDeviceManager { } private void setLeHearingAidActiveDevice(BluetoothDevice device) { - if (DBG) { - Log.d(TAG, "setLeHearingAidActiveDevice(" + device + ")"); - } if (!Objects.equals(mLeAudioActiveDevice, device)) { setLeAudioActiveDevice(device); } @@ -1027,32 +738,13 @@ class ActiveDeviceManager { } } - private boolean isMediaMode(int mode) { - switch (mode) { - case AudioManager.MODE_RINGTONE: - final HeadsetService headsetService = mFactory.getHeadsetService(); - if (headsetService != null && headsetService.isInbandRingingEnabled()) { - return false; - } - return true; - case AudioManager.MODE_IN_CALL: - case AudioManager.MODE_IN_COMMUNICATION: - case AudioManager.MODE_CALL_SCREENING: - case AudioManager.MODE_CALL_REDIRECT: - case AudioManager.MODE_COMMUNICATION_REDIRECT: - return false; - default: - return true; - } - } - - private boolean setFallbackDeviceActive() { + private void setFallbackDeviceActive() { if (DBG) { Log.d(TAG, "setFallbackDeviceActive"); } DatabaseManager dbManager = mAdapterService.getDatabase(); if (dbManager == null) { - return false; + return; } List<BluetoothDevice> connectedHearingAidDevices = new ArrayList<>(); if (!mHearingAidConnectedDevices.isEmpty()) { @@ -1070,31 +762,19 @@ class ActiveDeviceManager { Log.d(TAG, "set hearing aid device active: " + device); } setHearingAidActiveDevice(device); - setA2dpActiveDevice(null, true); + setA2dpActiveDevice(null); setHfpActiveDevice(null); - setLeAudioActiveDevice(null, true); - if (mActiveMediaProfile == PROFILE_NOT_DECIDED_YET) { - mActiveMediaProfile = BluetoothProfile.HEARING_AID; - } - if (mActiveCallProfile == PROFILE_NOT_DECIDED_YET) { - mActiveCallProfile = BluetoothProfile.HEARING_AID; - } + setLeAudioActiveDevice(null); } else { if (DBG) { Log.d(TAG, "set LE hearing aid device active: " + device); } setLeHearingAidActiveDevice(device); - setHearingAidActiveDevice(null, true); - setA2dpActiveDevice(null, true); + setHearingAidActiveDevice(null); + setA2dpActiveDevice(null); setHfpActiveDevice(null); - if (mActiveMediaProfile == PROFILE_NOT_DECIDED_YET) { - mActiveMediaProfile = BluetoothProfile.HAP_CLIENT; - } - if (mActiveCallProfile == PROFILE_NOT_DECIDED_YET) { - mActiveCallProfile = BluetoothProfile.HAP_CLIENT; - } } - return true; + return; } } @@ -1112,35 +792,41 @@ class ActiveDeviceManager { List<BluetoothDevice> connectedDevices = new ArrayList<>(); connectedDevices.addAll(mLeAudioConnectedDevices); - boolean isMediaMode = isMediaMode(mAudioManager.getMode()); - if (isMediaMode) { - if (a2dpFallbackDevice != null) { - connectedDevices.add(a2dpFallbackDevice); - } - } else { - if (headsetFallbackDevice != null) { - connectedDevices.add(headsetFallbackDevice); - } + switch (mAudioManager.getMode()) { + case AudioManager.MODE_NORMAL: + if (a2dpFallbackDevice != null) { + connectedDevices.add(a2dpFallbackDevice); + } + break; + case AudioManager.MODE_RINGTONE: + if (headsetFallbackDevice != null && headsetService.isInbandRingingEnabled()) { + connectedDevices.add(headsetFallbackDevice); + } + break; + default: + if (headsetFallbackDevice != null) { + connectedDevices.add(headsetFallbackDevice); + } } - BluetoothDevice device = dbManager.getMostRecentlyConnectedDevicesInList(connectedDevices); if (device != null) { - if (isMediaMode) { + if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) { if (Objects.equals(a2dpFallbackDevice, device)) { if (DBG) { Log.d(TAG, "set A2DP device active: " + device); } setA2dpActiveDevice(device); - setLeAudioActiveDevice(null, true); - mActiveMediaProfile = BluetoothProfile.A2DP; + if (headsetFallbackDevice != null) { + setHfpActiveDevice(device); + setLeAudioActiveDevice(null); + } } else { if (DBG) { Log.d(TAG, "set LE audio device active: " + device); } setLeAudioActiveDevice(device); - setA2dpActiveDevice(null, true); + setA2dpActiveDevice(null); setHfpActiveDevice(null); - mActiveMediaProfile = BluetoothProfile.LE_AUDIO; } } else { if (Objects.equals(headsetFallbackDevice, device)) { @@ -1148,21 +834,20 @@ class ActiveDeviceManager { Log.d(TAG, "set HFP device active: " + device); } setHfpActiveDevice(device); - setLeAudioActiveDevice(null); - mActiveCallProfile = BluetoothProfile.HEADSET; + if (a2dpFallbackDevice != null) { + setA2dpActiveDevice(a2dpFallbackDevice); + setLeAudioActiveDevice(null); + } } else { if (DBG) { Log.d(TAG, "set LE audio device active: " + device); } setLeAudioActiveDevice(device); - setA2dpActiveDevice(null, true); + setA2dpActiveDevice(null); setHfpActiveDevice(null); - mActiveCallProfile = BluetoothProfile.LE_AUDIO; } } - return true; } - return false; } private void resetState() { @@ -1218,8 +903,6 @@ class ActiveDeviceManager { if (DBG) { Log.d(TAG, "wiredAudioDeviceConnected"); } - mActiveMediaProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; - mActiveCallProfile = PROFILE_USE_BUILTIN_AUDIO_DEVICE; setA2dpActiveDevice(null); setHfpActiveDevice(null); setHearingAidActiveDevice(null); diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java index 78afdb7d11cd2027fc598d10faab8a1ef7d5b68c..419e3c5e551c7108b54b6fc09c4254215da0c9b1 100644 --- a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java +++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java @@ -585,24 +585,13 @@ public class HearingAidService extends ProfileService { * @return true on success, otherwise false */ public boolean setActiveDevice(BluetoothDevice device) { - return setActiveDevice(device, false); - } - - /** - * Set the active device. - * @param device the new active device - * @param hasFallbackDevice whether it has fallback device when the {@code device} - * is {@code null}. - * @return true on success, otherwise false - */ - public boolean setActiveDevice(BluetoothDevice device, boolean hasFallbackDevice) { if (DBG) { Log.d(TAG, "setActiveDevice:" + device); } synchronized (mStateMachines) { if (device == null) { if (mActiveDeviceHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID) { - reportActiveDevice(null, hasFallbackDevice); + reportActiveDevice(null); mActiveDeviceHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID; } return true; @@ -621,7 +610,7 @@ public class HearingAidService extends ProfileService { BluetoothHearingAid.HI_SYNC_ID_INVALID); if (deviceHiSyncId != mActiveDeviceHiSyncId) { mActiveDeviceHiSyncId = deviceHiSyncId; - reportActiveDevice(device, false); + reportActiveDevice(device); } } return true; @@ -772,7 +761,7 @@ public class HearingAidService extends ProfileService { * Report the active device change to the active device manager and the media framework. * @param device the new active device; or null if no active device */ - private void reportActiveDevice(BluetoothDevice device, boolean hasFallbackDevice) { + private void reportActiveDevice(BluetoothDevice device) { if (DBG) { Log.d(TAG, "reportActiveDevice(" + device + ")"); } @@ -783,7 +772,14 @@ public class HearingAidService extends ProfileService { BluetoothProfile.HEARING_AID, mAdapterService.obfuscateAddress(device), mAdapterService.getMetricId(device)); - boolean stopAudio = device == null && !hasFallbackDevice; + Intent intent = new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions()); + + boolean stopAudio = device == null + && (getConnectionState(mPreviousAudioDevice) != BluetoothProfile.STATE_CONNECTED); if (DBG) { Log.d(TAG, "Hearing Aid audio: " + mPreviousAudioDevice + " -> " + device + ". Stop audio: " + stopAudio); @@ -899,7 +895,7 @@ public class HearingAidService extends ProfileService { } } if (fromState == BluetoothProfile.STATE_CONNECTED && getConnectedDevices().isEmpty()) { - // When disconnected, ActiveDeviceManager will call setActiveDevice(null) + setActiveDevice(null); long myHiSyncId = getHiSyncId(device); mHiSyncIdConnectedMap.put(myHiSyncId, false); } diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java index deb917ba5dacde1268c0472f522fabda06d6cc30..e3b6af4934beae35c7d2a49bb8549a7210cf4397 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -164,6 +164,7 @@ public class LeAudioService extends ProfileService { private final Map<BluetoothDevice, Integer> mDeviceAudioLocationMap = new ConcurrentHashMap<>(); private BroadcastReceiver mBondStateChangedReceiver; + private BroadcastReceiver mConnectionStateChangedReceiver; private BroadcastReceiver mMuteStateChangedReceiver; private int mStoredRingerMode = -1; private Handler mHandler = new Handler(Looper.getMainLooper()); @@ -234,6 +235,10 @@ public class LeAudioService extends ProfileService { mBondStateChangedReceiver = new BondStateChangedReceiver(); registerReceiver(mBondStateChangedReceiver, filter); filter = new IntentFilter(); + filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); + mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver(); + registerReceiver(mConnectionStateChangedReceiver, filter); + filter = new IntentFilter(); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); mMuteStateChangedReceiver = new MuteStateChangedReceiver(); registerReceiver(mMuteStateChangedReceiver, filter); @@ -245,7 +250,7 @@ public class LeAudioService extends ProfileService { // Initialize Broadcast native interface if ((mAdapterService.getSupportedProfilesBitMask() - & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) != 0) { + & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) != 0) { Log.i(TAG, "Init Le Audio broadcaster"); mBroadcastCallbacks = new RemoteCallbackList<IBluetoothLeBroadcastCallback>(); mLeAudioBroadcasterNativeInterface = Objects.requireNonNull( @@ -338,6 +343,8 @@ public class LeAudioService extends ProfileService { // Unregister broadcast receivers unregisterReceiver(mBondStateChangedReceiver); mBondStateChangedReceiver = null; + unregisterReceiver(mConnectionStateChangedReceiver); + mConnectionStateChangedReceiver = null; unregisterReceiver(mMuteStateChangedReceiver); mMuteStateChangedReceiver = null; @@ -818,6 +825,12 @@ public class LeAudioService extends ProfileService { if (!Objects.equals(device, previousInDevice) || (oldSupportedByDeviceInput != newSupportedByDeviceInput)) { mActiveAudioInDevice = newSupportedByDeviceInput ? device : null; + if (DBG) { + Log.d(TAG, " handleBluetoothActiveDeviceChanged previousInDevice: " + + previousInDevice + ", mActiveAudioInDevice" + mActiveAudioInDevice + + " isLeOutput: false"); + } + return true; } Log.d(TAG, "updateActiveInDevice: Nothing to do."); @@ -870,6 +883,11 @@ public class LeAudioService extends ProfileService { if (!Objects.equals(device, previousOutDevice) || (oldSupportedByDeviceOutput != newSupportedByDeviceOutput)) { mActiveAudioOutDevice = newSupportedByDeviceOutput ? device : null; + if (DBG) { + Log.d(TAG, " handleBluetoothActiveDeviceChanged previousOutDevice: " + + previousOutDevice + ", mActiveOutDevice: " + mActiveAudioOutDevice + + " isLeOutput: true"); + } return true; } Log.d(TAG, "updateActiveOutDevice: Nothing to do."); @@ -929,12 +947,6 @@ public class LeAudioService extends ProfileService { */ private boolean updateActiveDevices(Integer groupId, Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections, boolean isActive) { - return updateActiveDevices(groupId, oldSupportedAudioDirections, - newSupportedAudioDirections, isActive, false); - } - - private boolean updateActiveDevices(Integer groupId, Integer oldSupportedAudioDirections, - Integer newSupportedAudioDirections, boolean isActive, boolean hasFallbackDevice) { BluetoothDevice device = null; BluetoothDevice previousActiveOutDevice = mActiveAudioOutDevice; BluetoothDevice previousActiveInDevice = mActiveAudioInDevice; @@ -943,19 +955,19 @@ public class LeAudioService extends ProfileService { device = getFirstDeviceFromGroup(groupId); } - boolean isActiveOutDeviceChanged = updateActiveOutDevice(device, groupId, + boolean isNewActiveOutDevice = updateActiveOutDevice(device, groupId, oldSupportedAudioDirections, newSupportedAudioDirections); - boolean isActiveInDeviceChanged = updateActiveInDevice(device, groupId, + boolean isNewActiveInDevice = updateActiveInDevice(device, groupId, oldSupportedAudioDirections, newSupportedAudioDirections); if (DBG) { - Log.d(TAG, " isActiveOutDeviceUpdated: " + isActiveOutDeviceChanged + ", " - + mActiveAudioOutDevice + ", isActiveInDeviceUpdated: " - + isActiveInDeviceChanged + ", " + mActiveAudioInDevice); + Log.d(TAG, " isNewActiveOutDevice: " + isNewActiveOutDevice + ", " + + mActiveAudioOutDevice + ", isNewActiveInDevice: " + isNewActiveInDevice + + ", " + mActiveAudioInDevice); } /* Active device changed, there is need to inform about new active LE Audio device */ - if (isActiveOutDeviceChanged || isActiveInDeviceChanged) { + if (isNewActiveOutDevice || isNewActiveInDevice) { /* Register for new device connection/disconnection in Audio Manager */ if (mActiveAudioOutDevice != null || mActiveAudioInDevice != null) { /* Register for any device connection in case if any of devices become connected */ @@ -968,14 +980,14 @@ public class LeAudioService extends ProfileService { } } - if (isActiveOutDeviceChanged) { + if (isNewActiveOutDevice) { int volume = IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME; if (mActiveAudioOutDevice != null) { volume = getAudioDeviceGroupVolume(groupId); } - final boolean suppressNoisyIntent = hasFallbackDevice || (mActiveAudioOutDevice != null) + final boolean suppressNoisyIntent = (mActiveAudioOutDevice != null) || (getConnectionState(previousActiveOutDevice) == BluetoothProfile.STATE_CONNECTED); @@ -983,7 +995,7 @@ public class LeAudioService extends ProfileService { previousActiveOutDevice, getLeAudioOutputProfile(suppressNoisyIntent, volume)); } - if (isActiveInDeviceChanged) { + if (isNewActiveInDevice) { mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioInDevice, previousActiveInDevice, BluetoothProfileConnectionInfo.createLeAudioInfo(false, false)); @@ -996,10 +1008,6 @@ public class LeAudioService extends ProfileService { * Set the active device group. */ private void setActiveGroupWithDevice(BluetoothDevice device) { - setActiveGroupWithDevice(device, false); - } - - private void setActiveGroupWithDevice(BluetoothDevice device, boolean hasFallbackDevice) { int groupId = LE_AUDIO_GROUP_ID_INVALID; if (device != null) { @@ -1030,7 +1038,7 @@ public class LeAudioService extends ProfileService { * However we would like to notify audio framework that LeAudio is not * active anymore and does not want to get more audio data. */ - handleGroupTransitToInactive(currentlyActiveGroupId, hasFallbackDevice); + handleGroupTransitToInactive(currentlyActiveGroupId); } } @@ -1041,21 +1049,9 @@ public class LeAudioService extends ProfileService { * @return true on success, otherwise false */ public boolean setActiveDevice(BluetoothDevice device) { - return setActiveDevice(device, false); - } - - /** - * Set the active group represented by device. - * - * @param device the new active device - * @param hasFallbackDevice whether it has fallback device when the {@code device} - * is {@code null}. - * @return true on success, otherwise false - */ - public boolean setActiveDevice(BluetoothDevice device, boolean hasFallbackDevice) { /* Clear active group */ if (device == null) { - setActiveGroupWithDevice(device, hasFallbackDevice); + setActiveGroupWithDevice(device); return true; } if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { @@ -1211,7 +1207,7 @@ public class LeAudioService extends ProfileService { } } - private void handleGroupTransitToInactive(int groupId, boolean hasFallbackDevice) { + private void handleGroupTransitToInactive(int groupId) { synchronized (mGroupLock) { LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor == null || !descriptor.mIsActive) { @@ -1221,7 +1217,7 @@ public class LeAudioService extends ProfileService { descriptor.mIsActive = false; updateActiveDevices(groupId, descriptor.mDirection, AUDIO_DIRECTION_NONE, - descriptor.mIsActive, hasFallbackDevice); + descriptor.mIsActive); /* Clear lost devices */ if (DBG) Log.d(TAG, "Clear for group: " + groupId); clearLostDevicesWhileStreaming(descriptor); @@ -1394,8 +1390,9 @@ public class LeAudioService extends ProfileService { LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor != null) { if (descriptor.mIsActive) { - descriptor.mIsActive = updateActiveDevices( - groupId, descriptor.mDirection, direction, descriptor.mIsActive); + descriptor.mIsActive = + updateActiveDevices(groupId, descriptor.mDirection, direction, + descriptor.mIsActive); if (!descriptor.mIsActive) { notifyGroupStatusChanged(groupId, BluetoothLeAudio.GROUP_STATUS_INACTIVE); } @@ -1425,7 +1422,7 @@ public class LeAudioService extends ProfileService { break; } case LeAudioStackEvent.GROUP_STATUS_INACTIVE: { - handleGroupTransitToInactive(groupId, false); + handleGroupTransitToInactive(groupId); break; } case LeAudioStackEvent.GROUP_STATUS_TURNED_IDLE_DURING_CALL: { @@ -1650,92 +1647,100 @@ public class LeAudioService extends ProfileService { return result; } - /** - * Handles the connection of LE Audio device. - * - * @param device THe device that is connected. - */ - public synchronized void deviceConnected(BluetoothDevice device) { - int myGroupId = getGroupId(device); - if (myGroupId == LE_AUDIO_GROUP_ID_INVALID - || getConnectedPeerDevices(myGroupId).size() == 1) { - // Log LE Audio connection event if we are the first device in a set - // Or when the GroupId has not been found - // MetricsLogger.logProfileConnectionEvent( - // BluetoothMetricsProto.ProfileId.LE_AUDIO); + @VisibleForTesting + synchronized void connectionStateChanged(BluetoothDevice device, int fromState, int toState) { + if ((device == null) || (fromState == toState)) { + Log.e(TAG, "connectionStateChanged: unexpected invocation. device=" + device + + " fromState=" + fromState + " toState=" + toState); + return; } + if (toState == BluetoothProfile.STATE_CONNECTED) { + int myGroupId = getGroupId(device); + if (myGroupId == LE_AUDIO_GROUP_ID_INVALID + || getConnectedPeerDevices(myGroupId).size() == 1) { + // Log LE Audio connection event if we are the first device in a set + // Or when the GroupId has not been found + // MetricsLogger.logProfileConnectionEvent( + // BluetoothMetricsProto.ProfileId.LE_AUDIO); + } - LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); - if (descriptor != null) { - descriptor.mIsConnected = true; - } else { - Log.e(TAG, "no descriptors for group: " + myGroupId); - } + LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); + if (descriptor != null) { + descriptor.mIsConnected = true; + } else { + Log.e(TAG, "no descriptors for group: " + myGroupId); + } - McpService mcpService = mServiceFactory.getMcpService(); - if (mcpService != null) { - mcpService.setDeviceAuthorized(device, true); + McpService mcpService = mServiceFactory.getMcpService(); + if (mcpService != null) { + mcpService.setDeviceAuthorized(device, true); + } } - } + // Check if the device is disconnected - if unbond, remove the state machine + if (toState == BluetoothProfile.STATE_DISCONNECTED) { + int bondState = mAdapterService.getBondState(device); + if (bondState == BluetoothDevice.BOND_NONE) { + if (DBG) { + Log.d(TAG, device + " is unbond. Remove state machine"); + } + removeStateMachine(device); + } - /** - * Handle the disconnection of LE Audio device. - * - * @param device The device that is disconnected - * @param hasFallbackDevice whether it has fallback device when the {@code device} - * is {@code null}. - */ - public synchronized void deviceDisconnected(BluetoothDevice device, boolean hasFallbackDevice) { - // If unbond, remove the state machine - int bondState = mAdapterService.getBondState(device); - if (bondState == BluetoothDevice.BOND_NONE) { - if (DBG) { - Log.d(TAG, device + " is unbond. Remove state machine"); + McpService mcpService = mServiceFactory.getMcpService(); + if (mcpService != null) { + mcpService.setDeviceAuthorized(device, false); } - removeStateMachine(device); - } - McpService mcpService = mServiceFactory.getMcpService(); - if (mcpService != null) { - mcpService.setDeviceAuthorized(device, false); - } + int myGroupId = getGroupId(device); + LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); + if (descriptor == null) { + Log.e(TAG, "no descriptors for group: " + myGroupId); + return; + } - int myGroupId = getGroupId(device); - LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); - if (descriptor == null) { - Log.e(TAG, "no descriptors for group: " + myGroupId); - return; - } + List<BluetoothDevice> connectedDevices = getConnectedPeerDevices(myGroupId); + /* Let's check if the last connected device is really connected */ + if (connectedDevices.size() == 1 && Objects.equals( + connectedDevices.get(0), descriptor.mLostLeadDeviceWhileStreaming)) { + clearLostDevicesWhileStreaming(descriptor); + return; + } - List<BluetoothDevice> connectedDevices = getConnectedPeerDevices(myGroupId); - /* Let's check if the last connected device is really connected */ - if (connectedDevices.size() == 1 && Objects.equals( - connectedDevices.get(0), descriptor.mLostLeadDeviceWhileStreaming)) { - clearLostDevicesWhileStreaming(descriptor); - return; - } + if (getConnectedPeerDevices(myGroupId).isEmpty()) { + descriptor.mIsConnected = false; + if (descriptor.mIsActive) { + /* Notify Native layer */ + setActiveDevice(null); + descriptor.mIsActive = false; + /* Update audio framework */ + updateActiveDevices(myGroupId, + descriptor.mDirection, + descriptor.mDirection, + descriptor.mIsActive); + return; + } + } - if (getConnectedPeerDevices(myGroupId).isEmpty()) { - descriptor.mIsConnected = false; if (descriptor.mIsActive) { - /* Notify Native layer */ - setActiveDevice(null); - descriptor.mIsActive = false; - /* Update audio framework */ updateActiveDevices(myGroupId, descriptor.mDirection, descriptor.mDirection, - descriptor.mIsActive, - hasFallbackDevice); - return; + descriptor.mIsActive); } } + } - if (descriptor.mIsActive) { - updateActiveDevices(myGroupId, - descriptor.mDirection, - descriptor.mDirection, - descriptor.mIsActive); + private class ConnectionStateChangedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (!BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED + .equals(intent.getAction())) { + return; + } + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); + int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); + connectionStateChanged(device, fromState, toState); } } diff --git a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java index 7b44d0c56aceb22b7d43c647b4d0e2cda433d640..0f1fef7416fd45f52ab152944e9d1f59f6c61bfa 100644 --- a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java +++ b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java @@ -24,6 +24,8 @@ import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_READ; import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE; import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE; +import static java.util.Map.entry; + import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothDevice; @@ -41,7 +43,9 @@ import android.os.Looper; import android.util.Log; import android.util.Pair; +import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.btservice.AdapterService; +import com.android.bluetooth.hearingaid.HearingAidService; import com.android.bluetooth.le_audio.LeAudioService; import com.android.internal.annotations.VisibleForTesting; @@ -889,6 +893,12 @@ public class MediaControlGattService implements MediaControlGattServiceInterface } if (req.getOpcode() == Request.Opcodes.PLAY) { + if (mAdapterService.getActiveDevices(BluetoothProfile.A2DP).size() > 0) { + A2dpService.getA2dpService().setActiveDevice(null); + } + if (mAdapterService.getActiveDevices(BluetoothProfile.HEARING_AID).size() > 0) { + HearingAidService.getHearingAidService().setActiveDevice(null); + } if (mLeAudioService == null) { mLeAudioService = LeAudioService.getLeAudioService(); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java index abe94fb1c3d3176ff3ed7bd77e6ae861b10ac3df..08549629d98199192e5d770a45829bd8efe8c42d 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java @@ -17,11 +17,10 @@ package com.android.bluetooth.btservice; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -60,6 +59,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +import java.util.Objects; @MediumTest @RunWith(AndroidJUnit4.class) @@ -86,7 +86,6 @@ public class ActiveDeviceManagerTest { @Mock private LeAudioService mLeAudioService; @Mock private AudioManager mAudioManager; @Mock private DatabaseManager mDatabaseManager; - private AudioManager.OnModeChangedListener mAudioModeChangedListener; @Before public void setUp() throws Exception { @@ -99,10 +98,6 @@ public class ActiveDeviceManagerTest { MockitoAnnotations.initMocks(this); TestUtils.setAdapterService(mAdapterService); - doAnswer(invocation -> { - mAudioModeChangedListener = invocation.getArgument(1); - return null; - }).when(mAudioManager).addOnModeChangedListener(any(), any()); when(mAdapterService.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager); when(mAdapterService.getSystemServiceName(AudioManager.class)) .thenReturn(Context.AUDIO_SERVICE); @@ -127,20 +122,24 @@ public class ActiveDeviceManagerTest { mDeviceConnectionStack = new ArrayList<>(); mMostRecentDevice = null; - when(mA2dpService.setActiveDevice(any(), anyBoolean())).thenReturn(true); + when(mA2dpService.setActiveDevice(any())).thenReturn(true); when(mHeadsetService.setActiveDevice(any())).thenReturn(true); - when(mHearingAidService.setActiveDevice(any(), anyBoolean())).thenReturn(true); - when(mLeAudioService.setActiveDevice(any(), anyBoolean())).thenReturn(true); + when(mHearingAidService.setActiveDevice(any())).thenReturn(true); + when(mLeAudioService.setActiveDevice(any())).thenReturn(true); when(mA2dpService.getFallbackDevice()).thenAnswer(invocation -> { - int index = Math.max(mDeviceConnectionStack.indexOf(mA2dpDevice), - mDeviceConnectionStack.indexOf(mA2dpHeadsetDevice)); - return (index == -1) ? null : mDeviceConnectionStack.get(index); + if (!mDeviceConnectionStack.isEmpty() && Objects.equals(mA2dpDevice, + mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1))) { + return mA2dpDevice; + } + return null; }); when(mHeadsetService.getFallbackDevice()).thenAnswer(invocation -> { - int index = Math.max(mDeviceConnectionStack.indexOf(mHeadsetDevice), - mDeviceConnectionStack.indexOf(mA2dpHeadsetDevice)); - return (index == -1) ? null : mDeviceConnectionStack.get(index); + if (!mDeviceConnectionStack.isEmpty() && Objects.equals(mHeadsetDevice, + mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1))) { + return mHeadsetDevice; + } + return null; }); when(mDatabaseManager.getMostRecentlyConnectedDevicesInList(any())).thenAnswer( invocation -> { @@ -178,7 +177,7 @@ public class ActiveDeviceManagerTest { @Test public void onlyA2dpConnected_setA2dpActive() { a2dpConnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); } /** @@ -187,10 +186,10 @@ public class ActiveDeviceManagerTest { @Test public void secondA2dpConnected_setSecondA2dpActive() { a2dpConnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); a2dpConnected(mA2dpHeadsetDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); } /** @@ -199,10 +198,10 @@ public class ActiveDeviceManagerTest { @Test public void lastA2dpDisconnected_clearA2dpActive() { a2dpConnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); a2dpDisconnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(null, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); } /** @@ -211,15 +210,15 @@ public class ActiveDeviceManagerTest { @Test public void a2dpActiveDeviceSelected_setActive() { a2dpConnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); a2dpConnected(mA2dpHeadsetDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); a2dpActiveDeviceChanged(mA2dpDevice); // Don't call mA2dpService.setActiveDevice() TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mA2dpService).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, times(1)).setActiveDevice(mA2dpDevice); Assert.assertEquals(mA2dpDevice, mActiveDeviceManager.getA2dpActiveDevice()); } @@ -230,14 +229,14 @@ public class ActiveDeviceManagerTest { @Test public void a2dpSecondDeviceDisconnected_fallbackDeviceActive() { a2dpConnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); a2dpConnected(mSecondaryAudioDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice); Mockito.clearInvocations(mA2dpService); a2dpDisconnected(mSecondaryAudioDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); } /** @@ -245,8 +244,6 @@ public class ActiveDeviceManagerTest { */ @Test public void onlyHeadsetConnected_setHeadsetActive() { - when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); - headsetConnected(mHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); } @@ -256,8 +253,6 @@ public class ActiveDeviceManagerTest { */ @Test public void secondHeadsetConnected_setSecondHeadsetActive() { - when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); - headsetConnected(mHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); @@ -270,8 +265,6 @@ public class ActiveDeviceManagerTest { */ @Test public void lastHeadsetDisconnected_clearHeadsetActive() { - when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); - headsetConnected(mHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); @@ -284,8 +277,6 @@ public class ActiveDeviceManagerTest { */ @Test public void headsetActiveDeviceSelected_setActive() { - when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); - headsetConnected(mHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); @@ -295,7 +286,7 @@ public class ActiveDeviceManagerTest { headsetActiveDeviceChanged(mHeadsetDevice); // Don't call mHeadsetService.setActiveDevice() TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mHeadsetService).setActiveDevice(mHeadsetDevice); + verify(mHeadsetService, times(1)).setActiveDevice(mHeadsetDevice); Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice()); } @@ -318,33 +309,6 @@ public class ActiveDeviceManagerTest { verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); } - /** - * Test setActiveDevice(null) for both A2dpService and HeadsetService are called when an - * activated combo (A2DP + Headset) device is disconnected while in call. - */ - @Test - public void a2dpHeadsetDisconnected_callsSetActiveDeviceNull() { - when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); - // A2dpHeadset connected - headsetConnected(mA2dpHeadsetDevice); - a2dpConnected(mA2dpHeadsetDevice); - // Verify activation of A2DP in media mode - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice, false); - - // Mode changed to call mode - when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); - mAudioModeChangedListener.onModeChanged(AudioManager.MODE_IN_CALL); - // Verify activation of HFP in call mode - verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); - - // A2dpHeadset disconnected - headsetDisconnected(mA2dpHeadsetDevice); - a2dpDisconnected(mA2dpHeadsetDevice); - // Verify setActiveDevice(null) called - verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(null); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(null, false); - } - /** * A combo (A2DP + Headset) device is connected. Then a Hearing Aid is connected. */ @@ -355,11 +319,11 @@ public class ActiveDeviceManagerTest { a2dpConnected(mA2dpHeadsetDevice); headsetConnected(mA2dpHeadsetDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); hearingAidActiveDeviceChanged(mHearingAidDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(null, true); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); } @@ -376,7 +340,7 @@ public class ActiveDeviceManagerTest { headsetConnected(mA2dpHeadsetDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice, false); + verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice); verify(mHeadsetService, never()).setActiveDevice(mA2dpHeadsetDevice); } @@ -393,9 +357,9 @@ public class ActiveDeviceManagerTest { a2dpActiveDeviceChanged(mA2dpHeadsetDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mHearingAidService).setActiveDevice(null, true); + verify(mHearingAidService).setActiveDevice(isNull()); // Don't call mA2dpService.setActiveDevice() - verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice, false); + verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice); Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getA2dpActiveDevice()); Assert.assertEquals(null, mActiveDeviceManager.getHearingAidActiveDevice()); } @@ -413,7 +377,7 @@ public class ActiveDeviceManagerTest { headsetActiveDeviceChanged(mA2dpHeadsetDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mHearingAidService).setActiveDevice(null, true); + verify(mHearingAidService).setActiveDevice(isNull()); // Don't call mHeadsetService.setActiveDevice() verify(mHeadsetService, never()).setActiveDevice(mA2dpHeadsetDevice); Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice()); @@ -426,7 +390,7 @@ public class ActiveDeviceManagerTest { @Test public void onlyLeAudioConnected_setHeadsetActive() { leAudioConnected(mLeAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); } /** @@ -435,10 +399,10 @@ public class ActiveDeviceManagerTest { @Test public void secondLeAudioConnected_setSecondLeAudioActive() { leAudioConnected(mLeAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); leAudioConnected(mSecondaryAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice); } /** @@ -447,10 +411,10 @@ public class ActiveDeviceManagerTest { @Test public void lastLeAudioDisconnected_clearLeAudioActive() { leAudioConnected(mLeAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); leAudioDisconnected(mLeAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(null, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); } /** @@ -459,15 +423,15 @@ public class ActiveDeviceManagerTest { @Test public void leAudioActiveDeviceSelected_setActive() { leAudioConnected(mLeAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); leAudioConnected(mSecondaryAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice); leAudioActiveDeviceChanged(mLeAudioDevice); // Don't call mLeAudioService.setActiveDevice() TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mLeAudioService).setActiveDevice(mLeAudioDevice, false); + verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice); Assert.assertEquals(mLeAudioDevice, mActiveDeviceManager.getLeAudioActiveDevice()); } @@ -478,14 +442,14 @@ public class ActiveDeviceManagerTest { @Test public void leAudioSecondDeviceDisconnected_fallbackDeviceActive() { leAudioConnected(mLeAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); leAudioConnected(mSecondaryAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice); Mockito.clearInvocations(mLeAudioService); leAudioDisconnected(mSecondaryAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); } /** @@ -498,11 +462,11 @@ public class ActiveDeviceManagerTest { a2dpConnected(mA2dpHeadsetDevice); headsetConnected(mA2dpHeadsetDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); leAudioActiveDeviceChanged(mLeAudioDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(null, true); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); } @@ -519,7 +483,7 @@ public class ActiveDeviceManagerTest { headsetConnected(mA2dpHeadsetDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mA2dpService).setActiveDevice(mA2dpHeadsetDevice, false); + verify(mA2dpService).setActiveDevice(mA2dpHeadsetDevice); verify(mHeadsetService).setActiveDevice(mA2dpHeadsetDevice); } @@ -536,8 +500,8 @@ public class ActiveDeviceManagerTest { a2dpActiveDeviceChanged(mA2dpHeadsetDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mLeAudioService).setActiveDevice(null, true); - verify(mA2dpService).setActiveDevice(mA2dpHeadsetDevice, false); + verify(mLeAudioService).setActiveDevice(isNull()); + verify(mA2dpService).setActiveDevice(mA2dpHeadsetDevice); Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getA2dpActiveDevice()); Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice()); } @@ -549,13 +513,13 @@ public class ActiveDeviceManagerTest { public void leAudioActive_setHeadsetActiveExplicitly() { Assume.assumeTrue("Ignore test when LeAudioService is not enabled", LeAudioService.isEnabled()); - when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); + leAudioActiveDeviceChanged(mLeAudioDevice); headsetConnected(mA2dpHeadsetDevice); headsetActiveDeviceChanged(mA2dpHeadsetDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mLeAudioService).setActiveDevice(null, false); + verify(mLeAudioService).setActiveDevice(isNull()); verify(mHeadsetService).setActiveDevice(mA2dpHeadsetDevice); Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice()); Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice()); @@ -570,15 +534,15 @@ public class ActiveDeviceManagerTest { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); leAudioConnected(mLeAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); a2dpConnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); Mockito.clearInvocations(mLeAudioService); a2dpDisconnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS).atLeast(1)).setActiveDevice(null, true); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS).atLeast(1)).setActiveDevice(isNull()); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); } /** @@ -590,15 +554,15 @@ public class ActiveDeviceManagerTest { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); a2dpConnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); leAudioConnected(mLeAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice); Mockito.clearInvocations(mA2dpService); leAudioDisconnected(mLeAudioDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS).atLeast(1)).setActiveDevice(null, true); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS).atLeast(1)).setActiveDevice(isNull()); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); } /** @@ -608,15 +572,14 @@ public class ActiveDeviceManagerTest { @Test public void hearingAidSecondDeviceDisconnected_fallbackDeviceActive() { hearingAidConnected(mHearingAidDevice); - verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice, false); + verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice); hearingAidConnected(mSecondaryAudioDevice); - verify(mHearingAidService, timeout(TIMEOUT_MS)) - .setActiveDevice(mSecondaryAudioDevice, false); + verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice); Mockito.clearInvocations(mHearingAidService); hearingAidDisconnected(mSecondaryAudioDevice); - verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice, false); + verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice); } /** @@ -628,7 +591,7 @@ public class ActiveDeviceManagerTest { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); hearingAidConnected(mHearingAidDevice); - verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice, false); + verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice); leAudioConnected(mLeAudioDevice); a2dpConnected(mA2dpDevice); @@ -636,14 +599,14 @@ public class ActiveDeviceManagerTest { a2dpActiveDeviceChanged(mA2dpDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mHearingAidService).setActiveDevice(null, true); + verify(mHearingAidService).setActiveDevice(isNull()); verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); - verify(mA2dpService, never()).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, never()).setActiveDevice(mA2dpDevice); a2dpDisconnected(mA2dpDevice); - verify(mA2dpService, timeout(TIMEOUT_MS).atLeast(1)).setActiveDevice(null, true); + verify(mA2dpService, timeout(TIMEOUT_MS).atLeast(1)).setActiveDevice(isNull()); verify(mHearingAidService, timeout(TIMEOUT_MS).times(2)) - .setActiveDevice(mHearingAidDevice, false); + .setActiveDevice(mHearingAidDevice); } /** @@ -656,7 +619,7 @@ public class ActiveDeviceManagerTest { verify(mLeAudioService, never()).setActiveDevice(mLeHearingAidDevice); leAudioConnected(mLeHearingAidDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeHearingAidDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeHearingAidDevice); } /** @@ -667,7 +630,7 @@ public class ActiveDeviceManagerTest { public void leAudioConnectedAfterLeHearingAid_setLeAudioActiveShouldNotBeCalled() { leHearingAidConnected(mLeHearingAidDevice); leAudioConnected(mLeHearingAidDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeHearingAidDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeHearingAidDevice); leAudioConnected(mLeAudioDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); @@ -684,24 +647,24 @@ public class ActiveDeviceManagerTest { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); hearingAidConnected(mHearingAidDevice); - verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice, false); + verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice); leHearingAidConnected(mLeHearingAidDevice); leAudioConnected(mLeHearingAidDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeHearingAidDevice, false); + verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeHearingAidDevice); a2dpConnected(mA2dpDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mA2dpService, never()).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, never()).setActiveDevice(mA2dpDevice); Mockito.clearInvocations(mHearingAidService, mA2dpService); leHearingAidDisconnected(mLeHearingAidDevice); leAudioDisconnected(mLeHearingAidDevice); - verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice, false); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(null, true); + verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(mHearingAidDevice); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); hearingAidDisconnected(mHearingAidDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); } /** @@ -711,57 +674,13 @@ public class ActiveDeviceManagerTest { public void wiredAudioDeviceConnected_setAllActiveDevicesNull() { a2dpConnected(mA2dpDevice); headsetConnected(mHeadsetDevice); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); mActiveDeviceManager.wiredAudioDeviceConnected(); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(null, false); + verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); - verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(null, false); - } - - @Test - public void leAudioActive_whileMediaPlayback_useLeAudioForMediaAndHeadsetForCall() { - leAudioConnected(mLeAudioDevice); - a2dpConnected(mA2dpDevice); - headsetConnected(mHeadsetDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); - verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); - - Mockito.clearInvocations(mLeAudioService, mA2dpService, mHeadsetService); - leAudioActiveDeviceChanged(mLeAudioDevice); - TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mA2dpService).setActiveDevice(null, true); - verify(mHeadsetService).setActiveDevice(null); - - Mockito.clearInvocations(mLeAudioService, mA2dpService, mHeadsetService); - mAudioModeChangedListener.onModeChanged(AudioManager.MODE_IN_CALL); - verify(mHeadsetService).setActiveDevice(mHeadsetDevice); - verify(mLeAudioService).setActiveDevice(null, true); - } - - @Test - public void leAudioActive_whileInCall_useLeAudioForCallAndHeadsetForMedia() { - when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); - - leAudioConnected(mLeAudioDevice); - headsetConnected(mHeadsetDevice); - a2dpConnected(mA2dpDevice); - verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice, false); - verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice, false); - verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); - - Mockito.clearInvocations(mLeAudioService, mA2dpService, mHeadsetService); - leAudioActiveDeviceChanged(mLeAudioDevice); - TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); - verify(mA2dpService).setActiveDevice(null, true); - verify(mHeadsetService).setActiveDevice(null); - - Mockito.clearInvocations(mLeAudioService, mA2dpService, mHeadsetService); - mAudioModeChangedListener.onModeChanged(AudioManager.MODE_NORMAL); - verify(mA2dpService).setActiveDevice(mA2dpDevice, false); - verify(mLeAudioService).setActiveDevice(null, true); + verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); } /** diff --git a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java index 44d0c8deb222ec6cbb1c4dd13afb0087561c6b35..cf74dfd060a9b8b0a0322c5347b676488096dabb 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java @@ -183,10 +183,6 @@ public class HearingAidServiceTest { if (newState == BluetoothProfile.STATE_CONNECTED) { // ActiveDeviceManager calls setActiveDevice when connected. mService.setActiveDevice(device); - } else if (newState == BluetoothProfile.STATE_DISCONNECTED - && mService.getConnectedDevices().isEmpty()) { - // ActiveDeviceManager calls setActiveDevice(null) when all devices are disconnected. - mService.setActiveDevice(null); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java index 8f14e37567016c6cfe64c120fa82472a234606d1..fcb6779de20759a3cfd4cd1050c6e1157cafecbd 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java @@ -32,7 +32,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.annotation.NonNull; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeAudio; @@ -48,6 +47,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.media.BluetoothProfileConnectionInfo; +import android.os.Parcel; import android.os.ParcelUuid; import androidx.test.InstrumentationRegistry; @@ -55,12 +55,14 @@ import androidx.test.filters.MediumTest; import androidx.test.rule.ServiceTestRule; import androidx.test.runner.AndroidJUnit4; +import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.vc.VolumeControlService; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -281,24 +283,15 @@ public class LeAudioServiceTest { } } - private void verifyConnectionStateIntent(int timeoutMs, @NonNull BluetoothDevice device, + private void verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState) { Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device)); assertThat(intent).isNotNull(); assertThat(intent.getAction()) .isEqualTo(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); assertThat((BluetoothDevice)intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).isEqualTo(device); - int newConnectionState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); - assertThat(newConnectionState).isEqualTo(newState); + assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)).isEqualTo(newState); assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1)).isEqualTo(prevState); - - if (newConnectionState == BluetoothProfile.STATE_CONNECTED) { - // ActiveDeviceManager calls deviceConnected when connected. - mService.deviceConnected(device); - } else if (newConnectionState == BluetoothProfile.STATE_DISCONNECTED) { - // ActiveDeviceManager calls deviceDisconnected when connected. - mService.deviceDisconnected(device, false); - } } /** @@ -1353,6 +1346,7 @@ public class LeAudioServiceTest { verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(any(), eq(leadDevice), any(BluetoothProfileConnectionInfo.class)); + } /**