Loading android/app/src/com/android/bluetooth/a2dp/A2dpService.java +113 −96 Original line number Diff line number Diff line Loading @@ -75,6 +75,9 @@ public class A2dpService extends ProfileService { private final ConcurrentMap<BluetoothDevice, A2dpStateMachine> mStateMachines = new ConcurrentHashMap<>(); // Protect setActiveDevice() so all invoked is handled squentially private final Object mActiveSwitchingGuard = new Object(); // Upper limit of all A2DP devices: Bonded or Connected private static final int MAX_A2DP_STATE_MACHINES = 50; // Upper limit of all A2DP devices that are Connected or Connecting Loading Loading @@ -436,16 +439,16 @@ public class A2dpService extends ProfileService { } private void removeActiveDevice(boolean forceStopPlayingAudio) { BluetoothDevice previousActiveDevice = mActiveDevice; synchronized (mActiveSwitchingGuard) { BluetoothDevice previousActiveDevice = null; synchronized (mStateMachines) { if (mActiveDevice == null) return; previousActiveDevice = mActiveDevice; } // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why. updateAndBroadcastActiveDevice(null); if (previousActiveDevice == null) { return; } // 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 Loading @@ -458,6 +461,8 @@ public class A2dpService extends ProfileService { mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, suppressNoisyIntent, -1); synchronized (mStateMachines) { // Make sure the Active device in native layer is set to null and audio is off if (!mA2dpNativeInterface.setActiveDevice(null)) { Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native " Loading @@ -465,6 +470,7 @@ public class A2dpService extends ProfileService { } } } } /** * Process a change in the silence mode for a {@link BluetoothDevice}. Loading Loading @@ -499,20 +505,26 @@ public class A2dpService extends ProfileService { */ public boolean setActiveDevice(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); synchronized (mStateMachines) { BluetoothDevice previousActiveDevice = mActiveDevice; if (DBG) { Log.d(TAG, "setActiveDevice(" + device + "): previous is " + previousActiveDevice); } synchronized (mActiveSwitchingGuard) { if (device == null) { // Remove active device and continue playing audio only if necessary. removeActiveDevice(false); return true; } BluetoothCodecStatus codecStatus = null; A2dpStateMachine sm = mStateMachines.get(device); A2dpStateMachine sm = null; BluetoothDevice previousActiveDevice = null; synchronized (mStateMachines) { if (Objects.equals(device, mActiveDevice)) { Log.i(TAG, "setActiveDevice(" + device + "): current is " + mActiveDevice + " no changed"); // returns true since the device is activated even double attempted return true; } if (DBG) { Log.d(TAG, "setActiveDevice(" + device + "): current is " + mActiveDevice); } sm = mStateMachines.get(device); if (sm == null) { Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: " + "no state machine"); Loading @@ -523,21 +535,16 @@ public class A2dpService extends ProfileService { + "device is not connected"); return false; } if (!mA2dpNativeInterface.setActiveDevice(device)) { Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer"); return false; previousActiveDevice = mActiveDevice; } codecStatus = sm.getCodecStatus(); boolean deviceChanged = !Objects.equals(device, mActiveDevice); // Switch from one A2DP to another A2DP device if (DBG) { Log.d(TAG, "Switch A2DP devices to " + device + " from " + previousActiveDevice); } // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why. updateAndBroadcastActiveDevice(device); if (deviceChanged) { // Send an intent with the active device codec config if (codecStatus != null) { broadcastCodecConfig(mActiveDevice, codecStatus); } // Make sure the Audio Manager knows the previous Active device is disconnected, // and the new Active device is connected. // Also, mute and unmute the output during the switch to avoid audio glitches. Loading @@ -553,26 +560,41 @@ public class A2dpService extends ProfileService { BluetoothProfile.A2DP, true, -1); } BluetoothDevice newActiveDevice = null; synchronized (mStateMachines) { if (!mA2dpNativeInterface.setActiveDevice(device)) { Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native " + "layer"); // Remove active device and stop playing audio. removeActiveDevice(true); return false; } // Send an intent with the active device codec config BluetoothCodecStatus codecStatus = sm.getCodecStatus(); if (codecStatus != null) { broadcastCodecConfig(mActiveDevice, codecStatus); } newActiveDevice = mActiveDevice; } // Tasks of Bluetooth are done, and now restore the AudioManager side. int rememberedVolume = -1; if (mFactory.getAvrcpTargetService() != null) { rememberedVolume = mFactory.getAvrcpTargetService() .getRememberedVolumeForDevice(mActiveDevice); .getRememberedVolumeForDevice(newActiveDevice); } mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, newActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, rememberedVolume); // Inform the Audio Service about the codec configuration // change, so the Audio Service can reset accordingly the audio // feeding parameters in the Audio HAL to the Bluetooth stack. mAudioManager.handleBluetoothA2dpDeviceConfigChange(mActiveDevice); mAudioManager.handleBluetoothA2dpDeviceConfigChange(newActiveDevice); if (wasMuted) { mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_UNMUTE, AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); } } } return true; } Loading Loading @@ -956,11 +978,11 @@ public class A2dpService extends ProfileService { Log.d(TAG, "updateAndBroadcastActiveDevice(" + device + ")"); } synchronized (mStateMachines) { // Make sure volume has been store before device been remove from active. if (mFactory.getAvrcpTargetService() != null) { mFactory.getAvrcpTargetService().volumeDeviceSwitched(device); } synchronized (mStateMachines) { mActiveDevice = device; } Loading Loading @@ -1025,13 +1047,12 @@ public class A2dpService extends ProfileService { if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { return; } } if (mFactory.getAvrcpTargetService() != null) { mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device); } removeStateMachine(device); } } private void removeStateMachine(BluetoothDevice device) { synchronized (mStateMachines) { Loading Loading @@ -1110,7 +1131,6 @@ public class A2dpService extends ProfileService { if ((device == null) || (fromState == toState)) { return; } synchronized (mStateMachines) { if (toState == BluetoothProfile.STATE_CONNECTED) { MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP); } Loading @@ -1124,17 +1144,14 @@ public class A2dpService extends ProfileService { } // 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 (mAdapterService.getBondState(device) == BluetoothDevice.BOND_NONE) { if (mFactory.getAvrcpTargetService() != null) { mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device); } removeStateMachine(device); } } } } /** * Receiver for processing device connection state changes. Loading Loading
android/app/src/com/android/bluetooth/a2dp/A2dpService.java +113 −96 Original line number Diff line number Diff line Loading @@ -75,6 +75,9 @@ public class A2dpService extends ProfileService { private final ConcurrentMap<BluetoothDevice, A2dpStateMachine> mStateMachines = new ConcurrentHashMap<>(); // Protect setActiveDevice() so all invoked is handled squentially private final Object mActiveSwitchingGuard = new Object(); // Upper limit of all A2DP devices: Bonded or Connected private static final int MAX_A2DP_STATE_MACHINES = 50; // Upper limit of all A2DP devices that are Connected or Connecting Loading Loading @@ -436,16 +439,16 @@ public class A2dpService extends ProfileService { } private void removeActiveDevice(boolean forceStopPlayingAudio) { BluetoothDevice previousActiveDevice = mActiveDevice; synchronized (mActiveSwitchingGuard) { BluetoothDevice previousActiveDevice = null; synchronized (mStateMachines) { if (mActiveDevice == null) return; previousActiveDevice = mActiveDevice; } // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why. updateAndBroadcastActiveDevice(null); if (previousActiveDevice == null) { return; } // 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 Loading @@ -458,6 +461,8 @@ public class A2dpService extends ProfileService { mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, suppressNoisyIntent, -1); synchronized (mStateMachines) { // Make sure the Active device in native layer is set to null and audio is off if (!mA2dpNativeInterface.setActiveDevice(null)) { Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native " Loading @@ -465,6 +470,7 @@ public class A2dpService extends ProfileService { } } } } /** * Process a change in the silence mode for a {@link BluetoothDevice}. Loading Loading @@ -499,20 +505,26 @@ public class A2dpService extends ProfileService { */ public boolean setActiveDevice(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); synchronized (mStateMachines) { BluetoothDevice previousActiveDevice = mActiveDevice; if (DBG) { Log.d(TAG, "setActiveDevice(" + device + "): previous is " + previousActiveDevice); } synchronized (mActiveSwitchingGuard) { if (device == null) { // Remove active device and continue playing audio only if necessary. removeActiveDevice(false); return true; } BluetoothCodecStatus codecStatus = null; A2dpStateMachine sm = mStateMachines.get(device); A2dpStateMachine sm = null; BluetoothDevice previousActiveDevice = null; synchronized (mStateMachines) { if (Objects.equals(device, mActiveDevice)) { Log.i(TAG, "setActiveDevice(" + device + "): current is " + mActiveDevice + " no changed"); // returns true since the device is activated even double attempted return true; } if (DBG) { Log.d(TAG, "setActiveDevice(" + device + "): current is " + mActiveDevice); } sm = mStateMachines.get(device); if (sm == null) { Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: " + "no state machine"); Loading @@ -523,21 +535,16 @@ public class A2dpService extends ProfileService { + "device is not connected"); return false; } if (!mA2dpNativeInterface.setActiveDevice(device)) { Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer"); return false; previousActiveDevice = mActiveDevice; } codecStatus = sm.getCodecStatus(); boolean deviceChanged = !Objects.equals(device, mActiveDevice); // Switch from one A2DP to another A2DP device if (DBG) { Log.d(TAG, "Switch A2DP devices to " + device + " from " + previousActiveDevice); } // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why. updateAndBroadcastActiveDevice(device); if (deviceChanged) { // Send an intent with the active device codec config if (codecStatus != null) { broadcastCodecConfig(mActiveDevice, codecStatus); } // Make sure the Audio Manager knows the previous Active device is disconnected, // and the new Active device is connected. // Also, mute and unmute the output during the switch to avoid audio glitches. Loading @@ -553,26 +560,41 @@ public class A2dpService extends ProfileService { BluetoothProfile.A2DP, true, -1); } BluetoothDevice newActiveDevice = null; synchronized (mStateMachines) { if (!mA2dpNativeInterface.setActiveDevice(device)) { Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native " + "layer"); // Remove active device and stop playing audio. removeActiveDevice(true); return false; } // Send an intent with the active device codec config BluetoothCodecStatus codecStatus = sm.getCodecStatus(); if (codecStatus != null) { broadcastCodecConfig(mActiveDevice, codecStatus); } newActiveDevice = mActiveDevice; } // Tasks of Bluetooth are done, and now restore the AudioManager side. int rememberedVolume = -1; if (mFactory.getAvrcpTargetService() != null) { rememberedVolume = mFactory.getAvrcpTargetService() .getRememberedVolumeForDevice(mActiveDevice); .getRememberedVolumeForDevice(newActiveDevice); } mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, newActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, rememberedVolume); // Inform the Audio Service about the codec configuration // change, so the Audio Service can reset accordingly the audio // feeding parameters in the Audio HAL to the Bluetooth stack. mAudioManager.handleBluetoothA2dpDeviceConfigChange(mActiveDevice); mAudioManager.handleBluetoothA2dpDeviceConfigChange(newActiveDevice); if (wasMuted) { mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_UNMUTE, AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); } } } return true; } Loading Loading @@ -956,11 +978,11 @@ public class A2dpService extends ProfileService { Log.d(TAG, "updateAndBroadcastActiveDevice(" + device + ")"); } synchronized (mStateMachines) { // Make sure volume has been store before device been remove from active. if (mFactory.getAvrcpTargetService() != null) { mFactory.getAvrcpTargetService().volumeDeviceSwitched(device); } synchronized (mStateMachines) { mActiveDevice = device; } Loading Loading @@ -1025,13 +1047,12 @@ public class A2dpService extends ProfileService { if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { return; } } if (mFactory.getAvrcpTargetService() != null) { mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device); } removeStateMachine(device); } } private void removeStateMachine(BluetoothDevice device) { synchronized (mStateMachines) { Loading Loading @@ -1110,7 +1131,6 @@ public class A2dpService extends ProfileService { if ((device == null) || (fromState == toState)) { return; } synchronized (mStateMachines) { if (toState == BluetoothProfile.STATE_CONNECTED) { MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP); } Loading @@ -1124,17 +1144,14 @@ public class A2dpService extends ProfileService { } // 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 (mAdapterService.getBondState(device) == BluetoothDevice.BOND_NONE) { if (mFactory.getAvrcpTargetService() != null) { mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device); } removeStateMachine(device); } } } } /** * Receiver for processing device connection state changes. Loading