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

Commit 4e243ee3 authored by Eric Laurent's avatar Eric Laurent Committed by Ajay Panicker
Browse files

Add field to set A2DP device volume on connect (2/2)

Add a field to setBluetoothA2dpDeviceConnectionStateInt() to allow
Bluetooth to set a volume for the new device when it connects.

Bug: 79529581
Test: Switch repeatedly between devices and see that the old volume isn't
used on the new device or the new volume isn't used on the old device.
Change-Id: If6dba6e08a9a449733db2b05c6523939c5fa9cea
parent 40bfd245
Loading
Loading
Loading
Loading
+18 −11
Original line number Diff line number Diff line
@@ -435,6 +435,11 @@ public class A2dpService extends ProfileService {
            if (DBG) {
                Log.d(TAG, "setActiveDevice(" + device + "): previous is " + previousActiveDevice);
            }

            if (previousActiveDevice != null && AvrcpTargetService.get() != null) {
                AvrcpTargetService.get().storeVolumeForDevice(previousActiveDevice);
            }

            if (device == null) {
                // Clear the active device
                mActiveDevice = null;
@@ -452,7 +457,7 @@ public class A2dpService extends ProfileService {
                            previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
                            BluetoothProfile.A2DP,
                            getConnectionState(previousActiveDevice)
                                == BluetoothProfile.STATE_CONNECTED);
                                == BluetoothProfile.STATE_CONNECTED, -1);
                    // 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 "
@@ -495,11 +500,21 @@ public class A2dpService extends ProfileService {
                if (previousActiveDevice != null) {
                    mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
                            previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
                            BluetoothProfile.A2DP, true);
                            BluetoothProfile.A2DP, true, -1);
                }

                int rememberedVolume = -1;
                if (AvrcpTargetService.get() != null) {
                    AvrcpTargetService.get().volumeDeviceSwitched(mActiveDevice);

                    rememberedVolume = AvrcpTargetService.get()
                            .getRememberedVolumeForDevice(mActiveDevice);
                }

                mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
                        mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
                        true);
                        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.
@@ -803,14 +818,6 @@ public class A2dpService extends ProfileService {
            Log.d(TAG, "broadcastActiveDevice(" + device + ")");
        }

        // We need to inform AVRCP that the device has switched before informing the audio service
        // so that AVRCP can prepare and wait on audio service connecting the new stream before
        // restoring the previous volume. Otherwise the updated volume could be applied to the
        // old active device before the switch has fully completed.
        if (AvrcpTargetService.get() != null) {
            AvrcpTargetService.get().volumeDeviceSwitched(device);
        }

        Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+22 −3
Original line number Diff line number Diff line
@@ -198,9 +198,9 @@ public class AvrcpTargetService extends ProfileService {
    }

    /**
     * Signal to the service that the current audio out device has changed. The current volume
     * for the old device is saved and the new device has its volume restored. If there is no
     * saved volume use the current system volume.
     * Signal to the service that the current audio out device has changed and to inform
     * the audio service whether the new device supports absolute volume. If it does, also
     * set the absolute volume level on the remote device.
     */
    public void volumeDeviceSwitched(BluetoothDevice device) {
        if (DEBUG) {
@@ -209,6 +209,25 @@ public class AvrcpTargetService extends ProfileService {
        mVolumeManager.volumeDeviceSwitched(device);
    }

    /**
     * Store the current system volume for a device in order to be retrieved later.
     */
    public void storeVolumeForDevice(BluetoothDevice device) {
        if (device == null) return;

        mVolumeManager.storeVolumeForDevice(device);
    }

    /**
     * Retrieve the remembered volume for a device. Returns -1 if there is no volume for the
     * device.
     */
    public int getRememberedVolumeForDevice(BluetoothDevice device) {
        if (device == null) return -1;

        return mVolumeManager.getVolume(device, -1);
    }

    // TODO (apanicke): Add checks to blacklist Absolute Volume devices if they behave poorly.
    void setVolume(int avrcpVolume) {
        int deviceVolume =
+29 −69
Original line number Diff line number Diff line
@@ -22,8 +22,6 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.util.Log;

@@ -31,7 +29,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

class AvrcpVolumeManager extends AudioDeviceCallback {
class AvrcpVolumeManager {
    public static final String TAG = "NewAvrcpVolumeManager";
    public static final boolean DEBUG = true;

@@ -48,7 +46,6 @@ class AvrcpVolumeManager extends AudioDeviceCallback {

    HashMap<BluetoothDevice, Boolean> mDeviceMap = new HashMap();
    HashMap<BluetoothDevice, Integer> mVolumeMap = new HashMap();
    BluetoothDevice mDeferredDevice = null;
    BluetoothDevice mCurrentDevice = null;
    boolean mAbsoluteVolumeSupported = false;

@@ -67,31 +64,17 @@ class AvrcpVolumeManager extends AudioDeviceCallback {
        return mContext.getSharedPreferences(VOLUME_MAP, Context.MODE_PRIVATE);
    }

    private int getVolume(@NonNull BluetoothDevice device, int defaultValue) {
        if (!mVolumeMap.containsKey(device)) {
            Log.w(TAG, "getVolume: Couldn't find volume preference for device: " + device);
            return defaultValue;
        }

        return mVolumeMap.get(device);
    }

    private void switchVolumeDevice(@NonNull BluetoothDevice device) {
        // Inform the audio manager that the device has changed
        d("switchVolumeDevice: Set Absolute volume support to " + mDeviceMap.get(device));
        mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(), mDeviceMap.get(device));

        // Get the current system volume and try to get the preference volume
        int currVolume = mAudioManager.getStreamVolume(STREAM_MUSIC);
        int savedVolume = getVolume(device, currVolume);

        // If the preference volume isn't equal to the current stream volume then that means
        // we had a stored preference.
        d("switchVolumeDevice: currVolume=" + currVolume + " savedVolume=" + savedVolume);

        Log.i(TAG, "switchVolumeDevice: restoring volume level " + savedVolume);
        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, savedVolume,
                AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);

        // If absolute volume for the device is supported, set the volume for the device
        if (mDeviceMap.get(device)) {
            int avrcpVolume = systemToAvrcpVolume(savedVolume);
@@ -100,43 +83,6 @@ class AvrcpVolumeManager extends AudioDeviceCallback {
        }
    }

    @Override
    public synchronized void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
        if (mDeferredDevice == null) {
            d("onAudioDevicesAdded: Not expecting device changed");
            return;
        }

        boolean foundDevice = false;
        d("onAudioDevicesAdded: size: " + addedDevices.length);
        for (int i = 0; i < addedDevices.length; i++) {
            d("onAudioDevicesAdded: address=" + addedDevices[i].getAddress());
            if (addedDevices[i].getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
                    && Objects.equals(addedDevices[i].getAddress(), mDeferredDevice.getAddress())) {
                foundDevice = true;
                break;
            }
        }

        if (!foundDevice) {
            d("Didn't find deferred device in list: device=" + mDeferredDevice);
            return;
        }

        mCurrentDevice = mDeferredDevice;
        mDeferredDevice = null;

        // A2DP can sometimes connect and set a device to active before AVRCP has determined if the
        // device supports absolute volume. Defer switching the device until AVRCP returns the
        // info.
        if (!mDeviceMap.containsKey(mCurrentDevice)) {
            Log.w(TAG, "volumeDeviceSwitched: Device isn't connected: " + mCurrentDevice);
            return;
        }

        switchVolumeDevice(mCurrentDevice);
    }

    AvrcpVolumeManager(Context context, AudioManager audioManager,
            AvrcpNativeInterface nativeInterface) {
        mContext = context;
@@ -144,8 +90,6 @@ class AvrcpVolumeManager extends AudioDeviceCallback {
        mNativeInterface = nativeInterface;
        sDeviceMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);

        mAudioManager.registerAudioDeviceCallback(this, null);

        // Load the stored volume preferences into a hash map since shared preferences are slow
        // to poll and update. If the device has been unbonded since last start remove it from
        // the map.
@@ -166,19 +110,29 @@ class AvrcpVolumeManager extends AudioDeviceCallback {
        volumeMapEditor.apply();
    }

    void storeVolume() {
    void storeVolumeForDevice(BluetoothDevice device) {
        SharedPreferences.Editor pref = getVolumeMap().edit();
        int storeVolume =  mAudioManager.getStreamVolume(STREAM_MUSIC);
        Log.i(TAG, "storeVolume: Storing stream volume level for device " + mCurrentDevice
        Log.i(TAG, "storeVolume: Storing stream volume level for device " + device
                + " : " + storeVolume);
        mVolumeMap.put(mCurrentDevice, storeVolume);
        pref.putInt(mCurrentDevice.getAddress(), storeVolume);
        mVolumeMap.put(device, storeVolume);
        pref.putInt(device.getAddress(), storeVolume);

        // Always use apply() since it is asynchronous, otherwise the call can hang waiting for
        // storage to be written.
        pref.apply();
    }

    int getVolume(@NonNull BluetoothDevice device, int defaultValue) {
        if (!mVolumeMap.containsKey(device)) {
            Log.w(TAG, "getVolume: Couldn't find volume preference for device: " + device);
            return defaultValue;
        }

        d("getVolume: Returning volume " + mVolumeMap.get(device));
        return mVolumeMap.get(device);
    }

    synchronized void deviceConnected(@NonNull BluetoothDevice device, boolean absoluteVolume) {
        d("deviceConnected: device=" + device + " absoluteVolume=" + absoluteVolume);

@@ -198,17 +152,23 @@ class AvrcpVolumeManager extends AudioDeviceCallback {
            return;
        }

        // Store the previous volume if a device was active.
        if (mCurrentDevice != null) {
            storeVolume();
        }

        // Wait until AudioManager informs us that the new device is connected
        mDeferredDevice = device;
        mCurrentDevice = device;

        // If device is null, that means that there is no active Bluetooth device
        if (device == null) {
            mCurrentDevice = null;
            return;
        }

        // If the device is connected then switch the device which informs audio manager that
        // absolute volume is supported and set the volume for the device. Otherwise disable
        // absolute volume until the new device connects to prevent sending unattenuated audio.
        if (mDeviceMap.containsKey(device)) {
            switchVolumeDevice(device);
        } else {
            d("volumeDeviceSwitched: Set Absolute Volume support to false until device connects.");
            mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(), false);
        }
    }

@@ -220,7 +180,7 @@ class AvrcpVolumeManager extends AudioDeviceCallback {
    public void dump(StringBuilder sb) {
        sb.append("AvrcpVolumeManager:\n");
        sb.append("  mCurrentDevice: " + mCurrentDevice + "\n");
        sb.append("  mDeferredDevice: " + mDeferredDevice + "\n");
        sb.append("  Current System Volume: " + mAudioManager.getStreamVolume(STREAM_MUSIC) + "\n");
        sb.append("  Device Volume Memory Map:\n");
        sb.append(String.format("    %-17s : %-14s : %3s : %s\n",
                "Device Address", "Device Name", "Vol", "AbsVol"));