Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit adc0a585 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Reduced the audio effort while changing the A2DP active device"

parents 9e26d63c a84c0968
Loading
Loading
Loading
Loading
+113 −96
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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 "
@@ -465,6 +470,7 @@ public class A2dpService extends ProfileService {
                }
            }
        }
    }

    /**
     * Process a change in the silence mode for a {@link BluetoothDevice}.
@@ -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");
@@ -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.
@@ -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;
    }

@@ -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;
        }

@@ -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) {
@@ -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);
        }
@@ -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.