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

Commit d9cd008e authored by Ajay Panicker's avatar Ajay Panicker Committed by Android (Google) Code Review
Browse files

Merge "Add field to set A2DP device volume on connect (2/2)" into pi-dev

parents dd69a067 4e243ee3
Loading
Loading
Loading
Loading
+18 −11
Original line number Diff line number Diff line
@@ -438,6 +438,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;
@@ -455,7 +460,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 "
@@ -498,11 +503,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.
@@ -806,14 +821,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"));