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

Commit 2481379b authored by Vlad Popa's avatar Vlad Popa
Browse files

Use AudioDeviceAttributes for managing volumes

Before this change we were using the device type for volume changes.
This can become a problem when dealing with the device behaviour of
different devices. A device type can have multiple behaviours when
registering through the AudioDeviceVolumeManager API

Bug: 393657380
Test: atest AudioDeviceVolumeManagerTest, VolumeHelperTest, AbsoluteVolumeBehaviorTest
Flag: android.media.audio.unify_absolute_volume_management

Change-Id: Iff618f36bc89d208d3a376d80381e76316835ffb
parent c1e8d636
Loading
Loading
Loading
Loading
+14 −0
Original line number Original line Diff line number Diff line
@@ -331,6 +331,20 @@ public final class AudioDeviceAttributes implements Parcelable {
                + " descriptors:" + mAudioDescriptors.toString());
                + " descriptors:" + mAudioDescriptors.toString());
    }
    }


    /**
     * @hide
     * Creates a copy of an AudioDeviceAttributes that only stores the device type, role and
     * address.
     *
     * <p>It's recommended to use this method when comparing instances with equals (e.g.: when
     * using them as keys to maps) to recognize a given device based on type and address,
     * ignoring any other fields such as audio descriptors and profiles.
     * @return a new AudioDeviceAttributes from type and address of the original one
     */
    public AudioDeviceAttributes createFromTypeAndAddress() {
        return new AudioDeviceAttributes(mNativeType, mAddress);
    }

    @Override
    @Override
    public int describeContents() {
    public int describeContents() {
        return 0;
        return 0;
+107 −49
Original line number Original line Diff line number Diff line
@@ -935,7 +935,8 @@ public class AudioService extends IAudioService.Stub
    // For possible volume behaviors, see
    // For possible volume behaviors, see
    // {@link AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior}.
    // {@link AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior}.
    @GuardedBy("mAbsoluteVolumeDeviceInfoMapLock")
    @GuardedBy("mAbsoluteVolumeDeviceInfoMapLock")
    Map<Integer, AbsoluteVolumeDeviceInfo> mAbsoluteVolumeDeviceInfoMap = new ArrayMap<>();
    Map<AudioDeviceAttributes, AbsoluteVolumeDeviceInfo> mAbsoluteVolumeDeviceInfoMap =
            new ArrayMap<>();
    /**
    /**
     * Stores information about a device using absolute volume behavior.
     * Stores information about a device using absolute volume behavior.
@@ -991,8 +992,7 @@ public class AudioService extends IAudioService.Stub
        @Override
        @Override
        public void binderDied() {
        public void binderDied() {
            if (mParent.removeAudioSystemDeviceOutFromAbsVolumeDevices(mDevice.getInternalType())
            if (mParent.removeFromAbsoluteVolumeDevices(mDevice) != null) {
                    != null) {
                mParent.dispatchDeviceVolumeBehavior(mDevice,
                mParent.dispatchDeviceVolumeBehavior(mDevice,
                        AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE);
                        AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE);
            }
            }
@@ -4149,9 +4149,15 @@ public class AudioService extends IAudioService.Stub
        int oldIndex = getVssForStreamOrDefault(streamType).getIndex(deviceType);
        int oldIndex = getVssForStreamOrDefault(streamType).getIndex(deviceType);
        // Check if the volume adjustment should be handled by an absolute volume controller instead
        // Check if the volume adjustment should be handled by an absolute volume controller instead
        if (isAbsoluteVolumeDevice(deviceType)
        boolean isAbsoluteVolumeDevice = unifyAbsoluteVolumeManagement() ? isAbsoluteVolumeDevice(
                && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
                deviceAttr) : isAbsoluteVolumeDevice(deviceType);
            final AbsoluteVolumeDeviceInfo info = getAbsoluteVolumeDeviceInfo(deviceType);
        if (isAbsoluteVolumeDevice && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
            final AbsoluteVolumeDeviceInfo info;
            if (unifyAbsoluteVolumeManagement()) {
                info = getAbsoluteVolumeDeviceInfo(deviceAttr);
            } else {
                info = getAbsoluteVolumeDeviceInfo(deviceType);
            }
            if (info != null && info.mHandlesVolumeAdjustment) {
            if (info != null && info.mHandlesVolumeAdjustment) {
                dispatchAbsoluteVolumeAdjusted(streamType, info, oldIndex, direction,
                dispatchAbsoluteVolumeAdjusted(streamType, info, oldIndex, direction,
                        keyEventMode);
                        keyEventMode);
@@ -4279,14 +4285,20 @@ public class AudioService extends IAudioService.Stub
    }
    }
    private boolean handleAbsoluteVolume(int streamType, int streamTypeAlias,
    private boolean handleAbsoluteVolume(int streamType, int streamTypeAlias,
            AudioDeviceAttributes deviceAttr, int newIndex, int flags) {
            @NonNull AudioDeviceAttributes ada, int newIndex, int flags) {
            // Check if volume update should be handled by an external volume controller
            // Check if volume update should be handled by an external volume controller
        boolean registeredAsAbsoluteVolume = false;
        boolean registeredAsAbsoluteVolume = false;
        boolean volumeHandled = false;
        boolean volumeHandled = false;
        int deviceType = deviceAttr.getInternalType();
        int deviceType = ada.getInternalType();
        if (isAbsoluteVolumeDevice(deviceType)
        boolean isAbsoluteVolume = unifyAbsoluteVolumeManagement() ? isAbsoluteVolumeDevice(ada)
                && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
                : isAbsoluteVolumeDevice(deviceType);
            final AbsoluteVolumeDeviceInfo info = getAbsoluteVolumeDeviceInfo(deviceType);
        if (isAbsoluteVolume && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
            final AbsoluteVolumeDeviceInfo info;
            if (unifyAbsoluteVolumeManagement()) {
                info = getAbsoluteVolumeDeviceInfo(ada);
            } else {
                info = getAbsoluteVolumeDeviceInfo(deviceType);
            }
            if (info != null) {
            if (info != null) {
                dispatchAbsoluteVolumeChanged(streamType, info, newIndex);
                dispatchAbsoluteVolumeChanged(streamType, info, newIndex);
                registeredAsAbsoluteVolume = true;
                registeredAsAbsoluteVolume = true;
@@ -4665,7 +4677,8 @@ public class AudioService extends IAudioService.Stub
        final AudioDeviceAttributes currDevAttr = getDeviceAttributesForStream(streamType);
        final AudioDeviceAttributes currDevAttr = getDeviceAttributesForStream(streamType);
        final boolean skipping =
        final boolean skipping =
                (currDevAttr.getInternalType() == ada.getInternalType()) || (vss == null);
                (unifyAbsoluteVolumeManagement() ? currDevAttr.equalTypeAddress(ada)
                        : currDevAttr.getInternalType() == ada.getInternalType()) || (vss == null);
        AudioService.sVolumeLogger.enqueue(new DeviceVolumeEvent(streamType, index, ada,
        AudioService.sVolumeLogger.enqueue(new DeviceVolumeEvent(streamType, index, ada,
                currDevAttr.getInternalType(), callingPackage, skipping));
                currDevAttr.getInternalType(), callingPackage, skipping));
@@ -5208,8 +5221,7 @@ public class AudioService extends IAudioService.Stub
        if (absVolumeDevices.size() > 1) {
        if (absVolumeDevices.size() > 1) {
            Slog.w(TAG, "onUpdateContextualVolumes too many active devices: "
            Slog.w(TAG, "onUpdateContextualVolumes too many active devices: "
                    + absVolumeDevices.stream().map(AudioDeviceAttributes::toString)
                    + absVolumeDevices.stream().map(AudioDeviceAttributes::toString)
                    .collect(Collectors.joining(","))
                    .collect(Collectors.joining(",")) + ", for stream: " + streamType);
                    + ", for stream: " + streamType);
            return;
            return;
        }
        }
@@ -8289,8 +8301,13 @@ public class AudioService extends IAudioService.Stub
        if (register) {
        if (register) {
            AbsoluteVolumeDeviceInfo info = new AbsoluteVolumeDeviceInfo(this,
            AbsoluteVolumeDeviceInfo info = new AbsoluteVolumeDeviceInfo(this,
                    device, volumes, cb, handlesVolumeAdjustment, deviceVolumeBehavior);
                    device, volumes, cb, handlesVolumeAdjustment, deviceVolumeBehavior);
            final AbsoluteVolumeDeviceInfo oldInfo = getAbsoluteVolumeDeviceInfo(deviceOut);
            AbsoluteVolumeDeviceInfo oldInfo;
            addAudioSystemDeviceOutToAbsVolumeDevices(deviceOut, info);
            if (unifyAbsoluteVolumeManagement()) {
                oldInfo = getAbsoluteVolumeDeviceInfo(device);
            } else {
                oldInfo = getAbsoluteVolumeDeviceInfo(deviceOut);
            }
            addToAbsoluteVolumeDevices(device, info);
            boolean volumeBehaviorChanged = (oldInfo == null)
            boolean volumeBehaviorChanged = (oldInfo == null)
                    || (oldInfo.mDeviceVolumeBehavior != deviceVolumeBehavior);
                    || (oldInfo.mDeviceVolumeBehavior != deviceVolumeBehavior);
@@ -8322,8 +8339,7 @@ public class AudioService extends IAudioService.Stub
                }
                }
            }
            }
        } else {
        } else {
            AbsoluteVolumeDeviceInfo deviceInfo = removeAudioSystemDeviceOutFromAbsVolumeDevices(
            AbsoluteVolumeDeviceInfo deviceInfo = removeFromAbsoluteVolumeDevices(device);
                    deviceOut);
            if (deviceInfo != null) {
            if (deviceInfo != null) {
                deviceInfo.unlinkToDeath();
                deviceInfo.unlinkToDeath();
                dispatchDeviceVolumeBehavior(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE);
                dispatchDeviceVolumeBehavior(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE);
@@ -8363,10 +8379,15 @@ public class AudioService extends IAudioService.Stub
            return;
            return;
        }
        }
        setDeviceVolumeBehaviorInternal(device, deviceVolumeBehavior, pkgName);
        // sanitize the device to contain only type and address
        final AudioDeviceAttributes sanitizedDevice = new AudioDeviceAttributes(
                device.getInternalType(), device.getAddress());
        setDeviceVolumeBehaviorInternal(sanitizedDevice, deviceVolumeBehavior, pkgName);
        persistDeviceVolumeBehavior(device.getInternalType(), deviceVolumeBehavior);
        persistDeviceVolumeBehavior(device.getInternalType(), deviceVolumeBehavior);
    }
    }
    @SuppressLint("ShortCircuitBoolean")  // we need to execute all or statements
    private void setDeviceVolumeBehaviorInternal(@NonNull AudioDeviceAttributes device,
    private void setDeviceVolumeBehaviorInternal(@NonNull AudioDeviceAttributes device,
            @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior,
            @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior,
            @NonNull String caller) {
            @NonNull String caller) {
@@ -8378,22 +8399,19 @@ public class AudioService extends IAudioService.Stub
                volumeBehaviorChanged |=
                volumeBehaviorChanged |=
                        removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut)
                        removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut)
                        | removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut)
                        | removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut)
                        | (removeAudioSystemDeviceOutFromAbsVolumeDevices(audioSystemDeviceOut)
                        | (removeFromAbsoluteVolumeDevices(device) != null);
                                != null);
                break;
                break;
            case AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED:
            case AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED:
                volumeBehaviorChanged |=
                volumeBehaviorChanged |=
                        removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut)
                        removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut)
                        | addAudioSystemDeviceOutToFixedVolumeDevices(audioSystemDeviceOut)
                        | addAudioSystemDeviceOutToFixedVolumeDevices(audioSystemDeviceOut)
                        | (removeAudioSystemDeviceOutFromAbsVolumeDevices(audioSystemDeviceOut)
                        | (removeFromAbsoluteVolumeDevices(device) != null);
                                != null);
                break;
                break;
            case AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL:
            case AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL:
                volumeBehaviorChanged |=
                volumeBehaviorChanged |=
                        addAudioSystemDeviceOutToFullVolumeDevices(audioSystemDeviceOut)
                        addAudioSystemDeviceOutToFullVolumeDevices(audioSystemDeviceOut)
                        | removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut)
                        | removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut)
                        | (removeAudioSystemDeviceOutFromAbsVolumeDevices(audioSystemDeviceOut)
                        | (removeFromAbsoluteVolumeDevices(device) != null);
                                != null);
                break;
                break;
            case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
            case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
            case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
            case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
@@ -8454,17 +8472,15 @@ public class AudioService extends IAudioService.Stub
        if (mAbsVolumeMultiModeCaseDevices.contains(audioSystemDeviceOut)) {
        if (mAbsVolumeMultiModeCaseDevices.contains(audioSystemDeviceOut)) {
            return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
            return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
        }
        }
        // sanitize the device to contain only type and address
        final AudioDeviceAttributes sanitizedDevice = device.createFromTypeAndAddress();
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
            if (mAbsoluteVolumeDeviceInfoMap.containsKey(audioSystemDeviceOut)) {
            final AbsoluteVolumeDeviceInfo deviceInfo = mAbsoluteVolumeDeviceInfoMap.get(
            final AbsoluteVolumeDeviceInfo deviceInfo = mAbsoluteVolumeDeviceInfoMap.get(
                        audioSystemDeviceOut);
                    sanitizedDevice);
            if (deviceInfo != null) {
            if (deviceInfo != null) {
                return deviceInfo.mDeviceVolumeBehavior;
                return deviceInfo.mDeviceVolumeBehavior;
            }
            }
                Log.e(TAG,
                        "Null absolute volume device info stored for key " + audioSystemDeviceOut);
            }
        }
        }
        if (isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)
        if (isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)
@@ -12837,7 +12853,7 @@ public class AudioService extends IAudioService.Stub
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
            return mAbsoluteVolumeDeviceInfoMap.entrySet().stream()
            return mAbsoluteVolumeDeviceInfoMap.entrySet().stream()
                    .filter(entry -> entry.getValue().mDeviceVolumeBehavior == behavior)
                    .filter(entry -> entry.getValue().mDeviceVolumeBehavior == behavior)
                    .map(Map.Entry::getKey)
                    .map(entry -> entry.getKey().getInternalType())
                    .collect(Collectors.toSet());
                    .collect(Collectors.toSet());
        }
        }
    }
    }
@@ -15438,7 +15454,25 @@ public class AudioService extends IAudioService.Stub
    @Nullable
    @Nullable
    private AbsoluteVolumeDeviceInfo getAbsoluteVolumeDeviceInfo(int deviceType) {
    private AbsoluteVolumeDeviceInfo getAbsoluteVolumeDeviceInfo(int deviceType) {
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
            return mAbsoluteVolumeDeviceInfoMap.get(deviceType);
            Optional<AbsoluteVolumeDeviceInfo> info =
                    mAbsoluteVolumeDeviceInfoMap.entrySet().stream().filter(
                            entry -> entry.getKey().getInternalType() == deviceType).map(
                            Map.Entry::getValue).findFirst();
            return info.orElse(null);
        }
    }
    /**
     * Returns the input device which uses absolute volume behavior, including its variants,
     * or {@code null} if there is no mapping for the AudioDeviceAttributes.
     *
     * @param device the simplified attributes continaing onlye address and type
     */
    @Nullable
    private AbsoluteVolumeDeviceInfo getAbsoluteVolumeDeviceInfo(AudioDeviceAttributes device) {
        final AudioDeviceAttributes ada = device.createFromTypeAndAddress();
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
            return mAbsoluteVolumeDeviceInfoMap.get(ada);
        }
        }
    }
    }
@@ -15452,7 +15486,8 @@ public class AudioService extends IAudioService.Stub
    private boolean isAbsoluteVolumeDevice(int deviceType) {
    private boolean isAbsoluteVolumeDevice(int deviceType) {
        boolean hasAbsoluteVolumeDeviceKey;
        boolean hasAbsoluteVolumeDeviceKey;
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
            hasAbsoluteVolumeDeviceKey = mAbsoluteVolumeDeviceInfoMap.containsKey(deviceType);
            hasAbsoluteVolumeDeviceKey = mAbsoluteVolumeDeviceInfoMap.entrySet().stream().anyMatch(
                    entry -> entry.getKey().getInternalType() == deviceType);
        }
        }
        if (unifyAbsoluteVolumeManagement() && hasAbsoluteVolumeDeviceKey) {
        if (unifyAbsoluteVolumeManagement() && hasAbsoluteVolumeDeviceKey) {
            return true;
            return true;
@@ -15465,6 +15500,27 @@ public class AudioService extends IAudioService.Stub
        }
        }
    }
    }
    /**
     * Returns whether the input device uses absolute volume behavior, including its variants.
     * For included volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
     */
    private boolean isAbsoluteVolumeDevice(AudioDeviceAttributes ada) {
        boolean hasAbsoluteVolumeDeviceKey;
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
            hasAbsoluteVolumeDeviceKey = mAbsoluteVolumeDeviceInfoMap.containsKey(
                    ada.createFromTypeAndAddress());
        }
        if (unifyAbsoluteVolumeManagement() && hasAbsoluteVolumeDeviceKey) {
            return true;
        } else {
            return hasAbsoluteVolumeDeviceKey
                    || isA2dpAbsoluteVolumeDevice(ada.getInternalType())
                    || AudioSystem.isLeAudioDeviceType(ada.getInternalType())
                    || ada.getInternalType() == AudioSystem.DEVICE_OUT_HEARING_AID
                    || ada.getInternalType() == AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
        }
    }
    /**
    /**
     * Returns whether the input device is a Bluetooth A2dp device that uses absolute volume
     * Returns whether the input device is a Bluetooth A2dp device that uses absolute volume
     * behavior. This is distinct from the general implementation of absolute volume behavior
     * behavior. This is distinct from the general implementation of absolute volume behavior
@@ -15476,7 +15532,8 @@ public class AudioService extends IAudioService.Stub
    private boolean isHdmiAbsoluteVolumeDevice(int deviceType) {
    private boolean isHdmiAbsoluteVolumeDevice(int deviceType) {
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
            return mAbsoluteVolumeDeviceInfoMap.containsKey(deviceType)
            return mAbsoluteVolumeDeviceInfoMap.entrySet().stream().anyMatch(
                    entry -> entry.getKey().getInternalType() == deviceType)
                    && (deviceType == AudioSystem.DEVICE_OUT_HDMI
                    && (deviceType == AudioSystem.DEVICE_OUT_HDMI
                    || deviceType == AudioSystem.DEVICE_OUT_HDMI_ARC
                    || deviceType == AudioSystem.DEVICE_OUT_HDMI_ARC
                    || deviceType == AudioSystem.DEVICE_OUT_HDMI_EARC);
                    || deviceType == AudioSystem.DEVICE_OUT_HDMI_EARC);
@@ -15575,33 +15632,34 @@ public class AudioService extends IAudioService.Stub
        return mFullVolumeDevices.remove(audioSystemDeviceOut);
        return mFullVolumeDevices.remove(audioSystemDeviceOut);
    }
    }
    private void addAudioSystemDeviceOutToAbsVolumeDevices(int audioSystemDeviceOut,
    private void addToAbsoluteVolumeDevices(AudioDeviceAttributes ada,
            AbsoluteVolumeDeviceInfo info) {
            AbsoluteVolumeDeviceInfo info) {
        if (info == null) {
        if (info == null) {
            Log.e(TAG, "Cannot add null absolute volume info for audioSystemDeviceOut "
            Log.e(TAG, "Cannot add null absolute volume info for ada " + ada);
                    + audioSystemDeviceOut);
            return;
            return;
        }
        }
        final AudioDeviceAttributes adaAsKey = ada.createFromTypeAndAddress();
        if (DEBUG_VOL) {
        if (DEBUG_VOL) {
            Log.d(TAG, "Adding DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
            Log.d(TAG, "Adding device: " + adaAsKey
                    + " to mAbsoluteVolumeDeviceInfoMap with behavior "
                    + " to mAbsoluteVolumeDeviceInfoMap with behavior "
                    + AudioDeviceVolumeManager.volumeBehaviorName(info.mDeviceVolumeBehavior)
                    + AudioDeviceVolumeManager.volumeBehaviorName(info.mDeviceVolumeBehavior)
            );
            );
        }
        }
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
            mAbsoluteVolumeDeviceInfoMap.put(audioSystemDeviceOut, info);
            mAbsoluteVolumeDeviceInfoMap.put(adaAsKey, info);
        }
        }
    }
    }
    private AbsoluteVolumeDeviceInfo removeAudioSystemDeviceOutFromAbsVolumeDevices(
    private AbsoluteVolumeDeviceInfo removeFromAbsoluteVolumeDevices(
            int audioSystemDeviceOut) {
            AudioDeviceAttributes ada) {
        final AudioDeviceAttributes deviceOut = ada.createFromTypeAndAddress();
        if (DEBUG_VOL) {
        if (DEBUG_VOL) {
            Log.d(TAG, "Removing DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
            Log.d(TAG, "Removing device: " + deviceOut + " from mAbsoluteVolumeDeviceInfoMap");
                    + " from mAbsoluteVolumeDeviceInfoMap");
        }
        }
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
        synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
            return mAbsoluteVolumeDeviceInfoMap.remove(audioSystemDeviceOut);
            return mAbsoluteVolumeDeviceInfoMap.remove(deviceOut);
        }
        }
    }
    }